Conversation
- 펙토리 메서드에 파라미터를 추가하여 커스텀 가능하도록 변경
- 제보하기 화면의 UI 및 기본 로직을 구현합니다.
- 현재 위치를 기반으로 도로명 주소를 조회하는 UseCase를 추가합니다. - 주소 관련 기능을 위한 AddressRepository 인터페이스를 정의합니다.
- PresignedUrl 업로드를 위한 ImageUploader 구현 - Qualifier 선언 모듈 변경 (app -> network)
[Feature/#141] 제보하기 화면 UI를 구현합니다.
[Feature/#143] 현재 위치 기반 주소 조회 기능을 구현합니다.
- 이미지 파일 목록을 받아 Presigned URL을 요청합니다. - 발급받은 Presigned URL을 사용하여 S3에 이미지를 업로드합니다. - 업로드 성공 시, 이미지의 key 목록을 반환합니다.
- ReportCategory enum class 정의
- 제보하기 API service를 추가합니다. - 제보하기 관련 data, domain 레이어 모듈(DataSource, Repository, UseCase)을 추가합니다. - 제보하기 관련 DI Module을 추가합니다.
- ReportCategory -> ReportCategoryUi
[Feature/#145] 제보 제출 로직을 구현합니다..
- ReportState, ReportSideEffect, SubmitState를 model 패키지로 분리 - 제보 제출(submitReportWithImages) 시, 최소 1초의 딜레이 보장
- 제보 제목 입력 후 '완료'를 누르면 카테고리 선택 창이 나타나도록 수정 - 카테고리 선택 후 제보 내용 입력 필드에 자동으로 포커스가 가도록 수정 - 제보 내용 입력 후 '완료'를 누르면 키보드가 내려가도록 수정
[UI/#148] 제보하기 로딩 및 완료 화면을 구현합니다.
Walkthrough제보 기능 전체를 추가했습니다: 권한 처리(카메라/위치), 이미지 캡처·변환·업로드, 좌표→주소 조회(Kakao API), 제보 제출 API 연동, 네비게이션(Report 라우트) 및 여러 UI 컴포넌트와 ViewModel/UseCase/Repository/DataSource/Service 바인딩을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as ReportScreen
participant VM as ReportViewModel
participant UC as UseCases
participant Repo as Repository
participant DS as DataSource
participant API as RemoteService
participant Storage as S3/Presigned
User->>UI: 이미지 선택/카메라 실행
UI->>VM: addImages(uris)
VM-->>VM: state 업데이트
User->>UI: 위치 조회 요청
UI->>VM: fetchCurrentAddress()
VM->>UC: FetchCurrentAddressUseCase.invoke()
UC->>Repo: fetchCurrentLocation(), fetchCurrentAddress(lng,lat)
Repo->>DS: LocationDataSource / AddressDataSource
DS->>API: FusedLocationProviderClient / Kakao Address API
API-->>DS: 위치/주소 응답
DS-->>Repo: Result<String/Location>
Repo-->>UC: Result<CurrentAddress>
UC-->>VM: Result -> state 업데이트
User->>UI: 제보 제출
UI->>VM: submitReportWithImages()
VM->>UC: UploadReportImagesUseCase (이미지 DTO 생성)
UC->>Repo: uploadImages -> FileDataSource.fetchPresignedUrls()
Repo->>API: FileService POST /presigned-urls
API-->>Repo: Map<key,url>
par 병렬 업로드
VM->>Storage: PUT image -> presigned URL (ImageUploader)
VM->>Storage: ... (동시)
end
Storage-->>VM: 200 OK
VM->>UC: SubmitReportUseCase(reportDto)
UC->>Repo: submitReport
Repo->>API: ReportService POST /reports
API-->>Repo: BaseResponse<Long>
Repo-->>UC: Result<Long>
UC-->>VM: Result -> submitState 업데이트
VM->>UI: 완료 상태 표시 / navigate back
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 검토 시 추가 주의 항목:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🔇 Additional comments (1)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt (1)
21-76:road_addressnull 응답 처리 누락으로 디코딩 예외 발생
카카오coord2addressAPI는 좌표에 도로명주소가 없으면road_address필드 자체를null로 내려보냅니다.(devtalk.kakao.com) 현재 모델이roadAddress: RoadAddress로 고정돼 있어 해당 케이스에서JsonDecodingException이 발생하며, 확장 함수toAddress()도 그 상황을 전혀 다루지 못합니다.roadAddress를 nullable로 바꾸고, 매핑 시 지번 주소로 안전하게 폴백하도록 수정해 주세요.@@ -data class Document( - @SerialName("road_address") - val roadAddress: RoadAddress, +data class Document( + @SerialName("road_address") + val roadAddress: RoadAddress?, @@ -fun Coord2AddressResponse.toAddress(): String? { - return this.documents - .firstOrNull() - ?.roadAddress - ?.addressName -} +fun Coord2AddressResponse.toAddress(): String? { + val firstDocument = documents.firstOrNull() ?: return null + return firstDocument.roadAddress?.addressName + ?: firstDocument.address.addressName +}
Address도 nullable일 수 있다면 동일한 패턴을 적용해주면 더욱 안전합니다.
🧹 Nitpick comments (19)
core/designsystem/src/main/res/drawable/icon_down_arrow_black.xml (1)
1-16: 파일명 일관성 검토 필요: 다른 아이콘과 프리픽스 불일치현재 파일명
icon_down_arrow_black.xml은icon_프리픽스를 사용하고 있으나, 같은 디렉토리의 다른 모든 드로어블 아이콘은ic_프리픽스를 따르고 있습니다. (예: ic_water.xml, ic_camera.xml, ic_car.xml 등)네이밍 컨벤션을 일관성 있게 맞추는 것을 권장합니다.
ic_down_arrow_black.xml로 변경을 고려해주세요.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/PhotoItem.kt (2)
32-39: 접근성을 위한 contentDescription 추가를 권장합니다.이미지의
contentDescription이null로 설정되어 있습니다. 스크린 리더 사용자를 위해 "제보 사진" 등의 설명을 추가하는 것이 좋습니다. 또한, AsyncImage의 에러 상태나 로딩 상태에 대한 처리를 추가하면 사용자 경험이 개선됩니다.다음과 같이 개선할 수 있습니다:
AsyncImage( model = uri, - contentDescription = null, + contentDescription = "제보 사진", contentScale = ContentScale.Crop, + error = painterResource(id = R.drawable.ic_error_placeholder), // 에러 시 표시할 이미지 modifier = Modifier .fillMaxSize() .clip(RoundedCornerShape(8.dp)), )
56-57: Preview에서 실제 표시 가능한 이미지 사용을 권장합니다.빈 문자열로 생성된 Uri는 Preview에서 실제 이미지를 표시하지 못합니다. Preview의 유용성을 높이기 위해 drawable 리소스나 placeholder를 사용하는 것을 고려해보세요.
다음과 같이 개선할 수 있습니다:
@Preview @Composable private fun Preview() { PhotoItem( - uri = "".toUri(), + uri = "android.resource://com.threegap.bitnagil/drawable/sample_image".toUri(), modifier = Modifier.background(color = BitnagilTheme.colors.orange50), onRemove = {}, ) }presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportField.kt (2)
14-41: 재사용성 향상을 위해 선택적 필드 지원을 고려해보세요.현재 모든 필드에 필수 표시(*)가 항상 표시됩니다. 제보 기능에서 모든 필드가 필수라면 문제없지만, 향후 선택적 필드가 추가될 가능성을 고려하면 파라미터로 제어할 수 있으면 좋습니다.
다음과 같이
isRequired파라미터를 추가할 수 있습니다:@Composable fun ReportField( title: String, modifier: Modifier = Modifier, + isRequired: Boolean = true, content: @Composable () -> Unit, ) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(2.dp), ) { Text( text = title, color = BitnagilTheme.colors.coolGray10, style = BitnagilTheme.typography.body2SemiBold, ) + if (isRequired) { Text( text = "*", color = BitnagilTheme.colors.error10, style = BitnagilTheme.typography.body1SemiBold, ) + } } content() } }
22-22: 디자인 시스템 토큰 사용을 고려해보세요.spacing 값(8.dp, 2.dp)이 하드코딩되어 있습니다. 디자인 시스템에 spacing 토큰이 있다면 일관성을 위해 사용하는 것이 좋습니다.
예시:
verticalArrangement = Arrangement.spacedBy(BitnagilTheme.spacing.small), // 또는 horizontalArrangement = Arrangement.spacedBy(BitnagilTheme.spacing.xSmall),Also applies to: 26-26
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextField.kt (1)
46-53:modifier.fillMaxWidth()순서 조정이 필요합니다.현재
modifier = modifier.fillMaxWidth()구조라 호출 측에서Modifier.width(...)등을 전달해도 마지막에fillMaxWidth()가 덮어써 폭이 항상 확장됩니다. 디자인 시스템 컴포넌트라면 기본값으로 전폭을 채우되, 필요 시 호출자가 다른 크기를 지정할 수 있어야 합니다.Modifier.fillMaxWidth()를 먼저 적용한 뒤then(modifier)로 이어 붙이면 기본 동작은 유지하면서도 확장성을 확보할 수 있습니다.- modifier = modifier - .fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .then(modifier) .background( color = BitnagilTheme.colors.coolGray99, shape = RoundedCornerShape(12.dp), )data/src/main/java/com/threegap/bitnagil/data/file/model/request/FileInfoRequestDto.kt (1)
1-12: 파일 정보 DTO 정의가 직관적입니다
prefix,fileName두 필드만으로 presigned URL 요청에 필요한 정보가 잘 표현되고 있고,@Serializable설정도 적절합니다. 현재 JSON 키와 프로퍼티명이 동일하므로@SerialName은 없어도 동작하겠지만, 서버 스펙 변경에 대비한 명시적 매핑 용도라면 유지해도 괜찮아 보입니다.data/src/main/java/com/threegap/bitnagil/data/file/datasource/FileDataSource.kt (1)
1-12: presigned URL 조회 인터페이스는 명확합니다 (타입 래핑은 선택 사항)KDoc으로 계약이 잘 문서화되어 있고,
Result<Map<String, String>>형태도 간단하게 쓰기 좋습니다. 다만key: S3 경로, value: presigned URL의미가 고정이라면, 추후를 위해 별도 타입(예:PresignedUrlMap타입 alias 또는 data class)으로 한 번 래핑해 두면 오용을 줄이는 데 도움이 될 수 있습니다. 지금 단계에서는 선택 사항입니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategorySelector.kt (1)
53-60: Preview에서 선택된 상태도 보여주면 좋을 것 같습니다.현재 Preview는 기본 텍스트("카테고리 선택")를 사용하고 있어 선택되지 않은 상태만 보여줍니다. 실제 카테고리가 선택된 상태의 Preview도 추가하면 컴포넌트의 두 가지 상태를 모두 확인할 수 있습니다.
다음과 같이 두 가지 상태의 Preview를 추가할 수 있습니다:
@Preview @Composable private fun PreviewUnselected() { ReportCategorySelector( title = null, onClick = {}, ) } @Preview @Composable private fun PreviewSelected() { ReportCategorySelector( title = "보도블록 파손", onClick = {}, ) }domain/src/main/java/com/threegap/bitnagil/domain/report/model/ReportCategory.kt (1)
3-29: 문자열 매핑이 enum 이름과 완전히 동일해 다소 중복 코드입니다.백엔드에서 사용하는 문자열이 enum 이름과 정확히 동일하다면,
fromString→enumValueOf<ReportCategory>(value)toString→value.name형태로 단순화해서 중복을 줄이는 것도 고려해 볼 수 있습니다.
반대로, 서버/클라이언트 간 매핑 규칙이 나중에 달라질 가능성을 염두에 두고 현재처럼 명시적으로 유지하고 싶다면 이 구현도 충분히 괜찮습니다. 그 경우에는 enum 항목을 추가/변경할 때 companion 의 when 분기도 반드시 함께 갱신해 주어야 합니다.
data/src/main/java/com/threegap/bitnagil/data/address/datasourceImpl/LocationDataSourceImpl.kt (1)
14-22: 위치 조회를 Result 로 감싼 구조는 좋지만, 예외 타입을 구체화하면 더 다루기 쉬울 것 같습니다.
lastLocation.await()결과가 null 인 경우에 단순Exception("Location not available")을 던지고 있는데,
- 별도의 도메인 예외 타입 (예:
LocationNotAvailableException)- 혹은
IllegalStateException등 보다 의미가 드러나는 표준 예외로 구분해 두면 상위 레이어에서
isFailure인 경우를 처리할 때, 어떤 실패인지 분기하기가 더 수월해집니다. 현재 구현도 동작에는 문제 없지만, 예외 타입을 명확히 해두면 이후 에러 핸들링 확장에 도움이 될 것 같습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CurrentLocationInput.kt (1)
29-67: Row 의 modifier 를 Box 에 재사용하면 레이아웃이 의도치 않게 깨질 수 있습니다.현재
Row에 전달된modifier를Box에도 그대로 넘기고 있어서, 호출부에서 예를 들어Modifier.fillMaxWidth()를 넘기면 Row 전체뿐 아니라 오른쪽 아이콘 영역 Box 에도 동일하게 적용됩니다. 이 경우 Row 내부에서 아이콘 영역이 과도하게 넓어지거나, 의도치 않은 패딩/사이즈가 중복 적용되는 등 레이아웃 부작용이 생길 수 있습니다.Box 쪽은 독립적인 레이아웃을 가지는 게 자연스러워 보이므로, 아래처럼
Modifier로 시작하는 편이 안전합니다.- Box( - modifier = modifier + Box( + modifier = Modifier .background( color = BitnagilTheme.colors.orange500, shape = RoundedCornerShape(12.dp), ) .fillMaxHeight() .padding(14.dp) .clickableWithoutRipple( onClick = { onClick() }, ),또한 이 Box 가 "현재 위치 검색" 액션을 수행하는 버튼 역할을 하기 때문에, 추후에라도
semantics { contentDescription = "현재 위치 검색" }와 같은 접근성 정보를 추가해 두면 스크린 리더 사용자 입장에서도 의미 파악이 더 쉬워질 것입니다.domain/src/main/java/com/threegap/bitnagil/domain/address/repository/AddressRepository.kt (1)
5-7: 주소 반환 타입 확장 여지도메인 레벨에서 주소를
String으로만 노출하면 추후 도로명/지번/행정동 코드 등 메타데이터가 늘어날 때 확장성이 떨어질 수 있습니다. 지금은 충분하지만, 나중에 요구사항이 늘어나면 별도의AddressVO를 도입하는 것도 고려해보셔도 좋겠습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CompleteReportCard.kt (1)
96-114: 이미지 목록이 비어 있을 때의 UX 보완 제안카테고리/주소는
"... 없음"문구로 처리해 주셨는데, 이미지가 없을 때는 빈LazyRow만 보여서 사용자가 “정상으로 불러온 건가?” 헷갈릴 수 있습니다.
images.isEmpty()인 경우"제보 사진 없음"같은 텍스트를 보여주고, 있을 때만LazyRow를 노출하는 식으로 분기하면 일관성이 더 좋아질 것 같습니다.data/src/main/java/com/threegap/bitnagil/data/file/uploader/ImageUploader.kt (1)
18-38: 전체 업로드 처리 흐름은 안정적으로 보입니다
- IO 디스패처 전환,
execute().use {}로 응답 자원 정리까지 모두 적절합니다.- 다만 실패 시
IOException("Upload failed: code")만 던져 상위 레이어에서는 오류 원인을 파악하기 어렵습니다. 필요하다면errorBody일부를 예외 메시지에 포함하거나, 공통 TAG 상수로Log태그를 정리해 디버깅을 조금 더 쉽게 만드는 것도 고려해 볼 만합니다.app/src/main/AndroidManifest.xml (1)
6-10: 카메라 필수 선언(android:required="true") 의도가 맞는지 한 번만 확인해주세요
- 권한 추가(CAMERA, 위치)와
FileProvider설정 자체는 표준 패턴이라 문제 없어 보입니다.- 다만
uses-feature android.hardware.camera에android:required="true"를 주면 카메라가 없는 기기(일부 태블릿/Chromebook 등)에는 아예 설치가 불가능합니다.
앱이 그런 기기를 전부 지원 대상에서 배제해도 된다면 괜찮지만, 설치는 허용하되 카메라 기능만 비활성화하고 싶다면required="false"로 두고 런타임에서 카메라 사용 가능 여부를 체크하는 방식도 고려할 수 있습니다.Also applies to: 50-58
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/CompleteReportContent.kt (1)
30-105: 구현 자체는 깔끔하지만, 텍스트 리소스 분리와 버튼 속성 정리가 더 좋을 수 있습니다
- 전체 레이아웃/스크롤 구조,
CompleteReportCard에uiState를 그대로 넘기는 흐름은 자연스럽습니다.- 다만
"제보가 완료되었습니다.","빛나길에서 접수 후 완료되면\n신고를 진행합니다.","확인"등의 하드코딩된 문자열은stringResource+R.string.*로 분리해 두면 추후 카피 수정이나 다국어 지원 시 훨씬 관리가 수월합니다.BitnagilTextButton에서enabled = true로 고정인데disabledBackgroundColor/disabledTextColor만 커스터마이즈되어 있어 현재로서는 효과가 없습니다. 이후 상태 연동 계획이 없다면 필요할 때 추가하는 쪽으로 단순화해도 좋습니다.data/src/main/java/com/threegap/bitnagil/data/report/model/request/ReportRequestDto.kt (1)
8-35: 도메인 → DTO 매핑 구조가 명확하고 문제 없어 보입니다
ReportRequestDto필드 구성과Report.toDto()의 매핑이 직관적이고, 서버 계약만 맞다면 별다른 리스크는 없어 보입니다.- 코드를 조금 더 간결하게 하고 싶다면 아래처럼 표현식 본문으로 정리하는 정도만 선택 사항으로 고려해 볼 수 있습니다.
fun Report.toDto() = ReportRequestDto( reportTitle = title, reportContent = content, reportCategory = ReportCategory.toString(category), reportImageUrls = imageUrls, reportLocation = address, latitude = latitude, longitude = longitude, )data/src/main/java/com/threegap/bitnagil/data/address/repositoryImpl/AddressRepositoryImpl.kt (1)
15-24: 위임 구조는 적절하며, 주소 없음 케이스만 명시적으로 구분하면 더 좋습니다
LocationDataSource/AddressDataSource에 그대로 위임하고, 응답을Result로 감싸는 구조는 깔끔합니다.toAddress()가 null을 반환할 때 일반Exception("Address not found")을 던지면 상위 레이어에서 다른 네트워크 오류와 구분하기 어려울 수 있습니다. 필요하다면AddressNotFoundException같은 도메인 전용 예외나,Result.failure(DomainError.AddressNotFound)형태로 구분 가능한 에러 타입을 도입하는 것도 고려해 볼 만합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (82)
app/build.gradle.kts(1 hunks)app/src/main/AndroidManifest.xml(2 hunks)app/src/main/java/com/threegap/bitnagil/MainNavHost.kt(3 hunks)app/src/main/java/com/threegap/bitnagil/Route.kt(1 hunks)app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt(3 hunks)app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt(2 hunks)app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt(2 hunks)app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt(2 hunks)app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt(2 hunks)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilFloatingButton.kt(3 hunks)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt(1 hunks)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextField.kt(1 hunks)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.kt(5 hunks)core/designsystem/src/main/res/drawable/ic_btn_close_sm.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_camera.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_camera_gray.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_car.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_complaint.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_hammer.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_light.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_loading.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_location.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_outside_gray.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_report.xml(0 hunks)core/designsystem/src/main/res/drawable/ic_water.xml(1 hunks)core/designsystem/src/main/res/drawable/icon_down_arrow_black.xml(1 hunks)core/network/build.gradle.kts(1 hunks)core/network/src/main/java/com/threegap/bitnagil/network/Qualifier.kt(2 hunks)data/build.gradle.kts(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/datasource/AddressDataSource.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/datasource/LocationDataSource.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/datasourceImpl/AddressDataSourceImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/datasourceImpl/LocationDataSourceImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/di/LocationModule.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/model/response/Coord2AddressResponse.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/repositoryImpl/AddressRepositoryImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/address/service/AddressService.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/datasource/FileDataSource.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/datasourceImpl/FileDataSourceImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/model/request/FileInfoRequestDto.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/repositoryImpl/FileRepositoryImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/service/FileService.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/file/uploader/ImageUploader.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/report/model/request/ReportRequestDto.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt(1 hunks)data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/address/model/CurrentAddress.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/address/model/CurrentLocation.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/address/repository/AddressRepository.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/address/usecase/FetchCurrentAddressUseCase.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/file/model/ImageFile.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/file/repository/FileRepository.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/file/usecase/UploadReportImagesUseCase.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/report/model/Report.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/report/model/ReportCategory.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/report/usecase/SubmitReportUseCase.kt(1 hunks)gradle/libs.versions.toml(3 hunks)presentation/build.gradle.kts(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/file/ImageFileConverter.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/premission/PermissionHandler.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/premission/RememberPermissionHandler.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportViewModel.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/AddPhotoButton.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CompleteReportCard.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CurrentLocationInput.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ImageSourceBottomSheet.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/PhotoItem.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategoryBottomSheet.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategorySelector.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportField.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/CompleteReportContent.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/SubmittingReportContent.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/ReportCategoryExtension.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/ReportSideEffect.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/ReportState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/SubmitState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt(2 hunks)presentation/src/main/res/xml/file_paths.xml(1 hunks)
💤 Files with no reviewable changes (1)
- core/designsystem/src/main/res/drawable/ic_report.xml
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-03T09:05:30.067Z
Learnt from: wjdrjs00
Repo: YAPP-Github/Bitnagil-Android PR: 16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
Applied to files:
app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt
📚 Learning: 2025-07-21T10:38:49.104Z
Learnt from: l5x5l
Repo: YAPP-Github/Bitnagil-Android PR: 38
File: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt:30-35
Timestamp: 2025-07-21T10:38:49.104Z
Learning: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt의 TextButton 컴포넌트는 임시로 구현된 컴포넌트로, 디자인 시스템 구현시 대체 예정입니다.
Applied to files:
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilFloatingButton.ktpresentation/src/main/java/com/threegap/bitnagil/presentation/report/component/AddPhotoButton.ktcore/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextField.ktcore/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.ktcore/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt
🧬 Code graph analysis (16)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategorySelector.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/SubmittingReportContent.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ImageSourceBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt (1)
data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1)
safeApiCall(10-25)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt (14)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportViewModel.kt (1)
navigateToBack(115-119)presentation/src/main/java/com/threegap/bitnagil/presentation/common/premission/RememberPermissionHandler.kt (1)
rememberPermissionHandler(20-92)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ImageSourceBottomSheet.kt (1)
ImageSourceBottomSheet(25-79)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategoryBottomSheet.kt (1)
ReportCategoryBottomSheet(32-78)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/SubmittingReportContent.kt (1)
SubmittingReportContent(21-58)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/CompleteReportContent.kt (1)
CompleteReportContent(30-105)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilTopBar.kt (1)
BitnagilTopBar(22-63)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportField.kt (1)
ReportField(14-41)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/AddPhotoButton.kt (1)
AddPhotoButton(22-62)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/PhotoItem.kt (1)
PhotoItem(23-51)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextField.kt (1)
BitnagilTextField(25-73)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategorySelector.kt (1)
ReportCategorySelector(21-51)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CurrentLocationInput.kt (1)
CurrentLocationInput(23-68)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
BitnagilTextButton(34-85)
app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt (1)
ReportScreenContainer(76-201)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CurrentLocationInput.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.kt (1)
BitnagilAlertDialog(23-88)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/PhotoItem.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIconButton(35-58)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/CompleteReportContent.kt (3)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/CompleteReportCard.kt (1)
CompleteReportCard(28-116)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
BitnagilTextButton(34-85)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/premission/RememberPermissionHandler.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.kt (1)
BitnagilAlertDialog(23-88)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategoryBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/AddPhotoButton.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportViewModel.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/file/ImageFileConverter.kt (1)
convertUriToImageFile(36-58)
data/src/main/java/com/threegap/bitnagil/data/file/datasourceImpl/FileDataSourceImpl.kt (1)
data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1)
safeApiCall(10-25)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
BitnagilTextButton(34-85)
🪛 detekt (1.23.8)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/file/ImageFileConverter.kt
[warning] 55-55: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
⏰ 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
🔇 Additional comments (58)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilFloatingButton.kt (3)
20-20: 필요한 import 추가로 적절합니다.
width()수정자 사용을 위한 필수 import로 문제없습니다.
104-106: 메뉴 컨테이너 패딩 변경 사항을 설계 사양과 확인하세요.외부 Column의 패딩이 수직(16.dp) 전용으로 변경되었습니다. 이는 가로 패딩을 개별 Row 항목 레벨로 이동시킵니다. 이 변경이 제보하기 기능의 FAB 메뉴 설계 사양과 일치하는지 확인하세요.
172-179: 고정 너비(112.dp)로 인한 텍스트 잘림 위험을 확인하세요.FloatingActionMenuItem Row에
padding(horizontal = 16.dp)와width(112.dp)가 추가되어 텍스트의 가용 폭이 약 42dp로 제한됩니다(icon 24dp + spacing 14dp 제외). body2Medium 스타일의 메뉴 텍스트가 이 너비 내에서 제대로 표시되는지, 특히 제보하기 기능의 실제 메뉴 항목으로 테스트하여 텍스트 잘림이나 줄바꿈 문제가 없는지 확인하세요.core/designsystem/src/main/res/drawable/ic_location.xml (1)
1-38: 잘 작성된 위치 핀 아이콘 자산입니다.XML 구조가 올바르고 경로 정의가 명확합니다. 투명 배경에 흰색 스트로크와 채우기를 사용하여 일관된 디자인 시스템 스타일을 따릅니다.
core/designsystem/src/main/res/drawable/ic_camera_gray.xml (1)
1-23: 회색 카메라 아이콘이 제대로 구성되어 있습니다.여러 경로의 계층화를 통해 아웃라인과 세부 정보를 표현합니다. 디자인 시스템의 회색 색상(#46474C)과 흰색 악센트를 일관되게 사용합니다.
core/designsystem/src/main/res/drawable/ic_outside_gray.xml (1)
1-33: 회색 아이콘 자산의 구조가 정확합니다.여러 경로를 계층화하여 세부 표현을 만듭니다. 스트로크 속성(lineWidth, lineCap, lineJoin)이 모두 올바르게 설정되어 있습니다.
core/designsystem/src/main/res/drawable/ic_light.xml (1)
1-29: 조명 아이콘이 잘 설계되어 있습니다.노란색(#FFDB12) 채우기와 회색 스트로크의 조합으로 명확한 시각적 계층을 만듭니다. 클립 경로 사용과 그룹 구조가 적절합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/SubmittingReportContent.kt (1)
21-58: 전반적인 구현이 깔끔합니다.Jetpack Compose 패턴을 잘 따르고 있으며, 테마를 적절히 활용하고 있습니다. 레이아웃 구조도 명확하고 유지보수하기 좋습니다.
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
98-112: 구현이 올바르며 실제 사용 사례가 확인되었습니다.코드베이스 검증 결과, 추가된 매개변수화된
default()시그니처가 다음 위치에서 적극적으로 활용되고 있습니다:
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/template/CompleteReportContent.kt(lines 95-98)presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt(lines 339-342)두 곳 모두 제보하기 기능에서 비활성화 상태의 색상을 커스터마이징하고 있으며, 나머지 매개변수는 기본값을 사용합니다. 또한 기존의 매개변수 없는
default()호출도 정상적으로 작동하여 후방 호환성이 완벽하게 유지됩니다.이 설계는 불필요한 일반화가 아니라 실제 요구사항을 충족하는 합리적인 구현입니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/PhotoItem.kt (1)
41-49: 코드는 의도된 디자인입니다. 수정이 필요하지 않습니다.검증 결과,
offset(x = 10.dp, y = (-10).dp)로 인해 닫기 버튼이 64dp Box 경계를 벗어나는 것은 의도된 디자인입니다. 이는 모서리 버튼의 일반적인 UI 패턴으로, 코드베이스의 다른 컴포넌트(EmotionLoadingView.kt)에서도 동일한 패턴(align(Alignment.TopEnd)+offset)이 사용되고 있습니다. 버튼이 이미지 모서리에 걸쳐서 표시되도록 설계된 것으로, 접근성과 시각적 배치를 고려한 설계 시스템의 일부입니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportField.kt (2)
1-12: 패키지 선언과 import 구문이 적절합니다.모든 import가 사용되고 있으며, 표준 Compose 라이브러리와 디자인 시스템에서 필요한 항목들만 포함되어 있습니다.
43-47: Preview 함수가 적절합니다.한글 텍스트로 실제 사용 사례를 잘 보여주고 있으며, private으로 적절히 캡슐화되어 있습니다.
domain/src/main/java/com/threegap/bitnagil/domain/address/model/CurrentAddress.kt (1)
3-7: LGTM!도메인 모델이 깔끔하게 구현되었습니다. 불변 속성으로 구성되어 있고 단순명료합니다.
domain/src/main/java/com/threegap/bitnagil/domain/address/model/CurrentLocation.kt (1)
3-6: LGTM!위치 정보를 담는 깔끔한 도메인 모델입니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ImageSourceBottomSheet.kt (3)
36-45: LGTM!바텀시트를 숨긴 후 액션을 실행하는 로직이 올바르게 구현되었습니다.
invokeOnCompletion을 사용하여 시트가 완전히 숨겨진 후 콜백을 실행하는 것이 좋습니다.
47-79: LGTM!바텀시트 UI가 깔끔하게 구현되었습니다. 디자인 시스템의 색상과 타이포그래피를 잘 활용하고 있습니다.
81-101: LGTM!옵션 아이템 컴포넌트가 재사용 가능하고 깔끔하게 구현되었습니다.
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilAlertDialog.kt (1)
25-88: LGTM!다이얼로그를 디자인 시스템으로 이동하고 모든 텍스트를 파라미터화하여 재사용 가능하도록 개선한 것이 좋습니다. 이제 다양한 상황에서 활용할 수 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/common/file/ImageFileConverter.kt (2)
15-34: LGTM!파일 이름 추출 로직이 견고하게 구현되었습니다. 콘텐츠 리졸버에서 이름을 가져올 수 없는 경우 타임스탬프와 UUID를 사용한 폴백 로직이 적절합니다.
60-64: LGTM!카메라 이미지 URI 생성 로직이 올바르게 구현되었습니다. 캐시 디렉토리를 사용하고 FileProvider를 통해 URI를 생성하는 것이 적절합니다.
data/build.gradle.kts (1)
21-22: LGTM!위치 기능 구현에 필요한 의존성이 적절하게 추가되었습니다.
play-services-location과kotlinx-coroutines-play는 위치 데이터 소스 구현에 필수적입니다.core/network/src/main/java/com/threegap/bitnagil/network/Qualifier.kt (1)
1-15: LGTM!패키지 경로가 올바르게 수정되었고, Kakao API 연동을 위한
@Kakao한정자가 적절하게 추가되었습니다. 기존 한정자들과 일관된 패턴을 따르고 있습니다.core/network/build.gradle.kts (1)
16-16: LGTM!DI 한정자 어노테이션 사용을 위해
javax.inject의존성이 적절하게 추가되었습니다.Qualifier.kt의@Qualifier어노테이션 사용에 필요한 의존성입니다.presentation/build.gradle.kts (1)
23-23: 변경사항 승인Accompanist Permissions 라이브러리 추가가 적절합니다. 권한 처리 UI 구현을 위한 표준 라이브러리입니다.
presentation/src/main/res/xml/file_paths.xml (1)
1-5: FileProvider 설정 확인카메라 이미지 캡처를 위한 FileProvider 경로 설정이 올바르게 구성되어 있습니다.
domain/src/main/java/com/threegap/bitnagil/domain/report/model/Report.kt (1)
3-11: 도메인 모델 검증 로직 고려Report 도메인 모델의 필드들에 대한 검증 요구사항을 확인하세요:
title,content: 길이 제한이나 빈 값 체크가 필요한가요?latitude,longitude: 유효한 좌표 범위(-9090, -180180) 검증이 필요한가요?imageUrls: 빈 리스트가 허용되나요?비즈니스 규칙에 따라 UseCase나 ViewModel 레벨에서 검증을 수행하거나, 도메인 모델에 팩토리 메서드를 추가하는 것을 고려하세요.
app/build.gradle.kts (1)
50-59: Kakao REST API 키 설정 확인기존
KAKAO_NATIVE_APP_KEY패턴과 일관되게 구현되어 있습니다. 환경 변수 및 local.properties 파일에서 키를 가져오는 로직이 적절합니다.app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt (1)
67-77: DI 바인딩 추가 확인세 개의 새로운 Repository 바인딩이 기존 패턴과 일관되게 추가되었습니다. Singleton 스코프 적용이 적절합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/SubmitState.kt (1)
3-3: 제출 상태 enum 확인제보 제출 라이프사이클을 나타내는 세 가지 상태가 적절하게 정의되어 있습니다.
app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (2)
46-46: 네비게이션 파라미터 추가 확인제보 화면으로의 네비게이션 콜백이 기존 패턴과 일관되게 추가되었습니다.
110-114: 제보하기 FAB 아이콘 리소스 확인 완료
ic_complaint아이콘 리소스가core/designsystem/src/main/res/drawable/ic_complaint.xml에 벡터 드로어블로 정상 정의되어 있으며, app 모듈이core:designsystem에 의존하고 있어R.drawable.ic_complaint참조가 컴파일 시 올바르게 해석됩니다.app/src/main/java/com/threegap/bitnagil/Route.kt (1)
52-53: Report Route 추가 확인제보 화면을 위한 새로운 Route가 기존 패턴과 일관되게 추가되었습니다. Serializable data object 사용이 적절합니다.
data/src/main/java/com/threegap/bitnagil/data/report/datasource/ReportDataSource.kt (1)
1-7: 제보 전송 데이터소스 설계 적절해 보입니다data 계층에서 DTO를 직접 받도록 분리한 구조와
Result<Long>반환 시그니처 모두 일관되고 명확합니다. 현재 수준에서는 추가 수정 없이 사용해도 될 것 같습니다.data/src/main/java/com/threegap/bitnagil/data/address/datasource/LocationDataSource.kt (1)
1-7: LocationDataSource 도메인 의존 방향이 좋습니다data 계층이지만 반환 타입으로
CurrentLocation도메인 모델을 사용하는 설계가 전체 레이어 구조와 잘 맞습니다.Result<CurrentLocation>형태도 이후 usecase에서 다루기 편해 보여요.domain/src/main/java/com/threegap/bitnagil/domain/file/usecase/UploadReportImagesUseCase.kt (1)
1-13: 이미지 업로드 usecase 위임 구조 괜찮습니다
operator fun invoke로 감싸서uploadReportImagesUseCase(imageFiles)형태로 사용할 수 있는 점이 가독성에 좋고, 책임도FileRepository로 잘 위임된 것 같습니다. 현재 요구사항 기준에서는 별도 로직 없이 이 정도 얇은 usecase로도 충분해 보여요.data/src/main/java/com/threegap/bitnagil/data/report/service/ReportService.kt (1)
1-13: 제보 API 시그니처가 계층 간 계약과 잘 맞습니다
ReportRequestDto를 바디로 받고BaseResponse<Long>을 반환하는 구조가ReportDataSource·ReportRepository에서 사용하는Result<Long>흐름과 자연스럽게 매핑될 수 있어 보입니다. 엔드포인트 경로(/api/v2/reports)도 의도가 분명해서 현재 PR 범위에서는 무리 없이 사용 가능해 보입니다.domain/src/main/java/com/threegap/bitnagil/domain/report/repository/ReportRepository.kt (1)
1-7: 도메인 ReportRepository 추상화가 깔끔합니다도메인 모델
Report를 인자로 받고Result<Long>을 반환하는 단일 책임 인터페이스로 잘 정의되어 있습니다. 이후 구현체(ReportRepositoryImpl)와 usecase에서 사용하기에 시그니처가 직관적이고 확장 여지도 충분해 보입니다.data/src/main/java/com/threegap/bitnagil/data/address/di/LocationModule.kt (1)
13-21: LGTM! 위치 서비스 DI 모듈 구현이 올바릅니다.FusedLocationProviderClient를 싱글톤으로 제공하는 Hilt 모듈이 표준 패턴을 따르고 있으며, ApplicationContext를 올바르게 사용하고 있습니다.
data/src/main/java/com/threegap/bitnagil/data/file/service/FileService.kt (1)
8-13: LGTM! Retrofit 서비스 인터페이스가 올바르게 정의되었습니다.Presigned URL을 가져오는 API 엔드포인트가 명확하게 정의되어 있으며, suspend 함수를 사용한 코루틴 지원이 적절합니다.
domain/src/main/java/com/threegap/bitnagil/domain/report/usecase/SubmitReportUseCase.kt (1)
7-13: LGTM! Use case 구현이 깔끔합니다.Repository에 위임하는 단순한 구조로, operator invoke 패턴을 사용하여 Kotlin 관용구를 잘 따르고 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
56-63: LGTM! 디자인 시스템 컴포넌트로의 리팩토링이 적절합니다.LogoutConfirmDialog에서 BitnagilAlertDialog로 교체하여 디자인 시스템 컴포넌트를 활용하는 것은 좋은 개선입니다. 모든 파라미터가 올바르게 전달되고 기존 로직이 유지됩니다.
domain/src/main/java/com/threegap/bitnagil/domain/file/repository/FileRepository.kt (1)
5-7: LGTM! 도메인 레이어 Repository 인터페이스가 명확합니다.Result 래퍼를 사용한 에러 처리와 suspend 함수를 통한 비동기 처리가 적절하며, 깔끔한 도메인 추상화를 제공합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/ReportSideEffect.kt (1)
3-6: LGTM! MVI 패턴의 Side Effect가 올바르게 정의되었습니다.Sealed interface와 data object를 사용한 side effect 모델링이 표준 MVI 패턴을 잘 따르고 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/common/premission/PermissionHandler.kt (1)
6-14: LGTM! 권한 처리 인터페이스가 잘 설계되었습니다.@stable 어노테이션과 Composable 함수를 포함한 인터페이스 설계가 Compose의 권한 처리 패턴을 잘 추상화하고 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategorySelector.kt (1)
21-51: LGTM! 카테고리 선택 컴포넌트가 올바르게 구현되었습니다.선택 여부에 따른 텍스트 색상 변경 로직이 명확하며, 디자인 시스템을 잘 활용하고 있습니다.
domain/src/main/java/com/threegap/bitnagil/domain/file/model/ImageFile.kt (1)
11-37: ByteArray 필드를 고려한 equals/hashCode 재정의가 적절합니다.
bytes에 대해contentEquals/contentHashCode를 사용해서 값 기반 비교가 되도록 잘 구현되어 있습니다. 도메인 값 객체로서 필요한 동등성 정의가 명확하고, 특별한 버그 포인트는 없어 보입니다.bytes가 mutable 이라 컬렉션의 key 로 사용한 뒤 내용을 변경하지 않도록만 주의하면 될 것 같습니다.app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (2)
96-144: HomeNavHost 에서 Report 화면으로의 네비게이션 연결이 기존 패턴과 잘 맞습니다.
navigateToReport가 다른 화면들과 동일하게navigator.navController.navigate(Route.Report) { launchSingleTop = true }형태로 구현되어 있고, Route 기반 네비게이션 구조와도 일관성이 있어서 유지보수 측면에서도 무난해 보입니다.
299-307: Report 경로 컴포저블 정의가 다른 화면들과 동일한 back 처리 패턴을 사용합니다.
composable<Route.Report>안에서ReportScreenContainer를 사용하고, 뒤로 가기 시previousBackStackEntry체크 후popBackStack()을 호출하는 패턴이 Setting/Emotion 등 다른 화면들과 동일해서, 네비게이션 동작이 예측 가능하고 안전합니다.data/src/main/java/com/threegap/bitnagil/data/report/datasourceImpl/ReportDataSourceImpl.kt (1)
9-14: 구현이 깔끔합니다.
safeApiCall을 사용하여 API 호출을 적절하게 래핑하고 있으며, 단순하고 명확한 위임 패턴을 따르고 있습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/ReportScreen.kt (1)
76-201: 전체적인 구조와 권한 처리가 잘 구현되어 있습니다.카메라/앨범/위치 권한 처리, 이미지 URI 상태 관리, 바텀시트 표시 로직이 모두 적절하게 구현되어 있습니다.
AnimatedContent를 사용한 제출 상태 전환도 사용자 경험을 향상시키는 좋은 접근입니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/AddPhotoButton.kt (1)
22-62: 깔끔한 구현입니다.이미지 카운트에 따른 활성화 상태 처리와 동적 색상 변경이 적절하게 구현되어 있습니다.
AnnotatedString을 사용한 카운트 표시도 좋습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/report/model/ReportCategoryExtension.kt (1)
6-28: 확장 프로퍼티를 사용한 UI 매핑이 적절합니다.모든
ReportCategory값에 대해 UI 문자열과 아이콘이 명확하게 매핑되어 있으며, exhaustive when 표현식으로 컴파일 타임 안전성이 보장됩니다.data/src/main/java/com/threegap/bitnagil/data/address/datasource/AddressDataSource.kt (1)
5-7: 인터페이스 정의가 명확합니다.좌표 타입으로
Double을 사용하고, 에러 처리를 위해Result타입을 반환하는 것이 적절합니다.data/src/main/java/com/threegap/bitnagil/data/report/repositoryImpl/ReportRepositoryImpl.kt (1)
9-15: 리포지토리 구현이 적절합니다.도메인 모델을 DTO로 변환하여 데이터 소스에 위임하는 패턴이 명확하게 구현되어 있습니다.
data/src/main/java/com/threegap/bitnagil/data/address/service/AddressService.kt (1)
8-12: 파라미터 매핑이 올바릅니다. 수정이 필요 없습니다.Kakao Local API 문서에 따르면 coord2address 엔드포인트는 x(경도), y(위도) 파라미터를 기대하며, 현재 코드의 매핑이 정확합니다. 예: x=126.9786567(경도), y=37.566826(위도)
app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt (1)
3-3: 새 Service DI 바인딩 구성 적절
AddressService에 Kakao용Retrofit,FileService/ReportService에 인증용Retrofit을 주입하는 패턴이 기존 Service 들과 동일해서 이해하기 쉽고, DI 그래프 상에서도 무리 없어 보입니다.Also applies to: 6-6, 9-9, 74-87
presentation/src/main/java/com/threegap/bitnagil/presentation/report/component/ReportCategoryBottomSheet.kt (1)
32-124: 카테고리 바텀시트 구현 전반적으로 안정적선택 시
onSelected호출 후 시트를 숨기고, 애니메이션 완료 시onDismiss를 호출하는 흐름이 명확해서 상태 관리가 깔끔합니다. 리스트/아이템 구성도ReportCategory확장 프로퍼티들을 잘 활용하고 있어 재사용성이 좋아 보입니다.domain/src/main/java/com/threegap/bitnagil/domain/address/usecase/FetchCurrentAddressUseCase.kt (1)
10-24: ****이 리뷰 의견은 잘못되었습니다. Result.mapCatching은 인라인 함수이며, 컴파일러가 비-서스펜드 람다가 존재하지 않음을 알고 있으므로 인라인 람다 내에서 suspend 호출을 허용합니다. 이 경우 invoke() 함수가 suspend이므로, 인라인 람다 내의 서스펜딩 함수 호출은 허용됩니다.
따라서 현재 코드는 컴파일 오류가 발생하지 않으며 정상적으로 작동합니다. 제안된 명시적 Result 분기 방식은 선택적 리팩토링이지만, 코드의 컴파일 가능성 측면에서는 변경이 필수적이지 않습니다.
Likely an incorrect or invalid review comment.
[ PR Content ]
Related issue
Screenshot 📸
Work Description
To Reviewers 📢
Summary by CodeRabbit
새로운 기능
권한
UI/디자인
기타
✏️ Tip: You can customize this high-level summary in your review settings.