🎨 3단계 : μœ μ € 도메인 ν…Œν¬μŠ€νŽ™ - 100-hours-a-week/7-team-ddb-wiki GitHub Wiki

λ¬Έμ„œ 관리

λ¬Έμ„œ 관리

μž‘μ„±μΌ 2025-04-23
버전 1.0
μž‘μ„±μž suzy.kang (κ°•μˆ˜μ§€)
κ²€ν† μž kevin
μƒνƒœ μ΄ˆμ•ˆ

λ³€κ²½ 이λ ₯

버전 λ‚ μ§œ μž‘μ„±μž κ²€ν† μž λ³€κ²½λ‚΄μš©
1.0 2025-04-23 suzy kevin μ΄ˆμ•ˆ
1.1 2025-05-20 suzy kevin MVP 개발 이후 λ¬Έμ„œ 동기화

λ°°κ²½ (Background)

ν”„λ‘œμ νŠΈ λͺ©ν‘œ (Objective)

  • 카카였 μ†Œμ…œ 둜그인으둜 κ°„νŽΈν•œ μ„œλΉ„μŠ€ μ ‘κ·Ό 제곡
  • κ°œμΈμ •λ³΄ 및 μœ„μΉ˜μ •λ³΄ λ™μ˜ ν”„λ‘œμ„ΈμŠ€λ₯Ό λͺ…ν™•ν•˜κ²Œ κ΅¬ν˜„
  • 토큰 기반 μ•ˆμ „ν•œ 인증 μ‹œμŠ€ν…œ ꡬ좕

λͺ©ν‘œκ°€ μ•„λ‹Œ 것 (Non-goals)

  • 카카였 μ™Έ λ‹€λ₯Έ μ†Œμ…œ 둜그인 κ΅¬ν˜„
  • 이메일 기반 νšŒμ›κ°€μž…/둜그인 μ‹œμŠ€ν…œ

섀계 및 기술 자료 (Architecture and Technical Documentation)

μ•„ν‚€ν…μ²˜ κ°œμš” (Architecture Overview)

ν”„λ ˆμž„μ›Œν¬/라이브러리

  • Next.js (v15.3.0) + React (v19.1.0)
    • App Router 기반 λΌμš°νŒ…
    • SSR/ISR ν™œμš©μœΌλ‘œ SEO μ΅œμ ν™”
    • React 19의 use() ν›… ν™œμš©ν•œ 데이터 페칭

μƒνƒœ κ΄€λ¦¬

  • Zustand (v5.0.3)
    • μž‘μ€ λ²ˆλ“€ μ‚¬μ΄μ¦ˆλ‘œ λͺ¨λ°”일 μ›Ή μ΅œμ ν™”
    • μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ ν˜Έν™˜μ„±
    • 직관적인 API둜 λΉ λ₯Έ 개발 κ°€λŠ₯

UI/μŠ€νƒ€μΌλ§

  • Tailwind CSS (v4.1)
    • μœ ν‹Έλ¦¬ν‹° νΌμŠ€νŠΈ μ ‘κ·ΌμœΌλ‘œ λΉ λ₯Έ 개발
    • μ»€μŠ€ν…€ ν…Œλ§ˆλ‘œ λΈŒλžœλ“œ λ””μžμΈ μ‹œμŠ€ν…œ ꡬ좕
  • shadcn/ui
    • Tailwind 기반 μ»΄ν¬λ„ŒνŠΈ
    • ν•„μš”ν•œ μ»΄ν¬λ„ŒνŠΈλ§Œ 가져와 λ²ˆλ“€ 크기 μ΅œμ ν™”

데이터 관리

  • React use() + TanStack Query (v5)
    • μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ: use() ν›…μœΌλ‘œ SEO μ΅œμ ν™”
    • ν΄λΌμ΄μ–ΈνŠΈ: TanStack Query둜 λ³΅μž‘ν•œ μƒνƒœ 관리
  • Zod (v3.24.3)
    • νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ—°λ™
    • μ„œλ²„-ν΄λΌμ΄μ–ΈνŠΈ μŠ€ν‚€λ§ˆ 곡유

폼 μƒνƒœ 관리

  • react-hook-form (v7.56.2)
    • κ°€λ²Όμš΄ λ²ˆλ“€ μ‚¬μ΄μ¦ˆλ‘œ λͺ¨λ°”일 μΉœν™”μ 
    • Zodκ³Ό 톡합해 μ •κ΅ν•œ μœ νš¨μ„± 검사 κ°€λŠ₯
    • Server Actionκ³Ό 병행 μ‚¬μš© κ°€λŠ₯ (ν•„μš” μ‹œ SSR + λΉ λ₯Έ UX λͺ¨λ‘ 확보)

지도 μ„œλΉ„μŠ€

  • Kakao Maps SDK (v2.7.5)
    • κ΅­λ‚΄ μ‚¬μš©μž μΉœν™”μ  UI
    • μ»€μŠ€ν…€ λ§ˆμ»€/ν΄λŸ¬μŠ€ν„°λ§ μ§€μ›

λΉŒλ“œ 도ꡬ

  • Turbopack(v1.5.3) + webpack (v5)
    • Next.js 15 λ‚΄μž₯ λΉŒλ“œ 도ꡬ ( Turbopack : 개발 ν™˜κ²½, Webpack : ν”„λ‘œλ•μ…˜ ν™˜κ²½ )
    • λΉ λ₯Έ κ°œλ°œ ν™˜κ²½κ³Ό HMR μ§€μ›

SSR / ISR / SSG 적용 λ²”μœ„

  • /oauth/kakao/callback : SSR μ‚¬μš©
    • ν™ˆ λ˜λŠ” μ•½κ΄€ λ™μ˜ νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈλ§Œ μ§„ν–‰ν•˜λŠ”λ°, SSR을 μ‚¬μš©ν•˜λ©΄ ν™”λ©΄ κΉœλΉ‘μž„ 없이 λ°”λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈ κ°€λŠ₯
    • query parameter codeλ₯Ό μ²˜λ¦¬ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— SSGλŠ” μ μ ˆν•˜μ§€ μ•ŠμŒ.
  • /auth/consent : CSR μ‚¬μš©
    • 검색엔진에 λ…ΈμΆœλ  ν•„μš” μ—†λŠ” νŽ˜μ΄μ§€
    • μ‚¬μš©μž μΈν„°λž™μ…˜μ΄ μ£Όμš” κΈ°λŠ₯ (μ²΄ν¬λ°•μŠ€ 선택, 폼 제좜)
  • /auth/signup : CSR μ‚¬μš©
    • μ‚¬μš©μž μΈν„°λ ‰μ…˜μ΄ μ£Όμš” κΈ°λŠ₯ (폼 제좜, μ‹€μ‹œκ°„ μœ νš¨μ„± 검증, ν”„λ‘œν•„ 이미지 μ—…λ‘œλ“œ)
  • /mypage : CSR μ‚¬μš©
    • SSR ν•  μ •λ„μ˜ SEO 이점이 μ—†μŒ. 개인 정보이기 λ•Œλ¬Έμ— 정적 νŽ˜μ΄μ§€λ‘œ μΊμ‹±ν•˜λ©΄ μ•ˆλ¨.
    • μ‚¬μš©μžκ°€ μ €μž₯ν•œ μž₯μ†Œλ¦¬μŠ€νŠΈλ₯Ό λ¬΄ν•œ 슀크둀둜 μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— CSR μ‚¬μš©
  • /mypage/edit : CSR μ‚¬μš©
    • μ‚¬μš©μž μΈν„°λ ‰μ…˜μ΄ μ£Όμš” κΈ°λŠ₯ (폼 제좜, μ‹€μ‹œκ°„ μœ νš¨μ„± 검증, ν”„λ‘œν•„ 이미지 μ—…λ‘œλ“œ)
  • /mypage/delete : CSR μ‚¬μš©
    • μ‚¬μš©μž μΈν„°λ ‰μ…˜μ΄ μ£Όμš” κΈ°λŠ₯ (μ‚¬μš©μž 확인 및 λ™μ˜)

μ£Όμš” νŽ˜μ΄μ§€

  • oauth/kakao/callback : 카카였 OAuth 콜백 처리 νŽ˜μ΄μ§€

    • μ£Όμš”κΈ°λŠ₯ : μΉ΄μΉ΄μ˜€ 인증 μ½”λ“œλ‘œ 둜그인/ν˜Έμ›κ°€μž… ν›„ μ μ ˆν•œ νŽ˜μ΄μ§€λ‘œ λΌμš°νŒ…
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • KakaoCallbackClient (ν΄λΌμ΄μ–ΈνŠΈ μ‚¬μ΄λ“œ 둜직 처리)
    • 데이터 λ‘œλ”© μ‹œμ  : νŽ˜μ΄μ§€ μ§„μž… μ‹œ Query Parameter (code)λ₯Ό μ‚¬μš©ν•˜μ—¬ POST /api/v1/auth/tokens API ν˜ΈμΆœ (by KakaoCallbackClient).
    • λΌμš°νŒ… (API 응닡에 따라 KakaoCallbackClientκ°€ 처리):
      • μ‹ κ·œ νšŒμ› β†’ /auth/consent
      • κΈ°μ‘΄ νšŒμ› β†’ /
      • μ—λŸ¬ λ°œμƒ β†’ /onboarding
  • /auth/consent: μ•½κ΄€ λ™μ˜ νŽ˜μ΄μ§€

    • μ£Όμš” κΈ°λŠ₯ : μ„œλΉ„μŠ€ μ΄μš©μ•½κ΄€(κ°œμΈμ •λ³΄ 처리방침, μœ„μΉ˜κΈ°λ°˜ μ„œλΉ„μŠ€) λ™μ˜.
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • ConsentPage (νŽ˜μ΄μ§€ λ ˆμ΄μ•„μ›ƒ 및 둜직 μ»¨ν…Œμ΄λ„ˆ)
      • PrivacyConsent (@/features/user/components/consent/PrivacyConsent.tsx)
      • LocationConsent (@/features/user/components/consent/LocationConsent.tsx)
      • useConsent (@/features/user/hooks/useConsent.ts)
    • 데이터 λ‘œλ”© μ‹œμ  :
      • μ•½κ΄€ λ‚΄μš©μ€ 각 PrivacyConsentLocationConsent μ»΄ν¬λ„ŒνŠΈ 내에 직접 μž‘μ„±λ˜μ–΄ 있음.
    • λΌμš°νŒ… : useConsent ν›…μ˜ handleAgreement ν•¨μˆ˜ λ‚΄μ—μ„œ POST /api/v1/users/agreement API 호좜 성곡 μ‹œ β†’ /auth/signup
  • /auth/signup: μΆ”κ°€ 정보 μž…λ ₯ νŽ˜μ΄μ§€

    • μ£Όμš” κΈ°λŠ₯ : λ‹‰λ„€μž„ μ„€μ •, μ†Œκ°œκΈ€ μ„€μ •, ν”„λ‘œν•„ 이미지 μ—…λ‘œλ“œν•˜μ—¬ νšŒμ›κ°€μž… μ™„λ£Œ.
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • ProfileForm (@/features/user/components/profile-form/ProfileForm.tsx)
    • 데이터 λ‘œλ”© μ‹œμ  : μ—†μŒ (μƒˆ 정보 μž…λ ₯).
    • λΌμš°νŒ… : ProfileForm의 onSubmit ν•Έλ“€λŸ¬μ—μ„œ POST /api/v1/users API 호좜 성곡 μ‹œ β†’ / (λ˜λŠ” μ˜¨λ³΄λ”© μ™„λ£Œ/ν™˜μ˜ νŽ˜μ΄μ§€)
  • /mypage : λ§ˆμ΄νŽ˜μ΄μ§€ - λ‚΄ ν”„λ‘œν•„

    • μ£Όμš”κΈ°λŠ₯ : μ‚¬μš©μž ν”„λ‘œν•„ 정보 쑰회 및 ν‘œμ‹œ, νšŒμ›μ •λ³΄ μˆ˜μ •/λ‘œκ·Έμ•„μ›ƒ/νƒˆν‡΄ κΈ°λŠ₯ 링크 제곡.
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • Profile (@/features/user/components/profile/Profile.tsx)
      • useUser (@/features/user/hooks/useUser.ts)
      • Header (@/shared/components)
    • 데이터 λ‘œλ”© μ‹œμ  : νŽ˜μ΄μ§€ μ§„μž… μ‹œ useUser ν›…을 톡해 GET /api/v1/users/me API ν˜ΈμΆœ.
    • λΌμš°νŒ… :
      • "νšŒμ›μ •λ³΄ μˆ˜μ •" λ²„νŠΌ 클릭 μ‹œ β†’ /mypage/edit
      • "λ‘œκ·Έμ•„μ›ƒ" λ²„νŠΌ 클릭 μ‹œ β†’ logout API 호좜 ν›„ /onboarding
      • "νšŒμ› νƒˆν‡΄ν•˜κΈ°" λ²„νŠΌ 클릭 μ‹œ β†’ /mypage/delete
  • /mypage/places : λ§ˆμ΄νŽ˜μ΄μ§€ - μ €μž₯ν•œ μž₯μ†Œ

    • μ£Όμš”κΈ°λŠ₯ : μ‚¬μš©μž ν”„λ‘œν•„ 정보 쑰회 및 ν‘œμ‹œ
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ : (ν˜„μž¬ μ½”λ“œ 뢄석 κΈ°μ€€, 이 νŽ˜μ΄μ§€μ˜ ꡬ체적인 μœ μ € κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈλŠ” ν™•μΈλ˜μ§€ μ•ŠμŒ. μž₯μ†Œ λͺ©λ‘ κ΄€λ ¨ κΈ°λŠ₯이 μ£Όκ°€ 될 κ²ƒμœΌλ‘œ μ˜ˆμƒ)
      • Profile (@/features/user/components/profile/Profile.tsx)
      • SavedPlaceListCard (λ‚΄κ°€ μ €μž₯ν•œ μž₯μ†Œ λͺ©λ‘) , v2 μ˜ˆμ •
    • 데이터 λ‘œλ”© μ‹œμ  : νŽ˜μ΄μ§€ μ§„μž… μ‹œ GET /api/v1/users/me API ν˜ΈμΆœ
  • /mypage/edit: λ§ˆμ΄νŽ˜μ΄μ§€ - μœ μ € 정보 μˆ˜μ •

    • μ£Όμš” κΈ°λŠ₯ : μœ μ € 정보 μˆ˜μ • (λ‹‰λ„€μž„ μ„€μ •, μ†Œκ°œκΈ€ μ„€μ •, ν”„λ‘œν•„ 이미지).
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • ProfileForm (@/features/user/components/profile-form/ProfileForm.tsx)
      • useUser (@/features/user/hooks/useUser.ts) (μˆ˜μ • μ „ 초기 데이터 λ‘œλ”©μš©)
    • 데이터 λ‘œλ”© μ‹œμ  : νŽ˜μ΄μ§€ μ§„μž… μ‹œ useUser ν›…을 톡해 GET /api/v1/users/me APIλ₯Ό ν˜ΈμΆœν•˜μ—¬ κΈ°μ‘΄ ν”„λ‘œν•„ 정보λ₯Ό ProfileForm의 defaultValues둜 전달.
    • λΌμš°νŒ… : ProfileForm의 onSubmit ν•Έλ“€λŸ¬μ—μ„œ PATCH /api/v1/users API 호좜 성곡 μ‹œ β†’ /mypage
  • /mypage/delete: λ§ˆμ΄νŽ˜μ΄μ§€ - νƒˆν‡΄

    • μ£Όμš” κΈ°λŠ₯ : μœ μ € νƒˆν‡΄ 처리.
    • μ‚¬μš© μ»΄ν¬λ„ŒνŠΈ :
      • DeleteAccountForm (@/features/user/components/delete-account/DeleteAccountForm.tsx)
    • λΌμš°νŒ… : DeleteAccountForm λ‚΄λΆ€μ—μ„œ DELETE /api/v1/users API 호좜 성곡 μ‹œ β†’ /onboarding

μ»΄ν¬λ„ŒνŠΈ

Consent : μ•½κ΄€ λ™μ˜ κΈ°λŠ₯

  • κ°œμš”

    • μ—­ν• src/app/(auth)/auth/consent/page.tsx λ‚΄μ—μ„œ PrivacyConsentLocationConsent μ»΄ν¬λ„ŒνŠΈμ™€ useConsent ν›…을 μ‘°ν•©ν•˜μ—¬ μ„œλΉ„μŠ€ μ΄μš©μ„ μœ„ν•œ μ•½κ΄€ λ™μ˜ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. 각 μ•½κ΄€ λ‚΄μš©μ€ ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈ 내에 직접 μž‘μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
  • κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ 및 ν›…

    • ConsentPage (src/app/(auth)/auth/consent/page.tsx): μ•½κ΄€ λ™μ˜ UI 전체λ₯Ό κ΅¬μ„±ν•˜λŠ” νŽ˜μ΄μ§€ μ»΄ν¬λ„ŒνŠΈμž…λ‹ˆλ‹€.
    • PrivacyConsent (@/features/user/components/consent/PrivacyConsent.tsx): κ°œμΈμ •λ³΄ 처리방침 λ™μ˜ UIλ₯Ό λ Œλ”λ§ν•©λ‹ˆλ‹€. (λ‚΄λΆ€μ μœΌλ‘œ ConsentCard μ‚¬μš©)
      • Propschecked: booleanonCheckedChange: (checked: boolean) => void
    • LocationConsent (@/features/user/components/consent/LocationConsent.tsx): μœ„μΉ˜κΈ°λ°˜ μ„œλΉ„μŠ€ μ΄μš©μ•½κ΄€ λ™μ˜ UIλ₯Ό λ Œλ”λ§ν•©λ‹ˆλ‹€. (λ‚΄λΆ€μ μœΌλ‘œ ConsentCard μ‚¬μš©)
      • Propschecked: booleanonCheckedChange: (checked: boolean) => void
    • ConsentCard (@/features/user/components/consent/ConsentCard.tsx): κ°œλ³„ μ•½κ΄€ λ‚΄μš©μ„ ν‘œμ‹œν•˜κ³  λ™μ˜ μ²΄ν¬λ°•μŠ€λ₯Ό μ œκ³΅ν•˜λŠ” UI μ»΄ν¬λ„ŒνŠΈμž…λ‹ˆλ‹€.
      • Propstitle: stringlabel: stringchecked: booleanonCheckedChange: (checked: boolean) => voidchildren: ReactNode
    • useConsent (@/features/user/hooks/useConsent.ts): μ•½κ΄€ λ™μ˜ μƒνƒœ 및 κ΄€λ ¨ λ‘œμ§μ„ κ΄€λ¦¬ν•˜λŠ” μ»€μŠ€ν…€ ν›…μž…λ‹ˆλ‹€.
      • λ°˜ν™˜ κ°’:

        {
          privacyConsent: boolean;
          setPrivacyConsent: (value: boolean) => void;
          locationConsent: boolean;
          setLocationConsent: (value: boolean) => void;
          handleAllConsent: () => void;
          handleAgreement: () => Promise<void>;
        }
  • μ£Όμš” 둜직 ( useConsent ν›… λ‚΄λΆ€ )

    • privacyConsentlocationConsent: 각 μ•½κ΄€μ˜ λ™μ˜ μƒνƒœ (boolean)
    • handleAllConsent: 전체 λ™μ˜ μƒνƒœλ₯Ό ν† κΈ€ν•©λ‹ˆλ‹€.
    • handleAgreement:
      • postAgreement({ location_agreed: boolean, privacy_agreed: boolean }) API (POST /api/v1/users/agreement)λ₯Ό ν˜ΈμΆœν•˜μ—¬ λ™μ˜ κ²°κ³Όλ₯Ό μ„œλ²„μ— μ „μ†‘ν•©λ‹ˆλ‹€.
      • API 호좜 성곡 μ‹œ /auth/signup νŽ˜μ΄μ§€λ‘œ λΌμš°νŒ…ν•©λ‹ˆλ‹€.
  • μ—λŸ¬ 처리 / μ—£μ§€ μΌ€μ΄μŠ€

    • handleAgreement ν•¨μˆ˜ λ‚΄ API 호좜 μ‹€νŒ¨ μ‹œ console.errorλ₯Ό 톡해 μ—λŸ¬κ°€ λ‘œκΉ…λ©λ‹ˆλ‹€.
    • ν•„μˆ˜ μ•½κ΄€ (κ°œμΈμ •λ³΄, μœ„μΉ˜μ •λ³΄) λͺ¨λ‘ λ™μ˜ν•΄μ•Ό 'λ‹€μŒ' λ²„νŠΌμ΄ ν™œμ„±ν™”λ©λ‹ˆλ‹€.
  • μŠ€νƒ€μΌλ§

    • ConsentCard λ‚΄λΆ€μ—μ„œ μ•½κ΄€ λ‚΄μš© 슀크둀 처리.
    • 각 μ•½κ΄€ 라벨에 "[ν•„μˆ˜]" ν…μŠ€νŠΈ 포함.

ProfileForm : μ‚¬μš©μž ν”„λ‘œν•„ μž…λ ₯/μˆ˜μ • 폼 μ»΄ν¬λ„ŒνŠΈ

  • κ°œμš”

    • μ—­ν• : μ‚¬μš©μž ν”„λ‘œν•„ 정보 (λ‹‰λ„€μž„, μ†Œκ°œκΈ€, ν”„λ‘œν•„ 이미지) μž…λ ₯ 및 μˆ˜μ •μ„ μœ„ν•œ 폼 μ»΄ν¬λ„ŒνŠΈμž…λ‹ˆλ‹€. (src/features/user/components/profile-form/ProfileForm.tsx)
  • Props & Interface

    interface ProfileFormProps {
      onSubmit: (data: ProfileFormValues) => Promise<void>;
      defaultValues?: Partial<ProfileFormValues>;
      buttonText?: string; // κΈ°λ³Έκ°’: 'μ €μž₯'
    }
    
    type ProfileFormValues = {
      profile_image?: string;
      username: string;
      introduction?: string;
    };
    • onSubmit (required): 폼 제좜 μ‹œ 호좜될 ν•¨μˆ˜. ProfileFormValuesλ₯Ό 인자둜 λ°›μŠ΅λ‹ˆλ‹€.
    • defaultValues (optional): 폼 μ΄ˆκΈ°κ°’μ„ μ„€μ •ν•©λ‹ˆλ‹€. (예: ν”„λ‘œν•„ μˆ˜μ • μ‹œ κΈ°μ‘΄ 데이터)
    • buttonText (optional): 제좜 λ²„νŠΌμ˜ ν…μŠ€νŠΈλ₯Ό μ„€μ •ν•©λ‹ˆλ‹€. (κΈ°λ³Έκ°’: "μ €μž₯")
  • λ‚΄λΆ€ μƒνƒœ & μ£Όμš” κΈ°λŠ₯

    • 폼 관리react-hook-form λΌμ΄λΈŒλŸ¬λ¦¬μ™€ zodλ₯Ό μ‚¬μš©ν•˜μ—¬ 폼 μƒνƒœ 관리 및 μœ νš¨μ„± 검증을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
    • ν”„λ‘œν•„ 이미지 처리:
      • useImageUpload μ»€μŠ€ν…€ 훅을 μ‚¬μš©ν•˜μ—¬ 이미지 μ—…λ‘œλ“œ λ‘œμ§μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
      • μ‚¬μš©μžκ°€ 이미지λ₯Ό μ„ νƒν•˜λ©΄ 미리보기λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
      • 파일 μ œν•œ: μ΅œλŒ€ 5MB, ν—ˆμš© ν™•μž₯자 (.jpg.jpeg.png.webp).
      • 이미지 μœ νš¨μ„± 검사 μ‹€νŒ¨ λ˜λŠ” μ—…λ‘œλ“œ μ‹€νŒ¨ μ‹œ μ—λŸ¬ λ©”μ‹œμ§€λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.
      • 이미지 μ—…λ‘œλ“œ 성곡 μ‹œ, onSubmit으둜 μ „λ‹¬λ˜λŠ” profile_image ν•„λ“œμ— μ—…λ‘œλ“œλœ URL이 ν¬ν•¨λ©λ‹ˆλ‹€.
    • λ‹‰λ„€μž„ (username):
      • ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.
      • μœ νš¨μ„± 검증: μ΅œμ†Œ 2자, μ΅œλŒ€ 10자, 특수문자 μ‚¬μš© λΆˆκ°€ (/^[κ°€-힣a-zA-Z0-9\s]+$/).
      • μœ νš¨μ„± 검증 μ‹€νŒ¨ μ‹œ μ—λŸ¬ λ©”μ‹œμ§€κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.
      • μ°Έκ³ : λ‹‰λ„€μž„ 쀑볡 μ‹€μ‹œκ°„ 체크 κΈ°λŠ₯은 ProfileForm μ»΄ν¬λ„ŒνŠΈ μžμ²΄μ—λŠ” ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 쀑볡 μ²΄ν¬λŠ” onSubmit ν•Έλ“€λŸ¬λ₯Ό 톡해 μ„œλ²„ μΈ‘μ—μ„œ μ²˜λ¦¬λ˜κ±°λ‚˜, λ³„λ„μ˜ 둜직으둜 κ΅¬ν˜„λ  수 μžˆμŠ΅λ‹ˆλ‹€.
    • μ†Œκ°œκΈ€ (introduction):
      • 선택 μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.
      • μœ νš¨μ„± 검증: μ΅œλŒ€ 70자.
      • μœ νš¨μ„± 검증 μ‹€νŒ¨ μ‹œ μ—λŸ¬ λ©”μ‹œμ§€κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.
  • μ‚¬μš© μ˜ˆμ‹œ (Usage)

    import { ProfileForm } from '@/features/user/components/profile-form';
    
    function SignupPage() {
      const handleSignup = async (data: ProfileFormValues) => {
        // νšŒμ›κ°€μž… API 호좜 (data.profile_imageλŠ” URLμž„)
        // 예: await api.signup(data);
      };
    
      return <ProfileForm onSubmit={handleSignup} buttonText="κ°€μž…ν•˜κΈ°" />;
    }
    
    function ProfileEditPage() {
      const userInitialData = await api.getUser(); // μœ μ € 정보 쑰회 API 호좜
    
      const handleProfileUpdate = async (data: ProfileFormValues) => {
        // ν”„λ‘œν•„ μˆ˜μ • API 호좜
        // 예: await api.updateProfile(data);
      };
    
      return (
        <ProfileForm
          defaultValues={userInitialData}
          onSubmit={handleProfileUpdate}
          buttonText="μˆ˜μ •μ™„λ£Œ"
        />
      );
    }
  • μ—λŸ¬μ²˜λ¦¬ / μ—£μ§€ μΌ€μ΄μŠ€

    • 이미지 κ΄€λ ¨: 파일 크기 초과, μ§€μ›ν•˜μ§€ μ•ŠλŠ” ν™•μž₯자 선택 μ‹œ 폼 λ‚΄ μ—λŸ¬ λ©”μ‹œμ§€ ν‘œμ‹œ.
    • 폼 μœ νš¨μ„±: 각 ν•„λ“œ(λ‹‰λ„€μž„, μ†Œκ°œκΈ€)의 μœ νš¨μ„± κ·œμΉ™ μœ„λ°˜ μ‹œ ν•΄λ‹Ή ν•„λ“œ μ•„λž˜ μ—λŸ¬ λ©”μ‹œμ§€ ν‘œμ‹œ.
    • ν•„μˆ˜ ν•„λ“œ (λ‹‰λ„€μž„) λˆ„λ½ μ‹œ μ—λŸ¬ λ©”μ‹œμ§€.
  • μŠ€νƒ€μΌλ§

    • μž…λ ₯ ν•„λ“œ λ ˆμ΄μ•„μ›ƒ 및 μ—λŸ¬ λ©”μ‹œμ§€ μŠ€νƒ€μΌλ§.
    • 이미지 미리보기 μ˜μ—­ (μ›ν˜•, ν΄λ¦­ν•˜μ—¬ 파일 선택).
    • 폼 제좜 쀑 λ²„νŠΌ λΉ„ν™œμ„±ν™” 및 λ‘œλ”© μƒνƒœ μ‹œκ°μ  ν”Όλ“œλ°± (예: 이미지 μ˜μ—­μ— "μ—…λ‘œλ“œμ€‘...").
  • Storybook

    • Empty FormdefaultValues μ—†μ΄ κΈ°λ³Έ μƒνƒœ (νšŒμ›κ°€μž… μ‹œλ‚˜λ¦¬μ˜€).
    • With ValuesdefaultValuesλ₯Ό μ „λ‹¬ν•˜μ—¬ μ΄ˆκΈ°κ°’μ΄ μ±„μ›Œμ§„ μƒνƒœ (ν”„λ‘œν•„ μˆ˜μ • μ‹œλ‚˜λ¦¬μ˜€).
    • With Errors: 각 ν•„λ“œμ— μœ νš¨μ„± 검증 μ—λŸ¬κ°€ λ°œμƒν•œ μƒνƒœ.

Profile : ν”„λ‘œν•„ 정보 ν‘œμ‹œ μ»΄ν¬λ„ŒνŠΈ

  • κ°œμš”

    • μ—­ν• : μ‚¬μš©μžμ˜ ν”„λ‘œν•„ 이미지, λ‹‰λ„€μž„, μ†Œκ°œκΈ€μ„ μ’…ν•©μ μœΌλ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€. (src/features/user/components/profile/Profile.tsx)
    • 이 μ»΄ν¬λ„ŒνŠΈλŠ” κΈ°μ‘΄ λ¬Έμ„œμ˜ ProfileSummaryCard와 ProfileDetailCard의 κΈ°λŠ₯을 ν†΅ν•©ν•˜μ—¬ μ œκ³΅ν•©λ‹ˆλ‹€.
  • Props & Interface

    interface ProfileProps {
      username: string;
      profile_image: string; // 이미지 URL
      introduction: string;
    }
    • username (required): μ‚¬μš©μžμ˜ λ‹‰λ„€μž„.
    • profile_image (required): μ‚¬μš©μžμ˜ ν”„λ‘œν•„ 이미지 URL. 이미지가 없을 경우 빈 λ¬Έμžμ—΄ 전달 μ‹œ κΈ°λ³Έ μ•„μ΄μ½˜μ΄ ν‘œμ‹œλ©λ‹ˆλ‹€.
    • introduction (required): μ‚¬μš©μžμ˜ μ†Œκ°œκΈ€. μ€„λ°”κΏˆμ€ whitespace-pre-line으둜 μ²˜λ¦¬λ©λ‹ˆλ‹€.
  • μ‚¬μš© μ˜ˆμ‹œ (Usage)

    <Profile
      username="λ©‹μ§„λ‹‰λ„€μž„"
      profile_image="/path/to/image.jpg"
      introduction="μ•ˆλ…•ν•˜μ„Έμš”.\nλ§Œλ‚˜μ„œ λ°˜κ°‘μŠ΅λ‹ˆλ‹€."
    />
  • μ—λŸ¬μ²˜λ¦¬ / μ—£μ§€ μΌ€μ΄μŠ€

    • profile_imageκ°€ μ œκ³΅λ˜μ§€ μ•Šκ±°λ‚˜ 빈 λ¬Έμžμ—΄μΌ 경우, κΈ°λ³Έ μ‚¬μš©μž μ•„μ΄μ½˜μ„ ν‘œμ‹œν•©λ‹ˆλ‹€.
  • μŠ€νƒ€μΌλ§

    • ν”„λ‘œν•„ 이미지 μ›ν˜• μŠ€νƒ€μΌλ§ 및 κΈ°λ³Έ μ•„μ΄μ½˜ μŠ€νƒ€μΌ.
    • λ‹‰λ„€μž„ (heading-2), μ†Œκ°œκΈ€ (body-text, text-gray-600) μŠ€νƒ€μΌλ§.
  • Storybook

    • Default: λͺ¨λ“  정보(이미지, λ‹‰λ„€μž„, μ†Œκ°œκΈ€)κ°€ μ •μƒμ μœΌλ‘œ ν‘œμ‹œλ˜λŠ” μƒνƒœ.
    • No Image: ν”„λ‘œν•„ 이미지가 μ—†λŠ” μƒνƒœ (κΈ°λ³Έ μ•„μ΄μ½˜ ν‘œμ‹œ).
    • Long Introduction: μ—¬λŸ¬ μ€„μ˜ κΈ΄ μ†Œκ°œκΈ€μ΄ μžˆλŠ” μƒνƒœ.

SavedPlaceListCard : λ‚΄κ°€ μ €μž₯ν•œ μž₯μ†Œ λͺ©λ‘

  • κ°œμš”
    • μ—­ν• : μ‚¬μš©μžκ°€ μ €μž₯ν•œ μž₯μ†Œ λͺ©λ‘μ„ μΉ΄λ“œ ν˜•νƒœλ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€.
  • ν˜„μž¬ μƒνƒœ: (2024-07-29 κΈ°μ€€ μ½”λ“œ) ν˜„μž¬ μ—†μŒ v2 에 μΆ”κ°€ 개발 μ˜ˆμ •

DeleteAccountForm : νšŒμ› νƒˆν‡΄ 폼 μ»΄ν¬λ„ŒνŠΈ

  • κ°œμš”
    • μ—­ν• : νšŒμ› νƒˆν‡΄ κ΄€λ ¨ μœ μ˜μ‚¬ν•­μ„ μ•ˆλ‚΄ν•˜κ³ , μ‚¬μš©μžμ˜ λ™μ˜λ₯Ό λ°›μ•„ μ‹€μ œ νƒˆν‡΄λ₯Ό μ²˜λ¦¬ν•˜λŠ” 폼 μ»΄ν¬λ„ŒνŠΈμž…λ‹ˆλ‹€. (src/features/user/components/delete-account/DeleteAccountForm.tsx)
    • 이 μ»΄ν¬λ„ŒνŠΈλŠ” κΈ°μ‘΄ λ¬Έμ„œμ˜ AccountDeletionNotice와 AccountDeletionButton의 κΈ°λŠ₯을 ν†΅ν•©ν•˜μ—¬ μ œκ³΅ν•©λ‹ˆλ‹€.
  • Props & Interface: λ³„λ„μ˜ Propsλ₯Ό λ°›μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • λ‚΄λΆ€ μƒνƒœ & μ£Όμš” κΈ°λŠ₯
    • μœ μ˜μ‚¬ν•­ μ•ˆλ‚΄: μ»΄ν¬λ„ŒνŠΈ 내에 ν•˜λ“œμ½”λ”©λœ νƒˆν‡΄ κ΄€λ ¨ μœ μ˜μ‚¬ν•­μ„ λͺ©λ‘ ν˜•νƒœλ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€.
    • νƒˆν‡΄ λ™μ˜: μ‚¬μš©μžλŠ” "μœ„ λ‚΄μš©μ„ λͺ¨λ‘ ν™•μΈν•˜μ˜€μœΌλ©°, νƒˆν‡΄μ— λ™μ˜ν•©λ‹ˆλ‹€." μ²΄ν¬λ°•μŠ€λ₯Ό 선택해야 ν•©λ‹ˆλ‹€.
    • μƒνƒœ 관리isAgreed (boolean) λ‚΄λΆ€ μƒνƒœλ₯Ό 톡해 μ‚¬μš©μžμ˜ λ™μ˜ μ—¬λΆ€λ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€.
    • νƒˆν‡΄ μ‹€ν–‰ (handleDelete ν•¨μˆ˜):
      • isAgreedκ°€ true일 λ•Œ ν™œμ„±ν™”λ˜λŠ” "νƒˆν‡΄ν•˜κΈ°" λ²„νŠΌμ„ 톡해 μ‹€ν–‰λ©λ‹ˆλ‹€.
      • deleteUser() API (DELETE /api/v1/users)λ₯Ό ν˜ΈμΆœν•˜μ—¬ νšŒμ› νƒˆν‡΄λ₯Ό μš”μ²­ν•©λ‹ˆλ‹€.
  • μŠ€νƒ€μΌλ§
    • μœ μ˜μ‚¬ν•­ ν…μŠ€νŠΈ, λ™μ˜ μ²΄ν¬λ°•μŠ€, "νƒˆν‡΄ν•˜κΈ°" λ²„νŠΌ (ν™œμ„±/λΉ„ν™œμ„± μƒνƒœ 포함)에 λŒ€ν•œ μŠ€νƒ€μΌλ§μ΄ μ μš©λ©λ‹ˆλ‹€.
    • "νƒˆν‡΄ν•˜κΈ°" λ²„νŠΌμ€ λ™μ˜ μ‹œ 빨간색 κ³„μ—΄λ‘œ κ°•μ‘°λ©λ‹ˆλ‹€.
  • Storybook
    • Default: κΈ°λ³Έ νšŒμ› νƒˆν‡΄ 폼 (μœ μ˜μ‚¬ν•­ 및 λ™μ˜ μ²΄ν¬λ°•μŠ€ ν‘œμ‹œ).
    • Agreed: μ‚¬μš©μžκ°€ νƒˆν‡΄μ— λ™μ˜ν•˜μ—¬ λ²„νŠΌμ΄ ν™œμ„±ν™”λœ μƒνƒœ.

μ»΄ν¬λ„ŒνŠΈ κ°„ 관계

  • Consent κ΄€λ ¨ κΈ°λŠ₯ (src/app/(auth)/auth/consent/page.tsx μ€‘μ‹¬μœΌλ‘œ):

    • μ—­ν• : μ•½κ΄€ λ™μ˜ νŽ˜μ΄μ§€μ˜ 전체적인 UI와 μ‚¬μš©μž μΈν„°λž™μ…˜ 및 데이터 흐름을 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
    • ꡬ성 μš”μ†Œ 및 관계:
      • ConsentPage (page.tsx):
        • useConsent ν›…을 μ‚¬μš©ν•˜μ—¬ μ•½κ΄€ λ™μ˜ μƒνƒœ (privacyConsentlocationConsent) 및 제좜 둜직 (handleAgreement)을 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
        • PrivacyConsent λ° LocationConsent μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ 각 μ•½κ΄€ λ‚΄μš©μ„ 화면에 ν‘œμ‹œν•˜κ³ , 이듀 μ»΄ν¬λ„ŒνŠΈμ— useConsent ν›…μ—μ„œ 받은 μƒνƒœμ™€ μƒνƒœ λ³€κ²½ ν•¨μˆ˜λ₯Ό props둜 μ „λ‹¬ν•©λ‹ˆλ‹€.
      • useConsent (@/features/user/hooks/useConsent.ts):
        • μ•½κ΄€ λ™μ˜ μ—¬λΆ€ (privacyConsentlocationConsent)λ₯Ό useState둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
        • setPrivacyConsentsetLocationConsent ν•¨μˆ˜λ₯Ό 톡해 각 λ™μ˜ μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
        • handleAllConsent ν•¨μˆ˜λ‘œ 전체 λ™μ˜/ν•΄μ œλ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
        • handleAgreement ν•¨μˆ˜μ—μ„œ postAgreement APIλ₯Ό ν˜ΈμΆœν•˜μ—¬ μ„œλ²„μ— λ™μ˜ 데이터λ₯Ό μ „μ†‘ν•˜κ³ , 성곡 μ‹œ λ‹€μŒ νŽ˜μ΄μ§€λ‘œ λΌμš°νŒ…ν•©λ‹ˆλ‹€.
      • PrivacyConsent / LocationConsent (@/features/user/components/consent/):
        • 각각 κ°œμΈμ •λ³΄ 처리방침과 μœ„μΉ˜κΈ°λ°˜ μ„œλΉ„μŠ€ μ΄μš©μ•½κ΄€μ˜ UIλ₯Ό λ‹΄λ‹Ήν•©λ‹ˆλ‹€.
        • λ‚΄λΆ€μ μœΌλ‘œ ConsentCard μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ μ•½κ΄€ 제λͺ©, λ‚΄μš©, λ™μ˜ μ²΄ν¬λ°•μŠ€λ₯Ό μΌκ΄€λœ ν˜•νƒœλ‘œ λ Œλ”λ§ν•©λ‹ˆλ‹€.
        • ConsentPageλ‘œλΆ€ν„° checked (λ™μ˜ μƒνƒœ)와 onCheckedChange (μƒνƒœ λ³€κ²½ ν•¨μˆ˜)λ₯Ό props둜 λ°›μ•„ ConsentCard λ° λ‚΄λΆ€ Checkbox와 μ—°λ™ν•©λ‹ˆλ‹€.
      • ConsentCard (@/features/user/components/consent/ConsentCard.tsx):
        • μ•½κ΄€ 제λͺ©, 슀크둀 κ°€λŠ₯ν•œ μ•½κ΄€ λ‚΄μš© μ˜μ—­, 그리고 ν•˜λ‹¨μ˜ λ™μ˜ 문ꡬ와 Checkboxλ₯Ό ν¬ν•¨ν•˜λŠ” μž¬μ‚¬μš© κ°€λŠ₯ν•œ UI μΉ΄λ“œμž…λ‹ˆλ‹€.
        • PrivacyConsent λ° LocationConsent μ»΄ν¬λ„ŒνŠΈμ—κ²Œ UI ꡬ쑰λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
  • νšŒμ›κ°€μž… νŽ˜μ΄μ§€ (src/app/(auth)/auth/signup/page.tsx) κ΄€λ ¨:

    • μ—­ν• : μ‚¬μš©μžκ°€ λ‹‰λ„€μž„, ν”„λ‘œν•„ 이미지, μ†Œκ°œκΈ€ λ“±μ˜ μΆ”κ°€ 정보λ₯Ό μž…λ ₯ν•˜μ—¬ νšŒμ›κ°€μž…μ„ μ™„λ£Œν•˜λŠ” νŽ˜μ΄μ§€μž…λ‹ˆλ‹€.
    • ꡬ성 μš”μ†Œ 및 관계:
      • SignupPage (page.tsx):
        • ProfileForm μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš©μž μž…λ ₯을 λ°›μŠ΅λ‹ˆλ‹€.
        • ProfileForm의 onSubmit ν•Έλ“€λŸ¬μ—μ„œ signup API (POST /api/v1/users)λ₯Ό ν˜ΈμΆœν•˜μ—¬ νšŒμ›κ°€μž… 처리λ₯Ό μˆ˜ν–‰ν•˜κ³ , 성곡 μ‹œ 메인 νŽ˜μ΄μ§€ λ“±μœΌλ‘œ λΌμš°νŒ…ν•©λ‹ˆλ‹€.
      • ProfileForm (@/features/user/components/profile-form/ProfileForm.tsx):
        • νšŒμ›κ°€μž…μ— ν•„μš”ν•œ ν•„λ“œ(λ‹‰λ„€μž„, ν”„λ‘œν•„ 이미지, μ†Œκ°œκΈ€)λ₯Ό μ œκ³΅ν•˜κ³  μœ νš¨μ„± 검증을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
        • 이미지 μ—…λ‘œλ“œ, μž…λ ₯ κ°’ 관리 λ“±μ˜ λ‘œμ§μ„ λ‚΄λΆ€μ μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
        • μ‚¬μš©μžκ°€ μž…λ ₯ν•œ 데이터λ₯Ό onSubmit prop을 톡해 SignupPage둜 μ „λ‹¬ν•©λ‹ˆλ‹€.
  • λ§ˆμ΄νŽ˜μ΄μ§€ (src/app/(main)/mypage/page.tsx) κ΄€λ ¨:

    • μ—­ν• : λ‘œκ·ΈμΈν•œ μ‚¬μš©μžμ˜ ν”„λ‘œν•„ 정보λ₯Ό 보여주고, ν”„λ‘œν•„ μˆ˜μ •, λ‘œκ·Έμ•„μ›ƒ, νšŒμ› νƒˆν‡΄ λ“±μ˜ κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” νŽ˜μ΄μ§€μž…λ‹ˆλ‹€.
    • ꡬ성 μš”μ†Œ 및 관계:
      • MyPage (page.tsx):
        • useUser ν›…을 μ‚¬μš©ν•˜μ—¬ ν˜„μž¬ 둜그인된 μ‚¬μš©μž 정보λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. (λ‚΄λΆ€μ μœΌλ‘œ getUser API - GET /api/v1/users/me ν˜ΈμΆœ)
        • 쑰회된 μ‚¬μš©μž 정보λ₯Ό Profile μ»΄ν¬λ„ŒνŠΈμ— μ „λ‹¬ν•˜μ—¬ 화면에 ν”„λ‘œν•„μ„ ν‘œμ‹œν•©λ‹ˆλ‹€.
        • "νšŒμ›μ •λ³΄ μˆ˜μ •", "λ‘œκ·Έμ•„μ›ƒ", "νšŒμ› νƒˆν‡΄ν•˜κΈ°" λ²„νŠΌμ„ μ œκ³΅ν•˜λ©°, 각 λ²„νŠΌ 클릭 μ‹œ ν•΄λ‹Ή νŽ˜μ΄μ§€λ‘œ λΌμš°νŒ…ν•˜κ±°λ‚˜ κ΄€λ ¨ API (logout)λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
      • useUser (@/features/user/hooks/useUser.ts):
        • μ‚¬μš©μž 정보λ₯Ό κ°€μ Έμ˜€κ³  μƒνƒœλ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€.
      • Profile (@/features/user/components/profile/Profile.tsx):
        • MyPageλ‘œλΆ€ν„° 받은 μ‚¬μš©μž 정보(λ‹‰λ„€μž„, ν”„λ‘œν•„ 이미지 URL, μ†Œκ°œκΈ€)λ₯Ό UI에 ν‘œμ‹œν•©λ‹ˆλ‹€.
  • ν”„λ‘œν•„ μˆ˜μ • νŽ˜μ΄μ§€ (src/app/(main)/mypage/edit/page.tsx) κ΄€λ ¨:

    • μ—­ν• : μ‚¬μš©μžκ°€ κΈ°μ‘΄ ν”„λ‘œν•„ 정보(λ‹‰λ„€μž„, ν”„λ‘œν•„ 이미지, μ†Œκ°œκΈ€)λ₯Ό μˆ˜μ •ν•˜λŠ” νŽ˜μ΄μ§€μž…λ‹ˆλ‹€.
    • ꡬ성 μš”μ†Œ 및 관계:
      • ProfileEditPage (page.tsx):
        • useUser ν›…을 μ‚¬μš©ν•˜μ—¬ ν˜„μž¬ μ‚¬μš©μž 정보λ₯Ό μ‘°νšŒν•˜κ³ , 이 정보λ₯Ό ProfileForm의 defaultValues둜 μ „λ‹¬ν•©λ‹ˆλ‹€.
        • ProfileForm μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš©μž μž…λ ₯을 λ°›μŠ΅λ‹ˆλ‹€.
        • ProfileForm의 onSubmit ν•Έλ“€λŸ¬μ—μ„œ patchUser API (PATCH /api/v1/users)λ₯Ό ν˜ΈμΆœν•˜μ—¬ ν”„λ‘œν•„ μˆ˜μ •μ„ μš”μ²­ν•˜κ³ , 성곡 μ‹œ λ§ˆμ΄νŽ˜μ΄μ§€ λ“±μœΌλ‘œ λΌμš°νŒ…ν•©λ‹ˆλ‹€.
      • useUser (@/features/user/hooks/useUser.ts): ν˜„μž¬ μ‚¬μš©μž 정보λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
      • ProfileForm (@/features/user/components/profile-form/ProfileForm.tsx):
        • ν”„λ‘œν•„ μˆ˜μ •μ— ν•„μš”ν•œ ν•„λ“œλ₯Ό μ œκ³΅ν•˜κ³ , defaultValues둜 κΈ°μ‘΄ 정보λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.
        • μ‚¬μš©μžκ°€ μˆ˜μ •ν•œ 데이터λ₯Ό onSubmit prop을 톡해 ProfileEditPage둜 μ „λ‹¬ν•©λ‹ˆλ‹€.
  • νšŒμ› νƒˆν‡΄ νŽ˜μ΄μ§€ (src/app/(main)/mypage/delete/page.tsx) κ΄€λ ¨:

    • μ—­ν• : μ‚¬μš©μžκ°€ μ„œλΉ„μŠ€μ—μ„œ νƒˆν‡΄ν•˜λŠ” 절차λ₯Ό μ§„ν–‰ν•˜λŠ” νŽ˜μ΄μ§€μž…λ‹ˆλ‹€.
    • ꡬ성 μš”μ†Œ 및 관계:
      • DeleteAccountPage (page.tsx):
        • DeleteAccountForm μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ νƒˆν‡΄ μœ μ˜μ‚¬ν•­ μ•ˆλ‚΄ 및 λ™μ˜λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
      • DeleteAccountForm (@/features/user/components/delete-account/DeleteAccountForm.tsx):
        • νƒˆν‡΄ μœ μ˜μ‚¬ν•­μ„ ν‘œμ‹œν•˜κ³ , μ‚¬μš©μžλ‘œλΆ€ν„° νƒˆν‡΄ λ™μ˜λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
        • λ™μ˜ μ‹œ, λ‚΄λΆ€μ μœΌλ‘œ deleteUser API (DELETE /api/v1/users)λ₯Ό ν˜ΈμΆœν•˜κ³ , 성곡 μ‹œ μ˜¨λ³΄λ”© νŽ˜μ΄μ§€ λ“±μœΌλ‘œ λΌμš°νŒ…ν•©λ‹ˆλ‹€.

μƒνƒœ 관리 μ „λž΅

  • User Hook (useUser - @/features/user/hooks/useUser.ts):
    • μ‚¬μš©μž 정보 (User | null)λ₯Ό React useStateλ₯Ό 톡해 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
    • μ»΄ν¬λ„ŒνŠΈ 마운트 μ‹œ useEffect λ‚΄μ—μ„œ getUser API (GET /api/v1/users/me)λ₯Ό ν˜ΈμΆœν•˜μ—¬ μ‚¬μš©μž 정보λ₯Ό 가져와 μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
    • Zustand와 같은 λ³„λ„μ˜ μ „μ—­ μƒνƒœ 관리 라이브러리λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ , React의 κΈ°λ³Έ 훅을 ν™œμš©ν•œ 둜컬 μƒνƒœ 관리 및 API 연동 방식을 λ”°λ¦…λ‹ˆλ‹€.
    • λ°˜ν™˜κ°’{ user: User | null }
  • Local State (React useState):
    • ProfileForm: 폼 μž…λ ₯ κ°’, μœ νš¨μ„± 검증 μƒνƒœ, 이미지 미리보기 URL, 제좜 쀑 μƒνƒœ 등을 useState λ° react-hook-form의 λ‚΄λΆ€ μƒνƒœλ‘œ κ΄€λ¦¬ν•©λ‹ˆλ‹€.
    • useConsent: μ•½κ΄€ λ™μ˜ μ—¬λΆ€ (privacyConsentlocationConsent)λ₯Ό useState둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
    • DeleteAccountForm: νƒˆν‡΄ λ™μ˜ μ—¬λΆ€ (isAgreed)λ₯Ό useState둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
  • Server Cache State (Tanstack Query λ“±):
    • ν˜„μž¬λŠ” useEffect λ˜λŠ” 일반 비동기 ν•¨μˆ˜ λ‚΄μ—μ„œ μ»€μŠ€ν…€ API ν•¨μˆ˜λ₯Ό 톡해 ν˜ΈμΆœν•˜λŠ” λ°©μ‹μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€.
    • ν”„λ‘œν•„ 이미지 μ—…λ‘œλ“œuseImageUpload ν›…을 톡해 이미지가 μ„œλ²„(예: S3)에 μ—…λ‘œλ“œλœ ν›„ λ°˜ν™˜λ˜λŠ” URL을 μ‚¬μš©ν•©λ‹ˆλ‹€.
    • μ €μž₯된 μž₯μ†Œ 데이터: v2 μ˜ˆμ •

API 연동 (API Integration)

  • ν˜ΈμΆœν•  λ°±μ—”λ“œ API λͺ©λ‘
    • GET /api/v1/auth/oauth : 둜그인 μš”μ²­
    • POST /api/v1/auth/tokens : 둜그인
    • POST /api/v1/auth/token/refresh : 토큰 μž¬λ°œκΈ‰
    • POST /api/v1/auth/logout : λ‘œκ·Έμ•„μ›ƒ
    • POST /api/v1/users/agreement : 개인 정보 및 μœ„μΉ˜ 정보 λ™μ˜
    • POST /api/v1/users : νšŒμ›κ°€μž…
    • GET /api/v1/users/me : 본인 ν”„λ‘œν•„ 쑰회
    • PATCH /api/v1/users : 본인 ν”„λ‘œν•„ μˆ˜μ •
    • DELETE /api/v1/users : μœ μ € νƒˆν‡΄
  • API 호좜 처리
    • API μš”μ²­/응닡 μƒνƒœμ— λ”°λ₯Έ λ‘œλ”© 문ꡬ ν‘œμ‹œ
    • fetch μ‚¬μš©
    • API 였λ₯˜ λ°œμƒ μ‹œ μ‚¬μš©μžμ—κ²Œ μΉœν™”μ μΈ λ©”μ‹œμ§€ ν‘œμ‹œ 및 λ‘œκΉ….
    • Tanstack Queryλ₯Ό ν™œμš©ν•˜μ—¬ 쀑볡 호좜 λ°©μ§€, μΊμ‹œ 관리, μžλ™ μž¬μš”μ²­ λ“± 처리 μ˜ˆμ •
⚠️ **GitHub.com Fallback** ⚠️