Picke는 일상의 가치관 차이를 오늘의 배틀, 1:1 토론, 사전·사후 투표, 리캡 공유로 이어 주는 iOS 앱입니다.
주요 기능:
- Google, Kakao, Apple 기반 소셜 로그인
- 오늘의 배틀, 사전 투표, 1:1 토론, 사후 투표, 리캡 공유
- 홈 피드, 탐색, 검색, 큐레이팅 추천
- 관점 등록, 대댓글, 좋아요, 신고
- 마이페이지, 포인트, 무료 충전, 배틀 기록, 알림 설정
- APNs 푸시 알림과 딥링크 라우팅
- Mixpanel 분석, Google Mobile Ads, Sentry 오류 수집
SwiftUI와 The Composable Architecture를 기반으로 한 Clean Architecture 멀티 모듈 프로젝트입니다. Tuist가 프로젝트 생성, 모듈 의존성, 테스트 스킴, 바이너리 캐시 흐름을 관리합니다.
Projects/
├── App/ # 앱 진입점, 스플래시, AppReducer, DI 조립, 리소스
├── Feature/
│ ├── FeatureAssembly/ # 전체 Feature 조립 결과를 앱에 제공
│ ├── FeatureSharedUI/ # Feature 공통 UI
│ ├── Ad/ # Kakao·서버 광고 표시
│ ├── Auth/ # 로그인
│ ├── Home/ # 홈·출석 모달
│ ├── Battle/ # 배틀 메인
│ ├── Chat/ # 채팅·투표·관점·큐레이팅
│ ├── Hifi/ # 탐색·검색
│ ├── Notification/ # 알림
│ ├── Profile/ # 마이페이지·설정·리캡
│ └── Web/ # WebView
├── Domain/
│ ├── DomainAssembly/ # 도메인별 라이브 의존성 조립
│ ├── AdDomain/ # 광고 모델·조회·노출 집계 (Repository/UseCase)
│ ├── AuthDomain/ # 인증 도메인
│ ├── BattleDomain/ # 배틀 도메인
│ ├── CommentDomain/ # 댓글·대댓글 도메인
│ ├── HomeDomain/ # 홈 도메인
│ ├── NotificationDomain/ # 알림 도메인
│ ├── PerspectiveDomain/ # 관점 도메인
│ ├── ProfileDomain/ # 프로필 도메인
│ ├── SearchDomain/ # 검색 도메인
│ ├── AttendanceDomain/ # 출석 도메인
│ └── AppUpdateDomain/ # 앱 업데이트 도메인
├── Service/
│ ├── ServiceAssembly/ # API·인증·분석·디바이스 서비스 조립
│ ├── API/ # API 경로 상수
│ ├── APIEndpoint/ # 요청 명세
│ ├── PickeAuth/ # 인증 세션과 토큰 갱신
│ ├── PickeAnalytics/ # 분석 이벤트
│ ├── PickeConfig/ # 앱 환경 설정
│ ├── DeviceService/ # 디바이스·푸시 토큰
│ └── AudioPlayerService/ # 오디오 재생
├── Core/
│ ├── CoreAssembly/ # Core 구현 묶음
│ ├── PickeNetwork/ # Alamofire 기반 네트워크
│ ├── PickeStorage/ # Keychain 저장소
│ ├── PickeCoreLogger/ # 로깅
│ ├── PickeCoreUtility/ # 공통 Swift·TCA 유틸리티
│ ├── PickeCoreUI/ # UI 기반 타입
│ └── PickeThirdParty/ # 외부 패키지 재노출
└── UI/
├── PickeDesignKit/ # 디자인 토큰·컴포넌트·리소스
├── PickeSharedUI/ # 앱 공통 화면 컴포넌트
└── PickeAnimation/ # 이미지·애니메이션 리소스
flowchart TD
App --> FeatureAssembly
App --> DomainAssembly
App --> ServiceAssembly
FeatureAssembly --> Features[Feature modules]
Features --> DomainInterfaces["*DomainInterface"]
Features --> UI[UI modules]
DomainAssembly --> Domains[Domain modules]
Domains --> DomainInterfaces
Domains --> ServiceAssembly
ServiceAssembly --> Services[Service modules]
ServiceAssembly --> CoreAssembly
Services --> CoreAssembly
CoreAssembly --> Network[PickeNetwork]
CoreAssembly --> Storage[PickeStorage]
CoreAssembly --> Logger[PickeCoreLogger]
설계 원칙:
- App은 FeatureAssembly, DomainAssembly, ServiceAssembly를 통해 앱 전체 의존성을 조립합니다.
- Feature 모듈은 화면, TCA Reducer, Coordinator를 소유하고 외부 IO는 Domain Interface와 UseCase를 통해 호출합니다.
- Domain 모듈은 기능별 Entity, Repository 계약, UseCase 구현을 소유합니다.
- ServiceAssembly는 API, 인증, 분석, 디바이스, 오디오 서비스를 CoreAssembly와 연결합니다.
- CoreAssembly는 Network, Storage, Logger, Utility 등 앱 기반 구현을 묶습니다.
- UI 계층은 디자인 토큰, 공통 컴포넌트, 애니메이션 리소스를 제공합니다.
별도 런타임 DI 컨테이너 대신 Point-Free Dependencies를 사용합니다.
- 각 Domain·Service·Core 모듈은 필요한
DependencyKey와 기본값을 선언합니다. - Assembly 모듈은 live 구현을
DependencyValues에 등록합니다. - Feature는 Repository 구현체를 직접 알지 않고 UseCase 또는 Interface만 사용합니다.
- 테스트·프리뷰 기본값은 Interface 또는 Testing 타깃에서 관리합니다.
현재 42개 모듈의 구현·Interface 타깃 의존성을 표시합니다. 각 항목을 펼치면 GitHub에서 SVG 그림을 바로 볼 수 있습니다.
화살표는 참조하는 타깃 → 참조되는 타깃, 점선 테두리는 Interface입니다. Project.swift의 dependencies·interfaceDependencies와 템플릿이 연결하는 자기 Interface를 반영합니다. 외부 SPM 패키지는 이름으로 별도 표기하고, Tests·Testing·Demo와 전이 의존성은 생략합니다.
APIEndpoint → AuthDomainInterface처럼 현재 코드에 존재하는 계층 간 참조도 그대로 표시합니다. 실행 순서나 이상적인 아키텍처를 나타내는 그림은 아닙니다.
광고 HTML · 전체 도메인 HTML — 파일을 내려받아 브라우저에서 열면 확대·검색할 수 있습니다.
PickeAnalytics
외부 패키지 선언: composableArchitecture, mixpanel, mixpanelSessionReplay, sentry, sentrySwiftUI.
갱신·검증:
python3 scripts/generate_module_diagrams.py
python3 scripts/generate_module_diagrams.py --checkSVG 생성에는 Graphviz의 dot이 필요합니다. 앱 빌드나 Tuist 캐시 생성은 실행하지 않습니다.
| 영역 | 기술 |
|---|---|
| 언어 | Swift 6, Swift Concurrency |
| UI | SwiftUI |
| 상태 관리 | The Composable Architecture 1.26.2 |
| 내비게이션 | TCAFlow 1.1.8 |
| 프로젝트 | Tuist 4.207.0, Mise |
| 의존성 주입 | Point-Free Dependencies |
| 네트워크 | PickeNetwork, Alamofire |
| 로컬 데이터 | SQLiteData |
| 인증 | Sign in with Apple, GoogleSignIn, AppAuth |
| 저장소 | PickeStorage, Keychain |
| 이미지 | SDWebImageSwiftUI, Kingfisher |
| 모니터링 | Firebase Crashlytics, Sentry |
| 분석·광고 | Mixpanel, Google Mobile Ads |
| 테스트 | Swift Testing, XCTest, Tuist |
의존성 버전의 실제 기준은 Tuist/Package.swift와 Tuist/Package.resolved입니다. 보조 패키지 swift-dependencies는 Tuist에서 확인된 traits 조건부 의존성 누락 문제를 피하도록 1.12.0에 고정했습니다. 이 제한은 소스 빌드와 외부 모듈 캐시 모두에 적용됩니다.
지원 환경:
- iOS 17.0 이상
- Swift 6
- Xcode 26 이상
- iPhone
로컬 확인 기준:
- Xcode 26.5
- Apple Swift 6.3.2
- Tuist 4.207.0
Ruby, Tuist, xcbeautify 버전은 mise.toml에 고정되어 있습니다.
mise install
mise exec -- tuist version
mise which ruby
mise exec -- xcbeautify --version빌드 환경별 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
REWARD_AD_UNIT = YOUR_REWARD_AD_UNIT
OAuth redirect URI는 서버 중계 흐름을 기준으로 등록합니다.
| Provider | Redirect URI |
|---|---|
https://picke.store/oauth/google |
|
| Kakao | https://picke.store/oauth/kakao |
| Apple | Native Sign in with Apple |
Tuist Dashboard 프로젝트는 picke2026/picke입니다. 모듈 캐시 프로필, 저장소, Xcode 컴파일 캐시와 업로드 정책의 기준은 Tuist.swift입니다.
TuistTool.swift는 install 이후 외부 모듈 캐시를 준비하고, CI 또는 --no-binary-cache 옵션에서는 이 준비 단계를 생략합니다. generate는 별도 인증 명령이나 캐시 비활성화 옵션을 추가하지 않고 전달받은 인자로 실행합니다.
./make가 프로젝트 명령의 단일 진입점이며 TuistTool.swift를 실행합니다. 캐시 준비·사용·CI 제외·캐시 비활성화 옵션을 이 실행 경로에서 처리합니다. 소스를 수정하면 실행 파일을 다시 컴파일하지 않아도 다음 실행에 반영됩니다.
./make install --no-open # 의존성 설치 → 로컬 외부 캐시 준비 → 프로젝트 생성
./make generate --no-open # 준비된 캐시를 사용해 프로젝트 생성
./make cache # 로컬 외부 바이너리 캐시 준비
./make cache:setup # Xcode Compilation Cache 설정
./make setup # mise 설치 → install → cache warm → generategenerate는 캐시를 새로 빌드하지 않습니다. 첫 실행이나 의존성 변경 후에는 install 또는 cache로 준비합니다. 캐시 적중은 Xcode 버전, 구성과 의존성 해시가 일치해야 합니다.
./make install --no-binary-cache --no-open # 캐시 준비와 사용 모두 생략
./make generate --no-binary-cache --no-open
./make test --no-binary-cache./make cache는 tuist cache warm --external-only를 실행합니다. TUIST_LOCAL_CACHE_ONLY=true 환경변수를 함께 전달하며, 실제 저장소·연결 정책은 Tuist.swift가 결정합니다. 캐시 준비가 실패하면 오류를 반환하고 generate로 넘어가지 않습니다. ./make cache:setup은 Attendance와 동일하게 tuist setup cache를 실행하는 별도 명령이며, Tuist.swift의 컴파일 캐시 설정과 함께 사용합니다.
명령 순서와 CI/opt-out 동작은 python3 scripts/tests/test_tuist_cache_commands.py로 검사합니다. 이 검사는 실제 패키지 설치나 캐시 빌드를 실행하지 않습니다.
git clone git@github.com:SWYP-Find/Picke-iOS.git
cd Picke-iOS
./make setup
open Picke.xcworkspaceXcode에서 Picke-Stage 또는 필요한 스킴과 사용할 iPhone 시뮬레이터를 선택해 실행합니다.
CLAUDE.md가 필요한 도구는 AGENTS.md와 같은 내용을 보도록 심볼릭 링크를 만들 수 있습니다.
ln -s AGENTS.md CLAUDE.md./make setup # mise 설치, install, 로컬 외부 캐시 준비, generate
./make generate # Demo 앱을 포함해 Xcode 프로젝트 생성
./make generate --no-open # Xcode를 열지 않고 프로젝트 생성
./make generate --no-binary-cache --no-open
./make build # clean, install, generate
./make install # 의존성 설치, 로컬 외부 캐시 준비 후 generate
./make test # 전체 테스트
./make test --no-binary-cache # 로컬 바이너리 캐시 없이 전체 테스트
./make cache # 외부 바이너리 캐시 준비
./make cache:setup # Xcode Compilation Cache 설정
./make format # SwiftFormat 적용
./make lint # SwiftFormat 검사
./make clean # 생성 프로젝트 정리
./make reset # DerivedData 정리 후 프로젝트 재생성Makefile은 사용하지 않습니다. 테스트는 ./make test 또는 mise exec -- tuist test로 실행합니다.
새 모듈 생성:
./make feature <이름>
./make core <이름>
./make service <이름>
./make domain <이름>
./make ui <이름>
./make module <레이어> <이름>
# 자동으로 만든 카탈로그 case가 원하는 이름과 다를 때
./make feature <이름> --case <케이스명>모듈 생성 명령은 scaffold뿐 아니라 모듈 카탈로그와 해당 레이어 Assembly 의존성도 함께 갱신합니다.
- Fastlane QA와 release 레인은 빌드 번호를 갱신한 뒤
tuist generate --no-binary-cache --no-open로 워크스페이스를 재생성합니다. - workspace가 없을 때 fallback 경로는
mise exec -- tuist install후./make generate --no-binary-cache --no-open을 실행합니다. - Fastlane QA/release 배포가 성공하면 같은 IPA를 Tuist Preview의
qa/release트랙에 공유합니다. Preview 업로드에는 Tuist 로그인이 필요합니다. - 이 IPA는 App Store Connect용으로 서명되어 Preview 링크에서 기기에 직접 설치할 수 없습니다. 기기 테스트는 TestFlight를 사용합니다.
- Tuist Preview 업로드가 실패해도 이미 완료된 TestFlight/App Store 배포는 유지되며, Fastlane 로그에 경고가 남습니다.
main: 프로덕션 배포develop: 개발 통합feature/*: 기능 작업fix/*: 버그 수정
작업 브랜치에서 검증 후 develop으로 Pull Request를 올립니다.
- Issues: github.com/SWYP-Find/Picke-iOS/issues
- Discussions: github.com/SWYP-Find/Picke-iOS/discussions