Skip to content

Conversation

@0Hooni
Copy link
Member

@0Hooni 0Hooni commented Sep 4, 2025

📌 이슈

✅ 작업 사항

  • 회원가입 화면에서 비정상적인 텍스트 UI가 표시되는 문제 수정
  • 회원가입 후 바로 로그인이 적용되지 않는 문제 수정
  • 애플로그인을 통한 회원가입 반복시 반복횟수만큼 화면이 쌓이던 문제 수정

Summary by CodeRabbit

  • New Features
    • 밑줄 텍스트 버튼(PPUnderlinedTextButton) 추가로 텍스트 버튼 가독성·재사용성 향상.
  • Bug Fixes
    • 리프레시 토큰 응답 헤더 인식 오류("Authorization-refresh" 사용) 수정으로 자동 로그인/토큰 갱신 안정화.
    • 로그아웃·회원탈퇴 시 모든 토큰 삭제 로직 추가로 보안 강화.
  • Style
    • 회원가입 등 UI 전반 타이포그래피·색상 일원화 및 버튼 텍스트 컬러 처리 개선.
  • Refactor
    • Then 도입을 통한 UI 초기화 간소화로 일관성·유지보수성 향상.
  • Chores
    • 앱 전송 보안·호환성 Info.plist 정리 및 RxRelay 의존성 추가.

@0Hooni 0Hooni added this to the v1.1.3 milestone Sep 4, 2025
@0Hooni 0Hooni requested a review from zzangzzangguy September 4, 2025 13:33
@0Hooni 0Hooni self-assigned this Sep 4, 2025
@0Hooni 0Hooni added the 🐛 fix 버그 수정, 잔잔바리 수정, 병합 시 충돌 해결 label Sep 4, 2025
@0Hooni 0Hooni linked an issue Sep 4, 2025 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

Walkthrough

Keychain logging adjusted and TokenType now CaseIterable. Network provider fixed refresh-token header key. AppleLoginService centralized ASAuthorizationController with a subject-based flow. Project plist/build settings updated. DesignSystem adds colorized setText, UILabel color updater, and a new PPUnderlinedTextButton. Presentation layers refactored to new APIs and Then; logout/withdraw now delete all KeyChain tokens.

Changes

Cohort / File(s) Summary of changes
Keychain service and enum
Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift
Added info logs for fetch/save/delete failure paths; adjusted success log to not print token value; TokenType now conforms to CaseIterable.
Network token handling
Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift
Fixed refresh token header lookup key from "authorization-refresh" to "Authorization-refresh"; access/refresh saving logic unchanged.
Apple login flow refactor
Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift
Centralized ASAuthorizationController creation via makeAuthController(); introduced stored authorizationController and PublishSubject<AuthServiceResponse>; fetchUserCredential() now assigns controller, calls performRequests() and returns the subject.
Sign-up repository cleanup
Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift
Removed debug print(endPoint).
Project build settings
Poppool/Poppool.xcodeproj/project.pbxproj
Added INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; to Debug and Release XCBuildConfiguration blocks.
App Info.plist updates
Poppool/Poppool/Resource/Info.plist
Removed ITSAppUsesNonExemptEncryption, CFBundleShortVersionString, CFBundleVersion; moved/added NSAppTransportSecurity with NSAllowsArbitraryLoads = true; added UIDesignRequiresCompatibility = true.
DesignSystem core APIs
.../DesignSystem/Components/PPButton.swift, .../DesignSystem/Components/PPUnderlinedTextButton.swift, .../DesignSystem/Extension/UIButton+.swift, .../DesignSystem/Extension/UILabel+.swift
PPButton now applies text color via setText(..., color:) and default disabledText changed to " "; added PPUnderlinedTextButton component; UIButton.setText gained a color: parameter; UILabel.updateTextColor(to:) added.
Presentation: SignUp & UI usage updates
.../Presentation/Scene/SignUp/**, .../Presentation/Scene/SignUp/Step*/**
Replaced legacy PPLabel/PPButton initializers with new PPLabel(text:style:) / PPButton(buttonStyle:text:) APIs; adopted Then for config; switched many label/text updates to updateText/updateTextColor; added SignUpStep4View.init() and setNickName.
Logout / withdraw token cleanup
.../Presentation/Scene/MyPage/Main/MyPageReactor.swift, .../Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift
On completion, now iterate TokenType.allCases and call keyChainService.deleteToken(type:) to remove all tokens in addition to UserDefaults cleanup.
Search header underline -> component
Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift
Replaced custom-underlined UIButton plus underline view with PPUnderlinedTextButton; removed separate underline view and its constraints.
Presentation project package
Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj
Added RxRelay package/product entry and linked it into the target (build file, frameworks, product dependency).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as User
  participant AppleSvc as AppleLoginService
  participant ASCtrl as ASAuthorizationController
  participant Delegate as ASAuthorizationControllerDelegate
  participant Subject as PublishSubject<AuthServiceResponse>

  User->>AppleSvc: fetchUserCredential()
  activate AppleSvc
  AppleSvc->>AppleSvc: subject = PublishSubject
  AppleSvc->>ASCtrl: performRequests() (via stored controller)
  deactivate AppleSvc

  ASCtrl-->>Delegate: authorization completed
  activate Delegate
  Delegate->>Subject: onNext(AuthServiceResponse) or onError
  deactivate Delegate

  Note over Subject: subscribers receive AuthServiceResponse observable
Loading
sequenceDiagram
  autonumber
  actor User as User
  participant Reactor as MyPage/Withdrawl Reactor
  participant UD as UserDefaults
  participant KC as KeyChainService

  User->>Reactor: Logout / Withdraw complete
  Reactor->>UD: clear except lastLogin
  loop for each TokenType.allCases
    Reactor->>KC: deleteToken(type:)
    KC-->>Reactor: status
  end
  Reactor-->>User: completed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🔄 refactor

Suggested reviewers

  • zzangzzangguy

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 현재 PR 제목 '[FIX] 회원가입 관련 오류 수정'은 변경의 핵심인 회원가입 관련 버그 수정 사항을 간결하게 요약하고 있어 팀원들이 히스토리를 빠르게 파악하기에 적절합니다.
Description Check ✅ Passed 현재 PR 설명은 이슈 번호(#173)와 완료된 작업 항목들을 명확히 포함해 주요 변경 목적을 잘 전달하고 있으나, 리포지토리 템플릿에서 요구하는 '🚀 테스트 방식'과 '👀 ETC' 섹션이 비어 있어 검증 방법과 추가 참고사항이 빠져 있습니다; 특히 키체인 토큰 삭제, AppleLoginService 흐름 변경, Info.plist 수정 등 검증이 필요한 변경점이 여러 곳에 분포합니다.

Poem

봄빛 코드에 귀 쫑긋, 토끼가 폴짝 띄며 말해요 🐇
글꼴 갈아입고 버튼도 새단장 — 찰칵!
사과 로그인은 한 줄로 술술,
토큰은 싹싹 지웠네, 깔끔하게 휙휙.
팝풀은 또 한 번 훨훨, 다음 점프를 준비해요 ✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 962aac4 and 5c48860.

📒 Files selected for processing (5)
  • Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift (1 hunks)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPUnderlinedTextButton.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj (4 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPUnderlinedTextButton.swift
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift
  • Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 🏗️ Build Workflow
🔇 Additional comments (4)
Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj (4)

13-13: RxRelay 링크 항목(PBXBuildFile) 추가 적절

productRef가 아래 XCSwiftPackageProductDependency(라인 900-904)와 일치하며, UI에서 BehaviorRelay를 직접 import 하는 요구사항을 충족합니다. 문제 없습니다.


113-113: Frameworks 빌드 단계에 RxRelay 포함 OK

Presentation 타깃에만 연결되어 있어 범위도 적절합니다. 추가적인 Embed 설정은 필요 없습니다(SPM 정적 링크 기본).


900-904: XCSwiftPackageProductDependency 선언 일치 확인

package = "RxSwift"에 대한 productName = RxRelay 매핑이 정확합니다. 상단 PBXBuildFile/Frameworks 참조들과도 정합성이 맞습니다.


269-269: RxRelay를 Presentation 타깃의 productDependencies에 추가하세요.

  • import RxRelay 사용 파일:
    • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift (line 6)
    • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (line 6)
    • Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift (line 6) — SearchFeature 타깃 소속
  • Presentation 타깃에 RxRelay 추가가 적합합니다. SearchFeature 타깃이 이미 RxRelay 의존성을 가지고 있는지 확인하거나 누락 시 동일하게 추가하세요.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/#173-singup-flow-error

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift (1)

99-108: 헤더 키 대소문자 의존 제거 및 토큰 저장 분리

HTTP 헤더 키는 RFC 상 대소문자 비민감이지만, allHeaderFields 딕셔너리는 키 문자열의 대소문자를 그대로 담아옵니다. 현재처럼 하드코딩된 키 문자열 하나에 의존하면 환경/서버 변경 시 토큰 수집이 실패할 수 있습니다. 또한 두 토큰을 모두 발견해야만 저장하는 조건은 불필요하게 엄격합니다.

아래처럼 케이스-인센시티브 조회로 변경하고, 각 토큰을 독립적으로 저장하세요.

-                    // 만약 헤더에 새 토큰이 있으면 저장
-                    if var accessToken = response.response?.allHeaderFields["Authorization"] as? String,
-                       var refreshToken = response.response?.allHeaderFields["Authorization-refresh"] as? String {
-                        accessToken = accessToken.replacingOccurrences(of: "Bearer ", with: "")
-                        refreshToken = refreshToken.replacingOccurrences(of: "Bearer ", with: "")
-
-                        @Dependency var keyChainService: KeyChainService
-                        keyChainService.saveToken(type: .accessToken, value: accessToken)
-                        keyChainService.saveToken(type: .refreshToken, value: refreshToken)
-                    }
+                    // 헤더에서(대소문자 무시) 새 토큰이 있으면 각각 저장
+                    if let headers = response.response?.allHeaderFields {
+                        func headerValue(_ name: String) -> String? {
+                            headers.first { (key, _) in
+                                (key as? String)?.lowercased() == name.lowercased()
+                            }?.value as? String
+                        }
+                        @Dependency var keyChainService: KeyChainService
+                        if var access = headerValue("authorization") {
+                            access = access.replacingOccurrences(of: "Bearer ", with: "")
+                            keyChainService.saveToken(type: .accessToken, value: access)
+                        }
+                        if var refresh = headerValue("authorization-refresh") {
+                            refresh = refresh.replacingOccurrences(of: "Bearer ", with: "")
+                            keyChainService.saveToken(type: .refreshToken, value: refresh)
+                        }
+                    }

보완: 서버가 Authorization-Refresh/X-Refresh-Token 등으로 바뀌는 경우를 대비해, 후보 키 배열을 순회하도록 확장하는 것도 고려할 수 있습니다.

Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift (3)

71-77: authorizationCode 누락/변환 실패 시 onError 없이 return — 구독자 영구 대기

오류를 확정적으로 방출하고 상태를 리셋하세요.

-            guard let authorizationCode = appleIDCredential.authorizationCode else {
-                return
-            }
+            guard let authorizationCode = appleIDCredential.authorizationCode else {
+                authServiceResponse.onError(AuthError.unknownError(description: "AppleLogin AuthorizationCode is Not Found"))
+                isAuthorizing = false
+                authorizationController = nil
+                return
+            }
@@
-            guard let convertAuthorizationCode = String(data: authorizationCode, encoding: .utf8) else {
-                return
-            }
+            guard let convertAuthorizationCode = String(data: authorizationCode, encoding: .utf8) else {
+                authServiceResponse.onError(AuthError.unknownError(description: "AppleLogin AuthorizationCode Convert Fail"))
+                isAuthorizing = false
+                authorizationController = nil
+                return
+            }

78-80: 민감정보(ID Token, Auth Code) 로깅 금지

토큰/코드는 자격증명입니다. 로그에 남기면 보안사고로 직결됩니다. 성공 여부만 기록하거나 마스킹하세요.

-            Logger.log("IDToken: \(idToken)", category: .info)
-            Logger.log("Auth Code: \(convertAuthorizationCode)", category: .info)
+            Logger.log("Apple login succeeded", category: .info)
             authServiceResponse.onNext(.init(idToken: idToken, authorizationCode: convertAuthorizationCode))
+            authServiceResponse.onCompleted()
+            isAuthorizing = false
+            authorizationController = nil

마스킹이 필요하면:

Logger.log("Apple login succeeded (idToken=***\(idToken.suffix(4)))", category: .info)

86-95: 실패 콜백에서도 상태 리셋 필요

진행 상태/컨트롤러를 해제하지 않으면 다음 요청이 막히거나 중첩될 수 있습니다.

         Logger.log(
             "AppleLogin Fail",
             category: .error
         )
         authServiceResponse.onError(error)
+        isAuthorizing = false
+        authorizationController = nil
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (1)

82-86: 하단 Safe Area 반영 권장

홈 인디케이터가 있는 기기에서 버튼이 가려질 수 있습니다. Safe Area로 변경을 제안합니다.

-            make.bottom.equalToSuperview()
+            make.bottom.equalTo(self.safeAreaLayoutGuide)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift (2)

73-76: 멀티라인 타이틀의 고정 높이(56pt) 제거 필요

numberOfLines=0인데 높이를 56pt로 고정하면 Dynamic Type/다국어에서 잘림 위험이 큽니다. intrinsic size에 맡기고 고정 높이를 제거해주세요.

         titleLabel.snp.makeConstraints { make in
             make.top.equalToSuperview().inset(64)
             make.leading.trailing.equalToSuperview().inset(20)
-            make.height.equalTo(56)
+            // 고정 높이 제거: Dynamic Type/현지화 대응
         }

112-117: 하단 버튼 safeArea 미적용

홈 인디케이터와 겹칠 수 있습니다. safeArea로 앵커 변경 권장.

-        completeButton.snp.makeConstraints { make in
-            make.leading.trailing.equalToSuperview().inset(20)
-            make.bottom.equalToSuperview()
-            make.height.equalTo(52)
-        }
+        completeButton.snp.makeConstraints { make in
+            make.leading.trailing.equalToSuperview().inset(20)
+            make.bottom.equalTo(self.safeAreaLayoutGuide).inset(20)
+            make.height.equalTo(52)
+        }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (2)

63-67: 단일/복합 라벨의 고정 높이 제거 권장

라벨들에 28/22/18pt 고정 높이가 지정되어 있어 Dynamic Type/현지화 시 잘림 위험이 있습니다. 고정 높이를 제거하고 intrinsic size에 맡기세요.

-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(22)
+            // 고정 높이 제거
-            make.height.equalTo(18)
+            // 고정 높이 제거

Also applies to: 70-74, 77-81, 84-88, 91-95


98-101: 하단 버튼 스택 safeArea로 내려주세요

현재는 superview의 bottom에 붙어 있어 기종에 따라 겹침 가능성이 있습니다.

-        buttonStackView.snp.makeConstraints { make in
-            make.leading.trailing.bottom.equalToSuperview().inset(20)
-            make.height.equalTo(52)
-        }
+        buttonStackView.snp.makeConstraints { make in
+            make.leading.trailing.equalToSuperview().inset(20)
+            make.bottom.equalTo(self.safeAreaLayoutGuide).inset(20)
+            make.height.equalTo(52)
+        }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (1)

94-97: 하단 버튼 safeArea 미적용

홈 인디케이터와 겹침 가능성이 있습니다. safeArea로 변경 권장.

-        bottomButton.snp.makeConstraints { make in
-            make.leading.trailing.bottom.equalToSuperview().inset(20)
-            make.height.equalTo(52)
-        }
+        bottomButton.snp.makeConstraints { make in
+            make.leading.trailing.equalToSuperview().inset(20)
+            make.bottom.equalTo(self.safeAreaLayoutGuide).inset(20)
+            make.height.equalTo(52)
+        }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (2)

79-83: 다수 라벨의 고정 높이 제거 권장

Dynamic Type/현지화 대응을 위해 라벨 높이 고정을 제거하세요.

-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(28)
+            // 고정 높이 제거
-            make.height.equalTo(22)
+            // 고정 높이 제거
-            make.height.equalTo(18)
+            // 고정 높이 제거
-            make.height.equalTo(20)
+            // 고정 높이 제거
-            make.height.equalTo(20)
+            // 고정 높이 제거
-            make.height.equalTo(72)
+            // 고정 높이 제거(내부 컴포넌트에 맞게 intrinsic 활용)

Also applies to: 86-90, 93-97, 100-104, 107-111, 114-118, 127-131, 135-138


141-144: 하단 버튼 스택 safeArea로 이동

superview bottom 대신 safeArea bottom으로 변경해주세요.

-        buttonStackView.snp.makeConstraints { make in
-            make.leading.trailing.bottom.equalToSuperview().inset(20)
-            make.height.equalTo(52)
-        }
+        buttonStackView.snp.makeConstraints { make in
+            make.leading.trailing.equalToSuperview().inset(20)
+            make.bottom.equalTo(self.safeAreaLayoutGuide).inset(20)
+            make.height.equalTo(52)
+        }
🧹 Nitpick comments (18)
Poppool/Poppool/Resource/Info.plist (1)

58-59: 비표준 Info.plist 키 UIDesignRequiresCompatibility 제거
해당 키는 공식적으로 지원되지 않으며 코드 내 사용처가 전혀 확인되지 않으므로 삭제를 권장합니다.

Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (1)

49-52: 로그 메시지 오타/레벨 정정 제안

  • 문법/오타: "Failed to fetched" → "Failed to fetch", "Faied" → "Failed", "deleted" 시제 → "delete".
  • 의미: 저장 성공 메시지의 전치사 "from KeyChain" → "to KeyChain"이 자연스럽습니다.
  • 레벨: 실패 로그는 .error가 적절합니다(현재 .info).
-                    Logger.log(
-                        "Failed to fetched \(type.rawValue) from KeyChain",
-                        category: .info
-                    )
+                    Logger.log(
+                        "Failed to fetch \(type.rawValue) from KeyChain",
+                        category: .error
+                    )
-            Logger.log(
-                "Successfully saved \(type.rawValue) from KeyChain",
-                category: .info
-            )
+            Logger.log(
+                "Successfully saved \(type.rawValue) to KeyChain",
+                category: .info
+            )
-            Logger.log(
-                "Faied to save \(type.rawValue) from KeyChain",
-                category: .info
-            )
+            Logger.log(
+                "Failed to save \(type.rawValue) to KeyChain",
+                category: .error
+            )
-            Logger.log(
-                "Faied to deleted \(type.rawValue) from KeyChain",
-                category: .info
-            )
+            Logger.log(
+                "Failed to delete \(type.rawValue) from KeyChain",
+                category: .error
+            )

추가: 함수 주석의 반환 타입 표기가 실제 구현(Result)과 불일치합니다(주석의 Single/Completable). 이후 커밋에서 문서화 정합성도 맞춰주세요.

Also applies to: 88-90, 93-96, 123-126

Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift (1)

172-174: 토큰 삭제 로직 중복 제거

여기서 TokenType.allCases로 모두 삭제하고, 아래 reduce(.logout)에서도 개별 토큰을 다시 삭제합니다(라인 201-205). 기능상 문제는 없지만 중복 호출/로그만 늘어납니다. 한 곳으로 통합을 권장합니다. mutate의 .do(onCompleted:)에 모으는 쪽을 제안합니다.

-        case .logout:
-            @Dependency var keyChainService: KeyChainService
-            keyChainService.deleteToken(type: .accessToken)
-            keyChainService.deleteToken(type: .refreshToken)
+        case .logout:
+            // 토큰 삭제는 mutate(.logoutButtonTapped)에서 일괄 처리됨

또한 DI 일관성을 위해 @Dependency private var keyChainService를 클래스 프로퍼티로 승격해 재사용하는 것도 고려해주세요.

Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift (1)

113-114: 토큰 삭제 중복 제거

이미 바로 위(라인 110-112)에서 .accessToken/.refreshToken을 삭제한 뒤, 다시 TokenType.allCases로 중복 삭제하고 있습니다. 한 방식만 유지하면 됩니다. 향후 토큰 타입 추가를 고려하면 allCases만 남기는 편이 낫습니다.

-            keyChainService.deleteToken(type: .accessToken)
-            keyChainService.deleteToken(type: .refreshToken)
-            UserDefaultService.Key.allCases.forEach { userDefaultService.delete(keyType: $0) }
-            TokenType.allCases.forEach { keyChainService.deleteToken(type: $0) }
+            UserDefaultService.Key.allCases.forEach { userDefaultService.delete(keyType: $0) }
+            TokenType.allCases.forEach { keyChainService.deleteToken(type: $0) }
Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift (2)

43-45: 오탈자: windowSecne → windowScene

가독성·일관성.

-        let windowSecne = scenes.first as? UIWindowScene
-        guard let window = windowSecne?.windows.first else {
+        let windowScene = scenes.first as? UIWindowScene
+        guard let window = windowScene?.windows.first else {

30-35: (선택) Single로 의미론 정렬

단일 성공/실패만 방출하므로 Single<AuthServiceResponse>가 더 적합합니다. 인터페이스 호환성 이슈가 없다면 변경을 고려하세요.

Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift (1)

53-57: 터치 타깃 44pt 보장 및 고정 높이 제거 제안
현 20pt는 HIG 권장(44pt)에 미달합니다. 인셋으로 시각적 높이는 유지하면서 터치 면적만 키우는 접근을 권장합니다.

아래와 같이 수정 제안:

@@
-        removeAllButton.snp.makeConstraints { make in
+        removeAllButton.snp.makeConstraints { make in
             make.trailing.equalToSuperview()
             make.centerY.equalTo(sectionTitleLabel)
-            make.height.equalTo(20)
+            make.height.greaterThanOrEqualTo(44)
         }

그리고 초기화부에서 인셋 추가:

-    let removeAllButton = PPUnderlinedTextButton(fontStyle: .KOr13, text: "모두삭제").then {
-        $0.isHidden = true
-    }
+    let removeAllButton = PPUnderlinedTextButton(fontStyle: .KOr13, text: "모두삭제").then {
+        $0.isHidden = true
+        $0.contentEdgeInsets = UIEdgeInsets(top: 12, left: 8, bottom: 12, right: 8)
+    }

Also applies to: 15-17

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift (1)

15-17: 나이 범위 0...100 하드코딩 — 도메인 요구 검증 권장
법적/서비스 정책 최소 연령이 있다면 상수/설정으로 관리하세요(예: minAge). 현 하드코딩은 추후 변경에 취약합니다.

예) 외부 주입/상수화:

let ageRange = (minAge...maxAge).map { "\($0)" }
Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPUnderlinedTextButton.swift (3)

10-12: 프로퍼티 네이밍(로어카멜) 및 1px 헤어라인 보장
식별자 대소문자 컨벤션과 디바이스 스케일별 1px 라인을 권장합니다.

-    private let UnderlineView = UIView().then {
+    private let underlineView = UIView().then {
         $0.backgroundColor = .g1000
     }
@@
-            UnderlineView.backgroundColor = textColor
+            underlineView.backgroundColor = textColor
@@
-            UnderlineView.backgroundColor = disabledTextColor
+            underlineView.backgroundColor = disabledTextColor
@@
-        [UnderlineView].forEach {
+        [underlineView].forEach {
             self.addSubview($0)
         }
@@
-        UnderlineView.snp.makeConstraints { make in
-            make.height.equalTo(1)
+        underlineView.snp.makeConstraints { make in
+            make.height.equalTo(1.0 / UIScreen.main.scale)
             make.bottom.equalToSuperview()
             make.horizontalEdges.equalToSuperview()
         }

Also applies to: 35-41, 47-50, 53-57


32-42: state 변화 시 밑줄 색 반영 단순화
isEnabled 기준으로 색을 정하면 하이라이팅 등 추가 state에도 안정적입니다.

-        switch state {
-        case .normal:
-            UnderlineView.backgroundColor = textColor
-        case .disabled:
-            UnderlineView.backgroundColor = disabledTextColor
-        default: break
-        }
+        underlineView.backgroundColor = isEnabled ? textColor : disabledTextColor

추가로, 레이아웃 패스가 없을 때도 반영되도록 다음을 클래스 내부에 보강 권장:

public override var isEnabled: Bool {
    didSet { underlineView.backgroundColor = isEnabled ? textColor : disabledTextColor }
}

45-59: 내부 유틸 메서드 접근 제어 명시
외부 노출 불필요합니다. private 지정 권장.

-extension PPUnderlinedTextButton {
-    func addViews() {
+extension PPUnderlinedTextButton {
+    private func addViews() {
@@
-    func setupConstraints() {
+    private func setupConstraints() {
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (1)

59-61: 카테고리 비어있는 경우 UX 확인 필요

state.categoryTitles가 빈 배열이면 categoryString가 빈 문자열이 되어 이후 문장 앞부분(“와 …”)이 어색할 수 있습니다. 빈 배열 시 안내 문구를 대체하거나 해당 구문을 숨기는 처리를 고려해주세요.

Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)

34-44: nil 컬러 처리 일관성 개선

현재는 attributedText가 있을 때 nil을 .g1000으로 강제, 없을 때는 textColor = nil로 기본색으로 돌아갑니다. 분기별 동작이 달라 예측이 어렵습니다. nil을 “기본 색으로 복귀”로 통일하는 편이 자연스럽습니다(또는 둘 다 .g1000으로 통일).

아래 예시는 “기본 색으로 복귀”로 통일하고, attributed 분기에서는 컬러 속성을 제거합니다.

-    func updateTextColor(to color: UIColor?) {
-        if let current = self.attributedText, current.length > 0 {
-            let mutable = NSMutableAttributedString(attributedString: current)
-            let fullRange = NSRange(location: 0, length: mutable.length)
-            mutable.addAttribute(.foregroundColor, value: color ?? .g1000, range: fullRange)
-            self.attributedText = mutable
-        } else {
-            self.textColor = color
-        }
-    }
+    func updateTextColor(to color: UIColor?) {
+        if let current = self.attributedText, current.length > 0 {
+            let mutable = NSMutableAttributedString(attributedString: current)
+            let fullRange = NSRange(location: 0, length: mutable.length)
+            if let color {
+                mutable.addAttribute(.foregroundColor, value: color, range: fullRange)
+            } else {
+                mutable.removeAttribute(.foregroundColor, range: fullRange)
+            }
+            self.attributedText = mutable
+        } else {
+            self.textColor = color ?? .label
+        }
+    }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (1)

94-103: 접근성 값도 함께 갱신해주세요

선택된 나이를 VoiceOver가 읽을 수 있도록 accessibilityValue를 업데이트하는 편이 좋습니다.

     func injection(with input: Input) {
         if let age = input.age {
             verticalStackView.isHidden = false
             ageLabel.updateText(to: "\(age)세")
             defaultLabel.isHidden = true
         } else {
             verticalStackView.isHidden = true
             defaultLabel.isHidden = false
         }
+        // 접근성
+        self.isAccessibilityElement = true
+        self.accessibilityLabel = "나이"
+        self.accessibilityValue = input.age.map { "\($0)세" } ?? "미선택"
     }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (1)

50-61: 셀 접근성 속성 업데이트 제안

선택 상태를 보조기기에서 인지할 수 있도록 traits/label을 설정하는 것을 권장합니다.

     func injection(with input: Input) {
         titleLabel.updateText(to: input.title)
         if input.isSelected {
             contentView.backgroundColor = .blu500
             contentView.layer.borderWidth = 0
             titleLabel.updateTextColor(to: .w100)
         } else {
             contentView.backgroundColor = .clear
             contentView.layer.borderWidth = 1
             titleLabel.updateTextColor(to: .g400)
         }
+        // 접근성
+        isAccessibilityElement = true
+        accessibilityLabel = input.title
+        accessibilityTraits = input.isSelected ? [.button, .selected] : [.button]
     }
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (1)

58-62: 타이틀 고정 높이 제거 제안

두 줄 텍스트/다국어/다이내믹 타입에서 잘림 우려가 있습니다. 고정 높이를 제거하고 intrinsic 크기에 맡기는 것을 권장합니다.

             make.leading.trailing.equalToSuperview().inset(20)
-            make.height.equalTo(56)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift (2)

15-17: PPLabel 색상 지정은 updateTextColor 사용 권장

디자인시스템 일관성을 위해 textColor 직접 지정 대신 updateTextColor(to:)를 사용하는 편이 안전합니다(속성이 attributedText로 전환돼도 대응).

-    private let descriptionLabel = PPLabel(text: "이후 이 별명으로 팝풀에서 활동할 예정이에요.", style: .KOr15).then {
-        $0.textColor = .g600
-    }
+    private let descriptionLabel = PPLabel(text: "이후 이 별명으로 팝풀에서 활동할 예정이에요.", style: .KOr15).then {
+        $0.updateTextColor(to: .g600)
+    }

19-21: 버튼 라벨(확인/다음) 불일치 — UX 혼란 가능성

초기 비활성 상태는 "다음", 활성은 "확인"으로 달라 사용자 혼란 소지가 있습니다. 단계 흐름과 맞춰 통일해 주세요.

-    let completeButton = PPButton(buttonStyle: .primary, text: "확인", disabledText: "다음").then {
+    let completeButton = PPButton(buttonStyle: .primary, text: "다음", disabledText: "다음").then {
         $0.isEnabled = false
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ec77db8 and 962aac4.

📒 Files selected for processing (27)
  • Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (3 hunks)
  • Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift (1 hunks)
  • Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift (1 hunks)
  • Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift (1 hunks)
  • Poppool/Poppool.xcodeproj/project.pbxproj (2 hunks)
  • Poppool/Poppool/Resource/Info.plist (2 hunks)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift (3 hunks)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPUnderlinedTextButton.swift (1 hunks)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIButton+.swift (2 hunks)
  • Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (3 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift (3 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift (2 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (3 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (3 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift (1 hunks)
  • Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift (1 hunks)
  • Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: 0Hooni
PR: PopPool/iOS#164
File: Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift:61-63
Timestamp: 2025-07-21T09:55:51.700Z
Learning: PopPool iOS 프로젝트의 KeyChainService에서 saveToken과 deleteToken 메서드에 discardableResult를 사용하는 것은 의도적인 설계이다. 사용하는 곳에서 throws와 error 처리를 간편하게 만들고 return 값을 선택적으로 무시할 수 있도록 하기 위한 목적이다.
📚 Learning: 2025-07-21T09:55:51.700Z
Learnt from: 0Hooni
PR: PopPool/iOS#164
File: Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift:61-63
Timestamp: 2025-07-21T09:55:51.700Z
Learning: PopPool iOS 프로젝트의 KeyChainService에서 saveToken과 deleteToken 메서드에 discardableResult를 사용하는 것은 의도적인 설계이다. 사용하는 곳에서 throws와 error 처리를 간편하게 만들고 return 값을 선택적으로 무시할 수 있도록 하기 위한 목적이다.

Applied to files:

  • Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift
  • Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift
🧬 Code graph analysis (15)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (2)
  • updateText (25-32)
  • updateTextColor (35-44)
Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift (1)
Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (1)
  • deleteToken (104-129)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateText (25-32)
Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (1)
Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift (1)
  • log (84-106)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateText (25-32)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateText (25-32)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateText (25-32)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateTextColor (35-44)
Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift (1)
Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (1)
  • deleteToken (104-129)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPUnderlinedTextButton.swift (1)
Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift (2)
  • addViews (40-44)
  • setupConstraints (46-58)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIButton+.swift (1)
  • setText (5-27)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (2)
  • updateText (25-32)
  • updateTextColor (35-44)
Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift (5)
Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift (1)
  • fetchUserCredential (30-57)
Poppool/DataLayer/Data/Data/RepositoryImpl/AppleLoginRepositoryImpl.swift (1)
  • fetchUserCredential (12-14)
Poppool/DataLayer/Data/Data/RepositoryImpl/KakaoLoginRepositoryImpl.swift (1)
  • fetchUserCredential (12-14)
Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AppleLoginUseCaseImpl.swift (1)
  • fetchUserCredential (14-16)
Poppool/DomainLayer/Domain/Domain/UseCaseImpl/KakaoLoginUseCaseImpl.swift (1)
  • fetchUserCredential (14-16)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateTextColor (35-44)
Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (1)
Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift (1)
  • updateTextColor (35-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 🏗️ Build Workflow
🔇 Additional comments (29)
Poppool/Poppool.xcodeproj/project.pbxproj (1)

447-447: ITSAppUsesNonExemptEncryption = NO를 빌드 설정으로 이동한 점은 합리적입니다. 배포 시 수출 규정 설문과의 일치 여부만 최종 확인 부탁드립니다.

서드파티 SDK가 자체 암호화(예: E2E/키관리)를 제공하지 않고, 네트워크 암호화가 TLS 수준이라면 NO가 일반적입니다. 기능적으로 자체/커스텀 암호화가 있다면 YES로 전환 필요합니다.

Also applies to: 495-495

Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift (1)

132-135: TokenType의 CaseIterable 추가 적절

로그아웃/탈퇴 플로우에서 TokenType.allCases 순회 사용이 가능해져 일관된 정리 로직에 도움됩니다. 하위 호환성에도 문제 없어 보입니다.

Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift (1)

45-47: 디버그 출력 제거 LGTM

불필요한 print 제거로 노이즈 줄어들었습니다. 나머지 로직 영향 없음 확인됩니다.

Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift (2)

15-17: PPUnderlinedTextButton 도입으로 커스텀 밑줄 제거 — 깔끔합니다.
중복 UI 제거와 디자인 시스템 일관성 측면에서 👍


15-17: Then 모듈 재수출 확인 필요
TagCollectionHeaderView.swift에서 import Then 없이 .then을 사용하고 있습니다. Then@_exported import Then 등으로 재수출되어 이 파일에도 적용되는지 확인하고, 적용되지 않는다면 파일 상단에 import Then을 추가해주세요.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift (1)

11-12: DesignSystem API 마이그레이션 반영 — 좋습니다.
새 init 시그니처 사용이 일관되고 명확합니다.

Also applies to: 20-21, 24-25

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift (1)

26-29: UILabel.updateText(to:) 사용 — 일관성/속성 보존 측면에서 적절합니다.
기존 attributed 속성 보존 전략과 맞물려 안전합니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift (1)

11-12: PPLabel 스타일 초기화 방식 통일 — 👍
디자인 시스템 스타일(.KOb15) 적용으로 유지보수성 향상.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (1)

39-39: PPLabel 업데이트 방식 적합

초기화 시점에는 attributedText가 없어 updateText(to:)가 내부적으로 text에 대입되어 부작용 없습니다. 현 변경은 타이포 시스템 이관 방향과 일치합니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (1)

53-54: 닉네임 텍스트 업데이트 방식 적절

updateText(to:) 사용으로 기존 스타일 유지 + 텍스트만 교체가 보장됩니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift (3)

94-96: 라벨 스타일 보존형 업데이트 적용 적절

updateText(to:), updateTextColor(to:)로 스타일을 유지하며 변경되는 설명/색상만 반영합니다. 의도에 부합합니다.


103-105: 글자수 초과 처리 OK

카운트 문구 업데이트와 10자 초과 시 시각적 피드백(shake) 처리 흐름이 명확합니다.


108-111: 상태별 색상 전환 로직 적절

에러/경고 상태에만 .re500, 그 외 .g500으로 일관성 있게 매핑되어 있습니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (3)

6-6: Then 도입 일관성 OK

Step 계열 뷰들과의 초기화 패턴 통일에 기여합니다.


17-19: PPLabel API 전환 및 토큰 컬러 적용 적절

text-first 이니셜라이저+Then 체이닝으로 가독성/일관성 좋아졌습니다.

Also applies to: 27-29, 31-33


97-97: updateText 사용 타당

attributedText 보존 흐름과 일치합니다. PPLabel이 초기 attributed 설정이 없다면 현재 구현도 문제 없습니다.

Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIButton+.swift (2)

8-10: setText 컬러 파라미터 추가 OK

PPButton의 색상 일원화 전략과 일치합니다. 상태별 attributedTitle 적용도 적절합니다.

Also applies to: 21-23


5-10: 바이너리 소비자 없음 확인 레포 내에서 setText 호출은 모두 소스 의존이며, Pods 등 외부 모듈(사전 컴파일된 프레임워크)에서 해당 메서드 링크 사용이 발견되지 않았습니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (2)

12-12: PPLabel(style: .KOm13) 전환 적절

디자인 시스템 폰트 토큰 일관성이 좋아졌습니다.


51-51: updateText / updateTextColor 적용 타당

속성 유지 갱신 전략과 부합하며 선택/비선택 상태 전환이 명확합니다.

Also applies to: 55-55, 59-59

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (2)

11-16: 타이틀 레이블 이니셜라이저/Then 마이그레이션 좋습니다

다국어 줄바꿈 고려(numberOfLines=0)도 적절합니다.


34-40: PPButton(buttonStyle:) 전환 및 초기 비활성 처리 LGTM

버튼 상태 관리가 디자인 시스템과 정합합니다.

Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift (3)

79-81: 색상 인지 setText 적용 LGTM

상태별 텍스트 컬러를 attributedTitle로 관리해 setTitleColor 의존성을 제거한 점이 좋습니다.

Also applies to: 106-108


55-64: Secondary 스타일 비활성 대비 색상 검토 필요

  • .secondary 비활성 시 disabledTextColor(.g50)disabledBackgroundColor(.g50)가 동일해 텍스트가 보이지 않습니다. 디자인 토큰 확인 후 대비 확보 필요
         var disabledTextColor: UIColor {
             switch self {
             case .primary:
                 return .g400
             case .secondary:
-                return .g50
+                return .g400
             case .tertiary, .apple, .kakao:
                 return .blu500
             }
         }

71-72: disabledText 기본값 공백 변경 영향 검토 필요
PPButton(buttonStyle / style) 초기화 수십 곳에서 disabledText를 명시하지 않아 기본값인 " "(한 칸 공백)이 적용됩니다. 비활성 상태에서 공백 문자가 의도치 않게 노출되거나 레이아웃에 영향을 주는지, setTitleColor 등 스타일 혼용 시 색상·상태 매핑이 올바른지 직접 검토 부탁드립니다.

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (2)

10-12: DesignSystem/Then 전환 깔끔합니다

PPLabel/PPButton 신규 생성자와 Then 적용이 일관되고 명확합니다.

Also applies to: 29-34


10-12: 닉네임 라벨 색/스타일 보존 확인 요청

setNickName에서 .text로 교체 시, PPLabel이 attributed 기반이면 스타일이 초기화될 수 있습니다. 다회 호출 시 색/스타일 유지되는지 확인 부탁드립니다(필요 시 updateText 계열 API 사용 검토).

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (1)

17-30: 신규 라벨/버튼 생성자 적용 👍

PPLabel(text:, style:), PPButton(buttonStyle:, text:)로 일관성 있게 정리되어 가독성이 좋아졌습니다.

Also applies to: 38-49

Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (1)

11-13: DesignSystem/Then 적용 적절합니다

닉네임 색상 처리 및 버튼 생성자 교체가 일관되고 명확합니다.

Also applies to: 40-45

@PopPool PopPool deleted a comment from coderabbitai bot Sep 12, 2025
@PopPool PopPool deleted a comment from coderabbitai bot Sep 12, 2025
- 단일 컨트롤러 재사용시 상태 오류/중복 가능성 있음
- Then 사용중이어서 import문 추가
- trailing comma 제거
- RxRelay 사용중이어서 import문과 패키지 의존성 추가
@PopPool PopPool deleted a comment from coderabbitai bot Sep 12, 2025
@0Hooni 0Hooni merged commit 0bd2e1a into develop Sep 12, 2025
3 checks passed
@0Hooni 0Hooni deleted the fix/#173-singup-flow-error branch September 12, 2025 09:10
0Hooni added a commit that referenced this pull request Nov 12, 2025
* fix/#173: 버튼 활성시 텍스트 컬러가 적용되지 않던 문제 수정

* feat/#173: 밑줄 텍스트 버튼 공통 컴포넌트 구현

* feat/#173: 밑줄 텍스트 버튼 컴포넌트 구현 및 적용

* feat/#173: attr 텍스트 컬러 변경 메서드 구현

* fix/#173: 텍스트 스타일 적용

* fix/#173: 텍스트 스타일 적용

* feat/#173: 로그아웃, 회원탈퇴 시점에 키체인에서 토큰 제거

* refactor/#173: 키체인 실패 로그 추가

* fix/#173: 회원가입 후 바로 로그인되지 않던 문제 수정

* chore/#173: Xcode26 리퀴드글래서 효과 자동적용 제거

* fix/#173: 애플로그인 회원가입 화면 중복 이동 문제 해결

* refactor/#173: 애플로그인 코드 리팩터링

- 델리게이트의 생명주기를 메서드가 아닌 상위 클래스가 관리하도록 수정
- 인증 컨트롤러를 한번만 생성하도록 수정

* refactor/#173: ASAuthorizationController 인스턴스 재생성

- 단일 컨트롤러 재사용시 상태 오류/중복 가능성 있음

* fix/#173: PPUnderlined 컴포넌트 오류 수정

- Then 사용중이어서 import문 추가
- trailing comma 제거

* fix/#173: 컴포넌트 패키지 추가 오류 수정

- RxRelay 사용중이어서 import문과 패키지 의존성 추가
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 fix 버그 수정, 잔잔바리 수정, 병합 시 충돌 해결

Projects

None yet

Development

Successfully merging this pull request may close these issues.

회원가입 플로우 문제 수정

2 participants