ChatGPT로 네이티브 지식없이react-native-cookies 패키지 Android 버그 해결하기

SeokyoungYou
오토피디아
Published in
20 min readJun 28, 2023

안녕하세요. 오토피디아에서 프론트엔드 개발을 맡고 있는 유서경입니다. 이번 글은 네이티브(Android, iOS) 이해도가 높지 않은 웹 개발자가 ChatGPT를 활용한 Android 코드 수정으로 WebView 컴포넌트 내 자동 로그인을 구축한 과정을 담았습니다. 사실 저는 이 글을 작성하기 이전까지 AI 기술에 흥미가 적은 편이어서 능동적으로 활용해보지 않고 있었습니다. 그러나 최근 개인적으로 난도가 높다고 생각한 버그를 빠른 시간 내 수정해야했고, 사내 개발자 분들이 실제 업무에 ChatGPT를 활용하여 도움을 받는 모습들을 발견하면서 저도 사용해보게 되었습니다. 약 10일동안 OpenAI의 (유료) ChatGPT 서비스를 사용하면서 제가 느꼈던 개발 스코프의 확장을 상세한 예시로 설명한 후, 유용한 크롬 익스텐션들을 소개하며 저만의 활용법도 함께 공유해보겠습니다.

0. 들어가며

오토피디아의 닥터차 서비스는 웹과 앱 모두 지원하고 있으며 저는 주로 NextJS 프레임워크를 활용한 웹 프론트엔드 개발을 담당하고 있습니다. 최근 닥터차에서는 빠르게 새로운 기능들을 출시하고 있으며, 이 글은 6월 자동차세를 납부 기간을 맞아 진행된 자동차세 예상환급액 조회 프로젝트 개발기를 다루고 있습니다. 이 프로젝트는 신규 회원 유입을 목표로 하여 웹 우선 개발 후, 이벤트 랜딩 페이지를 앱 내 WebView로 연동하기로 되어있었습니다. 기존에는 이미 구현된 WebView 스크린을 사용하여 웹 url을 띄우면 쉽게 완료되는 일이었지만, 이번 프로젝트에서는 로그인을 통한 사용자 정보가 추가로 요구되었기 때문에 앱의 로그인 정보를 사용하여 웹뷰 내에서 자동 로그인을 짧은 기간 내에 구현해야 했습니다.

1. WebView 자동 로그인 구현을 위한 react-native-cookies 패키지 버그 해결하기

(좌 Fig 1–1.) 닥터차 웹의 로그인 화면 (우 Fig 1–2.) 닥터차 앱의 “자동차세 환급 랜딩” 웹뷰 화면
Fig 1–3. 웹 브라우저 Cookies 저장소에 담긴 refreshToken

닥터차 웹에서는 로그인 화면(Fig 1–1.)에서 소셜 인증으로 얻은 refreshToken을 사용하여 로그인이 이루어집니다. 그러나 이번 프로젝트에서는 로그인 화면없이 랜딩 화면(Fig 1–2.)에서 앱의 사용자 정보를 이용하여 바로 자동 로그인을 구현하여 앱 사용성을 높여야 했습니다. 개발 측면에서 단순화하자면 웹의 쿠키 저장소에refreshToken을 설정(Fig 1–3.)해둔 것과 동일하게 앱에서도 WebView 컴포넌트에 쿠키 설정이 필요했습니다. 다행히도 react-native-cookies 패키지를 활용하면 아래 코드와 같이 손쉽게 WebView에 쿠키를 세팅할 수 있다고 합니다.

import { WebView } from 'react-native-webview';
import CookieManager from '@react-native-cookies/cookies';
...

<WebView
source={{ uri: `${env.WEB_ENDPOINT}/vehicle-tax-refund` }} // 랜딩 페이지 url
sharedCookiesEnabled={true}
onLoadEnd={() => {
CookieManager.set(env.WEB_ENDPOINT, { // cookie를 설정한 도메인
name: 'refreshToken', // 쿠키 이름
value: refreshToken,
});
}}
/>

하지만 이 코드는 iOS에서는 잘 동작하지만 Android에서는 작동하지 않는 버그가 있습니다. 그리고 안타깝게도 최근에도 패키지 상에서 버그가 해결되지 않은 것으로 보입니다.

react-native-cookies 패키지의 안드로이드 버그

1.1 ChatGPT와 함께 Android에서 발생하는 버그 해결하기

이 문제를 해결하기 위해 유료 결제한 ChatGPT 4.0을 활용해 볼 시간입니다. 편의상 질문과 답변을 한국어로 간단히 요약하였고, 저의 질문은 이탤릭체로 표기하였습니다. ChatGPT의 실제 답변과 상세한 코드를 확인하시려면 오토피디아 블로그를 확인해주세요.

1.1.1 이미 알고있는 지식을 활용하여 문제 해결 방법 물어보기

먼저, ChatGPT에게 버그가 있음을 알리고 제가 예상하는 방법으로 해결이 가능한지 물어보았습니다.

react-native-cookies 패키지의 Android OS 버그가 있는데, 패키지를 직접 수정하거나 injectJavaScript를 활용하여 해결할 수 있을까?

GPT: WebView에서 쿠키를 설정하는 것은 플랫폼에 따라 차이가 존재할 수 있기 때문에 Android에서 이슈가 존재할 수도 있습니다. WebView의 injectJavaScript 메소드를 사용하여 쿠키를 세팅해보세요.

import React, { useRef, useEffect } from 'react';
import { WebView } from 'react-native-webview';

const YourComponent = () => {
const webViewRef = useRef(null);

useEffect(() => {
if (webViewRef.current) {
webViewRef.current.injectJavaScript(`document.cookie = "key=value; expires=Fri, 31 Dec 9999 23:59:59 GMT";`);
}
}, []);

return (
<WebView
ref={webViewRef}
source={{ uri: 'https://your-website.com' }}
/>
);
};

제가 제시한 해결책 중 하나를 선정하여 코드를 작성해주었습니다. 그러나 제가 열어야할 웹뷰 uri 와 쿠키가 세팅될 도메인의 url이 달라 추가 설정이 필요하므로 이에 대한 해결 방법을 질문하였습니다.

고마워(실제로 공손한 말투에 더 유용한 답변을 준다고 합니다.) 그런데 나는 웹뷰는 source = {{ uri : “https//my-website.com/another/pathname }}로 쿠키는 “https//my-website.com”로 세팅하고 싶어.

GPT: 웹뷰가 로딩되기 전에 특정 URL에 쿠키를 설정하고 리다이렉션을 시킬 수 있습니다. 그러나 브라우저 보안 설정에 따라 쿠키 설정 제약이 있을 수 있고 웹페이지 로딩에 시간이 오래 걸리는 경우 이 방법이 최선이 아닐 수 있습니다.

import React, { useRef } from 'react';
import { WebView } from 'react-native-webview';

const YourComponent = () => {
const webViewRef = useRef(null);

const handleNavigationStateChange = (navState) => {
if (navState.url === 'https://my-website.com' && !navState.loading) {
const INJECTED_CODE = `
document.cookie = "key=value; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
window.ReactNativeWebView.postMessage('navigationStateChange');
`;
webViewRef.current?.injectJavaScript(INJECTED_CODE);
} else if (navState.url === 'https://my-website.com' && navState.loading) {
webViewRef.current?.injectJavaScript(`window.location.href = "https://my-website.com/another/pathname";`);
}
};

return (
<WebView
ref={webViewRef}
source={{ uri: 'https://my-website.com' }}
onNavigationStateChange={handleNavigationStateChange}
/>
);
};

export default YourComponent;

1.1.2 새로운 해결책, I apologize for…의 반복, 그리고 해결

그러나 위 방법도 여전히 Android에서만 버그가 발생하였습니다.

여전히 버그가 존재하는데 Android 네이티브 패키지 수정과 같은 다른 방법으로 이 문제를 해결할 수 있을까?

GPT: 2021년 9월의 지식 기준으로, Android의 쿠키 설정 문제는 이미 이슈로 알려져있습니다. Android는 각 웹뷰간 자체 쿠키 저장소가 있어 이를 웹뷰 간 혹은 앱 HTTP 클라이언트와 쿠키를 공유할 수 있는 방법이 없습니다. 하지만 네이티브 Andorid 코드를 수정하여 쿠키를 설정할 수 있습니다.

// android/app/src/main/java/com/[YourAppName]/MainActivity.java

import android.webkit.CookieManager;
import android.webkit.WebView;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie("https://my-website.com", "key=value; Expires=Fri, 31 Dec 9999 23:59:59 GMT; Path=/");
cookieManager.flush();
}

위 답변을 이해하려면 React Native에 대한 간단한 지식이 필요합니다. React Native는 JavaScript 코드 베이스 하나로 Android, iOS 앱을 개발할 수 있는 크로스 플랫폼 프레임워크로 Native 사이드와 JavaScript 사이드는 Bridge로 연결되어 있습니다. ChatGPT는 JavaScript 패키지에서 버그가 발생하니 Android Native Module을 직접 수정하여 버그를 해결해보자고 한 것입니다.

출처: React Native: Bridging an Android Native Module For App Authentication

사실 이때까지만 해도 저는 Java 코드를 직접 짜본 적도 없으며 Android Studio도 에뮬레이터 실행 용도로만 사용하고 있었습니다. 그럼에도 불구하고 저에게 주어진 선택지가 적었기 때문에 ChatGPT를 믿고 Android 코드를 수정해보기로 합니다. 일단 ChatGPT는 Java 파일 내에서 쿠키 값(value)를 설정하고 있는데 저의 쿠키 값은 JavaScript 파일 내에 있습니다. 이 문제는 어떻게 해결해야 할까요?

나의 쿠키 값은 import EncryptedStorage from ‘react-native-encrypted-storage에 저장되어 있는데 Java 파일에서 JavaScript 코드로 어떻게 접근할 수 있을까?

GPT: React Native 애플리케이션의 컨텍스트에서 네이티브 Android 코드(Java 또는 Kotlin)와 JavaScript 코드는 서로 다른 런타임 환경에서 실행되므로 서로 직접 상호 작용할 수 없습니다. 하지만 React Native는 “bridging”이라는 메커니즘을 사용하여 이 두 환경 간에 통신할 수 있는 방법을 제공합니다. 네이티브 안드로이드 코드에서 EncryptedStorage의 데이터에 액세스하려면 이를 자바스크립트 코드에 노출하는 네이티브 모듈을 만들어야 합니다.

// android/app/src/main/java/com/[YourAppName]/CookieManagerModule.java

package com.yourappname; // replace with your actual app name

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

public class CookieManagerModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;

CookieManagerModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}

@Override
public String getName() {
return "CookieManager";
}

@ReactMethod
public void setCookie(String url, String cookie, Callback callback) {
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url, cookie);
cookieManager.flush();

// Notify JavaScript
callback.invoke(null);
}
}
// android/app/src/main/java/com/[YourAppName]/CookieManagerModule.java

package com.yourappname; // replace with your actual app name

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

public class CookieManagerModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;

CookieManagerModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}

@Override
public String getName() {
return "CookieManager";
}

@ReactMethod
public void setCookie(String url, String cookie, Callback callback) {
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url, cookie);
cookieManager.flush();

// Notify JavaScript
callback.invoke(null);
}
}

그런데 여기서 이상한 점이 있습니다. 분명 CookieManagerModule.java 파일을 만들어 CookieManagerModule 모듈을 구현했는데 MainActivity.java 파일에선는 CookieManagerPackage 를 가져다 사용하고 있습니다. 저는 이 코드가 틀렸다는 것을 금방 알아챘지만 Java로 작성된 Android 네이티브 코드를 제가 직접 수정하는 것은 생각보다 어려운 일이었습니다. 사실상 이 코드의 모든 디버깅과 구현은 이제ChatGPT의 몫이 되었습니다. 당연히 빌드 실행했을 때 에러가 발생했고 저는 해당 빌드 실패 메시지 전체를 긁어다 다시 ChatGPT에게 주었습니다.

터미널의 에러 메시지

error: cannot find symbol
packages.add(new CookieManagerPackage());
^
symbol: class CookieManagerPackage
...
FAILURE: Build completed with 2 failures.

GPT: 혼란을 드려 죄송합니다. 필요한 클래스를 가져오지 않은 것 같습니다. 또한 실수로 CookieManagerModule() 대신 CookieManagerPackage()를 작성했습니다. 이러한 문제를 수정하는 방법은 다음과 같습니다

이 이후로도 다수의 error: cannot find symbol Attempt to get length of null array app crash 등의 문제가 있었지만, ChatGPT는 트러블 슈팅을 위해 Android Studio에서 adb locat 을 사용하는 방법까지 친절히 알려주었습니다. 네이티브 Android 코드를 수정하며 제가 한 일은 ChatGPT가 작성해준 코드를 그대로 붙여넣고 에러가 발생한다면 해당 로그를 다시 긁어서 던져주는 작업의 반복이었습니다.

결국 3시간 동안 23번의 대화 끝에 저는 이 버그를 해결할 수 있었으며, Android에서도 쿠키를 설정하여 자동 로그인이 되도록 구현을 완료하였습니다. (가까스로 유료 버전 제한 내에 들었습니다: 2023.6 월 기준 GPT4 3시간 내 최대 25개 요청)

1.2 ChatGPT가 알려준 Android Native 패키지를 활용한 해결 방법

해결 방법에 대해 간단하게 정리하자면 Android에서는 JS 패키지(react-native-cookies)를 사용하지 않고 네이티브의 CookieManager를 활용하게 하면 됩니다. 네이티브 코드에 CookieManagerModule.javaCookieManagerPackage.java 파일을 추가하고, MainApplication.java 에서 해당 패키지를 사용할 수 있도록 코드를 추가합니다. 그리고 자바스크립트 파일에서 NativeModules 를 사용하여 Android의 CookieManager로 웹뷰에 쿠키를 설정하면 모든 것이 완료됩니다.

2. ChatGPT를 활용한 버그 수정 후기

대부분의 버그 수정도 마찬가지이지만 이번에도 글로 정리하고 보니 해결 방법이 단순하고 간결해보입니다. 그러나 이전의 개발 경험과의 차별점은 JavaScript 상 몇 줄의 코드를 제외하고 Java로 작성된 대부분의 코드는 제가 완벽히 이해하지도 그리고 이해할 필요도 없었다는 점입니다. 사실 크로스 플랫폼 앱을 만드는 React Native에서는 OS 별 버그가 종종 발생하며, 브릿지를 이용하여 네이티브 사이드와 통신하는 일도 다수 존재한다고 합니다. ChatGPT를 활용하지 않았다면 아마도 저는 자바스크립트 사이드에서만 버그를 해결하려 했을 것이며, 버그 수정도 상당히 힘들었을 것이라 생각합니다.

2.1 좋았던 점

제가 개발자라는 직업을 좋아하는 이유 중 하나는 문제 해결 과정과 솔루션을 커뮤니티에 올려 모두에게 공유한다는 점입니다. 다른 개발자가 실패를 답습하지 않도록 해결 방법을 올려 그대로 활용할 수 있게하여 서로 높은 생산성을 가져가는 문화는 저에게 잘 맞습니다. 긍정적인 의미가 아닐 수도 있지만 ChatGPT는 이러한 **Copy-and-paste programming**을 아주 쉽고 빠르게 도와줍니다. 웹 개발자인 저는 네이티브 코드를 한 번도 작성해 본 적이 없지만 ChatGPT가 만들어준 코드를 추상적으로 이해하며 문제를 해결할 수 있었고, 저의 개발 스코프는 웹에서 앱까지 확장될 수 있었습니다. 이 외에도 여러 환경에 대한 지식이 필요한 웹의 console.log 를 앱의 웹뷰에서 그대로 로깅해야 하는 기능도 ChatGPT를 사용하여 손쉽게 구현할 수 있었습니다.

2.2 실망한 점

하지만 ChatGPT가 마냥 긍정적인 결과만 주는 것은 아닙니다. 이미 버그 해결 과정에서도 언급되었지만 ChatGPT는 한 번에 정답을 내놓지 않고 심지어 오답을 주는 경우도 있습니다. 우리가 원하는 결과를 얻기까지 에러 메시지를 던져주며 질문을 고도화해야 하며 여러 번 오답을 수정해주어야 합니다. (사과에 사용하는 영어 표현 “My apologies for the confusion. I apologize for the inconvenience you’re facing.”을 여러 번 마주칠 수 있습니다.)

또한, 새로운 기능을 구현하는 것에는 큰 도움을 주지만 제가 작성한 코드를 디버깅하기에는 아직까지 무리가 있습니다. 방대한 코드 저장소 전체를 ChatGPT에게 던져주지 않는 한 단순한 에러 메시지만으로는 버그 수정이 어려운 경우가 간혹 있었지만, 버그를 해결할 수 있는 실마리를 제공해주는 “Assistant”의 역할은 잘 수행해낸다고 생각합니다. 제가 만들어낸 앱이 backgroud → foreground로 변경될 때 WebView 스크린이 새로고침되는 버그에 대해 직접적이지는 않지만 “url이 변경되면 WebView가 새로고침된다”는 힌트를 얻었고 해당 문제가 있는 코드를 찾아낸 수 있었습니다.

마치며

기술에 변화에 빠르게 적응하는 개발자가 되고싶지만 매번 모든 기술을 사용해보기는 어려운게 사실입니다. 개인적 선호도가 낮아 AI 서비스를 활용하지 않고 있었지만, 이번 업무에 ChatGPT를 활용해보면서 서비스 유무에 따른 정보의 격차가 상당할 것이라는 생각이 들었습니다. 아직 검색 플랫폼에 익숙하여 개발 외적으로는 잘 사용해보지 않았지만 이제는 신뢰도가 높아져 많이 활용해 볼 예정입니다. 앞으로 출시될 AI 서비스들을 더욱 기대하며 업무 효율성과 삶의 생산성을 더 높여주기를 기대해봅니다.

더 자세한 버그 수정기와 해결 방법 그리고 유용한 ChatGPT 크롬 익스텐션을 확인하고 싶으시다면 오토피디아 블로그를 방문해주세요!

Reference

--

--