diff --git a/README.md b/README.md index 33d657c1..e38a552a 100644 --- a/README.md +++ b/README.md @@ -1,172 +1,151 @@ # 🚌 μ•—μ°¨ (Atcha) -> 이제 클릭 ν•œ 번으둜 막차 ν™•μΈν•˜κ³ , νƒμ‹œλΉ„λ₯Ό μ•„λΌλŠ” μŠ€λ§ˆνŠΈν•œ λŒ€μ€‘κ΅ν†΅ λ™λ°˜μž +> 막차 μ‹œκ°„μ„ ν™•μΈν•˜κ³ , λ– λ‚˜μ•Ό ν•  λ•Œ μ•Œλ €μ£ΌλŠ” 막차 μ•ŒλžŒ μ•±
-![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) -![iOS](https://img.shields.io/badge/iOS-14.0+-blue.svg) +![Swift](https://img.shields.io/badge/Swift-6.0-orange.svg) +![iOS](https://img.shields.io/badge/iOS-26.0+-blue.svg) +![Tuist](https://img.shields.io/badge/Tuist-4.209-5E4BDB.svg) +![UIKit](https://img.shields.io/badge/UI-UIKit%20%2B%20SnapKit-lightgrey.svg)
## πŸ“± ν”„λ‘œμ νŠΈ μ†Œκ°œ -**μ•—μ°¨**λŠ” 막차 μ‹œκ°„μ„ λ†“μΉ κΉŒ κ±±μ •ν•˜λŠ” λͺ¨λ“  이듀을 μœ„ν•œ iOS μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€. -우리 μœ„μΉ˜λ₯Ό 기반으둜 λΉ λ₯΄κ²Œ 막차 정보λ₯Ό μ°Ύκ³ , 막차λ₯Ό 타면 μ ˆμ•½λ˜λŠ” νƒμ‹œλΉ„λ₯Ό μ‹œκ°μ μœΌλ‘œ λ³΄μ—¬μ€λ‹ˆλ‹€. -좜발 μ „ 미리 μ•Œλ¦Όμ„ μ„€μ •ν•˜λ©΄, 더 이상 막차λ₯Ό λ†“μΉ˜λŠ” 일은 없을 κ±°μ˜ˆμš”! - -436522408-f875c469-b3dc-4790-b35c-2833da2df21e +**μ•—μ°¨**λŠ” 막차λ₯Ό λ†“μΉ κΉŒ κ±±μ •ν•˜λŠ” μ‚¬λžŒλ“€μ„ μœ„ν•œ iOS μ•±μž…λ‹ˆλ‹€. +ν˜„μž¬ μœ„μΉ˜μ—μ„œ μš°λ¦¬μ§‘κΉŒμ§€ κ°€λŠ” 막차λ₯Ό μ°Ύμ•„ μ£Όκ³ , μΆœλ°œν•΄μ•Ό ν•˜λŠ” μˆœκ°„ μ‹œμŠ€ν…œ μ•ŒλžŒκ³Ό Live Activity둜 μ•Œλ €μ€λ‹ˆλ‹€. ## ✨ μ£Όμš” κΈ°λŠ₯ ### 🚏 막차 확인 -- **원클릭 검색**: ν˜„μž¬ μœ„μΉ˜ 기반 근처 막차 정보 μ¦‰μ‹œ 확인 -- **μ‹€μ‹œκ°„ 정보**: λ²„μŠ€, μ§€ν•˜μ²  λ“± λŒ€μ€‘κ΅ν†΅ 막차 μ‹œκ°„ν‘œ -- **졜적 경둜**: λ„λ³΄Β·μ‹œκ°„ λ“± μ›ν•˜λŠ” 경둜 선택 κ°€λŠ₯ +- **μœ„μΉ˜ 기반 검색**: ν˜„μž¬ μœ„μΉ˜μ—μ„œ μš°λ¦¬μ§‘κΉŒμ§€ λ²„μŠ€Β·μ§€ν•˜μ²  막차 경둜λ₯Ό ν•œ λ²ˆμ— +- **좜발 κΈ°μ€€ 정리**: μ§€κΈˆ μΆœλ°œν•΄μ•Ό ν•˜λŠ” κ²½λ‘œμ™€ 이미 λ– λ‚œ 막차λ₯Ό κ΅¬λΆ„ν•΄μ„œ ν‘œμ‹œ +- **μž₯μ†Œ 검색**: μΆœλ°œμ§€Β·λ„μ°©μ§€ 검색과 졜근 검색 기둝 + +### ⏰ 막차 μ•ŒλžŒ +- **AlarmKit μ•ŒλžŒ**: 무음 λͺ¨λ“œμ—μ„œλ„ μšΈλ¦¬λŠ” μ‹œμŠ€ν…œ μ•ŒλžŒμœΌλ‘œ 좜발 μ‹œμ  μ•Œλ¦Ό +- **Live Activity**: μž κΈˆν™”λ©΄Β·λ‹€μ΄λ‚˜λ―Ή μ•„μΌλžœλ“œμ—μ„œ 남은 μ‹œκ°„ 확인 +- **μ„œλ²„ 동기화**: μ•± μž¬μ‹€ν–‰Β·ν¬κ·ΈλΌμš΄λ“œ 볡귀 μ‹œ μ„œλ²„μ™€ μ•ŒλžŒ μƒνƒœλ₯Ό 맞좀 -### πŸ’° νƒμ‹œλΉ„ μ ˆμ•½ 계산 -- **μ¦‰μ‹œ 계산**: 막차 이용 μ‹œ μ ˆμ•½λ˜λŠ” νƒμ‹œλΉ„λ₯Ό μ‹€μ‹œκ°„μœΌλ‘œ ν‘œμ‹œ -- **비ꡐ 뢄석**: νƒμ‹œ vs λŒ€μ€‘κ΅ν†΅ λΉ„μš© 비ꡐ -- **μ ˆμ•½ 톡계**: λˆ„μ  μ ˆμ•½ κΈˆμ•‘ 확인 +### βš™οΈ κ°„νŽΈν•œ μ‹œμž‘ +- **게슀트 계정**: νšŒμ›κ°€μž… 없이 λ°”λ‘œ μ‹œμž‘ (κΈ°κΈ° μ‹λ³„μž 기반 인증) +- **μš°λ¦¬μ§‘ μ„€μ •**: μ„œλΉ„μŠ€ μ§€μ—­ 확인 ν›„ μ§‘ μ£Όμ†Œ 등둝 -### ⏰ 슀마트 μ•Œλ¦Ό -- **좜발 μ•Œλ¦Ό**: 막차 μ‹œκ°„ μ „ μ•Œλ¦ΌμœΌλ‘œ μ—¬μœ μžˆκ²Œ 좜발 -- **맞좀 μ„€μ •**: μ›ν•˜λŠ” μ‹œκ°„μ— 맞좀 μ•Œλ¦Ό μ„€μ • -- **ν‘Έμ‹œ μ•Œλ¦Ό**: FCM 기반 μ•ˆμ •μ μΈ μ•Œλ¦Ό μ‹œμŠ€ν…œ +## πŸ—οΈ μ•„ν‚€ν…μ²˜ -### πŸ—ΊοΈ μ‹€μ‹œκ°„ λ‚΄λΉ„κ²Œμ΄μ…˜ -- **경둜 μ•ˆλ‚΄**: TMapSDK 기반 μ •ν™•ν•œ κΈΈμ°ΎκΈ° -- **도보 μ‹œκ°„**: μ‹€μ‹œκ°„ 도보 μ†Œμš” μ‹œκ°„ ν‘œμ‹œ -- **지도 μ‹œκ°ν™”**: μΆœλ°œμ§€λΆ€ν„° μ •λ₯˜μž₯κΉŒμ§€ ν•œλˆˆμ— 확인 +**uFeatures(Tuist λͺ¨λ“ˆλŸ¬) + 클린 μ•„ν‚€ν…μ²˜.** μ•± νƒ€κ²Ÿμ΄ μ‘°ν•© 루트(Composition Root)κ°€ λ˜μ–΄ λͺ¨λ“  ꡬ체 νƒ€μž…μ„ μ‘°λ¦½ν•˜κ³ , 각 ν”Όμ²˜λŠ” Domain ν”„λ‘œν† μ½œλ§Œ λ°”λΌλ΄…λ‹ˆλ‹€. + +

+ + + AtchaV2 λͺ¨λ“ˆ μ•„ν‚€ν…μ²˜ + +

+ +[Archify](https://github.com/tt-a1i/archify)둜 μƒμ„±ν–ˆμŠ΅λ‹ˆλ‹€. λ…Έλ“œλ§ˆλ‹€ κ·Όκ±°κ°€ 된 `Project.swift`κ°€ μ—°κ²°λœ μΈν„°λž™ν‹°λΈŒ 버전: [`docs/architecture/atcha-v2-modules.html`](docs/architecture/atcha-v2-modules.html) (λ‚΄λ €λ°›μ•„ λΈŒλΌμš°μ €λ‘œ μ—΄κΈ°) Β· 원본 μŠ€νŽ™: [`atcha-v2-modules.architecture.json`](docs/architecture/atcha-v2-modules.architecture.json) + +### 의쑴 κ·œμΉ™ + +- **Presentation β†’ Domain ← Data** β€” Feature λͺ¨λ“ˆμ€ `AtchaData`λ₯Ό μ ˆλŒ€ importν•˜μ§€ μ•ŠμœΌλ©°, Domain은 아무것도 μ˜μ‘΄ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. +- **μ‘°ν•© λ£¨νŠΈλŠ” 앱뿐** β€” ꡬ체 Data/Network νƒ€μž…μ„ μ•„λŠ” 곳은 `AppDIContainer` ν•˜λ‚˜μž…λ‹ˆλ‹€. `NetworkClient β†’ RepositoryImpl β†’ UseCase β†’ ν”Όμ²˜ DIContainer` μˆœμ„œλ‘œ μ£Όμž…ν•©λ‹ˆλ‹€. +- **ν”Όμ²˜ κ°„ μ°Έμ‘°λŠ” Interface νƒ€κ²ŸμœΌλ‘œλ§Œ** β€” 예: Home은 `SearchFeatureInterface`Β·`SettingsFeatureInterface`만 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. +- **μ™ΈλΆ€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ•± νƒ€κ²Ÿμ—μ„œλ§Œ 링크** β€” λ‚΄λΆ€ λͺ¨λ“ˆμ€ λͺ¨λ‘ static frameworkλΌμ„œ μ—¬κΈ°μ €κΈ°μ„œ λ§ν¬ν•˜λ©΄ 심볼이 μ€‘λ³΅λ©λ‹ˆλ‹€. + +### λͺ¨λ“ˆ ꡬ성 + +| λ ˆμ΄μ–΄ | λͺ¨λ“ˆ | μ—­ν•  | +|---|---|---| +| App | `AtchaV2` | μ‘°ν•© 루트 Β· μŠ€ν”Œλž˜μ‹œ Β· μ•ŒλžŒ 동기화 | +| App | `AtchaWidget` | Live Activity UI (μœ„μ ― μ΅μŠ€ν…μ…˜, SwiftUI) | +| Feature | `HomeFeature` / `SearchFeature` / `SettingsFeature` | ν™”λ©΄ λ‹¨μœ„ 수직 슬라이슀 (각각 Interface Β· Tests Β· Example νƒ€κ²Ÿ 포함) | +| Domain | `Domain` | Entity Β· UseCase Β· Repository ν”„λ‘œν† μ½œ | +| Data | `AtchaData` | Request/Response DTO Β· RepositoryImpl Β· 응닡 μΊμ‹œ λ°μ½”λ ˆμ΄ν„° | +| Core | `CoreNetwork` | URLSession 기반 λ„€νŠΈμ›Œν¬ ν΄λΌμ΄μ–ΈνŠΈ | +| Core | `CoreStorage` | actor 기반 μ €μž₯μ†Œ (`DocumentStore` Β· `CollectionStore` Β· `ExpiringCache`) | +| Core | `CoreAuth` | 게슀트 μ„Έμ…˜ Β· 토큰 관리 | +| Core | `CoreAlarm` | AlarmKit λž˜ν•‘ (AlarmKit을 importν•˜λŠ” μœ μΌν•œ λͺ¨λ“ˆ) | +| Core | `CoreLiveActivity` | `ActivityAttributes` 계약 (μ•± Β· μœ„μ ― 곡유) | +| Core | `CoreCoordinator` / `CoreConcurrency` | ν™”λ©΄ 흐름 Β· λ™μ‹œμ„± μœ ν‹Έ | +| UI | `DesignSystem` | `DSColor` Β· `DSFont` Β· `DSSpacing` 토큰과 곡톡 μ»΄ν¬λ„ŒνŠΈ | + +### ν”Όμ²˜ λ‚΄λΆ€ μ»¨λ²€μ…˜ + +- **MVVM + Coordinator** β€” 쑰립은 ν”Όμ²˜ DIContainerκ°€, ν™”λ©΄ 흐름은 Coordinatorκ°€ λ§‘μŠ΅λ‹ˆλ‹€. ViewModel은 λͺ¨λ‘ `@MainActor`μž…λ‹ˆλ‹€. +- **ν™”λ©΄ μƒνƒœ 채널은 2개뿐** β€” `onStateChange`(μƒνƒœ) + `onToast`(μΌνšŒμ„± 사건). +- **UseCaseλŠ” ν•„μš”ν•  λ•Œλ§Œ** β€” 포트 2개 이상을 μ‘°ν•©ν•˜κ±°λ‚˜ λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™μ„ 담을 λ•Œλ§Œ λ§Œλ“€κ³ , λ‹¨μˆœ μœ„μž„μ΄λ©΄ ViewModel이 Repository ν”„λ‘œν† μ½œμ„ 직접 μ£Όμž…λ°›μŠ΅λ‹ˆλ‹€. +- **ViewData 뢄리** β€” Entityλ₯Ό 뷰에 직접 λ…ΈμΆœν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. +- **ν…ŒμŠ€νŠΈ** β€” Swift Testing(`@Test` / `#expect`). ν”Όμ²˜λ§ˆλ‹€ μŠ€ν… UseCase둜 단독 μ‹€ν–‰λ˜λŠ” Example 앱이 μžˆμŠ΅λ‹ˆλ‹€. ## πŸ›  기술 μŠ€νƒ -### iOS 개발 -- **μ–Έμ–΄**: Swift 5 -- **UI ν”„λ ˆμž„μ›Œν¬**: UIKit -- **λ ˆμ΄μ•„μ›ƒ**: SnapKit -- **λ°˜μ‘ν˜• ν”„λ‘œκ·Έλž˜λ°**: Combine - -### μ•„ν‚€ν…μ²˜ -- **νŒ¨ν„΄**: MVVM (Model-View-ViewModel) -- **섀계**: Clean Architecture -- **μ˜μ‘΄μ„± μ£Όμž…**: Custom DI Container +| λΆ„λ₯˜ | μ‚¬μš© 기술 | +|---|---| +| μ–Έμ–΄ Β· UI | Swift 6 (strict concurrency), UIKit (μ½”λ“œ 기반), SnapKit | +| λͺ¨λ“ˆν™” Β· λΉŒλ“œ | Tuist 4 (mise둜 버전 κ³ μ •), Debug / Stage / Release 3ꡬ성 | +| μ‹œμŠ€ν…œ ν”„λ ˆμž„μ›Œν¬ | AlarmKit, ActivityKit (Live Activity), WidgetKit, CoreLocation | +| μ™ΈλΆ€ 연동 | Firebase (Core Β· Crashlytics Β· Messaging) | +| ν…ŒμŠ€νŠΈ | Swift Testing | +| CI/CD | fastlane, GitHub Actions | +| λ°±μ—”λ“œ | Spring Boot 3.4 (Kotlin), MySQL 8.0, Redis | -### λ„€νŠΈμ›Œν‚Ή 및 λ°±μ—”λ“œ 연동 -- **HTTP ν΄λΌμ΄μ–ΈνŠΈ**: Alamofire -- **λ°±μ—”λ“œ μ„œλ²„**: Spring Boot 3.4.x (Kotlin 2.x) -- **λ°μ΄ν„°λ² μ΄μŠ€**: MySQL 8.0 -- **μΊμ‹œ**: Redis -- **ν‘Έμ‹œ μ•Œλ¦Ό**: Firebase Cloud Messaging (FCM) -- **API 연동**: 곡곡데이터포털 λŒ€μ€‘κ΅ν†΅ API +## πŸš€ μ‹œμž‘ν•˜κΈ° -### 지도 및 μœ„μΉ˜ -- **지도 SDK**: TMapSDK -- **μœ„μΉ˜ μ„œλΉ„μŠ€**: CoreLocation -- **λ‚΄λΉ„κ²Œμ΄μ…˜**: μ‹€μ‹œκ°„ 경둜 μ•ˆλ‚΄ +도ꡬ 버전은 [mise](https://mise.jdx.dev)둜 κ³ μ •ν•©λ‹ˆλ‹€(`mise.toml`). -### 뢄석 -- **μ‚¬μš©μž 뢄석**: Amplitude +```bash +# 0. 도ꡬ μ„€μΉ˜ (Tuist 4.209) +mise install +# μ‹œμŠ€ν…œμ— ꡬ버전 tuistκ°€ 있으면 mise 버전이 λ¨Όμ € μž‘νžˆλ„λ‘ +export PATH="$(dirname "$(mise which tuist)"):$PATH" -## πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰 +# 1. 졜초 1회: 둜컬 xcconfig μŠ€νƒ λ“œμΈ 생성 + μ˜μ‘΄μ„± μ„€μΉ˜ + μ›Œν¬μŠ€νŽ˜μ΄μŠ€ 생성 +sh Scripts/bootstrap.sh +# 2. λ§€λ‹ˆνŽ˜μŠ€νŠΈ(Project.swift λ“±)λ₯Ό μˆ˜μ •ν•œ λ’€μ—λŠ” λ‹€μ‹œ 생성 +tuist generate --no-open ``` -Atcha-iOS/ -β”œβ”€β”€ App/ -β”‚ β”œβ”€β”€ DIContainer/ -β”‚ β”œβ”€β”€ AppDelegate.swift -β”‚ β”œβ”€β”€ SceneDelegate.swift -β”‚ └── AppFlowCoordinator.swift -β”‚ -β”œβ”€β”€ Presentation/ -β”‚ β”œβ”€β”€ BusDetail/ -β”‚ β”œβ”€β”€ Popup/ -β”‚ β”œβ”€β”€ WebView/ -β”‚ β”œβ”€β”€ Setting/ -β”‚ β”œβ”€β”€ Course/ -β”‚ β”œβ”€β”€ Location/ -β”‚ β”œβ”€β”€ Lock/ -β”‚ β”œβ”€β”€ Onboarding/ -β”‚ β”œβ”€β”€ Login/ -β”‚ β”œβ”€β”€ Main/ -β”‚ β”œβ”€β”€ User/ -β”‚ β”œβ”€β”€ Common/ -β”‚ └── Splash/ -β”‚ -β”œβ”€β”€ Domain/ -β”‚ β”œβ”€β”€ Entity/ -β”‚ β”œβ”€β”€ UseCase/ -β”‚ β”œβ”€β”€ Repository/ -β”‚ └── Model/ -β”‚ -β”œβ”€β”€ Data/ -β”‚ β”œβ”€β”€ Repository/ -β”‚ └── Model/ -β”‚ -β”œβ”€β”€ Core/ -β”‚ β”œβ”€β”€ Notification/ -β”‚ β”œβ”€β”€ Manager/ -β”‚ β”œβ”€β”€ Share/ -β”‚ β”œβ”€β”€ ViewWrapper/ -β”‚ β”œβ”€β”€ Wrapper/ -β”‚ β”œβ”€β”€ Network/ -β”‚ β”œβ”€β”€ Storages/ -β”‚ └── Util/ -β”‚ -└── DesignSource/ - β”œβ”€β”€ AtchaBallon/ - β”œβ”€β”€ AtchaLottie/ - β”œβ”€β”€ AtchaTextField/ - β”œβ”€β”€ AtchaTextBox/ - β”œβ”€β”€ AtchaNavigationBar/ - β”œβ”€β”€ AtchaList/ - β”œβ”€β”€ AtchaToast/ - β”œβ”€β”€ AtchaButton/ - β”œβ”€β”€ AtchaColor/ - β”œβ”€β”€ AtchaFont/ - β”œβ”€β”€ AtchaImage/ - └── Assets/ + +```bash +# AtchaV2 λΉŒλ“œ +xcodebuild -workspace Atcha.xcworkspace -scheme AtchaV2 -configuration Debug \ + -destination 'generic/platform=iOS Simulator' build + +# ν”Όμ²˜ λͺ¨λ“ˆ ν…ŒμŠ€νŠΈ +xcodebuild -workspace Atcha.xcworkspace -scheme HomeFeature \ + -destination 'platform=iOS Simulator,name=iPhone 17' test + +# 의쑴 κ·Έλž˜ν”„ 확인 +tuist graph --format dot --no-open ``` -## πŸ—οΈ μ•„ν‚€ν…μ²˜ +> xcconfig 4개(Base/Dev/Stage/Live)λŠ” gitignore λŒ€μƒμž…λ‹ˆλ‹€. λ‘œμ»¬μ—μ„œλŠ” `bootstrap.sh`κ°€ 빈 νŒŒμΌμ„ λ§Œλ“€μ–΄ μ£Όκ³ , CIμ—μ„œλŠ” μ‹œν¬λ¦ΏμœΌλ‘œ μ‹€μ œ νŒŒμΌμ„ λ³΅μ›ν•©λ‹ˆλ‹€. AtchaV2λŠ” xcconfig에 μ˜μ‘΄ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. -### MVVM + Clean Architecture +## πŸ“ 디렉터리 ꡬ쑰 ``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Presentation Layer β”‚ -β”‚ (Views, ViewModels, Coordinators) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Domain Layer β”‚ -β”‚ (Entities, UseCases, Protocols) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Data Layer β”‚ -β”‚ (Repositories, Data Sources, DTOs) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +Atcha-iOS/ +β”œβ”€β”€ Projects/ +β”‚ β”œβ”€β”€ App/ # AtchaV2 μ•± + AtchaWidget μ΅μŠ€ν…μ…˜ +β”‚ β”œβ”€β”€ Feature/ # Home Β· Search Β· Settings (ν”Όμ²˜λ‹Ή Feature/Interface/Tests/Example) +β”‚ β”œβ”€β”€ Domain/ +β”‚ β”œβ”€β”€ Data/ # AtchaData λͺ¨λ“ˆ +β”‚ β”œβ”€β”€ Core/ # Network Β· Storage Β· Auth Β· Alarm Β· LiveActivity Β· Coordinator Β· Concurrency +β”‚ └── DesignSystem/ +β”œβ”€β”€ Tuist/ +β”‚ β”œβ”€β”€ Package.swift # μ™ΈλΆ€ SPM μ˜μ‘΄μ„± +β”‚ └── ProjectDescriptionHelpers/ # Project.feature / Project.layer DSL +β”œβ”€β”€ Scripts/bootstrap.sh +└── docs/ # μ•„ν‚€ν…μ²˜ λ‹€μ΄μ–΄κ·Έλž¨ Β· λ‘œλ“œλ§΅ Β· μ •μ±… λ¬Έμ„œ ``` -### μ£Όμš” λ””μžμΈ νŒ¨ν„΄ -- **MVVM**: View와 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 뢄리 -- **Repository Pattern**: 데이터 μ†ŒμŠ€ 좔상화 -- **Coordinator Pattern**: ν™”λ©΄ μ „ν™˜ 관리 -- **Dependency Injection**: μ˜μ‘΄μ„± μ£Όμž… - ## πŸ”— 링크 -- **App Store** - https://apps.apple.com/kr/app/%EC%95%97%EC%B0%A8/id6747877903 +- **App Store** β€” https://apps.apple.com/kr/app/%EC%95%97%EC%B0%A8/id6747877903 +- **Google Play** β€” https://play.google.com/store/apps/details?id=com.depromeet.team6&hl=ko +- **Behance** β€” https://www.behance.net/gallery/223967221/_ -- **Google Play** - https://play.google.com/store/apps/details?id=com.depromeet.team6&hl=ko - -- **Behance** - https://www.behance.net/gallery/223967221/_ - ## πŸ‘₯ νŒ€
@@ -175,3 +154,5 @@ Atcha-iOS/ |:---:|:---:| | **[유견희](https://github.com/YuGeonHui)** | **[μ—„μž¬μ›…](https://github.com/woolnd)** | | iOS Developer | iOS Developer | + +
diff --git a/docs/architecture/atcha-v2-modules.architecture.json b/docs/architecture/atcha-v2-modules.architecture.json new file mode 100644 index 00000000..af835a81 --- /dev/null +++ b/docs/architecture/atcha-v2-modules.architecture.json @@ -0,0 +1,456 @@ +{ + "schema_version": 1, + "diagram_type": "architecture", + "meta": { + "title": "AtchaV2 λͺ¨λ“ˆ μ•„ν‚€ν…μ²˜", + "quality_profile": "showcase", + "repository": { + "url": "https://github.com/Atcha-Project/Atcha-iOS", + "provider": "github", + "revision": "3dbd748a54c2d99ca4e9c33c724edaf765d0bc18" + }, + "views": [ + { + "id": "clean-arch", + "label": "Presentation β†’ Domain ← Data", + "focus": [ + "home", + "search", + "settings", + "domain", + "data" + ], + "note": "ν”Όμ²˜λŠ” Domain ν”„λ‘œν† μ½œλ§Œ 보고, AtchaDataκ°€ κ·Έ ν”„λ‘œν† μ½œμ„ κ΅¬ν˜„ν•œλ‹€." + }, + { + "id": "composition-root", + "label": "μ‘°ν•© 루트", + "focus": [ + "app", + "data", + "auth", + "home", + "search", + "settings" + ], + "note": "ꡬ체 Data/Network νƒ€μž…μ„ λ³΄λŠ” 곳은 μ•±μ˜ AppDIContainer뿐이닀." + }, + { + "id": "alarm-la", + "label": "μ•ŒλžŒ Β· Live Activity", + "focus": [ + "app", + "alarm", + "liveactivity", + "widget" + ], + "note": "AlarmKit은 CoreAlarmμ—μ„œλ§Œ import, LA 계약은 μ•±κ³Ό μœ„μ ―μ΄ κ³΅μœ ν•œλ‹€." + } + ], + "viewBox": [ + 1390, + 720 + ] + }, + "components": [ + { + "id": "app", + "type": "frontend", + "label": "AtchaV2", + "sublabel": "App Β· μ‘°ν•© 루트", + "tag": "AppDIContainer", + "pos": [ + 240, + 290 + ], + "size": [ + 150, + 70 + ], + "sources": [ + { + "path": "Projects/App/Project.swift", + "line": 27 + } + ] + }, + { + "id": "home", + "type": "frontend", + "label": "HomeFeature", + "sublabel": "막차 Β· μ•ŒλžŒ ν™ˆ", + "pos": [ + 460, + 130 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Feature/Home/Project.swift" + } + ] + }, + { + "id": "search", + "type": "frontend", + "label": "SearchFeature", + "sublabel": "μž₯μ†Œ 검색", + "pos": [ + 460, + 293 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Feature/Search/Project.swift" + } + ] + }, + { + "id": "settings", + "type": "frontend", + "label": "SettingsFeature", + "sublabel": "μš°λ¦¬μ§‘ Β· νƒˆν‡΄", + "pos": [ + 460, + 456 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Feature/Settings/Project.swift" + } + ] + }, + { + "id": "domain", + "type": "backend", + "label": "Domain", + "sublabel": "Entity Β· UseCase Β· Port", + "tag": "무의쑴", + "pos": [ + 690, + 293 + ], + "size": [ + 160, + 64 + ], + "sources": [ + { + "path": "Projects/Domain/Project.swift" + } + ] + }, + { + "id": "data", + "type": "backend", + "label": "AtchaData", + "sublabel": "RepositoryImpl Β· DTO", + "pos": [ + 920, + 293 + ], + "size": [ + 160, + 64 + ], + "sources": [ + { + "path": "Projects/Data/Project.swift" + } + ] + }, + { + "id": "network", + "type": "cloud", + "label": "CoreNetwork", + "sublabel": "URLSession client", + "pos": [ + 1160, + 130 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Core/Network/Project.swift" + } + ] + }, + { + "id": "storage", + "type": "database", + "label": "CoreStorage", + "sublabel": "actor stores Β· cache", + "pos": [ + 1160, + 360 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Core/Storage/Project.swift" + } + ] + }, + { + "id": "auth", + "type": "security", + "label": "CoreAuth", + "sublabel": "μ„Έμ…˜ Β· 토큰", + "pos": [ + 1160, + 590 + ], + "size": [ + 160, + 64 + ], + "sources": [ + { + "path": "Projects/Core/Auth/Project.swift" + } + ] + }, + { + "id": "alarm", + "type": "external", + "label": "CoreAlarm", + "sublabel": "AlarmKit 유일 지점", + "pos": [ + 40, + 140 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Core/Alarm/Project.swift" + } + ] + }, + { + "id": "liveactivity", + "type": "messagebus", + "label": "CoreLiveActivity", + "sublabel": "ActivityAttributes", + "pos": [ + 40, + 450 + ], + "size": [ + 150, + 64 + ], + "sources": [ + { + "path": "Projects/Core/LiveActivity/Project.swift" + } + ] + }, + { + "id": "widget", + "type": "frontend", + "label": "AtchaWidget", + "sublabel": "LA UI (SwiftUI)", + "pos": [ + 40, + 620 + ], + "size": [ + 160, + 64 + ], + "sources": [ + { + "path": "Projects/App/Project.swift", + "line": 87 + } + ] + } + ], + "boundaries": [ + { + "kind": "region", + "label": "Presentation (uFeatures)", + "wraps": [ + "home", + "search", + "settings" + ] + } + ], + "connections": [ + { + "id": "app-home", + "from": "app", + "to": "home", + "variant": "emphasis" + }, + { + "id": "app-search", + "from": "app", + "to": "search", + "variant": "emphasis" + }, + { + "id": "app-settings", + "from": "app", + "to": "settings", + "variant": "emphasis" + }, + { + "id": "home-domain", + "from": "home", + "to": "domain" + }, + { + "id": "search-domain", + "from": "search", + "to": "domain" + }, + { + "id": "settings-domain", + "from": "settings", + "to": "domain" + }, + { + "id": "data-domain", + "from": "data", + "to": "domain", + "label": "κ΅¬ν˜„", + "variant": "dashed" + }, + { + "id": "data-network", + "from": "data", + "to": "network" + }, + { + "id": "data-storage", + "from": "data", + "to": "storage" + }, + { + "id": "auth-network", + "from": "auth", + "to": "network", + "fromSide": "right", + "toSide": "right", + "via": [ + [ + 1340, + 622 + ], + [ + 1340, + 162 + ] + ] + }, + { + "id": "auth-storage", + "from": "auth", + "to": "storage", + "fromSide": "top", + "toSide": "bottom" + }, + { + "id": "app-auth", + "from": "app", + "to": "auth", + "label": "게슀트 μ„Έμ…˜", + "variant": "security", + "fromSide": "bottom", + "toSide": "left", + "via": [ + [ + 315, + 622 + ] + ] + }, + { + "id": "app-data", + "from": "app", + "to": "data", + "label": "DI 쑰립", + "fromSide": "top", + "toSide": "top", + "via": [ + [ + 315, + 60 + ], + [ + 1000, + 60 + ] + ] + }, + { + "id": "app-alarm", + "from": "app", + "to": "alarm" + }, + { + "id": "app-la", + "from": "app", + "to": "liveactivity" + }, + { + "id": "widget-la", + "from": "widget", + "to": "liveactivity", + "label": "계약 곡유", + "variant": "dashed", + "fromSide": "top", + "toSide": "bottom" + } + ], + "cards": [ + { + "dot": "cyan", + "title": "의쑴 κ·œμΉ™", + "items": [ + "FeatureλŠ” AtchaDataλ₯Ό importν•˜μ§€ μ•ŠμŒ", + "Domain은 무의쑴", + "μ™ΈλΆ€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ•± νƒ€κ²Ÿμ—μ„œλ§Œ 링크" + ] + }, + { + "dot": "emerald", + "title": "ν”Όμ²˜ 곡톡 의쑴", + "items": [ + "Feature β†’ Domain ν”„λ‘œν† μ½œλ§Œ 의쑴", + "DesignSystem Β· CoreCoordinator Β· SnapKit", + "Home β†’ Search/Settings Interface만 μ°Έμ‘°" + ] + }, + { + "dot": "rose", + "title": "인증", + "items": [ + "POST /auth/guest 게슀트 JWT", + "만료 μ‹œ 같은 deviceId둜 쑰용히 재인증" + ] + } + ] +} \ No newline at end of file diff --git a/docs/architecture/atcha-v2-modules.html b/docs/architecture/atcha-v2-modules.html new file mode 100644 index 00000000..5e3ae6ce --- /dev/null +++ b/docs/architecture/atcha-v2-modules.html @@ -0,0 +1,15024 @@ + + + + + + + AtchaV2 λͺ¨λ“ˆ μ•„ν‚€ν…μ²˜ Diagram + + + + + + + + +
+ +
+
+
+

AtchaV2 λͺ¨λ“ˆ μ•„ν‚€ν…μ²˜

+
+
+ + + + + + + +
+ + AtchaV2 λͺ¨λ“ˆ μ•„ν‚€ν…μ²˜ + An architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AtchaV2 Β· App Β· μ‘°ν•© 루트 Β· Architecture component Β· AppDIContainer + + + + AtchaV2 + App Β· μ‘°ν•© 루트 + AppDIContainer + + + + HomeFeature Β· 막차 Β· μ•ŒλžŒ ν™ˆ Β· Presentation (uFeatures) + + + + HomeFeature + 막차 Β· μ•ŒλžŒ ν™ˆ + + + + SearchFeature Β· μž₯μ†Œ 검색 Β· Presentation (uFeatures) + + + + SearchFeature + μž₯μ†Œ 검색 + + + + SettingsFeature Β· μš°λ¦¬μ§‘ Β· νƒˆν‡΄ Β· Presentation (uFeatures) + + + + SettingsFeature + μš°λ¦¬μ§‘ Β· νƒˆν‡΄ + + + + Domain Β· Entity Β· UseCase Β· Port Β· Architecture component Β· 무의쑴 + + + + Domain + Entity Β· UseCase Β· Port + 무의쑴 + + + + AtchaData Β· RepositoryImpl Β· DTO Β· Architecture component + + + + AtchaData + RepositoryImpl Β· DTO + + + + CoreNetwork Β· URLSession client Β· Architecture component + + + + CoreNetwork + URLSession client + + + + CoreStorage Β· actor stores Β· cache Β· Architecture component + + + + CoreStorage + actor stores Β· cache + + + + CoreAuth Β· μ„Έμ…˜ Β· 토큰 Β· Architecture component + + + + CoreAuth + μ„Έμ…˜ Β· 토큰 + + + + CoreAlarm Β· AlarmKit 유일 지점 Β· Architecture component + + + + CoreAlarm + AlarmKit 유일 지점 + + + + CoreLiveActivity Β· ActivityAttributes Β· Architecture component + + + + CoreLiveActivity + ActivityAttributes + + + + AtchaWidget Β· LA UI (SwiftUI) Β· Architecture component + + + + AtchaWidget + LA UI (SwiftUI) + + + + + + + + + + + + κ΅¬ν˜„ + + + + + + + + 게슀트 μ„Έμ…˜ + + + + DI 쑰립 + + + + + + 계약 곡유 + + + + + + Presentation (uFeatures) + + + + + +

+ + + + + + + + + +
+ + +
+
+
+
+

의쑴 κ·œμΉ™

+
+
    +
  • • FeatureλŠ” AtchaDataλ₯Ό importν•˜μ§€ μ•ŠμŒ
  • +
  • • Domain은 무의쑴
  • +
  • • μ™ΈλΆ€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ•± νƒ€κ²Ÿμ—μ„œλ§Œ 링크
  • +
+
+ +
+
+
+

ν”Όμ²˜ 곡톡 의쑴

+
+
    +
  • • Feature β†’ Domain ν”„λ‘œν† μ½œλ§Œ 의쑴
  • +
  • • DesignSystem Β· CoreCoordinator Β· SnapKit
  • +
  • • Home β†’ Search/Settings Interface만 μ°Έμ‘°
  • +
+
+ +
+
+
+

인증

+
+
    +
  • • POST /auth/guest 게슀트 JWT
  • +
  • • 만료 μ‹œ 같은 deviceId둜 쑰용히 재인증
  • +
+
+
+ +
+ + + + diff --git a/docs/assets/atcha-v2-architecture-dark.png b/docs/assets/atcha-v2-architecture-dark.png new file mode 100644 index 00000000..684f8ad0 Binary files /dev/null and b/docs/assets/atcha-v2-architecture-dark.png differ diff --git a/docs/assets/atcha-v2-architecture-light.png b/docs/assets/atcha-v2-architecture-light.png new file mode 100644 index 00000000..a119eb9d Binary files /dev/null and b/docs/assets/atcha-v2-architecture-light.png differ