Skip to content

SWYP-Find/Picke-iOS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

128 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Picke iOS

๊ฐ€์น˜๊ด€ ์ถฉ๋Œ์—์„œ ์‹œ์ž‘ํ•˜๋Š” 1:1 ์ฒ ํ•™ ๋ฐฐํ‹€ ํ”Œ๋žซํผ, Picke

Platform Language iOS Xcode TCA Tuist Fastlane

๐ŸŽฏ Features | ๐Ÿ— Architecture | ๐Ÿš€ Quick Start | ๐Ÿ” OAuth Flow


๐Ÿ“– ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

Picke ๋Š” ์ผ์ƒ์˜ ๊ฐ€์น˜๊ด€ ์ฐจ์ด๋ฅผ 1:1 ํ† ๋ก ์œผ๋กœ ํ’€์–ด๋‚ด๋Š” ๋ชจ๋ฐ”์ผ ํ† ๋ก ยทํˆฌํ‘œ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. "์˜ค๋Š˜์˜ ๋ฐฐํ‹€" ์ฃผ์ œ์— ๋Œ€ํ•œ ์‚ฌ์ „ยท์‚ฌํ›„ ํˆฌํ‘œ, ์‹ค์‹œ๊ฐ„ 1:1 ์ฑ„ํŒ… ํ† ๋ก , ๊ทธ๋ฆฌ๊ณ  ๋ฆฌ์บก ์นด๋“œ๊นŒ์ง€ ํ•œ ํ๋ฆ„์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

๐Ÿ’ก ์™œ ๋งŒ๋“ค์—ˆ๋‚˜? SNS ์˜ ๋‹จ๋ฐฉํ–ฅ ์˜๊ฒฌ ํ‘œ์ถœ ๋Œ€์‹ , ์งง๊ณ  ๋ช…ํ™•ํ•œ 1:1 ํ† ๋ก ์„ ํ†ตํ•ด "๋‚ด๊ฐ€ ์™œ ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋Š”์ง€" ๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ๋‹ค๋ฅธ ๊ฐ€์น˜๊ด€์„ ๋งˆ์ฃผํ•˜๋Š” ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ›  Setup

AI ๋„๊ตฌ ์—ฐ๋™

ํ”„๋กœ์ ํŠธ ๊ทœ์น™์€ AGENTS.md / CLAUDE.md ์— ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ln -s AGENTS.md CLAUDE.md

โœจ ์ฃผ์š” ๊ธฐ๋Šฅ

๐Ÿ” ์†Œ์…œ ๋กœ๊ทธ์ธ (server-mediated OAuth)

  • Google / Kakao: WKWebView ๊ธฐ๋ฐ˜ authorize โ†’ code ๊ฐ€๋กœ์ฑ„๊ธฐ โ†’ ๋ฐฑ์—”๋“œ ํ† ํฐ ๊ตํ™˜
  • Apple Sign-In: ASAuthorizationAppleIDProvider ๋„ค์ดํ‹ฐ๋ธŒ ํ†ตํ•ฉ
  • ์ž๋™ ํ† ํฐ ๊ฐฑ์‹ : AccessTokenCredential JWT exp ๋””์ฝ”๋”ฉ + ๋งŒ๋ฃŒ 5๋ถ„ ์ „ ์ž๋™ refresh
  • 401 ์ž๋™ ์ฒ˜๋ฆฌ: AuthInterceptor ๊ฐ€ 401 ๊ฐ์ง€ โ†’ refresh ์‹œ๋„ โ†’ ์‹คํŒจ ์‹œ ์ž๋™ ๋กœ๊ทธ์•„์›ƒ ์•Œ๋ฆผ ๋ฐœ์†ก
  • USER_404 ๊ฐ•์ œ ๋กœ๊ทธ์•„์›ƒ: SessionInvalidationPlugin ์ด ์‘๋‹ต ๋ฐ”๋”” ์—๋Ÿฌ์ฝ”๋“œ(USER_404) ๊ฐ์ง€ โ†’ Keychain/์„ธ์…˜ ์ •๋ฆฌ ํ›„ ๋กœ๊ทธ์ธ ์ „ํ™˜

๐ŸฅŠ ์˜ค๋Š˜์˜ ๋ฐฐํ‹€

  • ์‚ฌ์ „ ํˆฌํ‘œ โ†’ 1:1 ์ฑ„ํŒ… ํ† ๋ก  โ†’ ์‚ฌํ›„ ํˆฌํ‘œ ์˜ ํ•œ ํ๋ฆ„
  • ์žฌํˆฌํ‘œ ๋กœ ๊ฐ€์น˜๊ด€์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ์ถ”์ 
  • ๋ฆฌ์บก ์นด๋“œ ์ž๋™ ์ƒ์„ฑ + ๊ณต์œ 
  • ์ฑ„ํŒ…๋ฐฉ ์˜ค๋””์˜ค ์žฌ์ƒ + ๋กœ๋”ฉ ์‹คํŒจ ์‹œ ์ƒ๋‹จ floating ์˜ค๋ฅ˜ ๋ฐฐ๋„ˆ(FloatingErrorView)

๐Ÿ’ฌ ํ† ๋ก  / ๊ด€์ (๋Œ“๊ธ€)

  • ์ฑ„ํŒ…๋ฐฉํ˜• 1:1 ์Œ์„ฑ ํ† ๋ก 
  • ๊ด€์ (=๋Œ“๊ธ€) ๋“ฑ๋กยท์ˆ˜์ •ยท์‚ญ์ œ + ๋Œ€๋Œ“๊ธ€, ์ข‹์•„์š”, ์‹ ๊ณ 
  • ํˆฌํ‘œ ์ง„์˜(optionId)๋ณ„ ๊ด€์  ๋“ฑ๋ก / ์ง„์˜ ํƒญ ํ•„ํ„ฐ
  • ๋ณธ์ธ ๊ธ€ "๋‚˜" ํ‘œ์‹œ + ์ˆ˜์ •ยท์‚ญ์ œ ๋ฉ”๋‰ด, ๋“ฑ๋กยท๊ฐฑ์‹  ์‹œ ์Šค์ผˆ๋ ˆํ†ค

๐Ÿงญ ํƒ์ƒ‰ / ํ๋ ˆ์ดํŒ… / ํ™ˆ

  • ํ๋ ˆ์ดํŒ…๋œ ํ™ˆ ํ”ผ๋“œ
  • ํฅ๋ฏธ ๊ธฐ๋ฐ˜ ๋ฐฐํ‹€ ์ถ”์ฒœ(ํ๋ ˆ์ดํŒ… ํ™”๋ฉด) โ€” GET /battles/{id}/recommendations/interesting
  • ์นดํ…Œ๊ณ ๋ฆฌยทํƒœ๊ทธ ํƒ์ƒ‰
  • ํ† ํ”ฝ ๊ฒ€์ƒ‰

๐Ÿ‘ค ๋งˆ์ดํŽ˜์ด์ง€

  • ํ”„๋กœํ•„ ์นด๋“œ / ๋ณด์œ  ํฌ์ธํŠธ + ๋ฌด๋ฃŒ ์ถฉ์ „(๋ฆฌ์›Œ๋“œ ๊ด‘๊ณ )
  • ํฌ์ธํŠธ ๋‚ด์—ญ, ๋‚ด ๋ฐฐํ‹€ ๊ธฐ๋ก, ๋‚ด ์ฝ˜ํ…์ธ  ํ™œ๋™(๋Œ“๊ธ€/์ข‹์•„์š”), ๊ณต์ง€์‚ฌํ•ญยท์ด๋ฒคํŠธ
  • ๋‚˜์˜ ์ฒ ํ•™์ž ์œ ํ˜•(recap) โ€” ๋ฐฐํ‹€ 5๊ฐœ ๋ฏธ๋งŒ ์‹œ ์ž ๊ธˆ ํ™”๋ฉด ๋ถ„๊ธฐ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ ˆ์ด๋” ์ฐจํŠธ + ๊ณต์œ 
  • ๋ฐฐํ‹€ ์ฃผ์ œ ์ œ์•ˆ, ์•Œ๋ฆผ ์„ค์ •
  • ํšŒ์› ํƒˆํ‡ด โ€” ํƒˆํ‡ด ์‚ฌ์œ (๋ณต์ˆ˜ ์„ ํƒ) ์ž…๋ ฅ ํ™”๋ฉด ๋ถ„๋ฆฌ, ์ œ์ถœ ์‹œ ๋””๋ฐ”์ด์Šค ํ† ํฐ ํ•ด์ œ + ์„ธ์…˜ ์ข…๋ฃŒ

๐Ÿ”” ์•Œ๋ฆผ (Notification)

  • ์•Œ๋ฆผ๋ฐ›๊ธฐ ๋ชฉ๋ก โ€” ์นดํ…Œ๊ณ ๋ฆฌ ํƒญ(์ „์ฒดยท์ฝ˜ํ…์ธ ยท๊ณต์ง€์‚ฌํ•ญยท์ด๋ฒคํŠธ) + ๋ฌดํ•œ ์Šคํฌ๋กค
  • ํƒญ ์‹œ ์ฝ์Œ ์ฒ˜๋ฆฌ / ๋ชจ๋‘ ์ฝ์Œ โ€” GET /api/v1/notifications, ์ฝ์Œ์€ PATCH .../readยท/read-all
  • ๋ฏธ์ฝ์Œ ๋นจ๊ฐ„์  โ€” @Shared(.appStorage("HasUnreadNotification")) ๋กœ ํ™ˆยทํ”„๋กœํ•„ ์ข… ์•„์ด์ฝ˜์— ํ‘œ์‹œ
    • ๊ฐœ๋ณ„/๋ชจ๋‘ ์ฝ์Œ ์‹œ ์ฆ‰์‹œ ์ œ๊ฑฐ, ์ƒˆ ํ‘ธ์‹œ ์ˆ˜์‹  ์‹œ ๋‹ค์‹œ ํ‘œ์‹œ, ์•ฑ ์žฌ์‹คํ–‰์—๋„ ์ƒํƒœ ์œ ์ง€

๐Ÿ“ฒ ํ‘ธ์‹œ ์•Œ๋ฆผ / ๋”ฅ๋งํฌ (APNs)

  • APNs ๋‹ค์ด๋ ‰ํŠธ ๋ฐœ์†ก (Firebase SDK ๋ฏธ์‚ฌ์šฉ) โ€” ๊ถŒํ•œ ์š”์ฒญยทํ† ํฐ ์ˆ˜์‹  ํ›„ POST /api/v1/devices ๋“ฑ๋ก(platform: "IOS"), ๋กœ๊ทธ์•„์›ƒ/ํƒˆํ‡ด ์‹œ ํ•ด์ œ
  • ์•Œ๋ฆผ ํƒญ โ†’ ํŽ˜์ด๋กœ๋“œ(type/url)๋ฅผ PickeDeeplink ๋กœ ๋ณ€ํ™˜ํ•ด ๋ฐฐํ‹€ ์ƒ์„ธ ๋“ฑ์œผ๋กœ ๋ผ์šฐํŒ…, ์ฝœ๋“œ ์Šคํƒ€ํŠธ ๋Œ€๊ธฐ ๋”ฅ๋งํฌ ์ฒ˜๋ฆฌ
  • ์ปค์Šคํ…€ URL scheme picke://battle/55 ยท picke://perspective/45?commentId=678 (onOpenURL)

โฌ†๏ธ ์•ฑ ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด

  • ์Šคํ”Œ๋ž˜์‹œ์—์„œ App Store(iTunes lookup) ์ตœ์‹  ๋ฒ„์ „ ๋น„๊ต โ†’ ์—…๋ฐ์ดํŠธ ํ•„์š” ์‹œ ์•ˆ๋‚ด alert
  • "์ง€๊ธˆ ์—…๋ฐ์ดํŠธ" โ†’ App Store, "๋‚˜์ค‘์—" โ†’ ์ •์ƒ ์ง„์ž…

๐Ÿ’ฐ ๋ฌด๋ฃŒ ์ถฉ์ „ (๋ฆฌ์›Œ๋“œ ๊ด‘๊ณ )

  • GoogleMobileAds ๋ฆฌ์›Œ๋“œ ๋™์˜์ƒ ์‹œ์ฒญ โ†’ ํฌ์ธํŠธ ์ถฉ์ „ (๊ด‘๊ณ  ์œ ๋‹› ID ๋Š” REWARD_AD_UNIT config ์ฃผ์ž…)
  • RewardedAdClient(UseCase) โ€” ๋กœ๋“œยทํ‘œ์‹œยท๋ณด์ƒ ์ฝœ๋ฐฑ์„ async ๋กœ ์ถ”์ƒํ™”

๐Ÿ“Š ํ–‰๋™ ๋ถ„์„ (Mixpanel)

  • AnalyticsUseCase โ€” ํƒ€์ž… ์•ˆ์ „ ์ด๋ฒคํŠธ + PICKรฉ ํ•ต์‹ฌ ์ด๋ฒคํŠธ ๋ช…์„ธ์„œ ์ค€์ˆ˜(์ด๋ฒคํŠธ ํ†ตํ•ฉ ์ „๋žต)
  • sign_up / battle_step(pre_voteยทaudio_endยทpost_vote) / report_action / community_action / ad_revenue + ๋กœ๊ทธ์ธ ์‹œ identify

๐Ÿ— ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ฒ˜

๐ŸŽฏ Clean Architecture ร— Tuist ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ

Picke-iOS/
โ”œโ”€โ”€ ๐Ÿ“ฑ Projects/
โ”‚   โ”œโ”€โ”€ App/                       # ๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํƒ€๊ฒŸ
โ”‚   โ”‚   โ”œโ”€โ”€ Sources/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Application/       # AppDelegate(APNs) / PushTokenStore / Deeplink ๋ธŒ๋ฆฌ์ง€
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Di/                # WeaveDI ๋“ฑ๋ก (DiRegister, AppPresentationContextProvider)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Reducer/           # TCA Root AppReducer
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ View/              # Root Views
โ”‚   โ”‚   โ””โ”€โ”€ Derived/               # Tuist ์ƒ์„ฑ plist
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Presentation/              # ๐ŸŽจ UI Layer
โ”‚   โ”‚   โ”œโ”€โ”€ Auth/                  # ๋กœ๊ทธ์ธ / ์˜จ๋ณด๋”ฉ / ์ธ์ฆ ์ฝ”๋””๋„ค์ดํ„ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ Battle/                # ์˜ค๋Š˜์˜ ๋ฐฐํ‹€ / ๋น ๋ฅธ ๋ฐฐํ‹€ ํ™”๋ฉด ๋ชจ๋ธ๊ณผ ๋ฉ”์ธ ํ”Œ๋กœ์šฐ
โ”‚   โ”‚   โ”œโ”€โ”€ Chat/                  # ํˆฌํ‘œ / ์ฑ„ํŒ…๋ฐฉ / ๊ด€์ ยท๋Œ€๋Œ“๊ธ€ / ํ๋ ˆ์ดํŒ…
โ”‚   โ”‚   โ”œโ”€โ”€ Hifi/                  # ํƒ์ƒ‰ยท๊ฒ€์ƒ‰ ๊ธฐ๋ฐ˜ Hi-Fi ํ™”๋ฉด
โ”‚   โ”‚   โ”œโ”€โ”€ Home/                  # ํ™ˆ ํ”ผ๋“œ / ์ถ”์ฒœ / ์Šค์ผˆ๋ ˆํ†ค
โ”‚   โ”‚   โ”œโ”€โ”€ MainTab/               # ํƒญ ๋ผ์šฐํŒ… / GNB
โ”‚   โ”‚   โ”œโ”€โ”€ Notification/          # ์•Œ๋ฆผ๋ฐ›๊ธฐ ๋ชฉ๋ก / ์นดํ…Œ๊ณ ๋ฆฌ ํƒญ / ๋ฏธ์ฝ์Œ ๋ฑƒ์ง€
โ”‚   โ”‚   โ”œโ”€โ”€ Profile/               # ๋งˆ์ดํŽ˜์ด์ง€ / ํฌ์ธํŠธ / ์„ค์ • / ํƒˆํ‡ด / ๋ฐฐํ‹€์ œ์•ˆ / ๋ฐฐํ‹€๊ธฐ๋ก / ์ฝ˜ํ…์ธ ํ™œ๋™ / ๊ณต์ง€ / ๋ฆฌ์บก / ๋ฌด๋ฃŒ์ถฉ์ „
โ”‚   โ”‚   โ”œโ”€โ”€ Splash/                # ์Šคํ”Œ๋ž˜์‹œ / ์•ฑ ์—…๋ฐ์ดํŠธ ์ฒดํฌ
โ”‚   โ”‚   โ”œโ”€โ”€ Web/                   # ์•ฝ๊ด€ / ์™ธ๋ถ€ ๋งํฌ WebView
โ”‚   โ”‚   โ””โ”€โ”€ Presentation/          # ๊ณตํ†ต ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ์œ ํ‹ธ
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Domain/                    # ๐Ÿ”ฅ Business Logic Layer
โ”‚   โ”‚   โ”œโ”€โ”€ Entity/                # Auth / Battle / Comment / Home / OAuth / Profile / Notification / Device / Deeplink / AppUpdate / Share / Error ์—”ํ‹ฐํ‹ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ DomainInterface/       # Repository / Manager ์ธํ„ฐํŽ˜์ด์Šค (Device ยท AppUpdate ํฌํ•จ)
โ”‚   โ”‚   โ””โ”€โ”€ UseCase/               # Auth / Battle / Comment / Home / OAuth / Profile / Notification / Device / AppUpdate / Analytics / Ad ์œ ์Šค์ผ€์ด์Šค
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Data/                      # ๐Ÿ“ก Data Layer
โ”‚   โ”‚   โ”œโ”€โ”€ API/                   # Base / Auth / Battle / Comment / Home / Perspective / Profile / Notification / Device endpoint
โ”‚   โ”‚   โ”œโ”€โ”€ Service/               # Moya TargetType + ์š”์ฒญ ๋ฐ”๋””
โ”‚   โ”‚   โ”œโ”€โ”€ Model/                 # BaseResponseDTO + DTO โ†’ Entity ๋งคํผ
โ”‚   โ”‚   โ””โ”€โ”€ Repository/            # RepositoryImpl + OAuth / AudioPlayer ๊ตฌํ˜„
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Network/                   # ๐ŸŒ Network Layer
โ”‚   โ”‚   โ”œโ”€โ”€ Networking/            # ๋„คํŠธ์›Œํฌ ํด๋ผ์ด์–ธํŠธ export
โ”‚   โ”‚   โ”œโ”€โ”€ Foundations/           # APIHeader / TokenProviding / KeychainTokenProvider
โ”‚   โ”‚   โ””โ”€โ”€ ThirdPartys/           # AsyncMoya / WeaveDI ๋“ฑ SPM ์žฌ๋…ธ์ถœ
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ Shared/                    # ๐Ÿ”ง Shared Layer
โ”‚       โ”œโ”€โ”€ DesignSystem/          # ๊ณตํ†ต UI / ์ปฌ๋Ÿฌ ํ† ํฐ / ์ด๋ฏธ์ง€ / Toast / Floating ๋ฐฐ๋„ˆ / ํŒ์—…
โ”‚       โ”œโ”€โ”€ Shared/                # ๊ณต์œ  ๋ชจ๋ธยทํ™•์žฅ
โ”‚       โ”œโ”€โ”€ ThirdParty/            # ์จ๋“œํŒŒํ‹ฐ ๋ž˜ํผ
โ”‚       โ””โ”€โ”€ Utill/                 # ๋‚ ์งœ / ์ˆซ์ž / ๋ฌธ์ž์—ด ํ‘œ์‹œ ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚
โ”œโ”€โ”€ ๐Ÿ”ง Tuist/
โ”‚   โ”œโ”€โ”€ Package.swift              # SPM ์˜์กด์„ฑ ์ •์˜
โ”‚   โ””โ”€โ”€ ProjectDescriptionHelpers/ # ๋ชจ๋“ˆ ํ…œํ”Œ๋ฆฟ / Plist ํ—ฌํผ
โ””โ”€โ”€ ๐Ÿงฉ Plugins/
    โ”œโ”€โ”€ DependencyPlugin/          # ๋ชจ๋“ˆ ์˜์กด์„ฑ ํ—ฌํผ (.Data / .Domain / .Network ...)
    โ”œโ”€โ”€ DependencyPackagePlugin/   # SPM ์˜์กด์„ฑ ํ—ฌํผ (.SPM.asyncMoya ...)
    โ””โ”€โ”€ ProjectTemplatePlugin/     # ProjectConfig / Project.makeModule

๐Ÿ›๏ธ Clean Architecture Pattern

graph TD
    A[Presentation: SwiftUI + TCA Feature] --> B[Domain: UseCase + Entity]
    B --> C[DomainInterface: Repository Protocol]
    D[Data: RepositoryImpl] --> C
    D --> E[Data: DTO Model + Service + API]
    E --> F[Network: AsyncMoya + Header + Token]
    G[Shared: DesignSystem / Utill] --> A
    G --> B
    G --> D

    A -.-> H[Auth / Battle / Chat / Home / Hifi / MainTab / Splash / Web]
    B -.-> I[Auth / Battle / Comment / Perspective / Search UseCase]
    D -.-> J[Auth / Battle / Comment / Home / OAuth / Perspective / Search Repository]
Loading

๐Ÿ•ธ๏ธ TuistSpider ํ™•์žฅ ๋ทฐ

๋ ˆ์ด์–ด๋ณ„๋กœ ๋ฌถ์–ด ๋ณด๊ฑฐ๋‚˜(Grouped) ๋ชจ๋“  ๋ชจ๋“ˆ์„ ํŽผ์ณ ๋ณธ(Expanded) ์‹œ๊ฐํ™”์ž…๋‹ˆ๋‹ค. (TuistSpider ๊ฒฐ๊ณผ)

Grouped Expanded

๐Ÿ”„ ์˜์กด์„ฑ ๋ฐฉํ–ฅ ์›์น™

Presentation โ†’ Domain (UseCase Protocol)
       โ†“
Domain/UseCase โ†’ Domain (Repository Protocol)
       โ†“
Data/Repository โ†’ Domain (Entity + Repository Protocol)
       โ†“
Data/Model โ†’ Domain (Entity ๋ณ€ํ™˜)
       โ†“
Data/Service โ†’ Data/API + Network/Foundations

ํ•ต์‹ฌ ์„ค๊ณ„ ์›์น™

  • โœ… Presentation ์€ Domain ์˜ UseCase ๋งŒ ์˜์กดํ•ฉ๋‹ˆ๋‹ค.
  • โœ… Domain/UseCase ๋Š” Repository Protocol ์„ ํ†ตํ•ด ์™ธ๋ถ€ IO ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  • โœ… Data/Repository ๋Š” Domain ์˜ Repository Protocol ์„ ๊ตฌํ˜„ํ•˜๊ณ  Entity ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • โœ… Data/Model ์€ DTO ์™€ Entity ๋ณ€ํ™˜์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • โœ… Data/Service ๋Š” endpoint / method / parameter ์ •์˜๋งŒ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • โœ… ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํ๋ฆ„์€ Domain ์„ ์ค‘์‹ฌ์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ” OAuth ์ธ์ฆ ํ”Œ๋กœ์šฐ

Google / Kakao โ€” WKWebView server-mediated OAuth

์•ฑ
  โ”‚  authorize URL (response_type=code, redirect_uri=https://picke.store/oauth/<p>)
  โ–ผ
WKWebView (OAuthWebViewController)
  โ”‚  ์‚ฌ์šฉ์ž ๋™์˜ โ†’ ๊ตฌ๊ธ€/์นด์นด์˜ค๊ฐ€ redirect_uri ๋กœ 302
  โ”‚  WKNavigationDelegate.decidePolicyFor ๊ฐ€ picke.store/oauth/<p>?code=... ๊ฐ€๋กœ์ฑ„๊ธฐ
  โ”‚  decisionHandler(.cancel)   โ† 401 ์‘๋‹ต ์†ก์‹  ์ฐจ๋‹จ
  โ–ผ
authorizationCode ์ถ”์ถœ โ†’ dismiss
  โ–ผ
UnifiedOAuthUseCase
  โ”‚  POST /api/v1/auth/login/<provider>
  โ”‚  body: { authorizationCode, redirectUri }
  โ–ผ
AuthRepositoryImpl
  โ”‚  BaseResponseDTO<LoginDataDTO> ๋””์ฝ”๋”ฉ โ†’ LoginEntity
  โ–ผ
KeychainManager ์ €์žฅ + AuthSessionManager.credential ๊ฐฑ์‹ 

Apple โ€” ๋„ค์ดํ‹ฐ๋ธŒ Sign-In

ASAuthorizationAppleIDProvider ๋กœ ๋ฐ›์€ credential / nonce / authorizationCode ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐฑ์—”๋“œ์— ์ „๋‹ฌ.

ํ† ํฐ ์ž๋™ ๊ฐฑ์‹ 

  • AccessTokenCredential ๊ฐ€ access token JWT ์˜ exp ๋ฅผ ๋””์ฝ”๋”ฉํ•ด ๋งŒ๋ฃŒ ์‹œ์  ๋ณด๊ด€
  • AuthInterceptor.adapt ์—์„œ ๋งŒ๋ฃŒ 5๋ถ„ ์ „์ด๋ฉด TokenRefreshManager ๊ฐ€ ๋‹จ์ผํ™”๋œ refresh ์ˆ˜ํ–‰
  • 401 ์‘๋‹ต ์‹œ retry ๋กœ ํ† ํฐ ๊ฐฑ์‹  ํ›„ ์žฌ์‹œ๋„, ์‹คํŒจ ์‹œ NSNotification.refreshTokenExpired ๋ฐœ์†ก + ์ž๋™ ๋กœ๊ทธ์•„์›ƒ

๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

Core Technologies

  • ๐ŸŽฏ Architecture: The Composable Architecture (TCA)
  • ๐Ÿ“ฆ Modularization: Tuist 4.x (Micro Feature Architecture)
  • ๐Ÿ’‰ Dependency Injection: WeaveDI 3.4.1
  • ๐Ÿ”€ Navigation: TCAFlow (์ปค์Šคํ…€)
  • โšก Concurrency: Swift Concurrency (async/await)

๐Ÿ“š ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๐ŸŽฏ ์•„ํ‚คํ…์ฒ˜ & ์ƒํƒœ ๊ด€๋ฆฌ

  • ComposableArchitecture โ€” ๋‹จ๋ฐฉํ–ฅ ์ƒํƒœ ๊ด€๋ฆฌ
  • TCAFlow โญ๏ธ โ€” TCA ๊ธฐ๋ฐ˜ ํ™”๋ฉด ์ „ํ™˜ / ๋„ค๋น„๊ฒŒ์ด์…˜ (์ปค์Šคํ…€)
  • WeaveDI โญ๏ธ โ€” ์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ (์ปค์Šคํ…€)

๐Ÿ” ์ธ์ฆ & ๋ณด์•ˆ

  • AuthenticationServices โ€” Apple Sign-In, ASWebAuthenticationSession
  • WebKit โ€” WKWebView ๊ธฐ๋ฐ˜ server-mediated OAuth (Google / Kakao)
  • AppAuth-iOS โ€” OAuth 2.0 / OpenID Connect ํด๋ผ์ด์–ธํŠธ (์˜ต์…˜)

๐ŸŒ ๋„คํŠธ์›Œํ‚น

  • AsyncMoya โญ๏ธ โ€” async/await ๊ธฐ๋ฐ˜ HTTP ํด๋ผ์ด์–ธํŠธ (์ปค์Šคํ…€)
  • Alamofire / Moya โ€” AsyncMoya ์˜ ๊ธฐ๋ฐ˜ ์Šคํƒ

๐ŸŽจ UI & UX

  • SwiftUI โ€” ์„ ์–ธํ˜• UI
  • SDWebImageSwiftUI โ€” ๋น„๋™๊ธฐ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ / ์บ์‹ฑ

๐Ÿ”ฅ ๋ฐฑ์—”๋“œ / ๋ถ„์„

๐Ÿ›  ๊ฐœ๋ฐœ ๋„๊ตฌ & ์œ ํ‹ธ๋ฆฌํ‹ฐ

๐Ÿ“Š ๋กœ๊น… & ๋””๋ฒ„๊น…

  • LogMacro โ€” ์ปค์Šคํ…€ ๋กœ๊น… ๋งคํฌ๋กœ
  • IssueReporting โ€” ๊ฐœ๋ฐœ ๋‹จ๊ณ„ ์ด์Šˆ ์ถ”์ 
  • XCTestDynamicOverlay โ€” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์˜ค๋ฒ„๋ ˆ์ด

โšก ์„ฑ๋Šฅ & ๋™์‹œ์„ฑ

  • Clocks โ€” ์‹œ๊ฐ„ ๊ด€๋ จ ์œ ํ‹ธ๋ฆฌํ‹ฐ
  • ConcurrencyExtras โ€” Swift Concurrency ํ™•์žฅ
  • Swift 6.0 โ€” ์ตœ์‹  Swift ์–ธ์–ด ๊ธฐ๋Šฅ

๐Ÿ”ง ๋นŒ๋“œ & ๋ฐฐํฌ

  • Tuist โ€” ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ / ๋ชจ๋“ˆ ์˜์กด์„ฑ ๊ด€๋ฆฌ
  • Swift Package Manager โ€” ํŒจํ‚ค์ง€ ์˜์กด์„ฑ ๊ด€๋ฆฌ
  • fastlane + Bundler โ€” TestFlight / App Store ๋นŒ๋“œยท์—…๋กœ๋“œ ์ž๋™ํ™”

๐Ÿ“ฑ ์ง€์› ํ™˜๊ฒฝ

  • ๐Ÿ’ป Xcode: 16.0 ์ด์ƒ
  • ๐Ÿ“ฑ iOS: 17.0 ์ด์ƒ
  • โšก Swift: 6.0 ์ด์ƒ
  • ๐Ÿ”ง Tuist: 4.x ์ด์ƒ

๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘

โœ… ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ

  • ๐Ÿ’ป Xcode: 16.0 ์ด์ƒ
  • ๐Ÿ“ฑ iOS: 17.0 ์ด์ƒ
  • โšก Swift: 6.0 ์ด์ƒ
  • ๐Ÿ”ง Tuist: 4.x ์ด์ƒ

๐Ÿ›  ์„ค์น˜ ๋ฐ ์‹คํ–‰

1๏ธโƒฃ ์ €์žฅ์†Œ ํด๋ก 

git clone https://github.com/Roy-wonji/Picke-iOS.git
cd Picke-iOS

2๏ธโƒฃ Tuist ์„ค์น˜

curl -Ls https://install.tuist.io | bash

3๏ธโƒฃ Ruby / fastlane ์˜์กด์„ฑ ์„ค์น˜

rbenv install 3.3.9
rbenv global 3.3.9
bundle install

4๏ธโƒฃ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ / ์ƒ์„ฑ

# ์ „์ฒด ์›Œํฌํ”Œ๋กœ์šฐ (๊ถŒ์žฅ)
./make build      # clean โ†’ install โ†’ generate

# ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰
./make clean      # ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ์ •๋ฆฌ
./make install    # SPM ์˜์กด์„ฑ ์„ค์น˜
./make generate   # Xcode ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

5๏ธโƒฃ Xcode ์—ด๊ธฐ

open Picke.xcworkspace

โš™๏ธ ํ™˜๊ฒฝ ์„ค์ •

๋‹ค์Œ ํ‚ค๋“ค์„ Picke-Dev.xcconfig / Picke-Stage.xcconfig / Picke-Prod.xcconfig ์— ์ฑ„์›Œ์ฃผ์„ธ์š”.

BASE_URL              = picke.store
GOOGLE_CLIENT_ID      = YOUR_GOOGLE_WEB_CLIENT_ID
GOOGLE_IOS_CLIENT_ID  = YOUR_GOOGLE_IOS_CLIENT_ID
REVERSED_CLIENT_ID    = YOUR_REVERSED_CLIENT_ID
KAKAO_REST_API_KEY    = YOUR_KAKAO_REST_API_KEY

๐ŸŒ OAuth ์‚ฌ์ „ ๋“ฑ๋ก

Provider redirect_uri ๋น„๊ณ 
Google https://picke.store/oauth/google Web client ID + Google Cloud Console ๋“ฑ๋ก ํ•„์š”
Kakao https://picke.store/oauth/kakao Kakao Developers ์ฝ˜์†” ๋“ฑ๋ก ํ•„์š”
Apple (๋„ค์ดํ‹ฐ๋ธŒ) App Store Connect โ†’ Sign in with Apple

๐Ÿ› ๏ธ ์ฃผ์š” ๋ช…๋ น์–ด

๐Ÿ”„ ๊ธฐ๋ณธ ์›Œํฌํ”Œ๋กœ์šฐ

./make build      # ์ „์ฒด ๋นŒ๋“œ ํ”„๋กœ์„ธ์Šค (๊ถŒ์žฅ)
./make generate   # ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ๋งŒ
./make clean      # ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ์ •๋ฆฌ
./make install    # ์˜์กด์„ฑ ์„ค์น˜

๐Ÿšจ ๋ฌธ์ œ ํ•ด๊ฒฐ

tuist clean       # Tuist ์บ์‹œ ์ •๋ฆฌ
./make clean      # ๋ชจ๋“  ๋นŒ๋“œ ํŒŒ์ผ ์ •๋ฆฌ

๐Ÿ” ์ฝ”๋“œ ํ’ˆ์งˆ / ๊ทธ๋ž˜ํ”„

tuist graph       # ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
tuist test        # ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰

๐Ÿš€ ๋ฐฐํฌ

export MATCH_KEYCHAIN_PASSWORD="<match keychain password>"
bundle exec fastlane ios QA       # TestFlight ์—…๋กœ๋“œ
bundle exec fastlane ios release  # App Store ๋ฐฐํฌ

MATCH_KEYCHAIN_PASSWORD๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด fastlane์ด match_keychain์„ ๋จผ์ € unlockํ•ด์„œ macOS ํ‚ค์ฒด์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ํŒ์—…์„ ์ค„์ž…๋‹ˆ๋‹ค.

๐Ÿ“„ ๋ผ์ด์„ ์Šค

์ด ํ”„๋กœ์ ํŠธ๋Š” MIT ๋ผ์ด์„ ์Šค ํ•˜์— ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ LICENSE ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๐Ÿ‘ฅ ํŒ€ & ํฌ๋ ˆ๋”ง

๐Ÿ’ป ๊ฐœ๋ฐœํŒ€

  • iOS Lead Developer: ์„œ์›์ง€ (@Roy-wonji)

๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

  • iOS: Swift Xcode Fastlane

  • Server: AWS EC2 AWS Swagger

  • Design: Figma

  • VCS: Git GitHub

๐Ÿˆโ€โฌ› Git ๋ธŒ๋žœ์นญ ์ „๋žต

1๏ธโƒฃ Git Branching Strategy

  • main: ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ์šฉ
  • develop: ๊ฐœ๋ฐœ ํ†ตํ•ฉ ๋ธŒ๋žœ์น˜
  • feature/*: ๊ธฐ๋Šฅ๋ณ„ ๊ฐœ๋ฐœ ๋ธŒ๋žœ์น˜
  • fix/*: ๋ฒ„๊ทธ ํ”ฝ์Šค ๋ธŒ๋žœ์น˜

๐Ÿ“‹ ์›Œํฌํ”Œ๋กœ์šฐ

  1. develop ์—์„œ feature/ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ
  2. ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ โ†’ ์ž์ฒด ์ปค๋ฐ‹ ๋‹จ์œ„ SRP ๋ถ„๋ฆฌ
  3. feature/ โ†’ develop Pull Request, ์ฝ”๋“œ ๋ฆฌ๋ทฐ
  4. develop โ†’ main ๋ฐฐํฌ Pull Request

โœ๏ธ ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€

  • ํ•œ๊ตญ์–ด ์‚ฌ์šฉ
  • ๊ด€๋ จ GitHub ์ด์Šˆ ๋ฒˆํ˜ธ ๋งค์นญ (์˜ˆ: #20 #2)
  • ํ˜•์‹: <type>: <์š”์•ฝ> #<issue>
  • feat / fix / refactor / chore / docs / test

๐Ÿ“ž ๋ฌธ์˜ ๋ฐ ์ง€์›


Made with โค๏ธ by Picke Team

Star this repo

About

Picke iOS

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors