[FEAT] 로그인·회원가입 플로우 디자인 명세 반영 및 디자인 컴포넌트 구현#39
Conversation
타이포·색상 명세 반영, Button+ButtonStyle → View+DragGesture 전환
| case let .path(.element(id: _, action: .profile(.delegate(.proceedToDepartureSearch(nickname))))): | ||
| state.path.append(.departure(DepartureSearchFeature.State(nickname: nickname))) | ||
| case let .path(.element(id: _, action: .profile(.delegate(.proceedToDepartureSearch(name))))): | ||
| state.path.append(.departure(DepartureSearchFeature.State(nickname: name))) |
There was a problem hiding this comment.
🟡 [P3] Minor
ProfileInputFeature.State에서 nickname이 name으로 변경되었으므로, DepartureSearchFeature.State의 이니셜라이저 파라미터도 nickname 대신 name으로 일관되게 사용하는 것이 좋습니다. 현재 코드에서는 새로운 name 값을 nickname이라는 파라미터에 할당하고 있어 혼동을 줄 수 있습니다.
| state.path.append(.departure(DepartureSearchFeature.State(nickname: name))) | |
| state.path.append(.departure(DepartureSearchFeature.State(name: name))) |
ImageAsset.swift는 feature/#35에서 Tuist 에셋 시스템으로 대체되어 삭제함
|
{ |
duthd3
left a comment
There was a problem hiding this comment.
제미나이 리뷰만 수정할 거 있으면 수정해주세용
|
{ |
There was a problem hiding this comment.
새로운 DesignSystem 컴포넌트들이 잘 구현되었고, 기존 인증 플로우 화면에 성공적으로 적용되었습니다. 새로운 타이포그래피 시스템, 내비게이션, 버튼, 입력 필드 등 핵심 컴포넌트들이 디자인 명세와 SwiftUI 컨벤션에 따라 체계적으로 구축되었고, 각 컴포넌트의 프리뷰도 충실하게 작성되어 재사용성 및 유지보수성이 높을 것으로 예상됩니다. TCA 액션 및 상태 관리도 변경된 UI 플로우에 맞춰 일관성 있게 업데이트되었습니다. 전반적으로 코드 품질이 우수하며, 주어진 가이드라인을 잘 준수하고 있습니다.
| await MainActor.run { | ||
| store.send(.galleryImagePicked(data)) | ||
| photoItem = nil | ||
| photoItem = nil // 동일한 사진을 다시 선택할 수 있도록 리셋 (nil이 되지 않으면 onChange 재발화 안 됨) |
There was a problem hiding this comment.
⚪ [P5] Nitpick
줄 끝 주석은 일반적으로 120자 라인 제한을 초과하기 쉽습니다. 가독성을 위해 별도의 줄로 빼는 것이 좋습니다.
| photoItem = nil // 동일한 사진을 다시 선택할 수 있도록 리셋 (nil이 되지 않으면 onChange 재발화 안 됨) | |
| photoItem = nil | |
| // 동일한 사진을 다시 선택할 수 있도록 리셋 (nil이 되지 않으면 onChange 재발화 안 됨) |
| @@ -20,7 +20,10 @@ public extension View { | |||
| } | |||
|
|
|||
| func pretendardCustomFont(textStyle: CustomSizeFont) -> some View { | |||
| return self.modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) | |||
| self | |||
There was a problem hiding this comment.
🔵 [P4] Readability
.pretendardCustomFont 뷰 모디파이어 내부에서 self 키워드 사용은 불필요합니다. Swift 컨벤션에 따라 제거하는 것이 좋습니다.
| self | |
| modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) | |
| .kerning(textStyle.letterSpacing * textStyle.size) | |
| .lineSpacing(textStyle.lineHeight - textStyle.size) |
| return .send(.delegate(.authDidComplete)) | ||
|
|
||
| case .path(.element(id: _, action: .departure(.delegate(.dismiss)))): | ||
| state.path.removeLast() |
There was a problem hiding this comment.
🟡 [P3] Minor
AuthFlowFeature의 Reducer 내에서 state.path.removeLast()가 여러 case에서 반복되고 있습니다. 이처럼 동일한 로직이 반복될 경우, 공통 Delegate 액션(e.g. .delegate(.popPath))을 정의하고 해당 액션에서 state.path.removeLast()를 처리하도록 리팩토링하여 중복을 줄일 수 있습니다. 이는 유지보수성을 높이는 데 도움이 됩니다.
Terms 및 Profile 화면의 뒤로가기 로직은 동일하므로 하나의 delegate 액션으로 처리할 수 있습니다.
| public var body: some View { | ||
| NavigationStack(path: $store.scope(state: \.path, action: \.path)) { | ||
| LoginView(store: store.scope(state: \.login, action: \.login)) | ||
| .toolbar(.hidden, for: .navigationBar) |
There was a problem hiding this comment.
🔵 [P4] Readability
커스텀 내비게이션 바 컴포넌트(NavigationPage, NavigationMain)를 사용함에 따라 기본 navigationBar를 숨기는 것은 적절합니다. 동일한 코드가 여러 .navigationDestination 뷰에 반복되고 있으므로, ViewModifier를 만들어서 적용하면 코드 중복을 줄일 수 있습니다.
| .toolbar(.hidden, for: .navigationBar) | |
| private extension View { | |
| func hidesNavigationBar() -> some View { | |
| toolbar(.hidden, for: .navigationBar) | |
| } | |
| } | |
| // 사용 예시: | |
| // LoginView(store: store.scope(state: \.login, action: \.login)) | |
| // .hidesNavigationBar() |
| guard new != nil else { return } | ||
| // 이미 토스트가 노출 중일 때도 재선택 시 다시 보여주기 위해 리셋 후 재발화 | ||
| isToastVisible = false | ||
| Task { @MainActor in isToastVisible = true } |
There was a problem hiding this comment.
🟡 [P3] Minor
isToastVisible = false 직후 Task { @MainActor in isToastVisible = true }를 사용하는 패턴은 SwiftUI 뷰 업데이트 사이클과 토스트 컴포넌트의 애니메이션 구현 방식에 따라 의도치 않은 깜빡임이나 애니메이션 불일치를 유발할 수 있습니다. 이미 토스트가 true인 상태에서 재선택 시 onChange가 다시 호출되어 false -> true가 될 때 애니메이션이 부자연스러울 가능성이 있습니다.
토스트 컴포넌트 내부에서 isVisible 상태 변화를 감지하여 false -> true 시 재시작 애니메이션을 명시적으로 처리하거나, 이 로직이 필요한 이유에 대한 추가적인 디자인/UX 요구사항이 있다면 해당 내용을 주석으로 명시하는 것이 좋습니다.
| .overlay { | ||
| if isShowingMenu { | ||
| GeometryReader { geo in | ||
| DesignSystem.Menu(items: [ |
There was a problem hiding this comment.
🔵 [P4] Readability
DesignSystem.Menu를 초기화할 때 selectedIndex 바인딩을 빠뜨렸습니다. 메뉴 항목을 탭했을 때 선택 상태가 반영되도록 selectedIndex 바인딩을 추가하는 것이 좋습니다. 현재 코드에서는 단순히 isShowingMenu = false만 실행됩니다.
| DesignSystem.Menu(items: [ | |
| DesignSystem.Menu( | |
| selectedIndex: .constant(nil), // 또는 @State로 관리되는 실제 selectedIndex 바인딩 | |
| items: [ |
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| .padding(Spacing.spacing250) | ||
| .background( | ||
| RoundedRectangle(cornerRadius: 20) |
There was a problem hiding this comment.
🔵 [P4] Readability
매직 넘버(20) 대신 DesignSystem에 정의된 BorderRadius 토큰을 사용하는 것이 좋습니다. 일관된 디자인 시스템 적용을 위해 토큰 사용을 권장합니다.
| RoundedRectangle(cornerRadius: 20) | |
| RoundedRectangle(cornerRadius: BorderRadius.borderRadius400) |
| @@ -20,7 +20,9 @@ public extension View { | |||
| } | |||
|
|
|||
| func pretendardCustomFont(textStyle: CustomSizeFont) -> some View { | |||
| return self.modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) | |||
| modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) | |||
There was a problem hiding this comment.
🔵 [P4] Readability
return 키워드는 단일 표현식 함수 본문(single-expression function body)에서 생략 가능합니다. 일관성을 위해 return을 제거하는 것을 권장합니다.
| modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) | |
| modifier(PretendardFont(family: textStyle.fontFamily, size: textStyle.size)) |
Summary
Changes
회원가입·로그인 플로우 UI
LoginView에 로그인 3D 이미지 적용TermsAgreementView에 DesignSystem 컴포넌트 적용 및 네비게이션 액션 추가ProfileInputFeature이름 입력 및 프로필 이미지 선택 UI 개편StationSearchSheetDesignSystem 기반 UI 개편DesignSystem 컴포넌트
TextInput컴포넌트 추가Avatar컴포넌트 구현 (localImage 타입, edit 버튼 액션 포함)NavigationMain,NavigationPage컴포넌트 구현NavigationIconButton및NavigationIconenum 타입화BangawoText컴포넌트 구현Menu컴포넌트 구현 (선택 상태, 아이콘 색상 커스터마이징 지원)ActionButton컴포넌트 구현Toast컴포넌트 구현Asset컴포넌트 구현CheckboxRowAPI modifier → init 파라미터 방식으로 변경TopHerodescription optional 변경 및 이미지 표시 수정TextButton불필요한 padding 제거에셋
ic_arrow_big_right_24,ic_arrow_big_left_24,ic_album_24,ic_circle_exclamation,ic_star_24등 이미지 에셋 추가Demo
Test plan
To Reviewer
바텀시트 화면은 디자인 명세가 아직 확정되지 않아 이번 PR에는 포함하지 않았습니다.
컴포넌트 API 설계에 대한 의견이 있으시면 자유롭게 코멘트 부탁드립니다.
Closes #35
Closes #27
Closes #30
Closes #31
Closes #34
Closes #26
Closes #28
Closes #29