Skip to content

Spm 130#17

Merged
Sangyoon98 merged 5 commits into
devfrom
SPM-130
Oct 19, 2025
Merged

Spm 130#17
Sangyoon98 merged 5 commits into
devfrom
SPM-130

Conversation

@Sangyoon98
Copy link
Copy Markdown
Member

@Sangyoon98 Sangyoon98 commented Oct 19, 2025

📝 Summary

출고목록, 장바구니 구현
출고목록 추가 로직, 장바구니 추가 로직 구현
출고처리 구현

🙏 Question & PR point

📬 Reference

Summary by CodeRabbit

  • 새로운 기능

    • 출고 목록 화면 추가(조회/수량조정/삭제/출고처리) 및 관련 API·도메인·유스케이스·저장소 추가
    • 장바구니 화면 추가(조회/수량조정/삭제/주문) 및 관련 API·도메인·유스케이스·저장소 추가
    • 부품 상세 바텀시트 추가(수량 편집, 출고/장바구니 추가)
  • UI 개선

    • 네비게이션 메뉴 "배송" → "출고"로 업데이트(아이콘/경로 변경)
    • 버튼에 Error 변형 추가 및 중립/비활성 스타일 개선
    • 모달 바텀시트, 문자열 리소스 및 삭제 아이콘 추가

@Sangyoon98 Sangyoon98 self-assigned this Oct 19, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 19, 2025

Walkthrough

출고(Outbound) 라우트를 도입해 기존 Delivery 라우트를 대체하고, 출고·장바구니 기능(네트워크 DTO/API, 리포지토리, 유스케이스, DI, ViewModel, UI)과 부품 상세 바텀시트를 전층에 추가·연결했습니다. 공통 버튼 변형·문자열·리소스·소소한 로그 리팩터링도 포함됩니다.

Changes

Cohort / File(s) 변경 요약
네비게이션 및 라우팅
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
ROUTE_DELIVERY 제거, ROUTE_OUTBOUND = "outbound" 추가; BottomNavItem.Delivery를 outbound 라우트/아이콘으로 재바인딩; Delivery/Cart 스크린 매핑을 OutboundListScreen/CartListScreen으로 교체
네트워크 레이어
app/src/main/java/com/sampoom/android/core/network/ApiResponse.kt, app/src/main/java/com/sampoom/android/core/network/NetworkModule.kt
ApiSuccessResponse 데이터 클래스 추가; Gson 필드 네이밍 전략을 LOWER_CASE_WITH_UNDERSCORESIDENTITY로 변경
공통 UI 컴포넌트
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt
ButtonVariant.Error 추가 및 Neutral/Error 색상·비활성 처리 로직 조정
장바구니 — API·DTOs
app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt, .../dto/*
CartApi 인터페이스 추가(목록/추가/삭제/업데이트/비우기) 및 AddCartRequestDto, CartDto, UpdateCartRequestDto DTO 추가
장바구니 — 도메인·매퍼·리포지토리·DI
.../feature/cart/domain/*, .../data/mapper/CartMappers.kt, .../data/repository/CartRepositoryImpl.kt, .../di/CartModules.kt
도메인 모델(Cart/CartGroup/CartPart/CartList), DTO→도메인 매퍼, CartRepository 구현체와 Hilt 모듈(바인딩/제공) 추가
장바구니 — 유스케이스·UI
app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/*, .../ui/*
Get/Add/Delete/DeleteAll/Update 유스케이스 추가 및 CartListScreen, CartListViewModel, CartListUiState, CartListUiEvent 추가
출고 — API·DTOs
app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt, .../dto/*
OutboundApi 인터페이스 추가(목록/추가/처리/삭제/업데이트/비우기) 및 관련 DTO 추가
출고 — 도메인·매퍼·리포지토리·DI
.../feature/outbound/domain/*, .../data/mapper/OutboundMappers.kt, .../data/repository/OutboundRepositoryImpl.kt, .../di/OutboundModules.kt
도메인 모델(Outbound 등), DTO→도메인 매퍼, OutboundRepositoryImpl, Hilt 모듈 추가
출고 — 유스케이스·UI
app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/*, .../ui/*
Get/Process/Add/Delete/DeleteAll/Update 유스케이스 및 OutboundListScreen, OutboundListViewModel, OutboundListUiState, OutboundListUiEvent 추가
부품 상세 / 목록 개선 (바텀시트)
app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt, .../PartDetailViewModel.kt, PartDetailUiEvent.kt, PartDetailUiState.kt, PartListScreen.kt, PartListViewModel.kt, PartListUiEvent.kt, PartListUiState.kt
PartDetail 바텀시트 컴포저블·ViewModel 추가, AddToOutbound/AddToCart 흐름 통합; PartList에 ModalBottomSheet 상태·ShowBottomSheet/DismissBottomSheet 이벤트 및 selectedPart 상태 추가
리소스 및 문자열
app/src/main/res/drawable/delete.xml, app/src/main/res/values/strings.xml
삭제 아이콘 추가 및 부품/출고/장바구니 관련 한글 문자열 다수 추가
사소한 리팩터링 (로그 태그 등)
app/src/main/java/com/sampoom/android/feature/auth/ui/LoginViewModel.kt, SignUpViewModel.kt, PartViewModel.kt
각 ViewModel에 TAG companion object 추가 및 로그 태그 정리

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant App as AppNavHost
    participant OutboundScreen as OutboundListScreen
    participant OutboundVM as OutboundListViewModel
    participant UseCase as GetOutboundUseCase
    participant Repo as OutboundRepository
    participant API as OutboundApi
    participant Server

    User->>App: 하단 탭 선택 (출고)
    App->>OutboundScreen: 라우팅 -> ROUTE_OUTBOUND
    OutboundScreen->>OutboundVM: bindLabel / Load
    OutboundVM->>UseCase: invoke()
    UseCase->>Repo: getOutboundList()
    Repo->>API: GET agency/1/outbound
    API->>Server: 요청
    Server-->>API: ApiResponse<List<OutboundDto>>
    API-->>Repo: DTO 전달
    Repo->>Repo: dto.toModel()
    Repo-->>UseCase: OutboundList
    UseCase-->>OutboundVM: OutboundList
    OutboundVM-->>OutboundScreen: uiState 업데이트
    OutboundScreen->>User: 목록 렌더링
Loading
sequenceDiagram
    actor User
    participant PartList as PartListScreen
    participant PartVM as PartListViewModel
    participant Bottom as PartDetailBottomSheet
    participant DetailVM as PartDetailViewModel
    participant AddOutbound as AddOutboundUseCase
    participant OutRepo as OutboundRepository
    participant OutAPI as OutboundApi
    participant Server

    User->>PartList: 부품 카드 클릭
    PartList->>PartVM: ShowBottomSheet(part)
    PartVM-->>PartList: selectedPart 설정
    PartList->>Bottom: ModalBottomSheet 열기 (part)
    Bottom->>DetailVM: Initialize / bindLabel
    User->>Bottom: 수량 설정 및 '출고목록에 추가' 클릭
    Bottom->>DetailVM: AddToOutbound(partId, qty)
    DetailVM->>AddOutbound: invoke(partId, qty)
    AddOutbound->>OutRepo: addOutbound(...)
    OutRepo->>OutAPI: POST agency/1/outbound
    OutAPI->>Server: 요청
    Server-->>OutAPI: ApiSuccessResponse
    OutAPI-->>OutRepo: 응답
    OutRepo-->>AddOutbound: Result<Unit>
    AddOutbound-->>DetailVM: 결과
    DetailVM-->>Bottom: isOutboundSuccess 업데이트
    Bottom->>User: 성공 토스트 / 다이얼로그 닫기
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60-75 minutes

Possibly related PRs

Suggested labels

ready-to-merge

Suggested reviewers

  • Lee-Jong-Jin
  • yangjiseonn
  • taemin3

Poem

🐰 깡충, 깜짝 커밋 왔네 —
출고 길 바꾸고 장바구니 채웠네.
바텀시트로 톡, 서버로 휙,
ViewModel도 바쁘게 춤추네.
당근 들고 축하할게요! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive PR 제목인 "Spm 130"은 이슈 번호만 포함하고 있으며, 실제 변경사항의 내용을 전혀 설명하지 않습니다. 요약에 따르면 이 PR은 출고목록(Outbound) 및 장바구니(Cart) 기능 구현, 부품 상세 바텀시트 추가 등 의미 있는 변경사항을 포함하고 있지만, 제목에는 이러한 내용이 전혀 반영되어 있지 않습니다. 제목이 비구체적이고 비설명적이어서 팀원들이 커밋 이력을 스캔할 때 이 PR의 목적을 파악하기 어렵습니다. 제목을 변경사항의 주요 내용을 명확히 반영하는 것으로 개선해야 합니다. 예를 들어, "출고목록 및 장바구니 기능 구현" 또는 "SPM-130: 출고 및 장바구니 기능 추가"와 같이 변경사항의 핵심을 간결하게 설명하는 제목으로 수정하는 것을 권장합니다.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 SPM-130

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 324dfda and 1d90c18.

📒 Files selected for processing (2)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt
🔇 Additional comments (3)
app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (3)

23-26: 로깅 태그 중앙화 개선이 잘 되었습니다!

companion object에 TAG 상수를 선언하여 로그 태그를 중앙화한 것은 좋은 개선입니다. 프로젝트 내 다른 ViewModel들과 일관된 패턴을 따르고 있으며, 유지보수성이 향상됩니다.


74-74: 로그 메시지 접두사가 올바르게 수정되었습니다!

이전 리뷰에서 지적된 로그 접두사 불일치 문제가 해결되었습니다. "submit:"에서 "loadCategory:"로 변경되어 실제 메서드명과 일치하며, 중앙화된 TAG 상수를 사용하고 있습니다.


113-113: 로그 메시지 접두사가 올바르게 수정되었습니다!

이전 리뷰에서 지적된 로그 접두사 불일치 문제가 해결되었습니다. "submit:"에서 "loadGroup:"로 변경되어 실제 메서드명과 일치하며, 디버깅 시 혼란이 없도록 개선되었습니다.


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

Copy link
Copy Markdown

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

🧹 Nitpick comments (26)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)

3-3: 사용되지 않는 import 제거

android.R.attr.enabled는 파일 어디에서도 사용되지 않습니다. 이 import를 제거하세요.

-import android.R.attr.enabled
app/src/main/java/com/sampoom/android/feature/cart/domain/model/CartList.kt (1)

3-11: 래퍼 클래스 설계 고려사항

totalCountisEmpty는 단순히 items.sizeitems.isEmpty()를 래핑하는 derived 속성입니다. 추가적인 비즈니스 로직이나 불변성 보장이 필요하지 않다면, 도메인 레이어에서 List<Cart>를 직접 사용하는 것도 고려해볼 수 있습니다.

다만 페이지네이션이나 메타데이터 추가를 위한 확장성을 고려한 설계라면 현재 구현이 적절합니다.

app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/UpdateCartRequestDto.kt (1)

1-5: 장바구니 수량 업데이트 DTO 구현 확인

DTO 구현은 적절하나, UpdateOutboundRequestDto와 구조가 동일합니다. 현재는 모듈 경계를 유지하는 것이 합리적이지만, 향후 공통 DTO(예: UpdateQuantityRequestDto)를 core 모듈로 추출하여 중복을 줄이는 것을 고려해볼 수 있습니다.

app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1)

371-394: 주석 처리된 코드를 제거하세요.

24줄에 걸친 주석 처리된 OutlinedTextField 코드는 버전 관리 시스템에서 확인할 수 있으므로 제거하는 것이 좋습니다. 코드베이스를 깔끔하게 유지하는 데 도움이 됩니다.

이 코드가 나중에 필요하다면, Git 히스토리에서 복원할 수 있습니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/repository/CartRepository.kt (1)

6-10: 에러 처리 방식의 일관성을 고려하세요.

getCartList()CartList를 직접 반환하지만 (예외를 던질 수 있음), 다른 메서드들은 Result<Unit>을 반환합니다. 이러한 비일관성으로 인해 호출자가 getCartList()에는 try-catch를, 다른 메서드에는 Result 처리를 사용해야 합니다.

일관성을 위해 getCartList()Result<CartList>를 반환하도록 변경하는 것을 고려해보세요:

interface CartRepository {
    suspend fun getCartList(): Result<CartList>
    suspend fun addCart(partId: Long, quantity: Long): Result<Unit>
    suspend fun deleteCart(cartItemId: Long): Result<Unit>
    suspend fun deleteAllCart(): Result<Unit>
    suspend fun updateCartQuantity(cartItemId: Long, quantity: Long): Result<Unit>
}

이렇게 하면 모든 메서드에서 일관된 에러 처리 패턴을 사용할 수 있습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (1)

64-67: 사용되지 않는 변수를 제거하세요.

scope 변수가 선언되었지만 코드 내에서 사용되지 않습니다. rememberCoroutineScope()scope 변수 선언을 제거할 수 있습니다.

다음 diff를 적용하여 사용되지 않는 변수를 제거하세요:

     // ModalBottomSheet 상태 관리
     val sheetState = rememberModalBottomSheetState()
-    val scope = rememberCoroutineScope()
     var showBottomSheet by remember { mutableStateOf(false) }
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiState.kt (1)

5-15: 상태 플래그 분산 정리와 선택 상태 사용 여부 확인 제안

  • isUpdating/isDeleting/isOrderSuccess와 개별 에러 필드(cartError/updateError/deleteError)가 분산되어 있어 관리 비용이 큽니다. 단일 OperationState(Idle/Loading/Success/Failure)로 합치거나, 작업별 소형 상태 구조체로 묶는 방식을 권장합니다. 일회성 성공 신호(isOrderSuccess)는 Event/Channel(또는 one‑shot effect)로 처리하면 중복 토스트/재렌더를 줄일 수 있습니다.
  • selectedCart가 실제로 사용되지 않으면 제거해 상태 오염을 줄이는 것이 좋습니다. 사용 중이라면 주석/도큐먼트로 목적을 명시해 주세요.
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListUiEvent.kt (1)

3-12: 수량 유효성(1 이상) 보장 장치 추가 권장

UpdateQuantity.quantity가 0/음수로 전달될 수 있습니다. ViewModel/Reducer에서 require(quantity > 0)로 방어하거나, 가능하다면 @IntRange(from = 1) 주석(또는 KDoc로 계약 명시)으로 의도를 구체화해 주세요. 도메인에서 Long을 유지해야 한다면 주석 기반 계약이라도 명확히 하는 편이 좋습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/repository/OutboundRepository.kt (1)

5-11: 에러 처리 일관성 및 연산 안전성 정렬 권장

  • 반환형이 혼합(getOutboundList는 예외, 나머지는 Result)되어 호출부 에러 처리 정책이 일관되지 않습니다. 전부 Result로 통일하거나(예: Result) 전부 예외 전파로 맞추는 방식 중 하나로 정렬해 주세요.
  • processOutbound/deleteAllOutbound는 재시도 시 중복 처리 위험이 있습니다. 구현부에 idempotencyKey/서버 멱등 보장 또는 클라이언트 측 재시도/백오프 정책을 명시하는 것이 안전합니다. 최소한 KDoc로 계약을 남겨 주세요.
app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt (1)

84-104: 중복 제출 방지(이중 탭/빠른 연타) 가드 제안

외부 호출이 비동기라 isUpdating 플래그가 UI에 반영되기 전 중복 호출될 수 있습니다. 안전을 위해 함수 초입 가드 또는 클릭 스로틀을 추가해 주세요.

예:

private fun addToOutbound(partId: Long, quantity: Long) {
    if (_uiState.value.isUpdating) return
    // ...
}

Also applies to: 105-124

app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (1)

52-55: 네이밍 일관성: Delivery → Outbound로 리네이밍 고려

UI 라벨/아이콘은 변경됐으나 클래스명은 BottomNavItem.Delivery로 남아 있습니다. 장기적으로는 BottomNavItem.Outbound로 변경해 의미를 통일하는 편이 가독성에 좋습니다.

app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (4)

3-3: 불필요/오입력된 import 제거 권장

android.R.attr.onClick, shadow, OutboundListUiEvent, kotlin.collections.forEach 모두 미사용입니다. 빌드/린트 노이즈를 줄이기 위해 제거해 주세요.

-import android.R.attr.onClick
- import androidx.compose.ui.draw.shadow
- import com.sampoom.android.feature.outbound.ui.OutboundListUiEvent
- import kotlin.collections.forEach

Also applies to: 32-32, 50-51


72-81: 성공 상태 초기화 위치 조정

isOrderSuccess가 false일 때도 clearSuccess가 호출됩니다. 불필요한 상태 업데이트/재구독을 유발할 수 있습니다. if 블록 내부로 이동해 주세요.

 LaunchedEffect(uiState.isOrderSuccess) {
-    if (uiState.isOrderSuccess) {
-        Toast.makeText(
-            context,
-            context.getString(R.string.cart_toast_order_text),
-            Toast.LENGTH_SHORT
-        ).show()
-    }
-    viewModel.clearSuccess()
+    if (uiState.isOrderSuccess) {
+        Toast.makeText(
+            context,
+            context.getString(R.string.cart_toast_order_text),
+            Toast.LENGTH_SHORT
+        ).show()
+        viewModel.clearSuccess()
+    }
 }

163-176: LazyColumn: forEach+item 대신 items 사용 및 key 제공

현재 forEach로 item을 감싸 렌더링하고 있어 키 관리가 어렵고 스크롤/상태 보존 이슈가 생길 수 있습니다. items API와 key를 사용해 주세요.

- uiState.cartList.forEach { category ->
-     category.groups.forEach { group ->
-         item {
-             CartSection(
-                 categoryName = category.categoryName,
-                 groupName = group.groupName,
-                 parts = group.parts,
-                 isUpdating = uiState.isUpdating,
-                 isDeleting = uiState.isDeleting,
-                 onEvent = { viewModel.onEvent(it) }
-             )
-         }
-     }
- }
+ items(
+     items = uiState.cartList,
+     key = { it.categoryName } // 필요 시 고유 ID로 교체
+ ) { category ->
+     items(
+         items = category.groups,
+         key = { it.groupName } // 필요 시 고유 ID로 교체
+     ) { group ->
+         CartSection(
+             categoryName = category.categoryName,
+             groupName = group.groupName,
+             parts = group.parts,
+             isUpdating = uiState.isUpdating,
+             isDeleting = uiState.isDeleting,
+             onEvent = { viewModel.onEvent(it) }
+         )
+     }
+ }

157-162: 리스트 하단 패딩으로 버튼과 겹침 방지

하단 Spacer 아이템 대신 LazyColumn의 contentPadding을 사용하고, 버튼 쪽은 navigationBarsPadding을 적용하면 기기별 안전영역을 자동 처리합니다.

 LazyColumn(
     modifier = Modifier
         .fillMaxSize()
-        .padding(horizontal = 16.dp),
+        .padding(horizontal = 16.dp),
-    verticalArrangement = Arrangement.spacedBy(16.dp)
+    verticalArrangement = Arrangement.spacedBy(16.dp),
+    contentPadding = PaddingValues(bottom = 96.dp)
 ) {
-    item { Spacer(Modifier.height(100.dp)) }
 }
 
 CommonButton(
     modifier = Modifier
         .fillMaxWidth()
         .align(Alignment.BottomEnd)
-        .padding(16.dp)
-        .padding(end = 72.dp),
+        .padding(16.dp)
+        .navigationBarsPadding(),

Also applies to: 177-177, 181-186

app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (2)

4-4: 미사용 import 정리

android.util.Log.v는 미사용입니다. 제거해 주세요.

-import android.util.Log.v

45-46: 네이밍 정합성(nit)

processOutBound → processOutbound 로 메서드명을 통일하면 가독성이 좋아집니다.

app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt (1)

17-41: 엔드포인트 내 agency/1 하드코딩 확인 요청

멀티 에이전시 지원 계획이 있다면 path 파라미터/인터셉터 등으로 치환 가능하도록 고려해 주세요. 단일 테넌트라면 무시해도 됩니다.

app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1)

98-98: 로그 태그 오타(nit)

"OutboundListViewModel" → "CartListViewModel".

app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt (6)

71-77: 성공 상태 초기화 위치 조정 (2곳)

성공이 아닐 때도 clearSuccess가 호출됩니다. if 내부에서만 호출해 불필요한 상태 변경을 줄이세요.

 LaunchedEffect(uiState.isOutboundSuccess) {
-    if (uiState.isOutboundSuccess) {
-        Toast.makeText(context, context.getString(R.string.outbound_toast_success), Toast.LENGTH_SHORT).show()
-    }
-    viewModel.clearSuccess()
+    if (uiState.isOutboundSuccess) {
+        Toast.makeText(context, context.getString(R.string.outbound_toast_success), Toast.LENGTH_SHORT).show()
+        viewModel.clearSuccess()
+    }
 }
 
 LaunchedEffect(uiState.isCartSuccess) {
-    if (uiState.isCartSuccess) {
-        Toast.makeText(context, context.getString(R.string.cart_toast_success), Toast.LENGTH_SHORT).show()
-    }
-    viewModel.clearSuccess()
+    if (uiState.isCartSuccess) {
+        Toast.makeText(context, context.getString(R.string.cart_toast_success), Toast.LENGTH_SHORT).show()
+        viewModel.clearSuccess()
+    }
 }

Also applies to: 79-86


35-35: 미사용 import 정리

Preview, delay 미사용입니다. 제거해 주세요.

-import androidx.compose.ui.tooling.preview.Preview
-import kotlinx.coroutines.delay

Also applies to: 45-45


121-125: 수량 표기 문자열 국제화 개선

문자열 연결 대신 stringResource 포맷을 사용해 간격/현지화 문제를 방지하세요. 예: "현재 수량: %d개"

Text(stringResource(R.string.part_current_quantity_format, part.quantity))

(리소스에 형식 문자열 추가 필요)


150-175: 업데이트 중 입력 비활성화

로딩 중에도 수량 입력이 가능하여 레이스/UX 혼선이 생길 수 있습니다. OutlinedTextField에 enabled 플래그를 적용하세요.

 OutlinedTextField(
     value = uiState.quantity.toString(),
     onValueChange = { newValue -> /* ... */ },
     modifier = Modifier.width(100.dp),
     singleLine = true,
     keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
     textStyle = MaterialTheme.typography.titleMedium.copy(textAlign = TextAlign.Center),
+    enabled = !uiState.isUpdating,
     colors = OutlinedTextFieldDefaults.colors(

198-203: 아이콘 접근성(nit)

장식 목적이 아니라 버튼 의미 전달 용도라면 contentDescription을 제공하거나 click 영역에 의미 텍스트가 있는지 확인해 주세요. 현재는 null입니다.

Also applies to: 210-211


72-85: 성공 시 BottomSheet 닫기 여부 확인

주석과 달리 onDismiss 호출이 없어 시트가 유지됩니다. 성공 시 onDismiss() 호출 여부를 제품 기획에 맞춰 결정해 주세요.

app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (1)

20-59: 도메인 특화 예외를 사용하는 것을 권장합니다.

현재 모든 에러 처리에서 제네릭 Exception을 사용하고 있습니다. 더 명확한 에러 처리와 디버깅을 위해 도메인 특화 예외(예: OutboundException, OutboundApiException 등)를 정의하여 사용하는 것을 고려해보세요.

예시:

sealed class OutboundException(message: String) : Exception(message) {
    class ApiException(message: String) : OutboundException(message)
    class NetworkException(message: String) : OutboundException(message)
}

그런 다음 throw Exception(dto.message) 대신 throw OutboundException.ApiException(dto.message)를 사용할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de0a64d and 72c28ce.

📒 Files selected for processing (53)
  • app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (4 hunks)
  • app/src/main/java/com/sampoom/android/core/network/ApiResponse.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/core/network/NetworkModule.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (4 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/mapper/CartMappers.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/AddCartRequestDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/CartDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/UpdateCartRequestDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/di/CartModules.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/model/Cart.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/model/CartList.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/repository/CartRepository.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/AddCartUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/DeleteAllCartUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/DeleteCartUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/GetCartUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/UpdateCartQuantityUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiEvent.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiState.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/mapper/OutboundMappers.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/AddOutboundRequestDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/OutboundDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/UpdateOutboundRequestDto.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/di/OutboundModules.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/model/Outbound.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/model/OutboundList.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/repository/OutboundRepository.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/AddOutboundUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/DeleteAllOutboundUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/DeleteOutboundUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/GetOutboundUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/ProcessOutboundUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/UpdateOutboundQuantityUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListUiEvent.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListUiState.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailUiEvent.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailUiState.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (4 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListUiEvent.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListUiState.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt (1 hunks)
  • app/src/main/res/drawable/delete.xml (1 hunks)
  • app/src/main/res/values/strings.xml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
  • textColor (256-257)
  • backgroundCardColor (253-254)
  • textSecondaryColor (259-260)
app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
  • ErrorContent (18-37)
app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
  • EmptyContent (9-20)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
  • CommonButton (49-176)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1)
  • onEvent (41-52)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
  • textColor (256-257)
  • backgroundCardColor (253-254)
  • textSecondaryColor (259-260)
app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
  • ErrorContent (18-37)
app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
  • EmptyContent (9-20)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
  • CommonButton (49-176)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1)
  • onEvent (38-49)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (2)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1)
  • OutboundListScreen (68-253)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1)
  • CartListScreen (53-242)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
  • disableColor (262-263)
  • textColor (256-257)
  • textSecondaryColor (259-260)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (4)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (4)
  • updateQuantity (104-124)
  • updateLocalQuantity (126-145)
  • removeFromLocalList (191-210)
  • removeAllFromLocalList (212-216)
app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (2)
  • deleteCart (25-26)
  • deleteAllCart (36-37)
app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt (2)
  • deleteCart (30-35)
  • deleteAllCart (37-42)
app/src/main/java/com/sampoom/android/feature/cart/domain/repository/CartRepository.kt (2)
  • deleteCart (8-8)
  • deleteAllCart (9-9)
app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt (2)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (2)
  • textColor (256-257)
  • textSecondaryColor (259-260)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
  • CommonButton (49-176)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (4)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (4)
  • updateQuantity (102-122)
  • updateLocalQuantity (124-143)
  • removeFromLocalList (189-208)
  • removeAllFromLocalList (210-214)
app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt (2)
  • deleteOutbound (29-30)
  • deleteAllOutbound (40-41)
app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (2)
  • deleteOutbound (37-42)
  • deleteAllOutbound (44-49)
app/src/main/java/com/sampoom/android/feature/outbound/domain/repository/OutboundRepository.kt (2)
  • deleteOutbound (9-9)
  • deleteAllOutbound (10-10)
app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (1)
app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt (1)
  • PartDetailBottomSheet (47-274)
🔇 Additional comments (59)
app/src/main/res/drawable/delete.xml (1)

1-9: LGTM!

벡터 드로어블 리소스가 올바르게 정의되었습니다. 25x25 dp 크기는 UI 아이콘으로 적절하고, @color/white 리소스 참조를 사용하여 테마 기반 색상 변경이 가능하도록 설계되었습니다. 휴지통 아이콘 경로 데이터도 명확하고 표준적인 형태입니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/UpdateCartQuantityUseCase.kt (2)

1-5: 패키지 구조와 import가 적절합니다.

패키지명과 필요한 의존성들이 올바르게 선언되어 있습니다.


6-10: 백엔드 검증에 의존하는 현재 설계가 의도된 것임을 확인했습니다.

검증 결과:

  • UseCase, Repository, API, DTO 어디에도 클라이언트 측 입력 검증이 없습니다
  • Result<Unit> 반환 타입과 ViewModel의 onFailure 처리를 통해 백엔드 에러를 적절히 전파합니다

이 설계는 백엔드에서 dto.success 플래그와 메시지로 검증을 담당하도록 의도되었으며, 모던 Android 아키텍처에서 일반적인 패턴입니다. 다만, 백엔드 API에서 quantity > 0, 유효한 cartItemId 등의 검증이 제대로 구현되어 있는지 확인해 주세요.

app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)

158-174: 새로운 Error variant 구현이 적절합니다

Error variant가 다른 variant들과 일관된 패턴으로 구현되었으며, FailRed와 White 색상 조합이 오류 상태를 나타내기에 적합합니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/model/Cart.kt (1)

1-21: 도메인 모델 구현이 깔끔합니다

Cart, CartGroup, CartPart의 3계층 구조가 명확하고 불변 데이터 클래스로 적절히 구현되었습니다.

참고: 파일 끝에 개행 문자를 추가하는 것이 Kotlin 스타일 가이드에 부합합니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/model/Outbound.kt (1)

1-21: 도메인 모델이 Cart 모델과 일관성 있게 구현되었습니다

Outbound, OutboundGroup, OutboundPart의 구조가 Cart 도메인 모델과 동일한 패턴을 따르고 있어 코드베이스 전반의 일관성이 유지됩니다.

참고: 파일 끝에 개행 문자를 추가하는 것이 좋습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/model/OutboundList.kt (1)

3-11: CartList와 일관된 패턴 사용

OutboundList가 CartList와 동일한 패턴(derived 속성, empty() 팩토리)을 따르고 있어 코드베이스 전반의 일관성이 유지됩니다. 두 도메인에서 동일한 접근 방식을 사용하는 것이 유지보수성에 도움이 됩니다.

app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/OutboundDto.kt (1)

3-21: 서버 API 응답 형식에 대한 검증 필요

검증 결과 다음이 확인되었습니다:

  • FieldNamingPolicy.IDENTITY 설정: NetworkModule.kt:36에서 정확히 구성되어 있음 (리뷰어의 기술적 판단이 정확함)
  • @SerializedName 미사용: 전체 코드베이스에서 @SerializedName 어노테이션이 사용되지 않음 (OutboundDto 및 유사 DTO들 모두 해당)
  • 파일 끝 개행: OutboundDto.kt의 마지막이 개행 문자 없이 ")"로 종료됨 (리뷰어의 지적이 정확함)

남은 검증 사항: 서버 API가 실제로 camelCase(현재 DTO 필드명)를 반환하는지, 아니면 snake_case를 반환하는지 코드베이스에서 확인할 수 없습니다. 테스트 픽스처나 API 문서, 또는 실제 응답 샘플이 없기 때문입니다.

리뷰어의 우려가 유효하려면 서버 API 문서나 응답 샘플을 통해 필드명 형식을 직접 확인하고, 필요시 @SerializedName 어노테이션을 추가해야 합니다.

app/src/main/java/com/sampoom/android/core/network/NetworkModule.kt (1)

36-36: 리뷰 코멘트는 잘못된 가정에 기반함

이 PR에서 NetworkModule.kt이 변경되지 않았습니다. NetworkModule.kt의 FieldNamingPolicy는 이미 IDENTITY로 설정되어 있었고, 이 커밋에서 어떤 변경도 발생하지 않았습니다.

따라서 리뷰 코멘트에서 언급한 "LOWER_CASE_WITH_UNDERSCORES에서 IDENTITY로의 변경"은 실제로 발생하지 않았으며, 새로 추가된 DTO들(CartDto, OutboundDto)은 기존의 IDENTITY 정책에 이미 맞춰져 있습니다.

Likely an incorrect or invalid review comment.

app/src/main/res/values/strings.xml (4)

51-57: 부품 조회 Bottom Sheet 문자열 리소스 추가 확인

부품 상세 바텀시트에 필요한 모든 문자열 리소스가 적절하게 정의되어 있습니다.


59-68: 출고 목록 문자열 리소스 추가 확인

출고 기능에 필요한 모든 UI 텍스트가 체계적으로 정의되어 있습니다.


70-79: 장바구니 문자열 리소스 추가 확인

장바구니 기능에 필요한 모든 UI 텍스트가 일관성 있게 정의되어 있습니다.


84-89: 공통 문자열 리소스 추가 확인

여러 기능에서 재사용 가능한 공통 액션 문자열들이 적절하게 추가되었습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt (1)

43-44: 바텀시트 이벤트 핸들링 구현 확인

바텀시트 표시/숨김 처리가 명확하고 올바르게 구현되었습니다. 상태 업데이트 로직이 간결하며 부작용이 없습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/DeleteAllOutboundUseCase.kt (1)

1-10: 출고목록 전체 삭제 유스케이스 구현 확인

표준 유스케이스 패턴을 따르는 깔끔한 구현입니다. 도메인 레이어의 책임 분리가 적절합니다.

app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/UpdateOutboundRequestDto.kt (1)

1-5: 출고 수량 업데이트 DTO 구현 확인

API 요청 페이로드로 적절한 간단한 데이터 클래스입니다.

app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/AddCartRequestDto.kt (1)

1-6: 장바구니 추가 DTO 구현 확인

장바구니에 부품을 추가하는 데 필요한 필드들이 적절하게 정의된 깔끔한 데이터 클래스입니다.

app/src/main/java/com/sampoom/android/core/network/ApiResponse.kt (1)

10-14: 데이터 반환이 없는 API 응답 타입 추가 확인

데이터 페이로드가 없는 성공/실패 응답을 위한 ApiSuccessResponse 타입이 적절하게 추가되었습니다. 삭제, 업데이트 등의 작업에 유용한 설계입니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartListUiState.kt (1)

9-9: 부품 선택 상태 필드 추가 확인

바텀시트에서 표시할 선택된 부품을 추적하기 위한 selectedPart 필드가 적절하게 추가되었습니다. nullable 타입과 null 기본값이 적절합니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartListUiEvent.kt (1)

3-9: 바텀시트 이벤트 정의가 명확합니다!

Part 객체를 전달하는 ShowBottomSheet와 해제를 위한 DismissBottomSheet 이벤트가 타입 안전하게 정의되어 있으며, 기존 이벤트 패턴과 일관성 있게 구현되었습니다.

app/src/main/java/com/sampoom/android/feature/outbound/data/remote/dto/AddOutboundRequestDto.kt (1)

3-6: DTO 구현이 간결합니다.

partIdquantity 모두 Long 타입으로 정의되어 있습니다. quantity의 경우 일반적으로 Int 타입으로도 충분한 경우가 많은데, 매우 큰 수량을 다룰 필요가 있어 Long을 선택하셨는지 확인해 주세요.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/DeleteOutboundUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

표준 유스케이스 패턴을 따르며, OutboundRepository에 대한 의존성이 적절히 주입되고 deleteOutbound 작업이 깔끔하게 위임되었습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/AddOutboundUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

출고 아이템 추가를 위한 유스케이스가 표준 패턴에 따라 구현되었으며, 리포지토리로의 위임이 적절합니다. 다른 유스케이스들과 일관성 있는 구조입니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/DeleteAllCartUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

장바구니 전체 삭제 기능이 적절히 구현되었습니다. 리포지토리에 대한 의존성 주입과 위임이 올바르게 처리되었습니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/DeleteCartUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

개별 장바구니 아이템 삭제 유스케이스가 표준 패턴을 따라 구현되었으며, 출고 기능의 유사한 유스케이스와 일관성 있는 구조를 유지하고 있습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/GetOutboundUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

출고 목록 조회 유스케이스가 깔끔하게 구현되었습니다. 리포지토리로부터 데이터를 가져오는 로직이 적절히 위임되었습니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/GetCartUseCase.kt (1)

1-10: 유스케이스 구현이 올바릅니다!

장바구니 목록 조회 유스케이스가 표준 패턴을 따라 구현되었으며, 출고 기능의 GetOutboundUseCase와 동일한 구조로 일관성을 유지하고 있습니다.

app/src/main/java/com/sampoom/android/feature/cart/data/mapper/CartMappers.kt (1)

10-12: 깔끔한 매퍼 구현입니다.

DTO에서 도메인 모델로의 변환 로직이 명확하고 간결합니다. 중첩된 컬렉션 매핑도 적절하게 처리되었습니다.

app/src/main/java/com/sampoom/android/feature/cart/di/CartModules.kt (1)

15-27: DI 설정이 올바르게 구성되었습니다.

Hilt의 모듈 구성이 표준 패턴을 잘 따르고 있으며, Repository와 API의 바인딩이 적절합니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/UpdateOutboundQuantityUseCase.kt (1)

6-10: 표준 유스케이스 패턴을 잘 따르고 있습니다.

Repository로의 깔끔한 위임과 간결한 구현이 좋습니다.

app/src/main/java/com/sampoom/android/feature/outbound/data/mapper/OutboundMappers.kt (1)

10-12: Cart 매퍼와 일관된 구조입니다.

DTO에서 도메인 모델로의 변환이 명확하고, 코드베이스 전반에 걸쳐 일관성을 유지하고 있습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailUiState.kt (1)

5-12: UI 상태 관리가 잘 설계되었습니다.

적절한 기본값 설정과 nullable 타입 사용으로 다양한 UI 상태를 효과적으로 표현하고 있습니다.

app/src/main/java/com/sampoom/android/feature/cart/data/remote/dto/CartDto.kt (1)

3-21: JSON 직렬화 필드명을 확인하세요.

DTO에 @SerializedName 어노테이션이 없으므로, 백엔드 API가 camelCase를 사용해야 정상 작동합니다. 만약 백엔드가 snake_case를 사용한다면 직렬화가 실패할 수 있습니다.

백엔드 API 응답 형식을 확인하고, 필요한 경우 다음과 같이 어노테이션을 추가하세요:

data class CartDto(
    @SerializedName("category_id") val categoryId: Long,
    @SerializedName("category_name") val categoryName: String,
    val groups: List<CartGroupDto>
)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (2)

79-93: LaunchedEffect 로직을 검토하세요.

Line 79-81의 LaunchedEffect(Unit)에서 clearSuccess()를 호출하고, line 88-93에서도 다시 호출합니다. 또한 isOrderSuccess가 true인 상태로 유지되면 리컴포지션마다 Toast가 반복 표시될 수 있습니다.

isOrderSuccess가 이벤트 발생 후 즉시 false로 리셋되는지 확인하세요. 그렇지 않으면 다음과 같이 수정하는 것을 고려하세요:

LaunchedEffect(uiState.isOrderSuccess) {
    if (uiState.isOrderSuccess) {
        Toast.makeText(context, context.getString(R.string.outbound_toast_order_text), Toast.LENGTH_SHORT).show()
        viewModel.clearSuccess()
    }
}

// 중복 clearSuccess 호출 제거
LaunchedEffect(Unit) {
    // clearSuccess() 제거
}

95-204: 전반적인 UI 구현이 잘 되어 있습니다.

로딩, 에러, 빈 목록, 데이터 표시 등 다양한 UI 상태를 적절하게 처리하고 있으며, AlertDialog를 통한 사용자 확인 플로우도 잘 구현되어 있습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (3)

130-136: LGTM!

부품 아이템 클릭 시 이벤트 발행과 바텀시트 표시 로직이 올바르게 구현되었습니다.


143-161: LGTM!

바텀시트 구현이 올바르게 되어 있습니다. selectedPart에 대한 null 체크와 해제 로직이 적절합니다.


164-210: LGTM!

PartListItemCard에 클릭 핸들러가 올바르게 추가되었습니다.

app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt (5)

14-18: API 호출 실패 시 예외 처리를 확인하세요.

getCartList() 메서드는 다른 메서드들과 달리 runCatching으로 감싸지 않고 직접 결과를 반환합니다. API 호출이 실패할 경우 처리되지 않은 예외가 발생할 수 있습니다. 리포지토리 인터페이스에서 Result<CartList>를 반환하도록 의도했는지, 아니면 예외를 상위 레이어로 전파하는 것이 의도된 동작인지 확인해주세요.


20-28: LGTM!

장바구니 추가 로직이 올바르게 구현되었습니다. 에러 처리가 적절합니다.


30-35: LGTM!

장바구니 항목 삭제 로직이 올바르게 구현되었습니다.


37-42: LGTM!

전체 장바구니 비우기 로직이 올바르게 구현되었습니다.


44-52: LGTM!

수량 업데이트 로직이 올바르게 구현되었습니다.

app/src/main/java/com/sampoom/android/feature/outbound/domain/usecase/ProcessOutboundUseCase.kt (1)

6-10: LGTM!

출고 처리 UseCase가 올바르게 구현되었습니다. 리포지토리에 직접 위임하는 간단한 구조입니다.

app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListUiState.kt (1)

5-15: LGTM!

출고 목록 UI 상태가 잘 정의되었습니다. 로딩, 에러, 선택, 업데이트, 삭제 등 필요한 모든 상태를 포함하고 있습니다.

app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiEvent.kt (1)

3-12: LGTM!

장바구니 UI 이벤트가 잘 정의되었습니다. 필요한 모든 사용자 액션과 에러 처리 이벤트가 포함되어 있습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailUiEvent.kt (1)

5-14: LGTM!

부품 상세 UI 이벤트가 잘 정의되었습니다. 초기화, 수량 조정, 출고/장바구니 추가 등 필요한 모든 이벤트가 포함되어 있습니다.

app/src/main/java/com/sampoom/android/feature/outbound/di/OutboundModules.kt (2)

15-20: LGTM!

출고 리포지토리 바인딩이 올바르게 구현되었습니다. Hilt의 @Binds 패턴을 적절히 사용했습니다.


22-27: LGTM!

출고 API 제공이 올바르게 구현되었습니다. Retrofit API 인스턴스를 적절히 제공합니다.

app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (4)

16-18: 하드코딩된 agency ID를 확인하세요.

모든 엔드포인트에서 agency/1로 agency ID가 하드코딩되어 있습니다. 현재 단일 대리점만 지원하는 것이 의도된 동작인지, 향후 여러 대리점을 지원할 계획이 있는지 확인해주세요. 다중 대리점 지원이 필요하다면 agency ID를 파라미터로 받도록 리팩토링을 고려해야 합니다.


20-22: LGTM!

장바구니 추가 엔드포인트가 올바르게 정의되었습니다.


24-26: LGTM!

장바구니 항목 삭제 엔드포인트가 올바르게 정의되었습니다.


28-33: LGTM!

장바구니 수량 변경 엔드포인트가 올바르게 정의되었습니다.

app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (1)

25-26: 라우팅 전환 및 화면 바인딩 LGTM

ROUTE_OUTBOUND 도입과 OutboundListScreen/CartListScreen 연계가 일관됩니다. 내비게이션 동작 측면에서 문제 없어 보입니다.

Also applies to: 35-37, 135-137

app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (5)

11-13: 의존성 주입이 올바르게 구현되었습니다.

생성자에서 @Inject 애노테이션을 사용하여 의존성 주입이 적절하게 설정되었습니다.


20-25: 에러 처리가 적절히 구현되었습니다.

runCatchingdto.success 체크를 통한 에러 처리 패턴이 올바르게 적용되었습니다.


27-35: 에러 처리가 적절히 구현되었습니다.

일관된 에러 처리 패턴이 잘 적용되었습니다.


37-49: 에러 처리가 적절히 구현되었습니다.

deleteOutbound()deleteAllOutbound() 메서드 모두 일관된 에러 처리 패턴을 따르고 있습니다.


51-59: 에러 처리가 적절히 구현되었습니다.

일관된 에러 처리 패턴이 잘 적용되었습니다.

Comment thread app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt Outdated
Comment thread app/src/main/java/com/sampoom/android/feature/cart/di/CartModules.kt Outdated
Comment thread app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt Outdated
Comment thread app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt Outdated
Comment thread app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt Outdated
Copy link
Copy Markdown
Member

@CHOOSLA CHOOSLA 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
Copy Markdown

@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.

Wow

Copy link
Copy Markdown

@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

♻️ Duplicate comments (1)
app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt (1)

22-24: TAG 상수 오타 미수정

클래스명은 PartDetailViewModel인데 TAG 값이 "OutboundDetailViewModel"로 설정되어 있습니다. 로그 추적 시 혼란을 야기할 수 있습니다.

다음과 같이 수정하세요:

 private companion object {
-    private const val TAG = "OutboundDetailViewModel"
+    private const val TAG = "PartDetailViewModel"
 }
🧹 Nitpick comments (2)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1)

357-380: 주석 처리된 코드 정리 고려

OutlinedTextField 관련 코드가 대량으로 주석 처리되어 있습니다. 향후 사용 계획이 없다면 제거하여 코드 가독성을 높이는 것을 권장합니다. 재사용 가능성이 있다면 현재 상태로 유지해도 무방합니다.

app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1)

82-82: 프로덕션 로그 제거 권장

전체 상태를 로깅하는 Log.d는 디버깅에는 유용하지만, 프로덕션 빌드에서는 성능과 보안을 위해 제거하거나 디버그 플래그 뒤에 두는 것을 권장합니다. (이 코멘트는 lines 105, 127, 170, 192에도 동일하게 적용됩니다)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72c28ce and 324dfda.

📒 Files selected for processing (12)
  • app/src/main/java/com/sampoom/android/feature/auth/ui/LoginViewModel.kt (2 hunks)
  • app/src/main/java/com/sampoom/android/feature/auth/ui/SignUpViewModel.kt (2 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/di/CartModules.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/AddCartUseCase.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt (1 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt (3 hunks)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt
  • app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt
  • app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt
  • app/src/main/java/com/sampoom/android/feature/cart/di/CartModules.kt
🧰 Additional context used
🧬 Code graph analysis (3)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (4)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (4)
  • updateQuantity (109-129)
  • updateLocalQuantity (131-150)
  • removeFromLocalList (196-215)
  • removeAllFromLocalList (217-221)
app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (2)
  • deleteCart (25-26)
  • deleteAllCart (36-37)
app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt (2)
  • deleteCart (30-35)
  • deleteAllCart (37-42)
app/src/main/java/com/sampoom/android/feature/cart/domain/repository/CartRepository.kt (2)
  • deleteCart (8-8)
  • deleteAllCart (9-9)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
  • textColor (256-257)
  • backgroundCardColor (253-254)
  • textSecondaryColor (259-260)
app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
  • ErrorContent (18-37)
app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
  • EmptyContent (9-20)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
  • CommonButton (49-176)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1)
  • onEvent (43-54)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (4)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (4)
  • updateQuantity (107-127)
  • updateLocalQuantity (129-148)
  • removeFromLocalList (194-213)
  • removeAllFromLocalList (215-219)
app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt (2)
  • deleteOutbound (29-30)
  • deleteAllOutbound (40-41)
app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (2)
  • deleteOutbound (37-42)
  • deleteAllOutbound (44-49)
app/src/main/java/com/sampoom/android/feature/outbound/domain/repository/OutboundRepository.kt (2)
  • deleteOutbound (9-9)
  • deleteAllOutbound (10-10)
🔇 Additional comments (20)
app/src/main/java/com/sampoom/android/feature/auth/ui/SignUpViewModel.kt (2)

24-26: 로깅 일관성 개선을 위한 TAG 상수 도입

로깅 태그를 상수로 추출하여 유지보수성과 일관성을 높였습니다. 다른 ViewModel들과 동일한 패턴을 따르고 있어 좋습니다.


155-155: TAG 상수 사용으로 로깅 표준화

하드코딩된 문자열 대신 TAG 상수를 사용하여 로깅이 표준화되었습니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (1)

24-26: 로깅 일관성 개선을 위한 TAG 상수 도입

로깅 태그를 상수로 추출하여 유지보수성과 일관성을 높였습니다.

app/src/main/java/com/sampoom/android/feature/auth/ui/LoginViewModel.kt (2)

24-26: 로깅 일관성 개선을 위한 TAG 상수 도입

로깅 태그를 상수로 추출하여 유지보수성과 일관성을 높였습니다. 다른 ViewModel들과 동일한 패턴을 따르고 있어 좋습니다.


91-91: TAG 상수 사용으로 로깅 표준화

하드코딩된 문자열 대신 TAG 상수를 사용하여 로깅이 표준화되었습니다.

app/src/main/java/com/sampoom/android/feature/cart/domain/usecase/AddCartUseCase.kt (2)

4-4: import 수정 완료!

이전 리뷰에서 지적된 jakarta.inject.Inject 문제가 javax.inject.Inject로 올바르게 수정되었습니다. Hilt DI와 호환되는 올바른 import입니다.


6-10: LGTM!

UseCase 구현이 깔끔하고 표준 패턴을 따릅니다. Repository로의 단순 위임 구조가 적절합니다.

app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailViewModel.kt (4)

14-14: import 수정 완료!

이전 리뷰에서 지적된 jakarta.inject.Inject 문제가 javax.inject.Inject로 올바르게 수정되어 Hilt DI가 정상 작동합니다.


53-56: 수량 하한 처리 수정 완료!

이전 리뷰에서 지적된 수량이 0 이하로 내려가는 문제가 maxOf(1L, currentQuantity - 1)로 올바르게 수정되었습니다.


93-105: Result 처리 개선 완료!

이전 리뷰에서 지적된 runCatching 래핑 문제가 해결되었습니다. 이제 UseCase가 반환하는 Result<Unit>를 직접 처리하여 성공/실패를 정확하게 구분합니다.


114-126: Result 처리 개선 완료!

AddCartUseCase 호출도 동일하게 Result를 직접 처리하도록 개선되어 실패 케이스를 올바르게 감지합니다.

app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (3)

154-167: 삭제 실패 처리 수정 완료!

이전 리뷰에서 지적된 문제가 해결되었습니다. 삭제 실패 시 isDeleting=false, deleteError를 올바르게 설정하여 UI 상태를 정확히 반영합니다.


176-189: 전체 비우기 실패 처리 수정 완료!

deleteAllCart 실패 처리도 동일하게 isDeleting=false, deleteError를 올바르게 설정하도록 수정되었습니다.


84-105: TODO: 주문 생성 로직 구현 대기 중

processOrder 함수가 의도적으로 미구현 상태로 주석 처리되어 있습니다. 구현 시 주석된 패턴을 참고하여 cartLoading 상태를 올바르게 관리하고, 성공 시 loadCartList()를 호출하여 목록을 갱신하도록 구현하세요.

app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (2)

206-212: 다이얼로그 상태 처리 수정 완료!

이전 리뷰에서 지적된 취소 버튼의 상태 변경 오류가 수정되었습니다. 이제 showEmptyCartDialog = false로 올바른 다이얼로그 상태를 변경합니다.


50-239: LGTM!

CartListScreen의 전체 구조가 잘 구성되어 있습니다:

  • ViewModel 연동 및 상태 관리가 적절함
  • 로딩/에러/빈 목록 상태 처리가 명확함
  • 다이얼로그 흐름이 올바르게 구현됨
  • UI 이벤트 처리가 일관성 있게 구현됨
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (4)

17-17: javax.inject.Inject 사용 확인 완료

이전 리뷰에서 지적된 jakarta.inject.Inject 문제가 수정되어 Hilt와 호환되는 javax.inject.Inject를 올바르게 사용하고 있습니다.


109-150: 수량 업데이트 로직 확인 완료

updateQuantityupdateLocalQuantity 함수는 상태 관리와 불변 업데이트 패턴을 올바르게 구현하고 있으며, CartListViewModel과 일관된 패턴을 따르고 있습니다.


152-194: 삭제 작업 상태 관리 수정 확인

이전 리뷰에서 지적된 삭제 실패 시 잘못된 상태 필드 사용 문제(isUpdating/updateError 대신 isDeleting/deleteError)가 올바르게 수정되었습니다. deleteOutbounddeleteAllOutbound 모두 적절한 상태 필드를 사용하고 있습니다.


196-221: 로컬 목록 제거 로직 확인 완료

removeFromLocalListremoveAllFromLocalList 함수는 CartListViewModel과 동일한 패턴을 따르고 있으며, 불변성을 유지하면서 올바르게 구현되어 있습니다. 빈 그룹과 카테고리를 정리하는 로직도 적절합니다.

Comment thread app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt Outdated
Copy link
Copy Markdown

@taemin3 taemin3 left a comment

Choose a reason for hiding this comment

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

확인했습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge 3명 이상의 리뷰어에게 승인되어 병합 준비가 완료된 PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants