Skip to content

Auth Flow

l2juhan edited this page May 12, 2026 · 4 revisions

인증 플로우 (Auth Flow)

사용자가 앱에 접속하여 로그인/회원가입하는 전체 과정을 설명합니다.


1. 개요

항목 내용
인증 방식 카카오 로그인 (OAuth 2.0)
토큰 관리 accessToken (30분) + refreshToken (14일, 쿠키)
상태 관리 Zustand + AsyncStorage

2. 앱 시작 플로우

사용자가 앱을 실행했을 때의 분기 로직입니다.

┌─────────────────────────────────────────────────────────────────┐
│                         앱 시작 플로우                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [앱 시작]                                                      │
│      │                                                          │
│      ▼                                                          │
│  Zustand persist → AsyncStorage에서 상태 복원                    │
│      │                                                          │
│      ▼                                                          │
│  accessToken 있음?                                               │
│      │                                                          │
│      ├─→ ✅ 있음                                                │
│      │       │                                                  │
│      │       ▼                                                  │
│      │   userType에 따라 메인 화면 분기                          │
│      │       │                                                  │
│      │       ├─→ WORKER → [WorkerHome]                         │
│      │       └─→ EMPLOYER → [EmployerHome]                     │
│      │               │                                          │
│      │               └─→ 401 발생 시 → refresh 시도             │
│      │                       │                                  │
│      │                       ├─→ 성공 → 새 토큰 저장 → 계속 사용 │
│      │                       └─→ 실패 → [로그인 화면]           │
│      │                                                          │
│      └─→ ❌ 없음                                                │
│              │                                                  │
│              ▼                                                  │
│          isOnboardingCompleted 확인                              │
│              │                                                  │
│              ├─→ true  → [로그인 화면] (온보딩 스킵)             │
│              │                                                  │
│              └─→ false → [온보딩 화면]                          │
│                              │                                  │
│                              ▼                                  │
│                         "시작하기" 버튼                          │
│                              │                                  │
│                              ▼                                  │
│                         [로그인 화면]                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

시나리오별 동작

시나리오 온보딩 로그인 이동 화면
앱 최초 설치 후 첫 실행 ✅ 표시 필요 온보딩 → 로그인
회원가입 완료 후 앱 재시작 ❌ 스킵 자동 메인 화면
14일 후 토큰 만료 시 ❌ 스킵 필요 로그인 화면
로그아웃 후 ❌ 스킵 필요 로그인 화면
앱 삭제 후 재설치 ✅ 표시 필요 온보딩 → 로그인

3. 카카오 로그인 플로우

카카오 로그인 버튼 클릭 후의 처리 과정입니다.

┌─────────────────────────────────────────────────────────────────┐
│                      카카오 로그인 플로우                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [카카오 로그인 버튼 클릭]                                        │
│      │                                                          │
│      ▼                                                          │
│  카카오 SDK 호출 → kakaoAccessToken 획득                         │
│      │                                                          │
│      ▼                                                          │
│  백엔드 POST /api/auth/kakao/login { kakaoAccessToken }          │
│      │                                                          │
│      ├─→ 200 OK + status="LOGGED_IN" (기존 회원)                 │
│      │       │                                                  │
│      │       ▼                                                  │
│      │   accessToken 저장 (AsyncStorage)                        │
│      │   refreshToken 저장 (쿠키)                                │
│      │   userInfo 저장 (userId, name, userType)                 │
│      │       │                                                  │
│      │       ▼                                                  │
│      │   userType에 따라 메인 화면 분기                          │
│      │       │                                                  │
│      │       ├─→ WORKER → [WorkerHome]                         │
│      │       └─→ EMPLOYER → [EmployerHome]                     │
│      │                                                          │
│      ├─→ 200 OK + status="WITHDRAWN_PENDING" (탈퇴 보류 계정)    │
│      │       │                                                  │
│      │       ▼                                                  │
│      │   [WithdrawnAccountSheet] (§3-1 참고)                    │
│      │                                                          │
│      └─→ 404 USER_NOT_FOUND (신규 회원)                          │
│              │                                                  │
│              ▼                                                  │
│          signUpStore.mode = "register"                          │
│          kakaoAccessToken을 signUpStore에 임시 저장              │
│              │                                                  │
│              ▼                                                  │
│          [회원가입 화면] (STEP 1)                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

💡 카카오 로그인 응답은 2026-05 이후 KakaoLoginResult 구조로 변경됐다. status 필드(LOGGED_IN / WITHDRAWN_PENDING)로 분기하며, 신규 사용자는 기존처럼 404로 떨어진다.


3-1. 탈퇴 계정 복구/재가입 플로우 (2026-05 추가)

회원 탈퇴는 즉시 삭제가 아니라 soft delete + 30일 유예 방식이다. 유예 기간 안에 같은 카카오 계정으로 다시 로그인을 시도하면 사용자에게 "복구" 또는 "새 계정으로 가입" 두 가지 선택을 안내한다.

┌─────────────────────────────────────────────────────────────────┐
│                  탈퇴 보류 계정 처리 플로우                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [/kakao/login → status="WITHDRAWN_PENDING"]                    │
│      │                                                          │
│      │   응답에 withdrawnAccount 포함:                          │
│      │     • name                                               │
│      │     • userType ("WORKER" | "EMPLOYER")                   │
│      │     • withdrawnAt (탈퇴 시각)                            │
│      │     • profileImageUrl                                    │
│      │                                                          │
│      ▼                                                          │
│  [WithdrawnAccountSheet] 표시                                    │
│      │                                                          │
│      ├─→ "기존 계정 복구"                                       │
│      │       │                                                  │
│      │       ▼                                                  │
│      │   POST /api/auth/kakao/restore                          │
│      │       { kakaoAccessToken }                              │
│      │       │                                                  │
│      │       ├─→ 200 OK                                         │
│      │       │       └─→ User.deletedAt = null                  │
│      │       │           accessToken/refreshToken 발급          │
│      │       │           → 자동 로그인 → 메인 화면              │
│      │       │                                                  │
│      │       └─→ 실패 → 에러 안내 → 시트 닫기                   │
│      │                                                          │
│      └─→ "새 계정으로 가입"                                     │
│              │                                                  │
│              ▼                                                  │
│          signUpStore.mode = "purge-and-register"                │
│          signUpStore.kakaoAccessToken = (현재 토큰)              │
│              │                                                  │
│              ▼                                                  │
│          [회원가입 화면] (STEP 1부터 재입력)                     │
│              │                                                  │
│              ▼                                                  │
│          [STEP 4] 완료 시점에 분기:                              │
│          POST /api/auth/kakao/purge-and-register                │
│              │   (기존 탈퇴 계정 + 산하 데이터 영구 삭제 후      │
│              │    신규 사용자로 가입, 트랜잭션으로 원자성 보장)   │
│              ▼                                                  │
│          새 accessToken/refreshToken 발급 → 로그인 → 메인       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

복구 API

엔드포인트: POST /api/auth/kakao/restore

Request Body:

{ "kakaoAccessToken": "string" }

Response Body (성공 시): LoginResponse와 동일 구조(accessToken/refreshToken/userId/name/userType)

상태 의미
200 OK 복구 성공 — User.deletedAt이 null로 복구되고 토큰 발급
400 USER_NOT_WITHDRAWN 탈퇴 상태가 아닌 계정에 호출
404 USER_NOT_FOUND 등록되지 않은 카카오 계정

⚠️ 복구는 탈퇴 시 보존된 UserSettings만 그대로 유지한다. 사업장/계약/근무기록은 자동 복구되지 않는다 (다른 사용자 데이터 일관성 보호).

재가입(영구 삭제 후 신규 가입) API

엔드포인트: POST /api/auth/kakao/purge-and-register

Request Body: /api/auth/kakao/register와 동일 (kakaoAccessToken/name/userType/phone/bankName/accountNumber/profileImageUrl)

Response Body (성공 시): LoginResponse와 동일 구조

상태 의미
200 OK 기존 탈퇴 계정 hard delete + 신규 가입 성공
400 DUPLICATE_KAKAO_ACCOUNT 정상 상태 계정에 호출 (탈퇴 계정에만 사용 가능)

백엔드는 한 트랜잭션 안에서 hard delete → register를 순차 처리하며, register 실패 시 hard delete도 함께 롤백되어 데이터 영구 손실을 방지한다.

프론트 구현 메모

  • signUpStoremode 필드("register" | "purge-and-register")가 STEP 4 알람 화면에서 어느 API를 호출할지 결정한다.
  • SignUpNavigator 마운트 시에는 reset()이 아닌 resetForm()을 호출해 mode를 보존한다. 폼 필드만 초기화하며, mode/kakaoAccessToken은 호출자(WelcomeScreen)가 사전 설정한 값을 유지한다.
  • 일반 신규 가입(404 분기)에서도 setMode("register")를 명시 호출해 이전 세션의 mode 잔여를 방지한다.

4. 회원가입 플로우

신규 사용자의 회원가입 5단계 과정입니다.

┌─────────────────────────────────────────────────────────────────┐
│                       회원가입 플로우                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [STEP 1/5] 회원유형 선택                                        │
│      │                                                          │
│      ├─→ "근로자" 선택 → userType: "WORKER"                     │
│      └─→ "사장님" 선택 → userType: "EMPLOYER"                   │
│              │                                                  │
│              ▼                                                  │
│  [STEP 2/5] 프로필 사진 (선택)                                   │
│      │                                                          │
│      ├─→ 이미지 선택 → 400x400 리사이즈 → 압축 → base64 인코딩   │
│      └─→ "나중에 설정하기" → 스킵                               │
│              │                                                  │
│              ▼                                                  │
│  [STEP 3/5] 기본정보 입력                                        │
│      │                                                          │
│      │   • 이름 (필수)                                          │
│      │   • 핸드폰 번호 (010-XXXX-XXXX)                          │
│      │   • 은행명 (모달 선택) - 근로자만                         │
│      │   • 계좌번호 - 근로자만                                   │
│      │                                                          │
│              ▼                                                  │
│  [STEP 4/5] 알람 설정 + 회원가입 API 호출                        │
│      │                                                          │
│      ├─→ "알람 허용" → 시스템 권한 요청 → API 호출              │
│      └─→ "나중에 설정하기" → API 호출                           │
│              │                                                  │
│              ▼                                                  │
│          백엔드 POST /api/auth/kakao/register 호출               │
│              │                                                  │
│              ├─→ 성공                                           │
│              │       │                                          │
│              │       ▼                                          │
│              │   accessToken 저장 (AsyncStorage)                │
│              │   userInfo 저장 (userId, name, userType)         │
│              │       │                                          │
│              │       ▼                                          │
│              │   [STEP 5/5] 가입완료 화면                        │
│              │                                                  │
│              └─→ 실패 (400 등)                                  │
│                      │                                          │
│                      ▼                                          │
│                  에러 Toast 표시                                 │
│                  signUpStore 초기화                              │
│                  [로그인 화면]으로 이동                          │
│                                                                 │
│  [STEP 5/5] 가입완료                                             │
│      │                                                          │
│      │   signUpStore 초기화                                     │
│      │                                                          │
│      ├─→ WORKER → "시작하기" → [WorkerHome]                     │
│      └─→ EMPLOYER → "매장 관리하러 가기" → [WorkplaceManage]    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

회원가입 API

엔드포인트: POST /api/auth/kakao/register

Request Body:

{
  "kakaoAccessToken": "string",
  "name": "string",
  "userType": "WORKER",
  "phone": "010-1234-5678",
  "bankName": "string",
  "accountNumber": "string",
  "profileImageUrl": "base64 문자열 또는 빈 문자열"
}
필드 타입 필수 설명
kakaoAccessToken string 카카오 로그인 토큰
name string 사용자 이름
userType string "WORKER" 또는 "EMPLOYER"
phone string 핸드폰 번호 (하이픈 포함)
bankName string 은행명 (근로자만 필수)
accountNumber string 계좌번호 (근로자만 필수)
profileImageUrl string base64 인코딩된 이미지

Response Body (성공 시):

{
  "success": true,
  "data": {
    "accessToken": "string",
    "userType": "WORKER",
    "userId": 123,
    "name": "홍길동"
  }
}

5. 토큰 관리

저장 위치

토큰 저장 위치 수명 관리 방식
accessToken AsyncStorage + Zustand 30분 클라이언트 직접 관리
refreshToken 쿠키 (HttpOnly) 14일 서버가 Set-Cookie로 전송
kakaoAccessToken signUpStore (임시) - 회원가입 완료 시 삭제

토큰 갱신 플로우

┌─────────────────────────────────────────────────────────────────┐
│                        토큰 갱신 플로우                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [API 요청]                                                     │
│      │                                                          │
│      ▼                                                          │
│  Authorization: Bearer {accessToken}                            │
│      │                                                          │
│      ├─→ 200 OK → 정상 응답                                     │
│      │                                                          │
│      └─→ 401 Unauthorized (토큰 만료)                           │
│              │                                                  │
│              ▼                                                  │
│          CookieManager.get(baseURL)로 쿠키 직접 조회              │
│          → Cookie 헤더에 수동 첨부 (RN은 자동 전송 미지원)       │
│              │                                                  │
│              ▼                                                  │
│          POST /api/auth/refresh (Cookie 헤더에 refreshToken)     │
│              │                                                  │
│              ├─→ 200 OK                                         │
│              │       │                                          │
│              │       ▼                                          │
│              │   새 accessToken 저장                            │
│              │   원래 API 요청 재시도                            │
│              │                                                  │
│              └─→ 401/403 (refreshToken도 만료)                  │
│                      │                                          │
│                      ▼                                          │
│                  logoutCallback() 호출                          │
│                  → 저장된 토큰/쿠키 삭제                         │
│                  → [로그인 화면]으로 이동                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

React Native 쿠키 처리 주의사항 (2026-03-09 수정, PR #45)

React Native 환경은 Axios의 withCredentials: true만으로는 쿠키가 자동 전송되지 않는다. 이에 따라 갱신 인터셉터에서 쿠키를 수동으로 읽어 헤더에 첨부하는 구조를 사용한다.

이슈 이전 구현 현재 구현
리프레시 요청 시 쿠키 전송 withCredentials: true만 사용 → 쿠키 미전송 CookieManager.get(baseURL)로 조회 후 Cookie 헤더에 수동 첨부
갱신 실패 시 화면 이동 콜백 미등록 → 빈 UI RootNavigator에서 isNavReady 이후 setLogoutCallback 등록
로그아웃 API 실패 처리 에러 Toast 표시 + 갇힘 Toast 제거, finally 블록이 화면 이동 보장

📝 향후 계획: PR #65(OPEN)에서 @react-native-cookies/cookies 의존성 제거 후 refreshToken을 body로 전송하는 방식으로 전환 예정.


6. 상태 저장 구조

Zustand Store

authStore (AsyncStorage persist):

interface AuthState {
  accessToken: string | null;
  userInfo: UserInfo | null;  // { userId, name, userType }
  isLoggedIn: boolean;
  isHydrated: boolean;
}

onboardingStore (AsyncStorage persist):

interface OnboardingState {
  isOnboardingCompleted: boolean;
  isHydrated: boolean;
}

signUpStore (persist 없음 - 임시 저장):

type SignUpMode = "register" | "purge-and-register";

interface SignUpState {
  kakaoAccessToken: string | null;
  mode: SignUpMode;                  // 회원가입 분기 (탈퇴 계정 재가입 식별)
  userType: "WORKER" | "EMPLOYER" | null;
  profileImageUri: string | null;
  profileImageBase64: string | null;
  name: string;
  phone: string;
  bankName: string;
  accountNumber: string;
}

mode는 STEP 4에서 kakaoRegisterWithToken vs kakaoPurgeAndRegisterWithToken 분기에 사용된다. resetForm()은 폼 필드만, reset()은 전체(mode 포함) 초기화.

데이터 흐름

┌─────────────────────────────────────────────────────────────────┐
│                        데이터 흐름                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 앱 시작                                                     │
│     └─→ AsyncStorage에서 Zustand로 상태 복원 (authStore)         │
│     └─→ AsyncStorage에서 Zustand로 상태 복원 (onboardingStore)   │
│     └─→ 쿠키에서 refreshToken 자동 로드                         │
│                                                                 │
│  2. 앱 사용 중                                                  │
│     └─→ Zustand Store에서 읽기/쓰기 (빠른 접근)                 │
│                                                                 │
│  3. 중요한 변경 시 (로그인/로그아웃)                             │
│     └─→ AsyncStorage에 자동 동기화 (persist)                    │
│                                                                 │
│  4. 앱 종료                                                     │
│     └─→ Zustand 초기화됨 (메모리)                               │
│     └─→ AsyncStorage, 쿠키는 유지됨                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7. 화면 목록

화면 설명 관련 Issue
온보딩 (3페이지) 앱 소개 스와이프 화면 #1
로그인 (Welcome) 카카오 로그인 버튼 #1
회원가입 STEP 1 회원유형 선택 #2
회원가입 STEP 2 프로필 사진 #2
회원가입 STEP 3 기본정보 입력 #2
회원가입 STEP 4 알람 설정 + API 호출 #2
회원가입 STEP 5 가입완료 #2

8. 필수 라이브러리

# 네비게이션
npm install @react-navigation/native @react-navigation/native-stack

# 인증/저장
npm install @react-native-cookies/cookies
npm install @react-native-async-storage/async-storage
npm install @react-native-seoul/kakao-login
npm install zustand

# 이미지 처리
npm install expo-image-picker
npm install expo-image-manipulator

# 알림
npm install expo-notifications
npm install expo-device

# 기타
npm install react-native-alert-notification

9. API 엔드포인트 요약

메서드 엔드포인트 설명
POST /api/auth/kakao/login 카카오 로그인 (status: LOGGED_IN / WITHDRAWN_PENDING 분기)
POST /api/auth/kakao/register 카카오 회원가입
POST /api/auth/kakao/restore 탈퇴 계정 복구 (deletedAt=null)
POST /api/auth/kakao/purge-and-register 탈퇴 계정 영구 삭제 후 신규 가입
POST /api/auth/refresh 토큰 갱신
POST /api/auth/logout 로그아웃
DELETE /api/auth/withdraw 회원 탈퇴 (soft delete + 30일 유예)