Picke 는 일상의 가치관 차이를 1:1 토론으로 풀어내는 모바일 토론·투표 플랫폼입니다. "오늘의 배틀" 주제에 대한 사전·사후 투표, 실시간 1:1 채팅 토론, 그리고 리캡 카드까지 한 흐름으로 이어집니다.
💡 왜 만들었나? SNS 의 단방향 의견 표출 대신, 짧고 명확한 1:1 토론을 통해 "내가 왜 그렇게 생각하는지" 를 정리하고 다른 가치관을 마주하는 경험을 제공합니다.
프로젝트 규칙은 AGENTS.md / CLAUDE.md 에 정의되어 있습니다.
ln -s AGENTS.md CLAUDE.md- Google / Kakao: WKWebView 기반
authorize → code 가로채기→ 백엔드 토큰 교환 - Apple Sign-In:
ASAuthorizationAppleIDProvider네이티브 통합 - 자동 토큰 갱신:
AccessTokenCredentialJWT exp 디코딩 + 만료 5분 전 자동 refresh - 401 자동 처리:
AuthInterceptor가 401 감지 → refresh 시도 → 실패 시 자동 로그아웃 알림 발송
- 사전 투표 → 1:1 채팅 토론 → 사후 투표 의 한 흐름
- 재투표 로 가치관이 바뀌었는지 추적
- 리캡 카드 자동 생성 + 공유
- 채팅방형 1:1 토론
- 콘텐츠별 댓글·대댓글
- 신고·차단
- 큐레이팅된 홈 피드
- 카테고리·태그 탐색
- 토픽 검색
- 내 콘텐츠 활동 / 토론 기록
- 나의 철학자 유형
- 포인트 내역 / 알림 / 설정
Picke-iOS/
├── 📱 Projects/
│ ├── App/ # 메인 애플리케이션 타겟
│ │ ├── Sources/
│ │ │ ├── Application/ # AppDelegate, SceneDelegate
│ │ │ ├── Di/ # WeaveDI 등록 (DiRegister, AppPresentationContextProvider)
│ │ │ ├── Reducer/ # TCA Root AppReducer
│ │ │ └── View/ # Root Views
│ │ └── Derived/ # Tuist 생성 plist
│ │
│ ├── Presentation/ # 🎨 UI Layer
│ │ ├── Auth/ # 로그인 / 코디네이터 / Toast
│ │ ├── Home/ # 홈 피드 / 큐레이팅 / 스켈레톤
│ │ ├── MainTab/ # 탭 라우팅 / GNB
│ │ ├── Splash/ # 스플래시
│ │ └── Presentation/ # 공통 프레젠테이션 유틸
│ │
│ ├── Domain/ # 🔥 Business Logic Layer
│ │ ├── Entity/ # Auth / Home / OAuth / Error 도메인 엔티티
│ │ ├── DomainInterface/ # Auth / Home / OAuth Repository + Manager 인터페이스
│ │ └── UseCase/ # AuthUseCaseImpl, UnifiedOAuthUseCase, Provider/{Apple,Google,Kakao}
│ │
│ ├── Data/ # 📡 Data Layer
│ │ ├── Model/ # BaseResponseDTO / Auth·Home DTO + Entity Mapper
│ │ ├── API/ # PieckeDomain, AuthAPI, HomeAPI, BaseAPI
│ │ ├── Service/ # AuthService / HomeService (BaseTargetType), 요청 바디
│ │ └── Repository/ # Auth·Home RepositoryImpl + OAuth Repository
│ │ ├── Auth/ # Interceptor, RefreshToken Session, Pool, MoyaProvider 확장
│ │ └── OAuth/ # Apple / Google / Kakao / Web OAuth 구현
│ │
│ ├── Network/ # 🌐 Network Layer
│ │ ├── Networking/ # 네트워크 클라이언트 export
│ │ ├── Foundations/ # APIHeader / TokenProviding / KeychainTokenProvider
│ │ └── ThirdPartys/ # AsyncMoya / WeaveDI 등 SPM 재노출
│ │
│ └── Shared/ # 🔧 Shared Layer
│ ├── DesignSystem/ # 공통 UI / 컬러 / 이미지 / Toast
│ ├── Shared/ # 공유 모델·확장
│ ├── ThirdParty/ # 써드파티 래퍼
│ └── Utill/ # 공통 유틸리티
│
├── 🔧 Tuist/
│ ├── Package.swift # SPM 의존성 정의
│ └── ProjectDescriptionHelpers/ # 모듈 템플릿 / Plist 헬퍼
└── 🧩 Plugins/
├── DependencyPlugin/ # 모듈 의존성 헬퍼 (.Data / .Domain / .Network ...)
├── DependencyPackagePlugin/ # SPM 의존성 헬퍼 (.SPM.asyncMoya ...)
└── ProjectTemplatePlugin/ # ProjectConfig / Project.makeModule
graph TD
A[🎨 Presentation Layer] --> B[🔥 Domain Layer]
B --> C[📡 Data Layer]
D[🌐 Network Layer] --> C
E[🔧 Shared Layer] --> A
E --> B
E --> C
A -.-> F[SwiftUI Views]
A -.-> G[TCA Reducers]
B -.-> H[UseCases]
B -.-> I[Entities]
C -.-> J[Repositories]
C -.-> K[API Services]
레이어별로 묶어 보거나(Grouped) 모든 모듈을 펼쳐 본(Expanded) 시각화입니다. (TuistSpider 결과)
Presentation → Domain (UseCase / Entity)
↓
Domain/UseCase → Domain (Interface / Entity)
↓
Data/Repository → Domain (Interface / Entity) + Data (Model + Service + API)
↓
Data/Service → Data (API) + Network/Foundations (APIHeader) + Domain/Entity (요청 식별값)
↓
Network/Foundations → Network/ThirdPartys (AsyncMoya, WeaveDI)
핵심 설계 원칙
- ✅ Presentation 은 Domain UseCase / Entity 만 직접 참조
- ✅ Domain 은 외부 계층에 의존하지 않는 순수 비즈니스 로직
- ✅ Data/Repository 는 Domain 인터페이스를 구현, DTO ↔ Entity 매핑 담당
- ✅ Data/Service 는 endpoint / header / method / parameter 정의만 담당하고 DTO Model 에 의존하지 않음
- ✅ 모든 데이터 흐름은 Domain 을 중심으로 진행
앱
│ 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 갱신
ASAuthorizationAppleIDProvider 로 받은 credential / nonce / authorizationCode 를 그대로 백엔드에 전달.
AccessTokenCredential가 access token JWT 의exp를 디코딩해 만료 시점 보관AuthInterceptor.adapt에서 만료 5분 전이면TokenRefreshManager가 단일화된 refresh 수행- 401 응답 시
retry로 토큰 갱신 후 재시도, 실패 시NSNotification.refreshTokenExpired발송 + 자동 로그아웃
- 🎯 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 의 기반 스택
- SwiftUI — 선언형 UI
- SDWebImageSwiftUI — 비동기 이미지 로딩 / 캐싱
- Firebase iOS SDK — Crashlytics / Messaging
- Mixpanel — 행동 분석 / Session Replay
- Google Mobile Ads — 광고
- LogMacro — 커스텀 로깅 매크로
- IssueReporting — 개발 단계 이슈 추적
- XCTestDynamicOverlay — 테스트 환경 오버레이
- Clocks — 시간 관련 유틸리티
- ConcurrencyExtras — Swift Concurrency 확장
- Swift 6.0 — 최신 Swift 언어 기능
- Tuist — 프로젝트 생성 / 모듈 의존성 관리
- Swift Package Manager — 패키지 의존성 관리
- fastlane — 자동화된 빌드 / 배포 (예정)
- 💻 Xcode: 16.0 이상
- 📱 iOS: 17.0 이상
- ⚡ Swift: 6.0 이상
- 🔧 Tuist: 4.x 이상
- 💻 Xcode: 16.0 이상
- 📱 iOS: 17.0 이상
- ⚡ Swift: 6.0 이상
- 🔧 Tuist: 4.x 이상
git clone https://github.com/Roy-wonji/Picke-iOS.git
cd Picke-iOScurl -Ls https://install.tuist.io | bash# 전체 워크플로우 (권장)
./make build # clean → install → generate
# 단계별 실행
./make clean # 빌드 산출물 정리
./make install # SPM 의존성 설치
./make generate # 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
| Provider | redirect_uri | 비고 |
|---|---|---|
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 # 전체 테스트 실행이 프로젝트는 MIT 라이선스 하에 배포됩니다. 자세한 내용은 LICENSE 파일을 참고하세요.
- iOS Lead Developer: 서원지 (@Roy-wonji)
- main: 프로덕션 배포용
- develop: 개발 통합 브랜치
- feature/*: 기능별 개발 브랜치
- fix/*: 버그 픽스 브랜치
- develop 에서 feature/ 브랜치 생성
- 기능 개발 → 자체 커밋 단위 SRP 분리
- feature/ → develop Pull Request, 코드 리뷰
- develop → main 배포 Pull Request
- 한국어 사용
- 관련 GitHub 이슈 번호 매칭 (예:
#20 #2) - 형식:
<type>: <요약> #<issue> feat / fix / refactor / chore / docs / test
- 📧 이메일: suhwj81@gmail.com
- 🐛 버그 신고: Issues
- 💡 기능 제안: Discussions

