-
Notifications
You must be signed in to change notification settings - Fork 0
Auth Flow
μ¬μ©μκ° μ±μ μ μνμ¬ λ‘κ·ΈμΈ/νμκ°μ νλ μ 체 κ³Όμ μ μ€λͺ ν©λλ€.
| νλͺ© | λ΄μ© |
|---|---|
| μΈμ¦ λ°©μ | μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ (OAuth 2.0) |
| ν ν° κ΄λ¦¬ | accessToken (30λΆ) + refreshToken (14μΌ, μΏ ν€) |
| μν κ΄λ¦¬ | Zustand + AsyncStorage |
μ¬μ©μκ° μ±μ μ€ννμ λμ λΆκΈ° λ‘μ§μ λλ€.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β μ± μμ νλ‘μ° β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β [μ± μμ] β
β β β
β βΌ β
β Zustand persist β AsyncStorageμμ μν 볡μ β
β β β
β βΌ β
β accessToken μμ? β
β β β
β βββ β
μμ β
β β β β
β β βΌ β
β β userTypeμ λ°λΌ λ©μΈ νλ©΄ λΆκΈ° β
β β β β
β β βββ WORKER β [WorkerHome] β
β β βββ EMPLOYER β [EmployerHome] β
β β β β
β β βββ 401 λ°μ μ β refresh μλ β
β β β β
β β βββ μ±κ³΅ β μ ν ν° μ μ₯ β κ³μ μ¬μ© β
β β βββ μ€ν¨ β [λ‘κ·ΈμΈ νλ©΄] β
β β β
β βββ β μμ β
β β β
β βΌ β
β isOnboardingCompleted νμΈ β
β β β
β βββ true β [λ‘κ·ΈμΈ νλ©΄] (μ¨λ³΄λ© μ€ν΅) β
β β β
β βββ false β [μ¨λ³΄λ© νλ©΄] β
β β β
β βΌ β
β "μμνκΈ°" λ²νΌ β
β β β
β βΌ β
β [λ‘κ·ΈμΈ νλ©΄] β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| μλλ¦¬μ€ | μ¨λ³΄λ© | λ‘κ·ΈμΈ | μ΄λ νλ©΄ |
|---|---|---|---|
| μ± μ΅μ΄ μ€μΉ ν 첫 μ€ν | β νμ | νμ | μ¨λ³΄λ© β λ‘κ·ΈμΈ |
| νμκ°μ μλ£ ν μ± μ¬μμ | β μ€ν΅ | μλ | λ©μΈ νλ©΄ |
| 14μΌ ν ν ν° λ§λ£ μ | β μ€ν΅ | νμ | λ‘κ·ΈμΈ νλ©΄ |
| λ‘κ·Έμμ ν | β μ€ν΅ | νμ | λ‘κ·ΈμΈ νλ©΄ |
| μ± μμ ν μ¬μ€μΉ | β νμ | νμ | μ¨λ³΄λ© β λ‘κ·ΈμΈ |
μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ λ²νΌ ν΄λ¦ νμ μ²λ¦¬ κ³Όμ μ λλ€.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ νλ‘μ° β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β [μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ λ²νΌ ν΄λ¦] β
β β β
β βΌ β
β μΉ΄μΉ΄μ€ 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λ‘ λ¨μ΄μ§λ€.
νμ νν΄λ μ¦μ μμ κ° μλλΌ 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 λ°κΈ β λ‘κ·ΈμΈ β λ©μΈ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
μλν¬μΈνΈ: 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λ§ κ·Έλλ‘ μ μ§νλ€. μ¬μ μ₯/κ³μ½/근무기λ‘μ μλ 볡ꡬλμ§ μλλ€ (λ€λ₯Έ μ¬μ©μ λ°μ΄ν° μΌκ΄μ± 보νΈ).
μλν¬μΈνΈ: 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 μμ¬λ₯Ό λ°©μ§νλ€.
μ κ· μ¬μ©μμ νμκ°μ 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] β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
μλν¬μΈνΈ: 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": "νκΈΈλ"
}
}| ν ν° | μ μ₯ μμΉ | μλͺ | κ΄λ¦¬ λ°©μ |
|---|---|---|---|
| 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 νκ²½μ 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λ‘ μ μ‘νλ λ°©μμΌλ‘ μ ν μμ .
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μμkakaoRegisterWithTokenvskakaoPurgeAndRegisterWithTokenλΆκΈ°μ μ¬μ©λλ€.resetForm()μ νΌ νλλ§,reset()μ μ 체(mode ν¬ν¨) μ΄κΈ°ν.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β λ°μ΄ν° νλ¦ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. μ± μμ β
β βββ AsyncStorageμμ Zustandλ‘ μν 볡μ (authStore) β
β βββ AsyncStorageμμ Zustandλ‘ μν 볡μ (onboardingStore) β
β βββ μΏ ν€μμ refreshToken μλ λ‘λ β
β β
β 2. μ± μ¬μ© μ€ β
β βββ Zustand Storeμμ μ½κΈ°/μ°κΈ° (λΉ λ₯Έ μ κ·Ό) β
β β
β 3. μ€μν λ³κ²½ μ (λ‘κ·ΈμΈ/λ‘κ·Έμμ) β
β βββ AsyncStorageμ μλ λκΈ°ν (persist) β
β β
β 4. μ± μ’
λ£ β
β βββ Zustand μ΄κΈ°νλ¨ (λ©λͺ¨λ¦¬) β
β βββ AsyncStorage, μΏ ν€λ μ μ§λ¨ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| νλ©΄ | μ€λͺ | κ΄λ ¨ Issue |
|---|---|---|
| μ¨λ³΄λ© (3νμ΄μ§) | μ± μκ° μ€μμ΄ν νλ©΄ | #1 |
| λ‘κ·ΈμΈ (Welcome) | μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ λ²νΌ | #1 |
| νμκ°μ STEP 1 | νμμ ν μ ν | #2 |
| νμκ°μ STEP 2 | νλ‘ν μ¬μ§ | #2 |
| νμκ°μ STEP 3 | κΈ°λ³Έμ 보 μ λ ₯ | #2 |
| νμκ°μ STEP 4 | μλ μ€μ + API νΈμΆ | #2 |
| νμκ°μ STEP 5 | κ°μ μλ£ | #2 |
# λ€λΉκ²μ΄μ
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| λ©μλ | μλν¬μΈνΈ | μ€λͺ |
|---|---|---|
| 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μΌ μ μ) |