Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
378 changes: 378 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
# Dori-iOS ๊ฐœ๋ฐœ ์ง€์นจ์„œ

---

## 1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

**์•ฑ ์ด๋ฆ„**: Dori(๋„๋ฆฌ)
**๋ชฉ์ **: ๊ฒฝ์กฐ์‚ฌ ๊ธˆ์ „ ๊ฑฐ๋ž˜(์ฃผ๋„๋ฆฌ/๋ฐ›๋„๋ฆฌ)๋ฅผ ์ถ”์ ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” iOS ์•ฑ

### MVP ๋ฒ”์œ„

| ๊ตฌ๋ถ„ | ํ•ญ๋ชฉ |
|------|------|
| **IN** | KakaoLogin, Keychain ์ธ์ฆ, 3ํƒญ(์บ˜๋ฆฐ๋”/๋‚ด์—ญ/๋งˆ์ดํŽ˜์ด์ง€), ์ „์ฒด CRUD(๋“ฑ๋ก/์กฐํšŒ/์ˆ˜์ •/์‚ญ์ œ), ์„œ๋ฒ„ API ์—ฐ๋™ |
| **OUT** | Apple Login, ์•Œ๋ฆผ(Push Notification), ๋‚ด๋ณด๋‚ด๊ธฐ(CSV/PDF), ์†Œ์…œ ๊ธฐ๋Šฅ |

---

## 2. ๊ธฐ์ˆ  ์Šคํƒ

| ํ•ญ๋ชฉ | ๋ฒ„์ „/์„ค์ • |
|------|-----------|
| Swift | 6 |
| iOS Deployment Target | 17.6+ |
| Xcode | 26.2 |
| ์•„ํ‚คํ…์ฒ˜ | TCA (swift-composable-architecture) ์ตœ์‹  ์•ˆ์ • ๋ฒ„์ „ |
| ์ธ์ฆ SDK | KakaoSDK (`KakaoSDKAuth`, `KakaoSDKUser`) |
| ๋ณด์•ˆ ์ €์žฅ์†Œ | KeychainAccess |
| Concurrency | Swift 6 Strict Concurrency |

### Swift 6 ๋นŒ๋“œ ์„ค์ •

| Build Setting | ๊ฐ’ |
|---|---|
| `SWIFT_APPROACHABLE_CONCURRENCY` | `YES` |
| `SWIFT_DEFAULT_ACTOR_ISOLATION` | `MainActor` |

---

## 3. ์•„ํ‚คํ…์ฒ˜

### 3.1 TCA ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ”Œ๋กœ์šฐ

๊ธฐ์กด `temp/` MVVM ํ”„๋กœํ† ํƒ€์ž… ์ฝ”๋“œ๋ฅผ TCA๋กœ ์ „ํ™˜ํ•œ๋‹ค.

- ๊ฐ ํ™”๋ฉด = 1๊ฐœ `@Reducer` + 1๊ฐœ SwiftUI `View`
- ์ƒํƒœ ๋ณ€๊ฒฝ์€ ๋ฐ˜๋“œ์‹œ Action โ†’ Reducer โ†’ State ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ฅธ๋‹ค
- Side Effect๋Š” `Effect<Action>`์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค

### 3.2 Navigation ์ „๋žต

| ๋ฒ”์œ„ | ๋ฐฉ์‹ | ์„ค๋ช… |
|------|------|------|
| App Root | Tree-based | `enum State`๋กœ Intro / MainTab ๋ถ„๊ธฐ |
| Tab ๋‚ด๋ถ€ (History) | Stack-based | `StackState` / `StackAction`๋กœ list โ†’ detail push |
| ๋ชจ๋‹ฌ/์‹œํŠธ | Tree-based | `@Presents` / `ifLet`์œผ๋กœ ๋ชจ๋‹ฌ ํ‘œ์‹œ |

### 3.3 ์˜์กด์„ฑ ๊ด€๋ฆฌ

- TCA `@Dependency` ์‹œ์Šคํ…œ ์‚ฌ์šฉ
- Protocol ๋Œ€์‹  **struct + closure** ํŒจํ„ด (`DependencyKey` ์ค€์ˆ˜)
- `liveValue` / `previewValue` / `testValue` 3๋‹จ ๋ถ„๋ฆฌ

### 3.4 ์ฝ”๋“œ ํŒจํ„ด ์ฐธ์กฐ

**์ž๋™ํ™” ์Šคํ‚ฌ** (boilerplate ์ƒ์„ฑ์šฉ):

| Skill | ์šฉ๋„ |
|-------|------|
| `/tca-reducer` | @Reducer ๊ตฌ์กฐ ์ƒ์„ฑ |
| `/tca-view` | TCA Store ์—ฐ๊ฒฐ SwiftUI View |
| `/tca-client` | @Dependency Client ๊ตฌ์กฐ |

**๋ ˆํผ๋Ÿฐ์Šค ๋ฌธ์„œ** (๋ถ„๊ธฐ/๋„๋ฉ”์ธ ์ง€์‹ ํ•„์š”):

| ๋ฌธ์„œ | ์šฉ๋„ |
|------|------|
| `docs/tca-navigation.md` | Tree/Stack/Tab/AppRoot Navigation ํŒจํ„ด |
| `docs/tca-test.md` | TestStore ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ ํŒจํ„ด |
| `docs/tca-network.md` | API Client + ์—๋Ÿฌ ํ•ธ๋“ค๋ง |

---

## 4. Feature ๋ช…์„ธ

### 4.1 Intro (์ธํŠธ๋กœ/์ธ์ฆ)

**ํ๋ฆ„**: KakaoLogin โ†’ ํ† ํฐ ํš๋“ โ†’ Keychain ์ €์žฅ โ†’ MainTab ์ „ํ™˜

**IntroFeature State**:
- `isLoading: Bool`
- `errorMessage: String?`

**Dependencies**:
- `KakaoAuthClient` โ€” ์นด์นด์˜ค ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ
- `KeychainClient` โ€” ํ† ํฐ ์ €์žฅ/์กฐํšŒ/์‚ญ์ œ

**์ฃผ์š” Action**:
- `loginButtonTapped` โ†’ `.run`์—์„œ KakaoAuthClient ํ˜ธ์ถœ
- `loginResponse(Result<String, Error>)` โ†’ ์„ฑ๊ณต ์‹œ Keychain ์ €์žฅ, ๋ถ€๋ชจ์—๊ฒŒ ์ „ํ™˜ ์•Œ๋ฆผ

### 4.2 Calendar (์บ˜๋ฆฐ๋” ํƒญ)

**๊ธฐ๋Šฅ**: ์›”๋ณ„ ๊ฒฝ์กฐ์‚ฌ ์บ˜๋ฆฐ๋” ๋ทฐ, ์ฃผ๋„๋ฆฌ/๋ฐ›๋„๋ฆฌ ํ•„ํ„ฐ๋ง

**CalendarFeature State**:
- `currentMonth: Date`
- `selectedType: TransactionType` โ€” `.given` / `.received`
- `transactions: [Transaction]`

**์ฃผ์š” ์ปดํฌ๋„ŒํŠธ**:
- `MonthSelectorView` โ€” ์ด์ „/๋‹ค์Œ ์›” ๋„ค๋น„๊ฒŒ์ด์…˜
- `DoriSegmentControl` โ€” ์ฃผ๋„๋ฆฌ/๋ฐ›๋„๋ฆฌ ์ „ํ™˜
- `CalendarGridView` โ€” 7์—ด ๊ทธ๋ฆฌ๋“œ ์บ˜๋ฆฐ๋”
- `AmountLabel` โ€” ๊ธˆ์•ก ํ‘œ์‹œ

**FAB(Floating Action Button)**: ์ƒˆ ๊ฑฐ๋ž˜ ๋“ฑ๋ก ํ™”๋ฉด ์ง„์ž…

### 4.3 History (๋‚ด์—ญ ํƒญ)

**๊ธฐ๋Šฅ**: ์ธ๋ฌผ ๋ชฉ๋ก + ๊ฒ€์ƒ‰ โ†’ ์ƒ์„ธ ๋‚ด์—ญ (Stack navigation)

**HistoryListFeature State**:
- `persons: [Person]`
- `searchText: String`
- `path: StackState<HistoryDetailFeature.State>`

**HistoryDetailFeature State**:
- `person: Person`
- `transactions` (person ๋‚ด ๊ฑฐ๋ž˜ ๋‚ด์—ญ)
- ์ˆ˜์ •/์‚ญ์ œ ๊ธฐ๋Šฅ

**์ฃผ์š” ์ปดํฌ๋„ŒํŠธ**:
- `PersonCardView` โ€” ์ธ๋ฌผ ์นด๋“œ (์ด๋ฆ„, ๊ด€๊ณ„, ๋ฐ” ๊ทธ๋ž˜ํ”„, ํ•ฉ๊ณ„)
- `DoriBarGraphView` โ€” ์ฃผ๋„๋ฆฌ/๋ฐ›๋„๋ฆฌ ๋น„์œจ ๋ฐ” ์ฐจํŠธ
- `TransactionRowView` โ€” ๊ฐœ๋ณ„ ๊ฑฐ๋ž˜ ํ–‰
- `DateHeaderView` โ€” ๋‚ ์งœ ํ—ค๋”

### 4.4 MyPage (๋งˆ์ดํŽ˜์ด์ง€ ํƒญ)

**๊ธฐ๋Šฅ**: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ (์นด์นด์˜ค ์ •๋ณด), ๋กœ๊ทธ์•„์›ƒ, ํšŒ์› ํƒˆํ‡ด

**MyPageFeature State**:
- `userName: String`
- `profileImageURL: URL?`

**์ฃผ์š” Action**:
- `logoutButtonTapped` โ†’ ๋กœ๊ทธ์•„์›ƒ ํ™•์ธ Alert
- `deleteAccountButtonTapped` โ†’ ํšŒ์› ํƒˆํ‡ด ํ™•์ธ Alert
- `confirmLogout` โ†’ KakaoAuthClient.logout + Keychain ์‚ญ์ œ
- `confirmDeleteAccount` โ†’ KakaoAuthClient.unlink + Keychain ์‚ญ์ œ

---

## 5. ๋„คํŠธ์›Œํฌ ๋ ˆ์ด์–ด

### 5.1 ์„ค๊ณ„ ์›์น™

- API Client๋ฅผ TCA `@Dependency`๋กœ ์ •์˜ (์ธํ„ฐํŽ˜์ด์Šค ์šฐ์„  ์„ค๊ณ„)
- ์„œ๋ฒ„ API ์ŠคํŽ™ ์ „๋‹ฌ ์ „๊นŒ์ง€ **Mock ๊ตฌํ˜„**์œผ๋กœ ๊ฐœ๋ฐœ ์ง„ํ–‰
- `liveValue` / `previewValue` / `testValue` 3๋‹จ ๋ถ„๋ฆฌ

### 5.2 API Client ๊ตฌ์กฐ

```swift
@DependencyClient
struct DataClient: Sendable {
var fetchPersons: @Sendable () async throws -> [Person]
var fetchPerson: @Sendable (UUID) async throws -> Person
var fetchTransactions: @Sendable (Date, TransactionType?) async throws -> [Transaction]
var deletePerson: @Sendable (UUID) async throws -> Void
// ... CRUD ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€
}
```

### 5.3 ์—๋Ÿฌ ํ•ธ๋“ค๋ง

```swift
enum APIError: Error, Equatable, Sendable {
case networkError(String)
case decodingError
case unauthorized
case serverError(Int)
case unknown
}
```

### 5.4 ์ฝ”๋“œ ํŒจํ„ด

`/tca-network` skill ์ฐธ์กฐ

---

## 6. Swift 6 Concurrency ๊ฐ€์ด๋“œ

### 6.1 ํ”„๋กœ์ ํŠธ ์„ค์ •

- `MainActor` ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ (`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`)
- Approachable Concurrency ON (`SWIFT_APPROACHABLE_CONCURRENCY = YES`)

### 6.2 ๊ทœ์น™

| ํ•ญ๋ชฉ | ๊ทœ์น™ |
|------|------|
| ๋„๋ฉ”์ธ ๋ชจ๋ธ | `Sendable` + `Equatable` ์ค€์ˆ˜ |
| Dependency Client struct | `Sendable` ์ค€์ˆ˜ |
| Client closure ํ”„๋กœํผํ‹ฐ | `@Sendable` ์–ด๋…ธํ…Œ์ด์…˜ |
| `@unchecked Sendable` | **์‚ฌ์šฉ ๊ธˆ์ง€** |
| TCA Effect ๋‚ด async ์ž‘์—… | `.run { send in }` ํŒจํ„ด ์‚ฌ์šฉ |

### 6.3 ๋„๋ฉ”์ธ ๋ชจ๋ธ ์˜ˆ์‹œ

```swift
struct Person: Identifiable, Codable, Equatable, Sendable {
let id: UUID
var name: String
var relationship: Relationship
var transactions: [Transaction]
}
```

### 6.4 Effect ํŒจํ„ด

```swift
case .loginButtonTapped:
state.isLoading = true
return .run { send in
let result = await Result {
try await kakaoAuthClient.login()
}
await send(.loginResponse(result))
}
```

---

## 7. ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

```
Dori-iOS/
DoriApp.swift
Core/
Models/ โ†’ Person, Transaction, Relationship, TransactionType
Clients/ โ†’ DataClient, KeychainClient, KakaoAuthClient
DataClient.swift
DataClient+Live.swift
DataClient+Mock.swift
KeychainClient.swift
KeychainClient+Live.swift
KakaoAuthClient.swift
KakaoAuthClient+Live.swift
DesignSystem/
Components/ โ†’ AmountLabel, PersonCardView, DoriBarGraphView,
TransactionRowView, DateHeaderView,
DoriSegmentControl, MonthSelectorView,
CalendarGridView, PrimaryButton,
DoriCommonAlert, AlertButton,
FloatingActionButton
Extensions/ โ†’ Color+Extensions, Date+Extensions, Int+Extensions
Feature/
App/ โ†’ AppFeature (Root reducer), AppView
AppFeature.swift
AppView.swift
Intro/ โ†’ IntroFeature, IntroView
IntroFeature.swift
IntroView.swift
MainTab/ โ†’ MainTabFeature, MainTabView
MainTabFeature.swift
MainTabView.swift
Calendar/ โ†’ CalendarFeature, CalendarView
CalendarFeature.swift
CalendarView.swift
History/
HistoryList/ โ†’ HistoryListFeature, HistoryListView
HistoryListFeature.swift
HistoryListView.swift
HistoryDetail/ โ†’ HistoryDetailFeature, HistoryDetailView
HistoryDetailFeature.swift
HistoryDetailView.swift
MyPage/ โ†’ MyPageFeature, MyPageView
MyPageFeature.swift
MyPageView.swift
```

### ์˜์กด ๋ฐฉํ–ฅ

```
Feature/ โ†’ Core/Clients/ โ†’ Core/Models/
Feature/ โ†’ Core/DesignSystem/
```

- **๋ชจ๋“ˆํ™” ๋Œ€๋น„**: `Core/` โ†’ `DoriCore`, `DoriClients`, `DoriUI` ํŒจํ‚ค์ง€๋กœ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅํ•˜๋„๋ก ์˜์กด ๋ฐฉํ–ฅ์„ ์œ ์ง€ํ•œ๋‹ค
- **Feature ๊ฐ„ ์ง์ ‘ ์ฐธ์กฐ ๊ธˆ์ง€**: Feature ๊ฐ„ ํ†ต์‹ ์€ ๋ถ€๋ชจ Reducer๋ฅผ ํ†ตํ•œ๋‹ค

---

## 8. ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜

### 8.1 ๋„ค์ด๋ฐ

| ๊ตฌ๋ถ„ | ํŒจํ„ด | ์˜ˆ์‹œ |
|------|------|------|
| Feature Reducer | `{Screen}Feature` | `CalendarFeature`, `IntroFeature` |
| View | `{Screen}View` | `CalendarView`, `IntroView` |
| TCA Client | `{Service}Client` | `DataClient`, `KeychainClient` |
| Extension ํŒŒ์ผ | `{Type}+{Purpose}.swift` | `Color+Extensions.swift` |
| Live ๊ตฌํ˜„ | `{Client}+Live.swift` | `DataClient+Live.swift` |
| Mock ๊ตฌํ˜„ | `{Client}+Mock.swift` | `DataClient+Mock.swift` |

### 8.2 ํŒŒ์ผ ๊ตฌ์กฐ

๊ฐ ํŒŒ์ผ์€ ์•„๋ž˜ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฅธ๋‹ค:

```swift
import ComposableArchitecture
import SwiftUI

// 1. ํƒ€์ž… ์ •์˜ (Reducer ๋˜๋Š” View)
@Reducer
struct CalendarFeature {
// ...
}

// 2. Preview (View ํŒŒ์ผ์ธ ๊ฒฝ์šฐ)
#Preview {
CalendarView(store: Store(initialState: .init()) {
CalendarFeature()
})
}
```

### 8.3 ๋ฌธ์ž์—ด ๊ทœ์น™

- UI ํ‘œ์‹œ ๋ฌธ์ž์—ด: **ํ•œ๊ตญ์–ด** (`"๋กœ๊ทธ์•„์›ƒ"`, `"์ฃผ๋„๋ฆฌ"`, `"๋ฐ›๋„๋ฆฌ"`)
- ์ฝ”๋“œ ์‹๋ณ„์ž: **์˜์–ด** (`loginButtonTapped`, `fetchPersons`)

---

## 9. Git ์ „๋žต

### 9.1 ๋ธŒ๋žœ์น˜ ๊ตฌ์กฐ

| ๋ธŒ๋žœ์น˜ | ์šฉ๋„ |
|--------|------|
| `master` | ๋ฆด๋ฆฌ์ฆˆ |
| `develop` | ํ†ตํ•ฉ/CI |
| `feature/{issue}-{desc}` | ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ |

### 9.2 PR ํ๋ฆ„

```
feature/* โ†’ develop โ†’ master
```

### 9.3 ๋ฏผ๊ฐ ์„ค์ • ๊ด€๋ฆฌ

- Kakao App Key ๋“ฑ ๋ฏผ๊ฐ ๊ฐ’: `.xcconfig` ํŒŒ์ผ๋กœ ๊ด€๋ฆฌ
- `.xcconfig` ํŒŒ์ผ์€ `.gitignore`์— ๋“ฑ๋กํ•˜์—ฌ ์ €์žฅ์†Œ์— ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค

---

## 10. ํ…Œ์ŠคํŠธ ์ „๋žต

### 10.1 Reducer ํ…Œ์ŠคํŠธ

- TCA `TestStore`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Reducer ๋กœ์ง์„ ๊ฒ€์ฆํ•œ๋‹ค
- `withDependencies`๋กœ Mock ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•œ๋‹ค
- `store.send()` โ†’ State ๋ณ€๊ฒฝ ๊ฒ€์ฆ
- `store.receive()` โ†’ Effect ๊ฒฐ๊ณผ ๊ฒ€์ฆ
- ์ฝ”๋“œ ํŒจํ„ด: `/tca-test` skill ์ฐธ์กฐ

### 10.2 ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ…Œ์ŠคํŠธ

- `Date+Extensions`, `Int+Extensions` ๋“ฑ Extension ์œ ํ‹ธ ํ•จ์ˆ˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

### 10.3 MVP ์ œ์™ธ ํ•ญ๋ชฉ

- UI ํ…Œ์ŠคํŠธ / ์Šค๋ƒ…์ƒท ํ…Œ์ŠคํŠธ๋Š” MVP์—์„œ ์ œ์™ธ
Loading