Skip to content

Comments

리팩토링 및 토큰 로직 수정#31

Merged
Sangyoon98 merged 2 commits intomainfrom
dev
Nov 10, 2025
Merged

리팩토링 및 토큰 로직 수정#31
Sangyoon98 merged 2 commits intomainfrom
dev

Conversation

@Sangyoon98
Copy link
Member

@Sangyoon98 Sangyoon98 commented Nov 10, 2025

📝 Summary

리팩토링 및 토큰 로직 수정

🙏 Question & PR point

📬 Reference

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 관리자급 직위에 대한 대시보드 접근 권한 확대
  • 개선 사항

    • 대시보드 UI 업데이트: 부품 관련 라벨 변경, 레이아웃 개선, 텍스트 강조
    • 주문 상세 화면 시각적 개선
    • 로그아웃 시 세션 관리 강화
    • 서버 오류 응답 처리 개선

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Walkthrough

인증 로그아웃 흐름을 개선하고, 네트워크 오류 처리를 강화하며, UI 접근 제어를 확대하고, 사용자 데이터 매핑을 안전한 enum 기반 파싱으로 변경했습니다. 또한 문자열 리소스를 업데이트하고 대시보드 레이아웃을 조정했습니다.

Changes

Cohort / File(s) 변경 요약
인증 및 로그아웃 흐름
SampoomManagement/App/RootView.swift, SampoomManagement/Core/Network/AuthRequestInterceptor.swift, SampoomManagement/Features/Auth/UI/AuthViewModel.swift, SampoomManagement/Features/Auth/UI/LoginView.swift, SampoomManagement/Features/Auth/UI/LoginViewModel.swift
RootView에 로그아웃 상태 변화에 따른 런타임 반응 추가; AuthRequestInterceptor에 didRequestLogout 알림 추가 및 토큰 갱신 재시도 로직 개선; AuthViewModel 및 LoginView에 비동기 MainActor Task 래핑 추가; LoginViewModel 들여쓰기 수정
네트워크 오류 처리
SampoomManagement/Core/Network/NetworkManager.swift
모든 Alamofire 요청에 상태 코드 검증(200..<300) 추가; 오류 경로에서 ApiErrorResponse 파싱 및 폴백 처리로 다단계 오류 복원 전략 구현
문자열 리소스
SampoomManagement/Core/Resources/StringResources.swift
대시보드 관련 세 개의 새로운 문자열 리소스 추가(partsAll, partsOutOfStock, partLowStock); 기존 두 개의 문자열 리소스 제거(partsInProgress, shortageOfParts)
대시보드 UI 업데이트
SampoomManagement/Features/Dashboard/UI/DashboardView.swift
관리자 전용 UI 가시성 체크를 isManagerOrAbove 조건으로 확대; 텍스트 리소스 업데이트; 새 레이아웃 spacer 추가 및 값 텍스트 굵게 표시
주문 상세 UI
SampoomManagement/Features/Order/UI/OrderDetailContent.swift
OrderInfoCard에서 여러 Divider 제거; OrderPartItem의 마지막 divider를 스타일이 적용된 버전으로 변경
사용자 데이터 모델
SampoomManagement/Features/User/Data/Remote/DTO/EmployeeDTO.swift, SampoomManagement/Features/User/Data/Mappers/UserMappers.swift
EmployeeDTO에서 position을 UserPosition에서 String으로, status를 EmployeeStatus?에서 String?로 변경; UserMappers에서 enum 기반 초기화와 안전한 폴백을 이용한 매핑 로직 개선

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RootView
    participant AuthVM as AuthViewModel
    participant Interceptor as AuthRequestInterceptor
    participant Network as NetworkManager
    
    rect rgb(200, 220, 255)
    note right of User: Token Expired Scenario
    User->>Network: API Request
    Network->>Interceptor: Check/Adapt Request
    Interceptor->>Network: Forward Request
    Network->>Network: Failure (401)
    Network->>Interceptor: Retry with Refresh
    Interceptor->>Network: POST /auth/refresh
    Network->>Network: Refresh Success
    Interceptor->>AuthVM: Posts didRequestLogout (if retry count exceeded)
    end
    
    rect rgb(220, 250, 220)
    note right of AuthVM: Logout Reactions
    AuthVM->>RootView: isLoggedIn = false
    RootView->>RootView: Reset LoginUiState
    RootView->>RootView: Hide SignUp View
    end
    
    rect rgb(250, 240, 200)
    note right of AuthVM: Navigation to Login
    AuthVM->>RootView: shouldNavigateToLogin = true
    RootView->>RootView: Hide SignUp
    RootView->>AuthVM: Call resetNavigationState()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의가 필요한 영역:
    • AuthRequestInterceptor.swift: 토큰 갱신 재시도 로직 및 상태 관리 변경 — 엣지 케이스 검증 필요
    • NetworkManager.swift: 다단계 오류 파싱 전략 — 모든 오류 경로가 올바르게 처리되는지 확인 필요
    • UserMappers.swiftEmployeeDTO.swift: enum 기반 매핑 및 폴백 동작 — 기존 데이터 호환성 검증 필요
    • DashboardView.swift: isManagerOrAbove 접근 제어 로직 확대 — 권한 모델과의 정합성 확인 필요

Possibly related PRs

  • [FEAT] 검색 기능 구현 #16: NetworkManager.request 함수를 동일하게 수정하며, 본 PR에서 상태 코드 검증 및 향상된 오류 파싱을 추가한 것과 관련
  • [FEAT] 로그인 구현 #12: RootView 및 인증 흐름과 같은 파일을 수정하며, 본 PR에서 로그아웃/네비게이션 반응 및 didRequestLogout 처리를 추가한 것과 관련
  • [SPM-433] 프로필 수정 및 회원 관리 구현 #25: EmployeeDTO 및 사용자 매퍼와 같은 동일한 데이터 계층을 수정하며, 본 PR에서 DTO 필드 타입 및 enum 기반 파싱을 변경한 것과 관련

Suggested labels

ready-to-merge

Suggested reviewers

  • Lee-Jong-Jin
  • vivivim
  • CHOOSLA
  • yangjiseonn
  • taemin3

Poem

🐰 토큰이 만료되면 새로고침 하고,
접근은 관리자 위로 넓혀지며,
문자열 리소스는 부품별로 단장되고,
네트워크는 더욱 똑똑해지네요!
안전한 매핑으로 데이터도 든든하고,
우리의 앱은 한 발 더 나아갑니다! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 변경사항의 주요 내용을 포괄적으로 설명하고 있습니다. 리팩토링과 토큰 로직 수정이라는 두 가지 핵심 변경사항을 명확히 반영하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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

@vivivim vivivim left a comment

Choose a reason for hiding this comment

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

좋군요

Copy link
Contributor

@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: 2

🧹 Nitpick comments (4)
SampoomManagement/Features/Order/UI/OrderDetailContent.swift (1)

159-162: 색상 참조 패턴 불일치 및 불필요한 뷰 래핑

이 코드에는 두 가지 개선 가능한 부분이 있습니다:

  1. 색상 참조 불일치: 라인 160에서 .textSecondary를 직접 사용하고 있지만, 파일의 다른 부분(라인 68, 79, 100, 143)에서는 Color("TextSecondary") 패턴을 일관되게 사용하고 있습니다. 일관성을 위해 동일한 패턴을 사용하는 것이 좋습니다.

  2. 불필요한 VStack 래퍼: 단일 Divider를 VStack으로 감쌀 필요가 없습니다. 이는 불필요한 뷰 계층을 추가합니다.

다음 diff를 적용하여 코드를 간소화하고 일관성을 개선할 수 있습니다:

-            VStack {
-                Divider().background(.textSecondary)
-            }
-            .padding(.horizontal, 16)
+            Divider()
+                .background(Color("TextSecondary"))
+                .padding(.horizontal, 16)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift (1)

197-204: 권한 로직을 중앙화하는 것을 권장합니다

권한 체크 로직이 뷰 레이어에 구현되어 있습니다. 이러한 비즈니스 로직은 재사용성과 유지보수성을 위해 중앙화하는 것이 좋습니다.

다음 위치 중 하나로 이동을 고려해보세요:

  • UserPosition enum에 computed property로 추가 (예: var isManagerOrAbove: Bool)
  • 별도의 도메인 서비스나 유틸리티로 분리
  • ViewModel로 이동

예시: UserPosition enum에 추가하는 방법

// UserPosition enum에 추가
extension UserPosition {
    var isManagerOrAbove: Bool {
        switch self {
        case .manager, .deputyGeneralManager, .generalManager, .director, .vicePresident, .president, .chairman:
            return true
        default:
            return false
        }
    }
}

그런 다음 뷰에서는:

if let user = viewModel.user, user.position.isManagerOrAbove {
    // ...
}
SampoomManagement/Core/Network/AuthRequestInterceptor.swift (2)

46-51: URL 체크 방식을 더 견고하게 개선할 수 있습니다.

현재 absoluteString.contains("/auth/refresh")를 사용한 문자열 기반 체크는 대부분의 경우 잘 작동하지만, 쿼리 파라미터나 프래그먼트를 포함한 전체 URL을 대상으로 하므로 의도하지 않은 매칭이 발생할 수 있습니다.

더 명확한 체크를 원하신다면 다음과 같이 개선할 수 있습니다:

-        if let urlString = adaptedRequest.url?.absoluteString,
-           urlString.contains("/auth/refresh") {
+        if let url = adaptedRequest.url,
+           url.path.hasSuffix("/auth/refresh") {
             adaptedRequest.setValue(nil, forHTTPHeaderField: "Authorization")
             completion(.success(adaptedRequest))
             return
         }

77-85: 재시도 제한 로직이 정확하지만 주석으로 의도를 명확히 하면 좋겠습니다.

토큰 갱신 성공 후 retryCount >= 1을 체크하는 것은 무한 재시도 루프를 방지하는 서킷 브레이커 패턴입니다. 로직은 올바르게 동작하지만, 향후 유지보수를 위해 다음과 같은 주석을 추가하면 의도가 더 명확해집니다.

                 _ = try await refreshCoordinator.refresh(using: tokenRefreshService)
                 
+                // 서킷 브레이커: 토큰 갱신 성공 후에도 401이 재발하면 권한 문제로 간주하고 로그아웃
+                // (예: 새 토큰도 무효, 권한 변경 등)
                 if request.retryCount >= 1 {
                     await authPreferences.clear()
                     DispatchQueue.main.async {
                         NotificationCenter.default.post(name: .didRequestLogout, object: nil)
                     }
                     completion(.doNotRetry)
                     return
                 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fe726e and a36af27.

📒 Files selected for processing (11)
  • SampoomManagement/App/RootView.swift (1 hunks)
  • SampoomManagement/Core/Network/AuthRequestInterceptor.swift (3 hunks)
  • SampoomManagement/Core/Network/NetworkManager.swift (4 hunks)
  • SampoomManagement/Core/Resources/StringResources.swift (1 hunks)
  • SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1 hunks)
  • SampoomManagement/Features/Auth/UI/LoginView.swift (1 hunks)
  • SampoomManagement/Features/Auth/UI/LoginViewModel.swift (1 hunks)
  • SampoomManagement/Features/Dashboard/UI/DashboardView.swift (5 hunks)
  • SampoomManagement/Features/Order/UI/OrderDetailContent.swift (1 hunks)
  • SampoomManagement/Features/User/Data/Mappers/UserMappers.swift (1 hunks)
  • SampoomManagement/Features/User/Data/Remote/DTO/EmployeeDTO.swift (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
SampoomManagement/App/RootView.swift (1)
SampoomManagement/Features/Auth/UI/AuthViewModel.swift (2)
  • resetNavigationState (66-68)
  • handleTokenExpired (53-64)
SampoomManagement/Features/Auth/UI/LoginViewModel.swift (1)
SampoomManagement/Features/Auth/UI/LoginUiState.swift (1)
  • copy (47-65)
SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
SampoomManagement/Features/Auth/Domain/UseCase/CheckLoginStateUseCase.swift (1)
  • execute (17-19)
SampoomManagement/Core/Network/AuthRequestInterceptor.swift (2)
SampoomManagement/Core/Network/NetworkManager.swift (2)
  • request (42-112)
  • request (114-190)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
  • clear (145-164)
SampoomManagement/Features/Auth/UI/LoginView.swift (1)
SampoomManagement/Features/Auth/UI/LoginUiState.swift (1)
  • copy (47-65)
🔇 Additional comments (12)
SampoomManagement/Features/User/Data/Remote/DTO/EmployeeDTO.swift (1)

18-19: DTO 타입 변경이 적절하며 구현이 올바릅니다.

검증 결과, enum에서 String으로의 변경이 정확하게 구현되어 있습니다:

  • EmployeeDTO의 position: String, status: String?은 API 응답을 유연하게 수용합니다
  • 매퍼는 UserPosition(rawValue: self.position) ?? .staff, EmployeeStatus(rawValue: (self.status ?? "").uppercased()) ?? .active로 안전한 변환을 수행합니다
  • 도메인 모델의 position: UserPosition, status: EmployeeStatus 기대 타입과 일치합니다

DTO와 도메인 모델의 적절한 분리로 API 유연성과 타입 안전성이 동시에 확보되었습니다.

SampoomManagement/Features/User/Data/Mappers/UserMappers.swift (1)

82-83: Since I've confirmed that Swift enum rawValue matching is case-sensitive, let me search for the actual enum definitions to understand the design intent.

Based on my verification of the codebase and Swift language behavior:

검증된 일관성 문제 확인 - 기본값 지정 및 조용한 대체에 대한 재검토 필요

귀사의 코드에서 발견된 불일치는 검증되었습니다:

  • Position: UserPosition(rawValue: self.position) ?? .staff - 대문자 변환 없음
  • Status: EmployeeStatus(rawValue: (self.status ?? "").uppercased()) ?? .active - 항상 대문자 변환

Swift의 enum rawValue 초기화는 대소문자를 구분하므로, status를 먼저 대문자로 변환하는 것은 API 응답의 대소문자 변동을 처리하기 위한 의도된 방어 코드로 보입니다. 그러나 이 패턴은 다음 사항에 대해 재검토가 필요합니다:

  1. 일관성 확인: position도 같은 대문자 변환 처리가 필요한지 API 응답 형식 확인 필요
  2. 기본값 검증: 파싱 실패 시 .staff.active 모두가 안전한 기본값인지 비즈니스 로직 확인 필요
  3. 데이터 품질 추적: 조용한 대체로 인한 API 데이터 품질 문제 감지 불가능 - 로깅 고려
SampoomManagement/Core/Resources/StringResources.swift (1)

23-25: 문자열 리소스 추가 확인

대시보드에 사용될 새로운 문자열 상수들이 적절하게 추가되었습니다.

SampoomManagement/Features/Dashboard/UI/DashboardView.swift (4)

91-92: 문자열 리소스 업데이트 확인

새로운 문자열 리소스 키들이 올바르게 적용되었습니다. StringResources.swift의 변경사항과 일치합니다.

Also applies to: 95-95


46-46: 레이아웃 간격 조정 확인

섹션 간 간격이 적절하게 추가되었습니다.


113-113: 폰트 스타일링 추가 확인

버튼 카드의 값 텍스트에 굵은 글꼴이 적절하게 적용되었습니다.


28-28: 직원 관리 기능 접근 권한 확대 확인됨 — 백엔드 권한 검증 필요

코드 검증 결과, 직원 관리 기능에 대한 접근 권한이 다음과 같이 변경되었음이 확인되었습니다:

  • 변경 내용: 관리자(admin) 전용 → 매니저 이상 7개 직급 허용 (isManagerOrAbove 함수 사용)
  • 적용 위치: DashboardView.swift 라인 28, 85 (UI 레벨 조건부 렌더링)

보안 관련 주의사항: 현재 권한 검증이 UI 레이 에서만 수행되고 있습니다. EditEmployeeUseCaseUpdateEmployeeStatusUseCase에서는 권한 확인 로직이 없으며, 비즈니스 로직 계층에서 권한 검증이 이루어지지 않고 있습니다.

백엔드 API에서 해당 역할들에 대한 권한 검증이 제대로 이루어지고 있는지 확인이 필수입니다. 클라이언트 단에서만 UI를 숨기는 것으로는 충분하지 않습니다.

  • 백엔드 API가 매니저 이상의 역할에 대해 직원 관리 엔드포인트(editEmployee, updateEmployeeStatus 등)에 대한 접근을 허용하는지 확인
  • 권한 검증이 비즈니스 로직 계층(UseCase/Repository)에서도 이루어져야 하는지 검토
SampoomManagement/Features/Auth/UI/LoginViewModel.swift (1)

53-55: 의도 유지 확인
들여쓰기만 정리된 변경으로 동작 변화가 없는지 검토했는데, 기존 흐름이 그대로 유지됩니다.

SampoomManagement/Core/Network/NetworkManager.swift (1)

77-106: 에러 매핑 강화 👍
새롭게 추가된 단계별 파싱으로 서버 응답에서 상태 코드와 메시지를 최대한 복구해 주는 점이 좋습니다. 디버깅 시 원인을 훨씬 빠르게 파악할 수 있겠어요.

SampoomManagement/Features/Auth/UI/LoginView.swift (1)

114-120: 성공 후 상태 리셋 흐름 확인
짧은 딜레이 뒤에 onSuccess를 호출하고 success 플래그를 초기화해 다음 로그인에서도 onChange가 정상 동작하도록 한 점 좋습니다.

SampoomManagement/Core/Network/AuthRequestInterceptor.swift (2)

11-13: 로그아웃 알림을 위한 깔끔한 구현입니다.

Notification.Name 확장을 통해 로그아웃 이벤트를 전역적으로 전달하는 방식이 적절합니다. UI와 인증 로직의 결합도를 낮추는 좋은 접근입니다.


88-95: 토큰 갱신 실패 시 로그아웃 처리가 적절합니다.

에러 경로에서 인증 상태를 클리어하고 메인 큐에서 알림을 게시하는 것이 올바른 구현입니다. 재시도 제한 초과 경로(lines 78-84)와 일관성 있게 동일한 알림을 사용하고 있습니다.

@Sangyoon98 Sangyoon98 merged commit 9eef5ad into main Nov 10, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants