Skip to content

feat: 홈 메인 + 네트워크 인프라 + 로그인→홈 진입 연결#33

Merged
Roy-wonji merged 24 commits into
developfrom
feature/Home
May 15, 2026
Merged

feat: 홈 메인 + 네트워크 인프라 + 로그인→홈 진입 연결#33
Roy-wonji merged 24 commits into
developfrom
feature/Home

Conversation

@Roy-wonji
Copy link
Copy Markdown
Contributor

요약

  • 홈 메인 ([Home] 홈 + 큐레이팅 #3): Hero 카로셀 / Hot · Best 배틀 / 오늘의 Pické (Quiz · Vote) / 새로운 배틀 풀스택 구현 — Entity ▸ DomainInterface ▸ Service ▸ DTO+Mapper ▸ RepositoryImpl ▸ HomeFeature 까지 GET /api/v1/home 연동, Kingfisher KFImage 적용
  • 네트워크 인프라 정리 ([Network] API 클라이언트 인프라 셋업 #19): RepositoryImpl Provider 선언을 let + 팩토리 기본값(.default / .authorized) 직접 주입 패턴으로 통일, refresh token 헤더 전송 방식 보정, 모듈 그래프 / README 최신화
  • 로그인 → 홈 진입 흐름 ([Auth] 로그인 성공 → 홈 화면 진입 흐름 #32): Splash → Auth → MainTab 전환 라우팅 연결, Store 보유 규칙 문서화
  • 스켈레톤 로더 (홈) ([Skeleton] 스켈레톤 로더 #18): 홈 메인 화면에 스켈레톤 로더 애니메이션 적용
  • AGENTS.md 규칙 명시: RepositoryImpl Provider 선언 패턴 / TCA State inline default + 빈 init / AsyncAction Result+mapError 단일 Response 패턴

닫을 이슈 (Closes)

  • Closes #19 — [Network] API 클라이언트 인프라 셋업 (Provider 풀체인 + Repository 패턴 통일 완료)
  • Closes #32 — [Auth] 로그인 성공 → 홈 화면 진입 흐름

부분 완료 (Refs — 후속 PR 필요)

  • Refs #3 — 홈 메인은 완료, 큐레이팅 진입·소진 / 알림 받기 모달은 미구현
  • Refs #18 — 홈 스켈레톤만 적용, 다른 화면 스켈레톤은 후속
  • Refs #20 — Kakao OAuth ephemeral 세션 / authorize URL 정리 일부
  • Refs #2 — 온보딩 일러스트 PNG → SVG 교체 일부

테스트 플랜

  • 로그인 성공 시 Splash → Auth → MainTab 전환 정상 동작
  • onAppearGET /api/v1/home 호출 + 스켈레톤 → 데이터 전환 확인
  • Hero 카로셀 3초 자동 스크롤 + KFImage 로딩 확인
  • Hot/Best/Quiz/Vote/New 섹션 각 카드 디자인 1:1 매핑
  • 401 발생 시 refresh token 헤더로 재요청 성공
  • AuthRepositoryImpl 의 login(default provider) / logout·withdraw(authorized provider) 정상 동작

Roy-wonji added 24 commits May 15, 2026 05:45
- Home/MainTab 모듈과 GNB 리소스를 앱 라우팅에 연결
- Auth 온보딩 완료와 Splash 토큰 보유 상태에 따라 MainTab 진입을 위임 처리
- AppReducer scope 라우팅을 상태 매칭 검증 후 handleScopeNavigation으로 분리

Rejected: Splash scope onAppear를 AppReducer에서 무시하는 방식 | 사용자가 해당 분기는 건드리지 말라고 요청함
- SwiftUI View의 Store 보유 방식은 @bindable 기준으로 통일
- public/private 가시성 기준과 금지 패턴을 AGENTS 가이드에 추가

Rejected: 로그인 홈 진입 커밋에 문서 변경을 섞는 방식 | 기능 커밋과 지침 변경을 분리하기 위해 제외
- 백엔드 합의 스펙 (`https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=...&redirect_uri=...`) 과 동일하게 정렬
- 기존 강제 로그인 프롬프트는 제거 (필요 시 후속 옵션화)
탭바 UIKit 스타일에서 직접 hex를 들고 있지 않도록 DesignSystem UIColor 토큰을 기준으로 맞춥니다.

Rejected: MainTabView 로컬 hex enum 유지 | 디자인시스템 색상 정의와 중복됨
- Entity/Home: HeroBattle / HotBattle / BestBattle / QuizQuestion / VoteQuestion / NewBattle (각 mock 포함)
- HomeFeature: 6개 Entity 보유 State + heroIndex 캐러셀 인덱스 + 더보기 액션
- HomeView: 상단 헤더 + ScrollView 안 5개 섹션 (캐러셀 / 지금 뜨는 배틀 / Best 배틀 / 오늘의 Pické / 새로운 배틀) — 헤더도 본문과 함께 스크롤
- Components: HomeHeaderView / HomeSectionHeader / MetaLabelView / TagBadgeView / HeroCarouselView (TabView paging + 3초 자동 스크롤 wrap-around) / HotBattleCardView / BestBattleCardView / QuizCardView / VoteCardView / NewBattleCardView
- VoteCardView 는 Pencil .pen wZ4Yt 스펙(beige50 카드 / beige200 빈칸 / beige300 옵션 / 14pt 뱃지) 1:1 매핑
- 컬러/스트로크/배경/필 모두 디자인 토큰 점 단축형(.fill(.beige200) / .stroke(.beige700)) 사용
- KakaoOAuthRepository: OAuthWebPresenter.present 호출 시 usesEphemeralSession: true 전달 — 기존 카카오 로그인 쿠키로 자동 redirect 되어 폼이 뜨기 전에 닫히는 문제 해결
- OAuthWebViewController: usesEphemeralSession 파라미터(default false) 추가, 켜진 경우만 config.websiteDataStore = .nonPersistent() — Google 은 기존 SSO 유지
- finish(result:) 호출 시 webView.isHidden = true 로 가려서 401 응답 흰 화면 노출 방지 (Google/Kakao 공통)
- OAuthWebPresenter.present 시그니처에도 usesEphemeralSession 파라미터 전파
- AppReducer.handleScopeNavigation: splash.delegate(.presentAuth) / .presentMainTab 수신 시 clock.sleep(for: .seconds(3)) 후 화면 전환
- splash.view(.onAppear) 자체는 더 이상 즉시 presentAuth 보내지 않음 (스플래시 내부에서 분기 결정)
- SplashFeature 동기 정리 (스플래시 → 인증 분기 흐름 위임)
- ShapeStyle 을 받는 모든 modifier 에 점 단축형 사용 명시:
  .foregroundStyle(.neutral900) / .fill(.beige200) / .stroke(.beige700, lineWidth:) / .background(.beige50, in:) / .tint(.primary500)
- Color 가 View 자체로 쓰여 메서드 체이닝을 받는 경우(Color.beige50.ignoresSafeArea() 등) 는 예외로 두는 규칙 추가
- 등록된 토큰 목록에 beige50…beige900 추가
- HeroCarouselView: TabView 의 frame(height:) 360 → 341 (.pen 합산: control 53 + thumbnail 167 + subject 121)
- BestBattleCardView: padding(16) → padding(.vertical, 16) + padding(.horizontal, 12) (.pen 7UgWh padding [16, 12])
- ImageAssets.xcassets/Home/appLogo.imageset, bell.imageset (각 SVG)
- ImageAsset enum 에 appLogo / bell 케이스 추가 (Home 카테고리)
- BattleTag · HomeBundle 신규 + 기존 Hero/Hot/Best/New/Quiz/Vote Entity 를 battleId · BattleTag[] · audioDuration 기준으로 정렬
- HomeInterface 프로토콜 + DependencyKey + Default/Mock 구현체 추가 — DomainInterface 계층에서 홈 API 사용처 통일
…pl) 구성

- HomeAPI · HomeService(BaseTargetType) · HomeDataDTO + Mapper · HomeRepositoryImpl 풀체인 추가
- PieckeDomain 에 case home 추가하여 baseURL 조립
- Extension+TargetDependencySPM 에 kingfisher 외부 의존성 등록
- Home 프레젠테이션 모듈이 Kingfisher 를 참조하도록 Project.swift 갱신
- AppDIManager 에서 HomeInterface ↔ HomeRepositoryImpl 바인딩
- HomeFeature: Result + mapError(AuthError.from) 단일 Response 패턴으로 fetchHome 비동기 액션 구성, State 는 inline default + 빈 init() 으로 정리
- HeroCarousel · HotBattle · NewBattle 카드에 Kingfisher KFImage 적용, HomeView 스켈레톤/skipping 로직 정리
- Optional + nil 합치기 + MoyaProviderPool 인다이렉션 제거
- init 기본값으로 MoyaProvider<AuthService>.default / .authorized 를 그대로 사용해 호출처 노이즈 제거
…lt+mapError 규칙 명시

- RepositoryImpl 의 MoyaProvider 는 let + .default / .authorized 팩토리 기본값 직접 주입 (Pool / Optional 인다이렉션 금지)
- TCA State 는 inline default + 빈 public init() {} 패턴
- AsyncAction 은 Result { try await } + mapError + 단일 Response InnerAction
refresh API가 토큰을 body가 아닌 X-Refresh-Token 헤더로 요구해 인증 갱신 요청 형식을 서버 계약에 맞춘다.
홈 진입 시 onAppear 중복 호출을 막고, 서버가 빈 섹션을 내려줄 때 화면 검수용 mock 데이터를 채워 홈 UI가 비어 보이지 않게 한다.

Rejected: View 내부 중복 onAppear 유지 | 로딩 완료 뒤 다시 fetchHome이 반복됨
홈 스켈레톤을 HomeSkeletonView로 분리하고 attendance-ios 방식의 shimmer overlay를 공통 블록에 적용한다.

Rejected: 정적 스켈레톤 유지 | 로딩 상태 피드백이 부족함
TuistSpider 산출물을 docs/graphs 아래로 정리하고 README에서 Grouped/Expanded 그래프를 보여준다. Service 모듈은 DTO Model을 직접 쓰지 않으므로 불필요한 Model 의존성도 제거한다.

Rejected: 루트 PNG 유지 | 프로젝트 루트가 산출물로 지저분해짐
README의 프로젝트 트리와 의존성 방향 설명을 Home/MainTab 및 Home 데이터 모듈 추가 상태에 맞춘다.
@Roy-wonji Roy-wonji added ⚙️ 환경설정 프로젝트 설정 🎨 디자인 UI 디자인 작업 labels May 15, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc3fab617c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

state.hasLoadedHome = true
switch result {
case let .success(bundle):
let home = bundle.replacingEmptySectionsWithMocks
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove mock substitution from successful home payloads

Applying bundle.replacingEmptySectionsWithMocks in the success path causes the app to render fabricated battles/quizzes whenever the backend intentionally returns an empty section, so users can see data that never came from the server. This is a production data-integrity regression; successful responses should be rendered as-is, with empty-state UI handled in the view layer instead of mutating domain data.

Useful? React with 👍 / 👎.

switch action {
case let .homeResponse(result):
state.isLoading = false
state.hasLoadedHome = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark home as loaded only after a successful fetch

hasLoadedHome is set to true before checking whether the fetch succeeded, but .onAppear refuses to fetch when this flag is true. If the first request fails (e.g., transient network/auth issue), revisiting the screen will no longer retry automatically, leaving the user stuck with stale/empty content unless they manually pull to refresh.

Useful? React with 👍 / 👎.

@Roy-wonji Roy-wonji merged commit 9d64e82 into develop May 15, 2026
3 checks passed
@Roy-wonji Roy-wonji deleted the feature/Home branch May 15, 2026 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚙️ 환경설정 프로젝트 설정 ✨ 기능추가 새로운 기능 추가 🎨 디자인 UI 디자인 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant