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도 함께 롤백되어 데이터 영구 손실을 방지한다.

프론트 구현 메모

  • signUpStore의 mode 필드("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일 유예)