Skip to content

[Feat] 제보하기 화면 구현(2차), 역지오코딩 로직 구현 - #66

Merged
taipaise merged 4 commits into
developfrom
feat/report
Nov 17, 2025
Merged

[Feat] 제보하기 화면 구현(2차), 역지오코딩 로직 구현#66
taipaise merged 4 commits into
developfrom
feat/report

Conversation

@taipaise

@taipaise taipaise commented Nov 15, 2025

Copy link
Copy Markdown
Collaborator

🌁 Background

📱 Screenshot

카테고리 선택

iPhone SE3 iPhone 13 mini iPhone 16 Pro
se3 13mini 16pro

주소 불러오기

iPhone SE3 iPhone 13 mini iPhone 16 Pro
se3_loc 13mini_loc 16pro_loc

👩‍💻 Contents

  • 제보하기와 제보히스토리에서 사용하는 카테고리 선택 화면을 구현했습니다. (아이콘은 변경 가능성 있음)
  • 카카오 local API를 사용해서 역지오코딩 로직을 구현했습니다.

📝 Review Note

역지오코딩

  • UseCase에서 좌표를 불러오고, 해당 좌표를 카카오Local API를 이용해서 주소로 변환해 받도록 구현했습니다.

  • LocationReposiotry에서 CoreLocation 을 이용해 사용자의 좌표 정보를 불러온 후, 해당 좌표를 기반으로 주소를 가져오는 api를 실행합니다.

  • 아래 코드와 같이 Continuation을 이용해서 비동기적으로 실행되는 delegate 메서드를 처리하도록 했습니다.

    import CoreLocation
    import Domain
    
    final class LocationRepository: NSObject, LocationRepositoryProtocol {
        private let networkService = NetworkService.shared
        private let locationManager = CLLocationManager()
        private var continuation: CheckedContinuation<LocationEntity?, Never>?
     
        // 생략.. 
    
        func getCoordinate() async -> LocationEntity? {
            guard CLLocationManager.locationServicesEnabled() else { return nil }
    
            let currentStatus = await requestAuthorizationIfNeeded()
    
            if currentStatus == .authorizedAlways || currentStatus == .authorizedWhenInUse {
                return await withCheckedContinuation { continuation in
                    self.continuation = continuation
                    locationManager.requestLocation()
                }
            } else {
                return nil
            }
        }
    }
    
    extension LocationRepository: CLLocationManagerDelegate {
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            let coordinate = locations.last?.coordinate
            let entity = coordinate.map { LocationEntity(longitude: $0.longitude, latitude: $0.latitude, address: nil) }
            continuation?.resume(returning: entity)
            continuation = nil
        }
    
        func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            continuation?.resume(returning: nil)
            continuation = nil
        }
    }
  • 정책상의 이유로 카카오API가 아니라 다른 서비스의 API를 사용할 수도 있어 위 과정을 분리했습니다

NetworkService의 performRequest 변경

  • networkservice에서 네트워크 요청과 DTO로의 디코딩을 담당하는 performRequest가 일부 수정되었습니다.
private func performRequest<T: Decodable>(
        endpoint: Endpoint,
        type: T.Type,
        withPlugins: Bool = true
    ) async throws -> T? {
        var request = try endpoint.makeURLRequest()
        // 생략...

        let (data, response) = try await URLSession.shared.data(for: request)
      
        // 생략...

        do {
            let bitnagilResponse = try decoder.decode(BaseResponse<T>.self, from: data)

            guard let responseDTO = bitnagilResponse.data else { return nil }

            return responseDTO
        } catch {
            do {
                let generalResponse = try decoder.decode(T.self, from: data)
                return generalResponse
            } catch {
                throw NetworkError.decodingError
            }
        }
    }
  • 빛나길 API의 구조는 모두 아래와 같습니다
struct BaseResponse<T: Decodable>: Decodable {
    let code: String
    let data: T?
    let message: String
}
  • 다만 카카오의 API는 당연히 저 형식으로 생기지 않았기 때문에, 디코딩에 실패하는 문제가 있었습니다. 때문에 디코딩 실패 시에 전달받은 타입 T로 디코딩을 시도하도록 수정했습니다.

📣 Related Issue

  • close #

📬 Reference

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 현재 위치 기반 주소 조회 기능 추가
    • 신고 기능 및 신고 유형 선택 기능 개선
    • 신고 카테고리 선택 UI 업데이트
  • 버그 수정

    • 응답 데이터 처리 로직 개선
  • 디자인

    • 신고 카테고리 아이콘 및 컬러팔레트 추가
    • UI 레이아웃 및 크기 최적화

@taipaise
taipaise requested a review from choijungp November 15, 2025 08:41
@taipaise taipaise self-assigned this Nov 15, 2025
@coderabbitai

coderabbitai Bot commented Nov 15, 2025

Copy link
Copy Markdown

워크스루

이 변경사항은 위치 기반 기능과 보고 시스템을 구현하기 위해 데이터 소스, 도메인, 프레젠테이션 레이어에 걸쳐 다양한 저장소, 사용 사례, UI 컴포넌트를 추가합니다. Kakao API를 통한 주소 해석, 기기 위치 조회, 보고서 제출 기능이 포함됩니다.

변경 사항

응집 / 파일 요약
DI 등록 및 프로토콜 정의
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift, Projects/Domain/Sources/DomainDependencyAssembler.swift, Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
LocationRepositoryProtocol, ReportRepositoryProtocol, ReportUseCaseProtocol에 대한 DI 컨테이너 등록 및 의존성 주입 구성
도메인 엔티티 및 프로토콜
Projects/Domain/Sources/Entity/LocationEntity.swift, Projects/Domain/Sources/Entity/ReportEntity.swift, Projects/Domain/Sources/Entity/Enum/ReportType.swift, Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift, Projects/Domain/Sources/Protocol/Repository/ReportRepositoryProtocol.swift, Projects/Domain/Sources/Protocol/UseCase/ReportUseCaseProtocol.swift
위치 및 보고서 엔티티, 저장소 프로토콜, 사용 사례 프로토콜 정의; ReportType 열거형 사례 업데이트 (road/etc 제거, transportation/water/convenience 추가)
도메인 사용 사례
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift
LocationRepositoryProtocol 및 ReportRepositoryProtocol을 의존성으로 주입받아 현재 위치 조회 및 보고서 제출 기능 구현
데이터 소스 저장소
Projects/DataSource/Sources/Repository/LocationRepository.swift, Projects/DataSource/Sources/Repository/ReportRepository.swift
위치 서비스 및 Kakao API를 활용한 LocationRepository 구현, 보고서 제출을 위한 ReportRepository 스텁 구현
데이터 소스 DTO 및 엔드포인트
Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift, Projects/DataSource/Sources/Endpoint/LocationEndpoint.swift, Projects/DataSource/Sources/Common/Enum/AppProperties.swift, Projects/DataSource/Sources/NetworkService/NetworkService.swift
Kakao API 응답 DTO, 위치 엔드포인트, kakaoApiKey 설정 추가, 폴백 디코딩 경로 구현
프레젠테이션 ViewModel
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift
ReportUseCaseProtocol 의존성 주입, 위치 조회 및 카테고리 선택 로직 추가
프레젠테이션 UI 컴포넌트
Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift, Projects/Presentation/Sources/Report/View/ReportCategoryTableViewController.swift, Projects/Presentation/Sources/Report/View/ReportViewController.swift, Projects/Presentation/Sources/Report/Model/ReportType+.swift
보고서 카테고리 선택 UI 추가 (TableViewCell, TableViewController), 위임 패턴 구현, 레이아웃 상수 업데이트
디자인 시스템 자산
Projects/Presentation/Sources/Common/DesignSystem/BitnagilColor.swift, Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift, Projects/Presentation/Resources/Colors.xcassets/*, Projects/Presentation/Resources/Images.xcassets/Report/*
새로운 색상(green300, blue300) 및 아이콘(car, light, water, hammer) 자산 추가
설정
SupportingFiles/Info.plist
NSLocationWhenInUseUsageDescription, LSApplicationCategoryType, KakaoAPIKey 정보 추가

시퀀스 다이어그램

sequenceDiagram
    participant VC as ReportViewController
    participant VM as ReportViewModel
    participant UC as ReportUseCase
    participant LocRepo as LocationRepository
    participant KakaoAPI as Kakao API
    participant ReportRepo as ReportRepository

    VC->>VM: configureLocation()
    activate VM
    VM->>UC: fetchCurrentLocation()
    activate UC
    UC->>LocRepo: fetchCoordinate()
    activate LocRepo
    LocRepo->>LocRepo: requestAuthorizationIfNeeded()
    LocRepo->>LocRepo: await location updates
    LocRepo-->>UC: LocationEntity (coord)
    deactivate LocRepo
    UC->>LocRepo: fetchAddress(coordinate:)
    activate LocRepo
    LocRepo->>KakaoAPI: GET /v2/local/geo/coord2address.json
    KakaoAPI-->>LocRepo: KakaoLocationResponseDTO
    LocRepo-->>UC: LocationEntity (with address)
    deactivate LocRepo
    UC-->>VM: LocationEntity
    deactivate UC
    VM->>VM: update locationPublisher
    deactivate VM
    VM-->>VC: location updated

    VC->>VC: showCategoryBottomSheet()
    VC->>VC: create ReportCategoryTableViewController
    VC->>VC: set delegate
    VC->>VC: present modal

    VC->>VM: selectCategory(type:)
    VM->>VM: update selectedReportType
Loading

예상 코드 검토 노력

🎯 3 (Moderate) | ⏱️ ~25 분

주의가 필요한 영역:

  • LocationRepository 위치 서비스 통합: CLLocationManager 위임 구현, 권한 처리, 비동기 연속성 관리의 정확성 검증 필요
  • Kakao API 통합: 엔드포인트 구성, 인증 헤더, DTO 디코딩 로직 (폴백 경로 포함) 검토
  • DI 구성: 세 레이어(DataSource, Domain, Presentation)에 걸친 의존성 주입 완결성 확인
  • ReportType 열거형 변경: 기존 코드에서 제거된 road/etc 사례에 대한 호환성 영향 검토
  • UI 위임 패턴: ReportCategoryTableViewController 위임 통합 및 모달 표시/해제 흐름 검증

🐰 Whiskers twitched with glee,
Locations now flow wild and free,
With Kakao's API in hand so bright,
Categories arranged just right,
Reports shall hop from screen to sight! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
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.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 제보하기 화면 구현(2차)과 역지오코딩 로직 구현이라는 주요 변경사항을 명확하게 요약하고 있습니다.
✨ 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 feat/report

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (13)
Projects/Presentation/Sources/Report/Model/ReportType+.swift (1)

24-35: 코드 구현이 정확하고 깔끔합니다.

새로 추가된 name 프로퍼티의 구현이 올바르며, 각 케이스에 대한 한글 문자열이 적절하게 매핑되어 있습니다.

선택 사항: 다국어 지원을 고려한다면 로컬라이제이션 적용

현재는 한글 문자열이 하드코딩되어 있습니다. 만약 향후 다국어 지원 계획이 있다면 Localizable.strings 파일을 활용하는 것을 고려해보세요:

var name: String {
    switch self {
    case .transportation:
        NSLocalizedString("report.type.transportation", comment: "")
    case .lamp:
        NSLocalizedString("report.type.lamp", comment: "")
    case .water:
        NSLocalizedString("report.type.water", comment: "")
    case .convenience:
        NSLocalizedString("report.type.convenience", comment: "")
    }
}
Projects/DataSource/Sources/NetworkService/NetworkService.swift (1)

89-96: 에러 정보 보존을 위한 개선 제안

폴백 디코딩 로직은 Kakao API와 같은 외부 API를 지원하기 위한 좋은 접근 방식입니다. 다만, 첫 번째 디코딩 시도에서 발생한 에러 정보가 손실되어 디버깅이 어려워질 수 있습니다. 두 디코딩 모두 실패할 경우, 첫 번째 에러의 상세 정보를 로깅하거나 보존하는 것을 권장합니다.

다음과 같이 개선할 수 있습니다:

 } catch {
+    let baseResponseError = error
     do {
         let generalResponse = try decoder.decode(T.self, from: data)
         return generalResponse
     } catch {
+        // 디버깅을 위해 첫 번째 에러 정보를 로깅
+        print("BaseResponse decoding failed: \(baseResponseError)")
+        print("Direct T decoding failed: \(error)")
         throw NetworkError.decodingError
     }
 }

또는 더 상세한 에러 정보를 포함하도록 NetworkError를 확장할 수도 있습니다:

// NetworkError에 추가
case decodingError(baseResponseError: Error?, directError: Error?)
 } catch {
+    let baseResponseError = error
     do {
         let generalResponse = try decoder.decode(T.self, from: data)
         return generalResponse
     } catch {
-        throw NetworkError.decodingError
+        throw NetworkError.decodingError(
+            baseResponseError: baseResponseError,
+            directError: error
+        )
     }
 }
Projects/Domain/Sources/Protocol/Repository/ReportRepositoryProtocol.swift (1)

8-10: 프로토콜 정의는 적절하나 구현 필요

프로토콜 정의는 올바르지만, Projects/DataSource/Sources/Repository/ReportRepository.swift의 구현이 비어있습니다. 제보 기능을 완성하려면 실제 API 호출 로직을 구현해야 합니다.

API 엔드포인트와 요청/응답 모델 구현을 도와드릴까요?

Projects/Domain/Sources/Entity/LocationEntity.swift (1)

8-22: Equatable/Hashable 프로토콜 준수 고려

LocationEntity를 비교하거나 Set, Dictionary에서 사용할 가능성이 있다면 Equatable 및 Hashable 프로토콜 준수를 고려하세요.

-public struct LocationEntity {
+public struct LocationEntity: Equatable, Hashable {
     public let longitude: Double
     public let latitude: Double
     public let address: String?
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)

10-13: report 메서드 구현 필요

ReportRepository의 report 메서드가 비어있습니다. 실제 제보 API 호출 로직을 구현해야 합니다.

구현 예시 또는 TODO 추적을 위한 이슈 생성을 도와드릴까요?

Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)

23-25: report 메서드 구현 필요

report 메서드가 비어있습니다. 실제 제보 로직 구현이 필요합니다.

실제 구현 예시나 TODO 추적을 위한 이슈 생성을 도와드릴까요?

Projects/DataSource/Sources/Endpoint/LocationEndpoint.swift (1)

13-25: Kakao 주소 조회 URL 조합 방식 재확인 필요

baseURL"https://dapi.kakao.com/v2"이고, path에서 다시 baseURL + "/local/geo/coord2address.json"를 반환하고 있어서, NetworkServicebaseURL + path를 조합하는 구조라면 실제 요청 URL이 이중으로 붙을 위험이 있습니다.
프로젝트 내 다른 Endpoint 구현과 동일한 패턴인지 한 번만 확인 부탁드립니다.

Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (2)

33-56: 선언부 정리: location= nil 초기화는 제거 가능

private var location: LocationEntity? = nil은 옵셔널 기본값이 원래 nil이기 때문에 초기화 코드를 생략해도 동일하게 동작합니다. SwiftLint 경고(redundant_optional_initialization)도 이 부분을 지적하고 있으니, 다음처럼 정리하는 쪽이 깔끔합니다.

-    private var location: LocationEntity? = nil
+    private var location: LocationEntity?

Also applies to: 42-42


90-100: 위치 조회 에러 처리 및 상태 갱신 방식 보완 제안

configureLocation()에서 reportUseCase.getCurrentLocation()의 에러를 catch에서 완전히 무시하고, 이후에 locationSubject.send(location?.address)만 호출하고 있습니다. 이 경우:

  • 새 위치 조회에 실패해도 이전에 남아 있던 location 값이 다시 방출될 수 있고,
  • 사용자에게 실패 원인이나 안내 메시지가 전달되지 않습니다.

예를 들어 다음과 같이 에러 시에는 locationnil로 초기화하고, exceptionSubject를 통해 안내 문구를 전달하는 방식을 고려해 보시는 것을 권장드립니다.

Task {
    do {
        self.location = try await reportUseCase.getCurrentLocation()
    } catch {
        self.location = nil
        exceptionSubject.send("현재 위치를 가져오지 못했습니다. 위치 권한을 확인해 주세요.")
    }

    locationSubject.send(location?.address)
}
Projects/DataSource/Sources/Repository/LocationRepository.swift (1)

24-37: getCoordinate()의 동시 호출 시 단일 continuation 사용으로 인한 경쟁 상태 가능성

getCoordinate()continuation 프로퍼티 하나를 사용해 위치 응답을 대기하고 있어서, 동시에 여러 곳에서 getCoordinate()를 호출하면 마지막 호출만 정상적으로 resume되고 이전 호출용 continuation은 덮어써질 수 있습니다.

현재 사용 패턴상 동시에 여러 호출이 발생하지 않는다면 큰 문제는 아니겠지만, 방어적으로는:

  • continuation이 이미 존재하면 바로 nil을 반환하거나 에러를 표현,
  • 혹은 내부적으로 큐를 두고 순차적으로 처리

하는 방식을 고려해 볼 수 있습니다.

Projects/Presentation/Sources/Report/View/ReportCategoryTableViewController.swift (1)

27-38: 뷰 구성 시점: init 대신 viewDidLoad에서 설정하는 패턴 고려

현재 이 뷰 컨트롤러는 init(reportType:)에서 바로 configureAttribute()configureLayout()을 호출하면서 view.addSubview(categoryTableView)까지 수행하고 있습니다. Swift에서는 UIViewControllerview를 초기화 직후 바로 건드려도 동작은 하지만, 일반적으로는:

  • 뷰 계층 구성(addSubview, 제약 설정)
  • UI 속성 설정(delegate, dataSource 등)

viewDidLoad()에서 처리하는 패턴을 더 많이 사용합니다.

크게 문제 되지는 않지만, 다른 뷰 컨트롤러들과 일관성과 가독성을 위해 아래처럼 옮기는 것도 고려해 볼 수 있습니다.

init(reportType: ReportType?) {
    self.selectedCategory = reportType
    super.init(nibName: nil, bundle: nil)
}

override func viewDidLoad() {
    super.viewDidLoad()
    configureAttribute()
    configureLayout()
}

Also applies to: 39-60

Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (2)

8-13: Foundation / Domain 중복 import 정리

이 파일 상단에서 Foundation, Domain을 두 번씩 import 하고 있어 SwiftLint의 duplicate_imports 경고가 발생하고 있습니다. 아래처럼 한 번만 import 하도록 정리하면 됩니다.

-import Domain
-import Foundation
-
-import Foundation
-import Domain
+import Foundation
+import Domain

(또는 프로젝트 컨벤션에 맞게 순서만 맞추시면 됩니다.)


132-141: Kakao 응답 → LocationEntity 매핑 로직은 안정적으로 보입니다

toLocationEntity(fallbackLongitude:fallbackLatitude:)에서

  • 첫 번째 documents.first만 사용하고,
  • 문서의 longitude/latitude가 없으면 전달받은 fallback 좌표를 사용하며,
  • 주소는 도로명 주소 → 지번 주소 순으로 선택

하는 전략은 실제 Kakao coord2address 응답 구조를 고려했을 때 자연스럽고, 실패 시 nil을 반환해 상위 레이어에서 처리할 수 있게 한 것도 좋습니다.

추가로, 좌표와 주소를 모두 얻지 못한 경우에 한해 0 좌표로 LocationEntity를 만드는 대신 nil을 반환하도록 바꾸는 것도 한 번 검토해 볼 만합니다(지도 상에서 (0,0)으로 이동하는 부자연스러운 상황 방지).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f350a75 and 1d43da7.

📒 Files selected for processing (23)
  • Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1 hunks)
  • Projects/DataSource/Sources/Common/Enum/AppProperties.swift (1 hunks)
  • Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (1 hunks)
  • Projects/DataSource/Sources/Endpoint/LocationEndpoint.swift (1 hunks)
  • Projects/DataSource/Sources/NetworkService/NetworkService.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/LocationRepository.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/ReportRepository.swift (1 hunks)
  • Projects/Domain/Sources/DomainDependencyAssembler.swift (1 hunks)
  • Projects/Domain/Sources/Entity/Enum/ReportType.swift (1 hunks)
  • Projects/Domain/Sources/Entity/LocationEntity.swift (1 hunks)
  • Projects/Domain/Sources/Entity/ReportEntity.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/ReportRepositoryProtocol.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/UseCase/ReportUseCaserProtocol.swift (1 hunks)
  • Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Report/Model/ReportType+.swift (1 hunks)
  • Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift (1 hunks)
  • Projects/Presentation/Sources/Report/View/Component/ReportTextView.swift (1 hunks)
  • Projects/Presentation/Sources/Report/View/ReportCategoryTableViewController.swift (1 hunks)
  • Projects/Presentation/Sources/Report/View/ReportViewController.swift (4 hunks)
  • Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (5 hunks)
  • SupportingFiles/Info.plist (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
Projects/Domain/Sources/Protocol/UseCase/ReportUseCaserProtocol.swift (2)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (2)
  • getCurrentLocation (17-21)
  • report (23-25)
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)
  • report (11-13)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (2)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
  • getCoordinate (24-37)
  • getAddress (39-47)
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)
  • report (11-13)
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)
  • report (23-25)
Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift (1)
Projects/Presentation/Sources/Report/View/ReportCategoryTableViewController.swift (2)
  • configureAttribute (39-44)
  • configureLayout (46-60)
Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
  • getCoordinate (24-37)
  • getAddress (39-47)
Projects/Presentation/Sources/Report/View/ReportCategoryTableViewController.swift (2)
Projects/Presentation/Sources/Report/View/ReportViewController.swift (3)
  • reportCategoryTableViewController (481-484)
  • configureAttribute (72-117)
  • configureLayout (119-312)
Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift (3)
  • configureAttribute (45-55)
  • configureLayout (57-93)
  • configureCell (95-102)
Projects/Domain/Sources/DomainDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (2)
  • register (14-16)
  • resolve (18-25)
Projects/Domain/Sources/Protocol/Repository/ReportRepositoryProtocol.swift (2)
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)
  • report (11-13)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)
  • report (23-25)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
  • resolve (18-25)
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (1)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)
  • getCurrentLocation (17-21)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
Projects/DataSource/Sources/NetworkService/NetworkService.swift (1)
  • request (25-48)
Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (1)
  • toLocationEntity (134-141)
Projects/Presentation/Sources/Report/View/ReportViewController.swift (2)
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (3)
  • action (58-75)
  • configureLocation (90-100)
  • register (122-124)
Projects/Presentation/Sources/Common/Extension/UIViewController+.swift (1)
  • presentCustomBottomSheet (98-101)
🪛 SwiftLint (0.57.0)
Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift

[Warning] 11-11: Imports should be unique

(duplicate_imports)


[Warning] 12-12: Imports should be unique

(duplicate_imports)

Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift

[Warning] 42-42: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

⏰ 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 (20)
Projects/Presentation/Sources/Report/Model/ReportType+.swift (1)

37-48: 설명 문자열이 각 카테고리에 적합합니다.

각 제보 유형에 대한 설명이 구체적이고 이해하기 쉽게 작성되었습니다. 사용자가 어떤 카테고리를 선택해야 할지 판단하는 데 도움이 될 것입니다.

Projects/DataSource/Sources/NetworkService/NetworkService.swift (1)

84-88: 변수명 변경 및 로직 승인

baseResponse에서 bitnagilResponse로의 변수명 변경은 프로젝트의 BaseResponse 타입과의 연관성을 명확히 하여 가독성을 향상시킵니다. 데이터 접근 로직도 올바르게 처리되고 있습니다.

Projects/DataSource/Sources/Common/Enum/AppProperties.swift (1)

19-21: LGTM!

Kakao API 키 속성이 올바르게 추가되었습니다. 기존 패턴(kakaoNativeKey)을 따르고 있으며, Info.plist에서 값을 가져오는 방식이 일관적입니다.

SupportingFiles/Info.plist (2)

5-6: LGTM!

위치 권한 요청 메시지가 한국어로 적절하게 추가되었습니다.


42-43: LGTM!

Kakao API 키가 환경 변수로부터 올바르게 설정되었습니다. 기존 KakaoNativeKey 패턴과 일관성 있게 구현되었습니다.

Projects/Presentation/Sources/Report/View/Component/ReportTextView.swift (1)

29-30: 셰브론 이미지 크기 변경 확인

셰브론 이미지 크기가 10x6에서 24x24로 크게 증가했습니다(너비 140% 증가, 높이 300% 증가). PR 설명에 다양한 디바이스 크기의 스크린샷이 포함되어 있다고 하니, 모든 디바이스에서 레이아웃이 의도대로 표시되는지 확인해주세요.

Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)

44-50: LGTM!

LocationRepository와 ReportRepository의 DI 등록이 올바르게 추가되었습니다. 기존 패턴과 일관성 있게 구현되었습니다.

Projects/Domain/Sources/DomainDependencyAssembler.swift (1)

66-73: LGTM!

ReportUseCase의 DI 등록이 올바르게 구현되었습니다. 필요한 의존성(LocationRepository, ReportRepository)을 적절히 주입하고 있으며, 에러 처리도 명확합니다.

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)

124-129: LGTM!

ReportViewModel의 DI 등록이 올바르게 추가되었습니다. ReportUseCaserProtocol을 적절히 주입하고 있으며, 기존 패턴과 일관성이 있습니다.

Projects/Domain/Sources/Protocol/UseCase/ReportUseCaserProtocol.swift (1)

8-12: 프로토콜 정의 확인

UseCase 프로토콜이 적절하게 정의되어 있습니다. getCurrentLocation은 throwing 함수로, report는 비동기 함수로 선언되어 의도가 명확합니다.

Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1)

8-12: 프로토콜 메서드 시그니처 확인

getCoordinate는 non-throwing이고 getAddress는 throwing입니다. 이는 의도적인 설계로 보이며(좌표 획득 실패는 nil 반환, 주소 변환 실패는 에러 던지기), 호출자가 각각 적절하게 처리하는지 확인하세요.

Projects/Presentation/Sources/Report/View/ReportViewController.swift (4)

41-41: 카테고리 바텀시트 높이 증가 확인

categoryBottomSheetHeight가 226에서 362로 증가했습니다. 이는 추가된 카테고리 항목(transportation, water, convenience)을 수용하기 위한 것으로 보이며 적절합니다.


101-113: 버튼 액션 연결 확인

locationButton은 위치 조회(configureLocation)를, registerButton은 제보 등록(register)을 트리거하도록 올바르게 변경되었습니다.


367-371: 카테고리 선택 뷰 생성 방식 변경 확인

카테고리 선택 뷰를 프로퍼티에서 메서드 내 로컬 인스턴스로 변경했습니다. 메모리 효율성이 개선되었으며 delegate도 적절히 설정되어 있습니다.


480-485: ReportCategoryTableViewControllerDelegate 구현 확인

새로운 delegate 메서드가 추가되어 카테고리 선택 시 viewModel로 전달합니다. 플로우가 명확하고 적절합니다.

Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)

17-21: getCurrentLocation 에러 처리 확인

getCurrentLocation에서 getAddress가 실패하면 에러가 호출자에게 전파됩니다. ViewModel(ReportViewModel.configureLocation)에서 catch 블록이 비어있는데, 사용자에게 에러를 알리는 로직이 필요한지 확인하세요.

Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift (2)

95-102: 셀 구성 메서드 확인

configureCell 메서드가 적절하게 구현되어 있으며, 선택 상태에 따라 체크 아이콘을 표시합니다.


105-118: 아이콘 매핑 검증 필요

ReportType과 아이콘의 매핑이 의미적으로 일치하는지 확인하세요. 예를 들어:

  • transportation → restIcon
  • lamp → outsideIcon
  • water → wakeupIcon
  • convenience → growIcon

PR 설명에서 아이콘이 변경될 수 있다고 언급되었으므로, 최종 아이콘 리소스와 매핑을 재확인하세요.

Projects/Domain/Sources/Entity/Enum/ReportType.swift (1)

8-13: 제거된 ReportType 케이스 참조 없음 - 검증 완료

코드베이스 전체를 검색한 결과, 제거된 케이스 roadetc에 대한 참조가 없습니다. 현재 enum의 4개 케이스(transportation, lamp, water, convenience)만 사용되고 있으며, ReportType+.swift의 모든 switch 문이 이들 케이스를 완벽하게 처리하고 있습니다.

Projects/DataSource/Sources/Repository/LocationRepository.swift (1)

39-47: Kakao 주소 변환 로직 구성은 자연스럽습니다

getAddress(coordinate:)에서 LocationEndpoint.fetchAddress를 사용해 Kakao API를 호출하고, 응답을 KakaoLocationResponseDTO.toLocationEntity(fallbackLongitude:fallbackLatitude:)로 매핑하는 구조는 도메인 LocationEntity와 잘 분리되어 있어 유지보수 측면에서 좋아 보입니다.

좌표를 fallback으로 넘겨 두어 Kakao 응답에 좌표가 없더라도 최소한 기존 좌표를 유지하는 점도 합리적입니다.

Comment on lines +49 to +66
private func requestAuthorizationIfNeeded() async -> CLAuthorizationStatus {
let currentStatus = locationManager.authorizationStatus

switch currentStatus {
case .authorizedAlways, .authorizedWhenInUse:
return currentStatus
case .denied, .restricted:
return currentStatus
case .notDetermined:
return await withCheckedContinuation { (continuation: CheckedContinuation<CLAuthorizationStatus, Never>) in
self.authContinuation?.resume(returning: currentStatus)
self.authContinuation = continuation
self.locationManager.requestWhenInUseAuthorization()
}
default:
return currentStatus
}
}

@coderabbitai coderabbitai Bot Nov 15, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

위치 권한 요청용 authContinuation이 재개되지 않아 첫 호출이 영원히 대기 상태가 될 수 있습니다

requestAuthorizationIfNeeded()에서 .notDetermined인 경우 withCheckedContinuation을 사용해 authContinuation을 저장하고 requestWhenInUseAuthorization()을 호출하지만, 어디에서도 authContinuationresume하지 않고 있습니다.

그 결과:

  • 사용자가 권한 허용/거부를 선택해도 await requestAuthorizationIfNeeded()가 깨어나지 않아,
  • getCoordinate() 첫 호출이 영원히 대기 상태에 빠질 수 있습니다.

보통은 CLLocationManagerDelegate의 권한 변경 콜백에서 authContinuation을 재개해야 합니다. 예를 들어 아래와 같이 보완하는 것을 권장드립니다.

extension LocationRepository: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        let status = manager.authorizationStatus
        authContinuation?.resume(returning: status)
        authContinuation = nil
    }

    // 기존 didUpdateLocations / didFailWithError 유지
}

또한 requestAuthorizationIfNeeded() 내부의

self.authContinuation?.resume(returning: currentStatus)
self.authContinuation = continuation

부분은 이전 continuation을 안전하게 정리하려는 의도라면, 실제로 .notDetermined가 아닌 상태에서만 호출되도록 하거나, 중복 resume이 일어나지 않도록 좀 더 명시적으로 처리해 주시는 것이 좋겠습니다.

Also applies to: 14-15, 69-81



---

🏁 Script executed:

```shell
#!/bin/bash
# Locate and examine LocationRepository.swift
fd -t f "LocationRepository.swift" -x cat {}

Length of output: 3004


🏁 Script executed:

#!/bin/bash
# Search for authContinuation usage and CLLocationManagerDelegate in the file
rg -n "authContinuation|locationManagerDidChangeAuthorization|CLLocationManagerDelegate" "Projects/DataSource/Sources/Repository/"

Length of output: 535


🏁 Script executed:

#!/bin/bash
# Check the full context around lines 14-15, 49-66, and 69-81
rg -n -B2 -A8 "requestAuthorizationIfNeeded|func locationManager" "Projects/DataSource/Sources/Repository/LocationRepository.swift"

Length of output: 1514


authContinuation을 재개하는 locationManagerDidChangeAuthorization 델리게이트 메서드가 없어 권한 요청 시 무한 대기가 발생합니다

검증 결과, 리뷰 의견이 정확합니다:

  • requestAuthorizationIfNeeded() 메서드(49-66줄)의 .notDetermined 케이스에서 withCheckedContinuation으로 authContinuation을 설정하고 requestWhenInUseAuthorization()을 호출합니다.
  • 그러나 CLLocationManagerDelegate 확장(69-81줄)에는 didUpdateLocationsdidFailWithError만 구현되어 있고, locationManagerDidChangeAuthorization(_:) 메서드가 없습니다.
  • 따라서 사용자가 권한을 승인/거부해도 authContinuation이 재개되지 않으므로 await requestAuthorizationIfNeeded()는 영원히 대기 상태에 빠집니다.

추가로 59-60줄의 로직도 문제가 있습니다:

self.authContinuation?.resume(returning: currentStatus)  // 이전 continuation 재개 시도
self.authContinuation = continuation                      // 새 continuation 설정

첫 호출 시 authContinuation이 nil이면 59줄은 아무 작업도 하지 않으며, 중복 resume을 방지하는 명시적 처리가 부족합니다.

해결 방법:

CLLocationManagerDelegate 확장에 다음 메서드를 추가하세요:

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
    let status = manager.authorizationStatus
    authContinuation?.resume(returning: status)
    authContinuation = nil
}

또한 59-60줄을 정리하여 명확하게 변경하세요.

🤖 Prompt for AI Agents
Projects/DataSource/Sources/Repository/LocationRepository.swift lines 49-66: the
.notDetermined branch sets authContinuation and calls
requestWhenInUseAuthorization but there is no delegate callback to resume that
continuation, causing an indefinite await; add a CLLocationManagerDelegate
method locationManagerDidChangeAuthorization(_:) that reads
manager.authorizationStatus, resumes authContinuation with that status and then
sets authContinuation = nil, and simplify the .notDetermined logic by removing
the prior resume attempt (do not resume any previous continuation before
assigning the new one) so only the delegate resumes the stored continuation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

래빗아 .. Continuation 넘 어렵다 .....................

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@choijungp 이해합니다! Continuation은 처음 접하면 정말 어려운 개념이에요. 쉽게 설명해드릴게요! 😊

🎫 Continuation을 식당 대기표로 생각해보세요

1. 손님이 식당에 가서 대기표를 받습니다 (withCheckedContinuation)
2. 대기표를 들고 기다립니다 (await)
3. 식당에서 호명을 하면 들어갈 수 있습니다 (resume)

현재 코드의 문제:

  • 대기표를 받았는데 (authContinuation 저장)
  • 식당에서 절대 호명을 안 해요! (resume이 없음)
  • 손님은 영원히 기다립니다 😭

해결 방법 (간단 버전)

// 1️⃣ 권한 요청할 때 대기표 받기
private func requestAuthorizationIfNeeded() async -> CLAuthorizationStatus {
    // ... 기존 코드 ...
    case .notDetermined:
        return await withCheckedContinuation { continuation in
            self.authContinuation = continuation  // 대기표 보관
            self.locationManager.requestWhenInUseAuthorization()  // 권한 요청
        }
}

// 2️⃣ 권한 변경될 때 호명하기 - 이게 없어서 문제였어요!
extension LocationRepository: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        // 권한이 변경되면 대기 중인 사람을 깨워줍니다
        authContinuation?.resume(returning: manager.authorizationStatus)
        authContinuation = nil  // 대기표 처리 완료
    }
    
    // 기존 메서드들...
}

이렇게 하면 사용자가 권한을 허용/거부하는 순간 locationManagerDidChangeAuthorization이 자동으로 호출되고, 여기서 resume을 해줘서 기다리던 코드가 계속 실행됩니다!

천천히 이해하시면 됩니다. 궁금한 점 있으면 언제든 물어보세요! 💪


🧠 Learnings used
Learnt from: choijungp
Repo: YAPP-Github/Bitnagil-iOS PR: 30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.

Comment on lines +8 to +10
public struct ReportEntity {

}

@coderabbitai coderabbitai Bot Nov 15, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ReportEntity 구조체 구현 필요

현재 빈 구조체로 정의되어 있습니다. 제보 기능을 완성하려면 필요한 속성들(예: 위치 정보, 카테고리, 설명, 이미지 등)을 정의해야 합니다.

제보 데이터 모델의 속성 정의를 도와드릴까요? 어떤 정보들을 포함해야 하는지 알려주시면 구조체 구현을 제안해드리겠습니다.

🤖 Prompt for AI Agents
In Projects/Domain/Sources/Entity/ReportEntity.swift around lines 8–10, the
ReportEntity struct is empty; implement it with the typical report fields: add
an id (UUID/String), reporterId (optional UUID/String for anonymous reports),
location fields (latitude: Double, longitude: Double, optional address: String),
category (define a Category enum conforming to String/Codable), description/body
(String), images (array of URL or String identifiers), createdAt/updatedAt
(Date), status (e.g., enum for New/InProgress/Resolved), and any flags like
isAnonymous (Bool); make the struct conform to Codable, Equatable (and
Identifiable if using SwiftUI), and provide a memberwise initializer or default
values where appropriate.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

서버 연동할 때 채울 예정이야 ~~ 돈와리 래빗 ~

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@choijungp, 알겠습니다! 서버 연동 시점에 구현하시는 것이 합리적이네요. API 스펙이 확정되면 그에 맞춰 필드를 정의하시면 되겠습니다. 👍

그때 필요하시면 언제든 도움 요청해주세요!

˙˙˙˙˙ ʕ •ᴥ•ʔ
˙˙˙˙ \( ̄︶ ̄)/
˙˙˙ "알겠어요!"


🧠 Learnings used
Learnt from: choijungp
Repo: YAPP-Github/Bitnagil-iOS PR: 30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.

Comment on lines +20 to 21
case register
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

register 입력이 연결되어 있으나 실제 구현이 없어 제보 동작이 없습니다

Input.registeraction(input:)에서 register()로 정상적으로 연결되어 있지만, register() 본문이 비어 있어 사용자가 제보 버튼을 눌러도 아무 동작이 일어나지 않는 상태입니다.

  • 이번 PR에서 아직 서버 연동까지 포함하지 않는 WIP라면, 최소한 TODO 주석으로 의도를 명시하거나,
  • 실제 릴리즈 타깃이라면 selectedReportType, titleSubject, contentSubject, selectedPhotoSubject, location 등을 이용해 도메인 UseCase를 호출하는 구현이 필요해 보입니다.

원하시면 현재 상태를 기반으로 register() 내부에서 어떤 순서로 검증/제보 요청을 보내면 좋을지 구체적인 예시 코드를 같이 정리해 드릴 수 있습니다.

Also applies to: 72-74, 122-124

🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift around
lines 20-21 (also applicable to 72-74 and 122-124), the register() function is
empty so Input.register triggers no action; either add a TODO comment if this is
WIP or implement the registration flow: validate selectedReportType,
titleSubject, contentSubject, selectedPhotoSubject and location (ensure required
fields present, enforce length/format rules), map subjects to the domain DTO,
call the domain UseCase/repository to perform the report submission, and handle
success/error by publishing appropriate outputs (loading state, success, and
error messages) back to the view. Ensure asynchronous calls are
awaited/scheduled correctly and errors are logged/propagated.

Comment on lines +7 to +8
<key>LSApplicationCategoryType</key>
<string></string>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

LSApplicationCategoryType 빈 값 확인 필요

LSApplicationCategoryType 키가 빈 문자열로 설정되어 있습니다. 이 키가 의도적으로 추가된 것인지, 특정 값이 필요한 것인지 확인이 필요합니다. 일반적으로 이 키는 macOS 앱에서 사용되며, iOS 앱에서는 불필요할 수 있습니다.

이 키가 필요한지, 필요하다면 적절한 값이 무엇인지 확인해주세요.


LSApplicationCategoryType 키는 정상 값으로 설정되어야 함 (현재 빈 문자열로 설정됨)

LSApplicationCategoryType은 macOS 전용 키이며 앱 타입에 해당하는 UTI를 포함하는 문자열입니다. 현재 빈 문자열로 설정되어 있는 것은 문제입니다:

  • macOS 앱인 경우: LSApplicationCategoryType에 적절한 값을 지정해야 하며, 유효한 카테고리 값(예: public.app-category.productivity)으로 변경해야 합니다. 빈 값으로는 앱 검증 및 App Store 제출 시 오류가 발생합니다.

  • iOS 전용 앱인 경우: 이 키는 필요하지 않으며 제거해야 합니다.

적절한 카테고리 값으로 수정하거나 iOS 앱이면 키 자체를 삭제해주세요.


🌐 Web query:

LSApplicationCategoryType Info.plist iOS macOS requirements

💡 Result:

Short answer

  • LSApplicationCategoryType is a macOS Info.plist (Launch Services) key — NOT an iOS key. It must be present for Mac App Store submissions and its value must be one of Apple’s app-category UTIs (e.g. public.app-category.business, public.app-category.games, public.app-category.productivity, etc.). [1][4]

Details / requirements

  • Platform: macOS (Launch Services). iOS does not use LSApplicationCategoryType. [1]
  • Type: String key in the app’s top‑level Info.plist. [1]
  • Value: Exactly one of the published UTIs Apple lists (public.app-category.*). Use the exact UTI string; the App Store uses it to categorize your app. [1][4]
  • App Store requirement: The Mac App Store requires this key in Info.plist for submission. Xcode/project templates may add it but ensure it’s set for store builds. [4][5]

References

  • Apple Info.plist Launch Services keys (LSApplicationCategoryType and UTI list). [1]
  • “Submitting to the Mac App Store” / Categorize Your Application (requires LSApplicationCategoryType). [4]

If you want, I can paste the full list of valid public.app-category.* UTI strings.


LSApplicationCategoryType는 빈 문자열이 아닌 유효한 값으로 설정되어야 함

LSApplicationCategoryType은 macOS 전용 키입니다. 현재 빈 문자열로 설정되어 있는 것은 유효하지 않습니다:

  • macOS 앱인 경우: 이 키는 Mac App Store 제출 시 필수이며, Apple이 정의한 앱 카테고리 UTI 값(예: public.app-category.productivity, public.app-category.business, public.app-category.games 등)으로 정확히 설정되어야 합니다. 빈 값으로는 App Store 제출이 불가능합니다.

  • iOS 전용 앱인 경우: 이 키는 iOS에서 사용되지 않으므로 제거해야 합니다.

적절한 카테고리 UTI 값으로 변경하거나, iOS 앱이면 키를 삭제해주세요.

🤖 Prompt for AI Agents
In SupportingFiles/Info.plist around lines 7 to 8, the LSApplicationCategoryType
key is set to an empty string which is invalid; if this is a macOS app, replace
the empty string with a valid Apple-defined category UTI such as
public.app-category.productivity or another appropriate public.app-category.*
value, otherwise (for an iOS-only app) remove the LSApplicationCategoryType key
entirely; update the plist accordingly and ensure the chosen value matches
Apple's permitted category UTIs before submitting to the App Store.

@choijungp choijungp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

따봉 띵똥 !!!!!!!!!!!!!!! 👍🏻👍🏻👍🏻👍🏻👍🏻
늦어서 죄송합니다 ㅠ.ㅠ. ......... 매우 감사함니다 .................

Comment on lines +11 to +12
import Foundation
import Domain

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

오잉 import 2번 들어갔어요 !!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

요것도 수정하겠습니다!!

Comment on lines +91 to +92
let generalResponse = try decoder.decode(T.self, from: data)
return generalResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요 부분이 리뷰 노트에 말씀해주신 부분이시죵 !! 완전 이해 완 ~~~
따봉 !!!

locationManager.delegate = self
}

func getCoordinate() async -> LocationEntity? {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
func getCoordinate() async -> LocationEntity? {
func fetchCoordinate() async -> LocationEntity? {
Suggested change
func getCoordinate() async -> LocationEntity? {
func loadCoordinate() async -> LocationEntity? {
Suggested change
func getCoordinate() async -> LocationEntity? {
func requestCoordinate() async -> LocationEntity? {

저희 get / set 네이밍을 지양하자 라는 컨벤션이 있어가주구 요러한 네이밍은 어떠신지요 !!!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

악 요즘 자바하다보니까 깜빡했어요 ㅜㅜ 수정하겠습니다

}
}

func getAddress(coordinate: LocationEntity) async throws -> LocationEntity? {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요기도 getAddress 대신 fetchAddress, loadAddress, requestAddress 어떠신지용 ? ?!? ?

Comment on lines +49 to +66
private func requestAuthorizationIfNeeded() async -> CLAuthorizationStatus {
let currentStatus = locationManager.authorizationStatus

switch currentStatus {
case .authorizedAlways, .authorizedWhenInUse:
return currentStatus
case .denied, .restricted:
return currentStatus
case .notDetermined:
return await withCheckedContinuation { (continuation: CheckedContinuation<CLAuthorizationStatus, Never>) in
self.authContinuation?.resume(returning: currentStatus)
self.authContinuation = continuation
self.locationManager.requestWhenInUseAuthorization()
}
default:
return currentStatus
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

래빗아 .. Continuation 넘 어렵다 .....................

Comment on lines +11 to +13
func report(reportEntity: Domain.ReportEntity) async {

}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요기는 추후 서버 연동할 부분인 것이지요 !!?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

네네 맞습니다요

Comment on lines +8 to +10
public struct ReportEntity {

}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

서버 연동할 때 채울 예정이야 ~~ 돈와리 래빗 ~

// Created by 이동현 on 11/9/25.
//

public protocol ReportUseCaserProtocol {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
public protocol ReportUseCaserProtocol {
public protocol ReportUseCaseProtocol {

오타 발견 !!!!! 🚨🚨🚨🚨

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

어떻게 찾았나 신기할 따름입니다,,

//

public protocol ReportUseCaserProtocol {
func getCurrentLocation() async throws -> LocationEntity?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요기도 네이밍 수정이 필요할 것 같숨다 !!! load, fetch, request ... ......

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정하겠습니다!!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)

23-25: 제보 기능 구현이 필요합니다.

report() 메서드가 비어 있어 실제 제보 동작이 수행되지 않습니다. reportRepository.report(reportEntity:)를 호출하여 제보 로직을 완성해야 합니다.

다음과 같이 구현하세요:

 public func report(reportEntity: ReportEntity) async {
-
+    await reportRepository.report(reportEntity: reportEntity)
 }
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (1)

122-124: 제보 기능 구현이 필요합니다.

register() 메서드가 비어 있어 제보 버튼을 눌러도 아무 동작이 일어나지 않습니다. 필수 필드 검증 후 reportUseCase.report()를 호출해야 합니다.

다음과 같이 구현하세요:

 private func register() {
-
+    Task {
+        guard let selectedReportType = selectedReportType else {
+            exceptionSubject.send("카테고리를 선택해주세요.")
+            return
+        }
+        
+        guard let title = titleSubject.value, !title.isEmpty else {
+            exceptionSubject.send("제목을 입력해주세요.")
+            return
+        }
+        
+        guard let content = contentSubject.value, !content.isEmpty else {
+            exceptionSubject.send("내용을 입력해주세요.")
+            return
+        }
+        
+        let reportEntity = ReportEntity(
+            type: selectedReportType,
+            title: title,
+            content: content,
+            location: location,
+            photos: selectedPhotoSubject.value
+        )
+        
+        await reportUseCase.report(reportEntity: reportEntity)
+    }
 }
Projects/DataSource/Sources/Repository/LocationRepository.swift (1)

49-66: 권한 변경 델리게이트 메서드 구현이 필수입니다.

requestAuthorizationIfNeeded().notDetermined 케이스에서 authContinuation을 설정하고 requestWhenInUseAuthorization()을 호출하지만, 이를 재개(resume)하는 델리게이트 메서드가 없어 무한 대기 상태가 발생합니다.

또한 59-60번째 줄의 로직도 문제가 있습니다:

self.authContinuation?.resume(returning: currentStatus)  // 첫 호출 시 nil이므로 동작하지 않음
self.authContinuation = continuation

CLLocationManagerDelegate에 다음 메서드를 추가하세요:

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
    let status = manager.authorizationStatus
    authContinuation?.resume(returning: status)
    authContinuation = nil
}

그리고 59-60번째 줄을 제거하세요:

 case .notDetermined:
     return await withCheckedContinuation { (continuation: CheckedContinuation<CLAuthorizationStatus, Never>) in
-        self.authContinuation?.resume(returning: currentStatus)
         self.authContinuation = continuation
         self.locationManager.requestWhenInUseAuthorization()
     }
🧹 Nitpick comments (1)
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (1)

42-42: 불필요한 nil 초기화를 제거하세요.

Optional 변수를 명시적으로 nil로 초기화하는 것은 불필요합니다. Swift는 Optional 변수를 자동으로 nil로 초기화합니다.

-private var location: LocationEntity? = nil
+private var location: LocationEntity?
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d43da7 and 8b3fa8f.

⛔ Files ignored due to path filters (12)
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_car.imageset/icon_car.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_car.imageset/icon_car@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_car.imageset/icon_car@3x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_hammer.imageset/icon_hammer.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_hammer.imageset/icon_hammer@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_hammer.imageset/icon_hammer@3x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_light.imageset/icon_light.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_light.imageset/icon_light@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_light.imageset/icon_light@3x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_water.imageset/icon_water.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_water.imageset/icon_water@2x.png is excluded by !**/*.png
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_water.imageset/icon_water@3x.png is excluded by !**/*.png
📒 Files selected for processing (17)
  • Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/LocationRepository.swift (1 hunks)
  • Projects/Domain/Sources/DomainDependencyAssembler.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/UseCase/ReportUseCaseProtocol.swift (1 hunks)
  • Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1 hunks)
  • Projects/Presentation/Resources/Colors.xcassets/Blue/Blue300.colorset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Colors.xcassets/Blue/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Colors.xcassets/Green/Green300.colorset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_car.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_hammer.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_light.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_water.imageset/Contents.json (1 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilColor.swift (2 hunks)
  • Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1 hunks)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1 hunks)
  • Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (5 hunks)
✅ Files skipped from review due to trivial changes (4)
  • Projects/Presentation/Resources/Colors.xcassets/Green/Green300.colorset/Contents.json
  • Projects/Presentation/Resources/Colors.xcassets/Blue/Contents.json
  • Projects/Presentation/Resources/Colors.xcassets/Blue/Blue300.colorset/Contents.json
  • Projects/Presentation/Resources/Images.xcassets/Report/icon_car.imageset/Contents.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-30T03:56:18.617Z
Learnt from: choijungp
Repo: YAPP-Github/Bitnagil-iOS PR: 30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.

Applied to files:

  • Projects/DataSource/Sources/Repository/LocationRepository.swift
🧬 Code graph analysis (6)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (2)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
  • fetchCoordinate (24-37)
  • fetchAddress (39-47)
Projects/DataSource/Sources/Repository/ReportRepository.swift (1)
  • report (11-13)
Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
  • fetchCoordinate (24-37)
  • fetchAddress (39-47)
Projects/Domain/Sources/Protocol/UseCase/ReportUseCaseProtocol.swift (1)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (2)
  • fetchCurrentLocation (17-21)
  • report (23-25)
Projects/DataSource/Sources/Repository/LocationRepository.swift (2)
Projects/DataSource/Sources/NetworkService/NetworkService.swift (1)
  • request (25-48)
Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (1)
  • toLocationEntity (131-138)
Projects/Domain/Sources/DomainDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (2)
  • register (14-16)
  • resolve (18-25)
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (1)
Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (1)
  • fetchCurrentLocation (17-21)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift

[Warning] 42-42: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

🔇 Additional comments (22)
Projects/Presentation/Resources/Images.xcassets/Report/icon_hammer.imageset/Contents.json (1)

1-23: LGTM!

표준 Xcode 에셋 카탈로그 형식을 올바르게 따르고 있으며, 1x/2x/3x 스케일 변형이 모두 정의되어 있습니다.

Projects/Presentation/Resources/Images.xcassets/Report/icon_light.imageset/Contents.json (1)

1-23: LGTM!

표준 Xcode 에셋 카탈로그 형식을 올바르게 따르고 있으며, 모든 스케일 변형이 적절히 정의되어 있습니다.

Projects/Presentation/Sources/Common/DesignSystem/BitnagilColor.swift (2)

62-62: LGTM!

기존 패턴을 일관되게 따라 green300 색상 상수가 올바르게 추가되었습니다.


105-107: LGTM!

Blue Colors 섹션과 blue300 색상 상수가 기존 구조와 일관되게 추가되었습니다.

Projects/Presentation/Resources/Images.xcassets/Report/icon_water.imageset/Contents.json (1)

1-23: LGTM!

표준 Xcode 에셋 카탈로그 형식을 올바르게 따르고 있으며, 모든 스케일 변형이 적절히 정의되어 있습니다.

Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)

79-82: LGTM!

제보 카테고리 UI를 위한 4개의 아이콘 상수가 기존 패턴을 일관되게 따라 올바르게 추가되었습니다. 에셋 이름이 카탈로그와 일치합니다.

Projects/Domain/Sources/DomainDependencyAssembler.swift (1)

66-73: LGTM!

ReportUseCase의 의존성 주입 등록이 올바르게 구현되었습니다. LocationRepositoryProtocol과 ReportRepositoryProtocol을 모두 resolve하고, 없을 경우 명확한 에러 메시지와 함께 실패하도록 되어 있습니다.

Projects/Domain/Sources/Protocol/UseCase/ReportUseCaseProtocol.swift (1)

8-12: LGTM!

프로토콜 정의가 명확하고 적절합니다. 비동기 메서드 시그니처도 올바르게 선언되었습니다.

Projects/Domain/Sources/UseCase/Report/ReportUseCase.swift (2)

8-15: LGTM!

의존성 주입을 통한 초기화가 올바르게 구현되었습니다. 생성자에서 필요한 리포지토리들을 주입받고 있습니다.


17-21: LGTM!

위치 정보 조회 로직이 명확합니다. 먼저 좌표를 가져오고, 좌표가 있을 때만 주소를 조회하는 흐름이 적절합니다.

Projects/Domain/Sources/Protocol/Repository/LocationRepositoryProtocol.swift (1)

8-12: LGTM!

위치 리포지토리 프로토콜이 명확하게 정의되었습니다. 좌표 조회와 주소 변환 기능을 분리한 설계가 적절합니다.

Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift (3)

34-34: LGTM!

ReportUseCase 의존성 주입이 올바르게 추가되었습니다.


45-46: LGTM!

생성자를 통한 의존성 주입이 적절하게 구현되었습니다.


49-49: LGTM!

카테고리 퍼블리셔가 ReportType.name을 사용하도록 변경되어 과거 리뷰 피드백이 반영되었습니다.

Projects/DataSource/Sources/Repository/LocationRepository.swift (4)

17-22: LGTM!

CLLocationManager 초기화 및 설정이 적절합니다. delegate 설정과 정확도 지정이 올바르게 되어 있습니다.


24-37: LGTM!

좌표 조회 로직이 적절합니다. 위치 서비스 활성화 여부와 권한 상태를 확인한 후 continuation을 통해 비동기적으로 좌표를 가져오는 흐름이 올바릅니다.


39-47: LGTM!

Kakao API를 통한 역지오코딩 로직이 명확합니다. 좌표를 기반으로 주소를 조회하고 fallback 좌표를 함께 전달하는 구조가 적절합니다.


69-81: LGTM!

위치 업데이트 및 에러 처리 델리게이트 메서드가 올바르게 구현되었습니다. continuation을 적절히 재개하고 정리하고 있습니다.

Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift (4)

11-26: LGTM!

Kakao API 응답의 메타데이터 구조가 올바르게 정의되었습니다. CodingKeys를 통한 snake_case 매핑도 적절합니다.


29-59: LGTM!

Document 구조의 computed property를 통한 좌표 fallback 체인이 잘 설계되었습니다. y/x → address → roadAddress 순서로 값을 찾는 로직이 적절합니다.


62-92: LGTM!

도로명 주소 구조가 명확하게 정의되었습니다. 모든 필드에 대한 CodingKeys 매핑과 computed property가 올바릅니다.


95-127: LGTM!

지번 주소 구조가 명확하게 정의되었습니다. CodingKeys 매핑과 computed property가 적절합니다.

Comment on lines +131 to +138
func toLocationEntity(fallbackLongitude: Double? = nil, fallbackLatitude: Double? = nil) -> LocationEntity? {
guard let doc = documents.first else { return nil }
let longitude = doc.longitude ?? fallbackLongitude
let latitude = doc.latitude ?? fallbackLatitude
let address = doc.roadAddress?.addressName ?? doc.address?.addressName

return LocationEntity(longitude: longitude ?? 0, latitude: latitude ?? 0, address: address)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

좌표가 nil일 때 (0, 0) 반환을 재고하세요.

137번째 줄에서 longitude ?? 0latitude ?? 0를 사용하면 좌표를 가져오지 못했을 때 (0, 0)이 반환됩니다. 이는 기니만(Gulf of Guinea)의 실제 위치이므로 잘못된 좌표로 오해될 수 있습니다.

좌표가 없는 경우 nil을 반환하거나, LocationEntity를 Optional로 만드는 것을 고려하세요.

다음 중 하나를 선택하세요:

옵션 1: nil 반환 (권장)

 func toLocationEntity(fallbackLongitude: Double? = nil, fallbackLatitude: Double? = nil) -> LocationEntity? {
     guard let doc = documents.first else { return nil }
     let longitude = doc.longitude ?? fallbackLongitude
     let latitude = doc.latitude  ?? fallbackLatitude
+    
+    guard let longitude = longitude, let latitude = latitude else { return nil }
     let address = doc.roadAddress?.addressName ?? doc.address?.addressName

-    return LocationEntity(longitude: longitude ?? 0, latitude: latitude ?? 0, address: address)
+    return LocationEntity(longitude: longitude, latitude: latitude, address: address)
 }

옵션 2: LocationEntity의 좌표를 Optional로 변경
(이 경우 Domain 계층의 LocationEntity 수정 필요)

🤖 Prompt for AI Agents
Projects/DataSource/Sources/DTO/KakaoLocationResponseDTO.swift lines 131-138:
the current implementation substitutes missing longitude/latitude with 0 which
yields a misleading (0,0) location; instead either return nil when coordinates
are missing (recommended) or propagate optional coordinates into LocationEntity
(alternative). Implement the recommended fix by checking that doc.longitude and
doc.latitude (or the fallbacks) are non-nil before constructing a LocationEntity
and return nil if either is missing; if you opt for the alternative, update
LocationEntity to accept Optional<Double> for longitude/latitude and adjust all
call sites and domain types accordingly so they handle optional coordinates.

@@ -0,0 +1,12 @@
//
// ReportUseCaserProtocol.swift

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

파일명 주석의 오타를 수정하세요.

주석에 파일명이 ReportUseCaserProtocol.swift로 되어 있는데, 실제 파일명은 ReportUseCaseProtocol.swift이어야 합니다 ("Caser"가 아닌 "CaseProtocol").

-//  ReportUseCaserProtocol.swift
+//  ReportUseCaseProtocol.swift
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// ReportUseCaserProtocol.swift
// ReportUseCaseProtocol.swift
🤖 Prompt for AI Agents
In Projects/Domain/Sources/Protocol/UseCase/ReportUseCaseProtocol.swift around
line 2, the file header comment contains a typo: "ReportUseCaserProtocol.swift".
Replace that comment with the correct filename "ReportUseCaseProtocol.swift" so
the header matches the actual file name.

Comment on lines +91 to 100
Task {
do {
self.location = try await reportUseCase.fetchCurrentLocation()
} catch {

}

locationSubject.send(location?.address)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

빈 catch 블록에서 에러를 처리하세요.

위치 정보 조회 중 발생한 에러가 무시되고 있습니다. 사용자에게 에러를 알리거나 최소한 로그를 남겨야 합니다.

다음과 같이 에러를 처리하세요:

 private func configureLocation() {
     Task {
         do {
             self.location = try await reportUseCase.fetchCurrentLocation()
         } catch {
-            
+            BitnagilLogger.log(logType: .error, message: "위치 정보 조회 실패: \(error)")
+            exceptionSubject.send("위치 정보를 가져올 수 없습니다.")
         }

         locationSubject.send(location?.address)
     }
 }
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Report/ViewModel/ReportViewModel.swift around
lines 91–100, the catch block for fetchCurrentLocation() is empty and silently
swallows errors; log the error (e.g., print or os_log) and publish it so the UI
can react (e.g., send an Error or user-facing message via an existing
errorSubject / @Published error property or create one if missing), ensure
locationSubject.send(nil) or location?.address is still sent after handling, and
keep the Task structure intact.

@taipaise

Copy link
Copy Markdown
Collaborator Author

@choijungp 코드 리뷰 반영 + 색상 asset 추가 해두었습니다! 감사합니다!

@taipaise
taipaise merged commit 87035d6 into develop Nov 17, 2025
2 checks passed
@taipaise
taipaise deleted the feat/report branch November 19, 2025 14:09
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