안녕하세요~! 오늘은 회사에서 겪었던 🍪쿠키🍪 관련 이슈를 다뤄보려고 합니다.
현재 회사 앱에서는 사용자가 로그인 시 자동 로그인 여부를 선택할 수 있습니다.
자동 로그인을 선택하지 않은 경우, 앱을 재시작하면 로그아웃 상태로 앱에 진입하게 되는 플로우인데요.
이 과정에서 🍪쿠키🍪 관련 문제가 발생했습니다.
앱에서 로그인을 하면 서버로부터 set-cookie
를 통해 🍪쿠키🍪가 설정됩니다.
문제는 자동 로그인을 선택하지 않은 사용자가 앱을 재시작했을 때 발생합니다.
이 경우, request header에 이전에 설정된 🍪쿠키🍪가 그대로 남아있어, 비로그인 상태로 앱에 진입했음에도 불구하고 서버에서는 로그인 유저로 잘못 인식되는 상황이 생기게 됩니다.
특히 로그인 여부와 관계없이 공통적으로 호출되는 API에서 🍪쿠키🍪를 기반으로 유저를 로그인 상태로 인식하면서, 결과적으로 앱 화면은 로그아웃 상태인데 네트워크 요청은 로그인 상태로 처리되는 문제가 발생했습니다.
(🍪쿠키🍪 개념 및 흐름은 이전 포스팅을 참고해주세요.)
이 문제를 해결하기 위해 3가지의 방안을 고민했고, 그중 어떤 방안을 선택했는지 공유해 보려고 합니다.
🧐: 로그인 시 서버에서 set-cookie
로 🍪를 설정하고, 로그아웃 시에도 서버에서 🍪를 삭제하고 있으니 앱이 종료될 때도 서버에서 🍪를 삭제하도록 하면 되지 않을까?
일단 앱이 꺼졌다는 걸 앱에서도 감지할 수 없습니다. 그래서 소켓이 끊어진 경우를 앱이 종료되었다고 간주하려 하였습니다.
현재 앱에서 소켓을 활용해 다양한 이벤트를 처리하고 있는데요,
따라서 소켓의 disconnect 상태를 앱 종료로 간주해, 이 시점에 서버에서 🍪쿠키🍪를 삭제하면 문제를 해결할 수 있을 것 같았습니다.
(소켓을 통해 앱 여부 및 자동 로그인 여부를 파악할 수 있어 가능해 보였습니다.)
하지만...! 여기서 또 다른 문제가 있었습니다.
앱을 사용하다가 휴대폰을 잠그는 경우에도 소켓이 disconnect 되는 상황이 발생합니다.
폰을 다시 잠금 해제하고 앱에 진입하면 소켓이 재연결(connect)되긴 하지만,
이 경우라면 잠금 시점에서 🍪쿠키🍪를 삭제해야 하고, 재연결 시 다시 set-cookie
로 🍪쿠키🍪를 설정해야 하는 번거로움이 생깁니다.
이 방식은 복잡한 흐름을 초래하기 때문에 현실적으로 적합하지 않다고 판단했습니다.
해결방안 1 탈락
🧐: 자동 로그인을 해제한 상태에서 로그인했던 유저가 앱에 재접속 할 때, signout
API를 호출하여 서버에서 🍪를 삭제하게 하면 되지 않을까?
이 방법은 비효율적인 API 호출을 유발합니다. 또한, signout
API는 단순히 🍪쿠키🍪를 삭제하는 것 외에도 다른 기능을 포함하고 있을 수 있습니다.
현재 필요한 건 "앱에서 🍪쿠키🍪만 삭제" 하는 것이므로, 굳이 signout
API를 호출하지 않고, 🍪쿠키🍪를 삭제하는 기능만 별도로 구현하는 것이 더 적합하다고 판단했습니다.
해결방안 2 탈락
🧐: 자동 로그인을 해제한 상태에서 로그인했던 유저가 앱에 재접속 할 때, 앱에서 🍪를 지우면 되지 않을까?
해결 방안 1번과 2번을 제외하고 나니, 앱을 껐다 켜도 request header에 🍪쿠키🍪가 남아 있는 원인이 궁금해졌습니다. 결국, request header에 🍪쿠키🍪가 남아 있다는 것은 앱 내부 어딘가에 🍪쿠키🍪가 저장되어 있다는 뜻이었기 때문에 어디에서 남아 있는지를 확인해 보기로 했습니다.
바로바로...!
😎: React Native에서는
react-native/Libraries/Network/RCTNetworking
내부에서 네트워크 요청을 관리합니다. 이 모듈은 🍪의 영속 시간 동안 🍪를 캐싱하고 있는데, 이 곳에서 🍪가 남아있었습니다.
따라서 이 모듈에 접근하여, 자동 로그인을 선택하지 않은 사용자가 앱에 재접속했을 때 쿠키를 삭제하는 방법을 적용하기로 하였습니다.
const RCTNetworking = require("react-native/Libraries/Network/RCTNetworking").default
RCTNetworking.clearCookies()
해결 완...❗️❗️ 🤩🤩🤩 ❗️❗️❗️❗️
해결 방안3으로 결론 도출 중, 한 가지 의문이 들었습니다.
🧐: 이전에 로그인했던 기록을 찾아 로그인한 유저인 경우에 🍪를 지우는 것보다,
앱 진입 시 비로그인 상태인데 헤더에 🍪가 남아 있는 경우에 🍪를 삭제하는 방식이 더 확실하지 않을까?
이 생각을 바탕으로 React Native에서 🍪쿠키🍪를 가져오는 방법을 조사해 보았습니다.
찾아보니 서드파티 라이브러리를 사용하면 쿠키를 가져올 수 있었습니다.
다만, 지금 저에게 필요한 건 단순히 쿠키를 가져오는 기능뿐이었고, 굳이 라이브러리를 설치하기엔 배보다 배꼽이 더 큰 상황처럼 느껴졌습니다.
다음으로는 axios에서 쿠키를 가져오는 방법을 시도해 보았습니다.
하지만, axios로 interceptor를 통해 request header를 확인해 본 결과, 대부분의 헤더 정보는 출력되었지만 정작 Cookie
정보는 확인할 수 없었습니다.
(request header를 콘솔에 찍었더니 다음과 같았다.)
{
"Authorization": `Bearer ${token}`,
"common": {
"Accept": "application/json, text/plain, */*",
"Authorization": `Bearer ${token}`
},
"delete": {},
"get": {},
"head": {},
"patch": {
"Content-Type": "application/x-www-form-urlencoded"
},
"post": {
"Content-Type": "application/x-www-form-urlencoded"
},
"put": {
"Content-Type": "application/x-www-form-urlencoded"
}
}
🧐 이거 대체 왜이런가 하고 봤더니,
다음과 같은 이유가 있었습니다.
😎 : React Native는 기본적으로
RCTNetworking
모듈을 통해 네이티브 네트워크 요청을 처리합니다. axios에서withCredentials: true
설정을 활성화하면, 네이티브 계층에 저장된 🍪가 요청에 자동으로 추가됩니다.
이처럼 🍪는 네이티브 계층에서 자동으로 관리되기 때문에, 네트워크 탭에 보이는Cookie
헤더는 axios의 interceptor에서는 확인할 수 없습니다.
즉, 네이티브 계층에서 추가된 🍪는 axios 내부에서 직접 접근하거나 읽어들일 수 없는 구조입니다.
(혹시 이 외의 방법으로도 🍪를 볼 수 있는 방법을 아시는 분이 계신다면 댓글 달아주시면 감사하겠습니다! (_ _))
🥳: 이번 기회에 react native에서 🍪쿠키🍪를 지우는 방법에 대해 새롭게 알게 되었다. 역시 사람은 사고를 해야한다. 레벨 업+1 했다!!
참고
https://medium.com/syntx-io/cookie-management-in-react-native-545133fdcd14