chorock.page
소개글시리즈프로젝트
© 2026 chorock.page
← 목록으로
포에이 회고록 · 1/1
React Nativeexpoapp회고

expo로 소셜 로그인 붙이기 후기

2026.09.10 · 15분 읽기 · 조회 0

목차
소셜 로그인 붙이기도입 배경 - 시작하자 마자 바뀐 계획그 러 나!계약을 코드보다 먼저 썼다.1. 신규 회원에게 401•403을 쓰면 안 된다.2. 이메일이 없어도 성립해야 한다.3. 앱이 보낸 식별자를 신뢰하지 않는다.네이티브 호환성은 별도 프로젝트에서 먼저 검증했다연결 방식소셜 토큰과 포에이 토큰 분리가입을 두 단계로 구분이메일이 겹칠 때 - 자동으로 합치면 안되는 경우 존재엣지케이스1. 취소는 실패가 아니다.2. 끝나지 않는 작업을 로딩 상태로 쓰면 버튼이 영구히 잠긴다.애플은 이름과 이메일을 최초 1회만 제공한다4. 사용자가 OS 설정에서 연동을 끊으면 앱만 로그인 상태로 남는다5. 중간에 실패했을 때 어중간한 상태를 남기지 않는다6. 이메일 없는 계정의 로그인 문제 - 유저 권한 문제결과후기

소셜 로그인 붙이기

이메일•비밀번호 로그인만 있던 포에이 앱에 네이버•애플 소셜 로그인을 붙였다.
처음에는 간단히 라이브러리 연동만 하면 되겠거니 생각했으나, 세상에 쉬운 일은 하나도 없었고....

단순히 로그인 버튼을 붙이는 것만 생각해서는 안된다.
기존 회원과 소셜 계정을 어떻게 연동할 것인지, 정보 제공 범위의 제한에 따라 어떻게 분기처리를 해야 하는지, 또한 타사의 로그인 API 규정에 맞추는 것이 얼마나 복잡한 일인지, 그때는 알지 못했다...

2주 반 동안 작업 로그를 19개, 90개의 커밋과, 엣지케이스 49건을 남겼고. 네이버 로그인 사전 검수는 신청 2영업일만에 통과했다. (그리고 또 오류가 발생)


도입 배경 - 시작하자 마자 바뀐 계획

기존에는 클라아언트의 요청으로 소셜 로그인에, 카카오와 네이버 로그인을 넣기로 했었다. 때문에 expo에 관련된 라이브러리들을 찾으며 공부했고, 해당 회고록을 남겼다. 해당 문서를 정리하며 백엔드 측에서도 어떻게 데이터를 주고 받으면 좋을 지에 대한 문서를 미리 작성하였다. 결국은 토큰에 대한 관리를 잘 하면 되었다.

그 러 나!

백엔드에 넘길 연동 문서를 다 쓴 이틀 후, 제공자가 네이버로 바뀌었다. (아이고) 다행히 앱 코드는 착수 전이라 버릴 구현이 크지 않았다.

여기서 예상치 못한 소득이 있었다. 카카오 문서를 제공자에 종속되지 않게 써둔 덕에 전환 비용이 거의 없었다. 다시 쓴 것은 검증 절차 한 문장이랑 필드 대응 표 뿐이었고, 아래는 제공자가 바뀌어도 유효했다.

- 사용자를 (제공자, 제공자가 준 사용자 ID) 한 쌍으로 식별한다
- 소셜 제공자의 토큰과 포에이 서비스의 토큰을 분리해 관리한다
- 신규 회원 분기에 401•403을 쓰지 않는다 (현재 서비스는 그렇게 구현해야 하도록 되어있다)
- 가입의 단계를 두 가지로 나눈다

계약 문서를 특정 제공자의 이름으로 쓰지 않는 것만으로 교체 비용이 크게 줄었다. 소셜 로그인과 같이 공통된 범주에 있으면서 특정 제공자들이 나뉘는 작업들은 이에 유의하며 계획을 세우면 좋은 것 같다.
(부랴부랴 무언가를 썼었다)

애플 로그인은 선택이 아니었다. 앱스토어 심사 규정상 서드파티 로그인을 제공하면 이메일을 숨길 수 있는 동등한 수단을 함께 제공해야 하는데, 네이버에는 그 옵션이 없다. 네이버만 넣고 출시하면 리젝이라서, 애플 로그인이 사실상 강제였다(크아악 애플)


계약을 코드보다 먼저 썼다.

백엔드 작업하시는 분과 먼저 공동 규약을 지정해야 했기 때문에, 서버가 무엇을 검증하고 무엇을 결정해야 하는지 문서를 넘겨야 했다. 기존의 코드를 읽어 도출한 요구 세 가지가 핵심이었다.

1. 신규 회원에게 401•403을 쓰면 안 된다.

앱의 네트워크 계층에는 인터셉터가 있다. axios.Interceptors를 이용해 401을 받으면 자동으로 토큰 재발급을 시도하고, 실패하면 저장된 토큰을 지우는 로직으로 구현되어 있었다. 로그인 상태가 만료됐을 때를 위한 장치다.

그런데 서버가 "처음보는 회원이니 가입시켜라"는 뜻으로 401을 주면, 이 장치가 그대로 발동해서 토큰이 날아가고, 로그인 화면으로 튕겨버린다. 원인이 서버 응답 코드기 때문에 앱 로그만 봐서는 추적이 어렵다.

이를 방지하고자 신규 회원은 200 ok로 처리하며 내부 값에 "프로필이 없다"는 값을 넣어달라고 요청했다. 인터셉트 로직을 찾아보지 않으면 조용히 버그가 일어날 뻔 했다.

2. 이메일이 없어도 성립해야 한다.

소셜 제공자의 이메일은 선택 동의 항목이다. 기존에는 이메일을 필수 제공항목으로 하려고 했다. 네이버의 경우에는 마땅한 기능이 있는 것이 아닌 마케팅 목적일시 필수 동의는 개인정보보호법 위반으로 검수를 반려할 수 있기 때문에 (선택)으로 하여 Null 값을 보낼 수 있었고,

애플은 애초에 이메일 가리기 기능(랜덤한 이메일)을 제공해서 이메일을 기준으로 사용자 식별을 주로 하는 것은 쉽지 않았다.

3. 앱이 보낸 식별자를 신뢰하지 않는다.

앱이 "이 사람은 네이버 사용자 12345다"라고 말하는 걸 서버가 믿으면, 아무나 남의 계정으로 로그인할 수 있다. 앱은 소셜로그인 제공자가 발급한 토큰만 넘기고, 해당 토큰으로 서버가 직접 신원을 확인하는 api를 호출하는 방식을 택했다.

네이티브 호환성은 별도 프로젝트에서 먼저 검증했다

해당 앱은 New Architecture와 정적 프레임워크 링크 방식을 사용한다.

New Architecture?
React Native는 JS로 쓴 코드와 IOS/AOS 네이티브 코드가 대화해야 돌아간다.
옛날에는 JS <--> 네이티브 사이의 언어를 통역해주는 Bridge가 존재했다.
JS언어 명령 -> JSON으로 직렬화 -> 비동기로 전송 과정이 필요하다.
때문에 데이터가 오고가는 시간이 걸렸고, 화면이 버벅이거나 끊기는 현상이 발생했다.

이를 개선하고자 New Architecture이 탄생했다. 브릿지를 없애고, 곧바로 호출하며, lazy loading 을 사용하는 등의 최적화가 이루어졌다.

New Architecture는 비교적 최근에 등장했다. 문제는 라이브러리도 새 방식에 맞춰 고쳐져야 하는 것이었다.

또한 정적 프레임워크 링크는 라이브러리 코드를 앱 내부에 통째로 복사해서 빌드할 때 합쳐버리는 방식이다. (동적은 반대로 외부에서 불러오는 방식) 포에이 프로젝트는 푸시 알림에 쓰이는 firbase 가 해당 방식을 요구했기 때문에 강제로 사용해야 했다.

해당 조합에서 네이티브 모듈이 빌드 단계에 깨지는 일이 흔하기 때문에, 본 프로젝트에 바로 설치했다가 깨지면 원인 분리가 어려워지므로 동일한 설정을 가진 목 프로젝트를 따로 만들어서 네이버•애플 SDK를 각각 빌드하며 확인했다.

두 테스트를 통과한 후에 본 프로젝트에 적용했다.


연결 방식

소셜 토큰과 포에이 토큰 분리

앱이 소셜 SDK로 로그인 -> 제공자 토큰 획득
 
  ↓ 해당 토큰을 서버에 넘긴다
 
서버가 api 호출로 신원 확인 -> 포에이 서비스 토큰 발급
 
  ↓
 
그 뒤로 앱은 포에이 토큰만 사용. 제공자 토큰은 버림

제공자 토큰은 딱 한 번 교환에만 사용했다. 해당 결정 덕분에 나중에 엣지케이스 하나를 해결했다.

가입을 두 단계로 구분

소셜 로그인은 성공했지만 닉네임도 약관 동의도 없는 상태가 존재한다. 이걸 권한이 제한된 역할로 표현했다. 가입 단계에서 프로필 설정을 마치만 정식 USER로 승격된다.

기존 회원가입은 3단계의 가입 폼을 사용했으나, 그것을 재사용할 수 없었다. 첫 화면이 이메일•비밀번호 설정인데 네이버 검수 규정에 따르면 "소셜 가입 화면에서 별도 비밀번호를 요구하면 즉시 반려"라는 조항이 있었다.

이는 애플도 마찬가지이며, 때문에 소셜 전용 가입 화면을 2단계로 구성하여 따로 만들었다.

이메일이 겹칠 때 - 자동으로 합치면 안되는 경우 존재

가장 오래 고민한 부분이다. 소셜 로그인으로 들어온 이메일이 이미 우리 서비스에 가입된 이메일 이라면 같은 사람일까?

├ 이 소셜 계정으로 로그인한 이력이 있다              → 그대로 로그인
 
├ 이메일이 기존 회원과 일치
 
│   ├ 소셜 쪽에서 그 이메일을 검증했다               → 자동으로 합쳐서 로그인
 
│   └ 소셜 쪽에서 검증하지 않았다                    → 자동 병합 거부
 
└ 아무것도 걸리지 않는다                             → 신규 가입

제공자가 이메일을 검증하지 않았다면, 공격자가 아무 이메일을 "내 이메일"이라고 주장하는 소셜 계정을 만들어 그 이메일로 가입된 남의 계정을 그대로 가져갈 수도 있다.

그래서 미검증인 경우 서버가 자동 병합을 거부하고, 앱에 수명 5분짜리 임시 키를 내려준다. 해당 키는 그 자체로는 아무 권한이 없다. 앱은 "이 이메일로 가입된 계정이 있어요" 라고 안내하고, 기존 계정의 비밀번호로 본인 확인을 받은 뒤에야 두 계정을 연결한다.

네이버와 애플은 실제로 검증된 이메일을 주지만, 설계 단계에서는 모든 경우의 수를 방어하기 위해서 코드로 구현해놓았다.
(후에 엣지케이스를 보강하며 추가한 코드로, 이메일이 검증되지 않은 계정은 연결 대상이 될 수도 없다.)

엣지케이스

정상적인 흐름이 실기기에서 돌아간 후, 버튼을 누르고, 동의하고, 가입을 마치고, 앱에 들어가는 것 까지 그 밖의 경우는 어디까지 막혀 있는지 로그인 -> 가입 -> 계정 연결 -> 세션 정리까지 전 경로를 반복적으로 흝었고, 48건의 엣지 케이스를 발견했다(....)

해당 표를 정리한 페이지를 들어가보면 경우의 수가 엄청나게 많다...끄아악 너무 고통스러웠다.

1. 취소는 실패가 아니다.

사용자가 로그인 창을 닫은 것과 로그인이 실패한 것은 다르다. 그러나 SDK는 둘을 비슷하게 돌려준다. 로그인을 취소했는데 "로그인에 실패했어요" 토스트가 뜨면 사용자는 자기가 뭘 잘못한 줄 안다. 취소는 조용히 되도록 했다.

한 제공자는 여기서 한 겹 더 있었다. 실패해도 성공 콜백으로 들어온다. 응답 안의 플래그를 봐야 성공·실패·취소가 갈린다. 그대로 쓰면 실패가 성공으로 처리되므로, 세 상태를 구분하는 타입으로 정규화한 뒤 화면에 넘겼다.

2. 끝나지 않는 작업을 로딩 상태로 쓰면 버튼이 영구히 잠긴다.

마치 멀티스레딩에서 서로를 기다리며 Deadlock이 일어나는 것과 같은 현상이 있었다.

  • 증상 — 비행기 모드에서 소셜 로그인을 누르면 버튼이 영구히 눌리지 않는다

  • 원인 — 그 SDK는 제공자 앱에서 결과 콜백이 와야 작업이 끝난다. 사용자가 제공자 앱에서
    아무것도 하지 않고 돌아오면 콜백이 영원히 오지 않는다. "진행 중" 상태가 절대 풀리지 않는다

  • 판단 — 그 상태로 버튼을 잠그면 앱을 재시작해야 풀린다. 잠금 조건에서 뺐다

  • 결과 — 다시 눌러도 안전하다. 네이티브가 이전 대기를 새 요청으로 덮어쓰기 때문이다

    로딩 상태는 "언젠가는 반드시 끝난다"는 가정 위에 있다. 그 가정이 깨지는 작업에는 쓰면 안 된다. (비행기모드와 같은 예외사항이 있을 줄 누가 알았을까...)

애플은 이름과 이메일을 최초 1회만 제공한다

문서로 읽고 넘어갈 수도 있었지만 실기기로 확인했다. 첫 로그인에만 이름·이메일이 오고, 두 번째부터는 사용자 식별자만 온다. 즉, NULL 값이 되어버린다.

때문에 서버가 첫 응답을 저장하지 못하면 복구할 방법이 없다.

그리고 앱 쪽에서도 함정이 있었다. "이름이 왔으면 신규 회원" 이라고 판단하면 안된다.
사용자가 OS설정에서 연동을 끊고 다시 로그인하면 애플이 이를 최초 인증처럼 취급해 이름을 다시 준다.

4. 사용자가 OS 설정에서 연동을 끊으면 앱만 로그인 상태로 남는다

  • 증상 — 애플 설정에서 앱 연결을 끊었는데 앱은 여전히 로그인된 상태다
  • 원인 — 제공자 쪽 인증 정보는 폐기되지만 우리 서비스가 발급한 토큰은 그대로 살아있다
  • 판단 — 사용자 입장에선 "끊었는데 왜 아직 들어가지지" 다. 감지해서 로그아웃까지 이어야 한다

애플은 연동이 끊기면 알아차릴 수 있는 SDK가 존재해, 백그라운드에서 돌아올 때 연동이 해제되었다면 자동 로그아웃 되도록 처리했다. 유저정보는 여전히 DB에 남아있으므로 다시 로그인 하면 동일한 계정으로 들어온다.

그러나 네이버는 같은 대응이 불가능했다. 연동이 끊긴 것을 앱에 알려주는 SDK가 없었고, 서버도 알 방법이 없었다. 때문에 해당 증상을 일단은 놔두었다.

5. 중간에 실패했을 때 어중간한 상태를 남기지 않는다

계정 연결은 로그인 -> 토큰 저장 -> 연결 순서로 진행된다. 중간 단계에서 실패하면, SDK토큰만 저장된 채 연결은 안된 상태로 로그인 화면으로 돌아간다.

실패 시 저장한 토큰을 제거하는 방향으로 했다. 해당 연결을 하나의 트랜잭션처럼 관리해, anomaly가 일어나지 않도록 방지했다.

초기 설계 결정이 나중 엣지케이스의 난이도를 결정한다는 걸 체감했다....

6. 이메일 없는 계정의 로그인 문제 - 유저 권한 문제

네이버 검수를 통과한 후, 실기기 테스트를 하다 나온 문제다. 이메일 제공에 동의하지 않은 사용자가 가입을 끝내도 앱에 접속할 수 없었따. 로그인 버튼을 눌러도 아무 반응이 없어 사용자에게는 고장으로 보였다.

서버는 이메일이 없어도 가입을 시키되, 권한을 GUEST로 제한하고 **마이페이지에서 이메일을 추가하면 USER로 승격시켜주는 구졸로 설계되어 있었다.
그러나 권한이 GUEST인 유저는 마이페이지에 도달할 수 없었다. 대부분의 요청이 최소 USER의 권한을 요구했기 때문이다. 이것은 명백한 설계 미스이다,,,,,

이메일을 추가해야 로그인이 되는데, 추가하는 화면이 로그인 뒤에 있었다. 닫힌 고리다.
또한 여러 문제가 얽혀 있어, 선택의 분기에 놓였다,,,
백엔드 분과의 긴 긴 토론 끝에 USER의 조건을 완화시키는 방안으로 타협을 했다.

이메일 등록은 강제에서 권장으로 내리고, 마이페이지에 "이메일을 등록해 계정을 보호하세요" 안내를 넣는 것으로 마무리했다.
이메일이 여전히 필요한 이유는 분명하다. 계정 병합 기준이 이메일이라, 비밀번호도 이메일도
없는 소셜 계정은 그 소셜 계정을 잃으면 되찾을 방법이 없다.

이 논의에서 배운 게 하나 더 있다. 백엔드가 안내한 구조(마이페이지에서 추가)는 서버 입장에서는 완결적이었다. 앱의 로그인 상태 판정 방식을 모르면 그게 도달 불가능하다는 걸 알 수 없다.

경계를 넘는 문제는 양측의 상황을 잘 고려해서 설계를 해야한다는 것을 깊게 깨달았다,,,,,,,

결과

기간2026-08-25 ~ 09-10 (2주 반)
네이버 사전 검수신청 → 2영업일 만에 승인
남긴 기록작업 로그 19편, 커밋 90여 개
정리한 엣지케이스48건 (로그인·가입·계정연결·세션 5개 구간)
잡은 오진3건 (내가 세운 가설 중 틀린 것)

후기

정말 만만하게 봤다가 큰 코를 너무 세게 맞아버렸다. 각 지점이 하나로 합쳐지는 부분을 특히 더 주의해야 함을 알게 되었고, 설계의 치밀함에 대해 다시 생각하게 되었다.

공동 프로젝트를 진행할 때, 항상 기획과 설계 단계에서 내가 알지 못하는 엣지 케이스들, unknown unknowns같은 것들이 즐비함을 느낀다. 결국은 경험을 많이 쌓고, 열심히 공부하고, 적용하는 수밖에 없는 것 같다.

작은 실수, 경험이 사용자 이탈률을 증가시킬 수 있기 때문에 유저 플로우를 계속 고려하며, 엣지케이스들에 대해 생각하고, UX를 고려하는 것에 많이 집중했다. 포에이 프로젝트를 진행하며 UX적인 생각(?)이 많이 늘어났음을 느낀다.

(이리저리 빠르게 피드백 반영해주신 백엔드 분께도 너무 감사하다,,,,,,,)


디자이너 분 없어서 열심히 피그마로 디자인을 해봤다 ^_^ (로고 가이드가 은근 빡셈)

초록
초록

앱과 웹을 만들고, 직접 운영하며 기록합니다.

댓글

giscus로 동작하며 GitHub Discussions에 저장됩니다.

관련 글

React Native 공부React Nativeappexpo

Expo 에서 네이버 로그인하기

2026.08.27·2분 읽기·조회 0
React Native 공부React Nativeappexpo

Expo에서 카카오 로그인 추가하기

2026.08.23·4분 읽기·조회 0
하이브리드 앱 만들기appReact NativeNext.js

React Native: 웹앱 만들기 일지(2) - 웹과 앱의 통신

2026.07.04·3분 읽기·조회 0

목차

소셜 로그인 붙이기도입 배경 - 시작하자 마자 바뀐 계획그 러 나!계약을 코드보다 먼저 썼다.1. 신규 회원에게 401•403을 쓰면 안 된다.2. 이메일이 없어도 성립해야 한다.3. 앱이 보낸 식별자를 신뢰하지 않는다.네이티브 호환성은 별도 프로젝트에서 먼저 검증했다연결 방식소셜 토큰과 포에이 토큰 분리가입을 두 단계로 구분이메일이 겹칠 때 - 자동으로 합치면 안되는 경우 존재엣지케이스1. 취소는 실패가 아니다.2. 끝나지 않는 작업을 로딩 상태로 쓰면 버튼이 영구히 잠긴다.애플은 이름과 이메일을 최초 1회만 제공한다4. 사용자가 OS 설정에서 연동을 끊으면 앱만 로그인 상태로 남는다5. 중간에 실패했을 때 어중간한 상태를 남기지 않는다6. 이메일 없는 계정의 로그인 문제 - 유저 권한 문제결과후기