Skip to content

[Work 45] 장소 검색/주변 장소/역지오코딩을 카카오 로컬 API로 연동했습니다. - #30

Open
snughnu wants to merge 23 commits into
developfrom
WORK-45
Open

snughnu wants to merge 23 commits into
developfrom
WORK-45

Conversation

@snughnu

@snughnu snughnu commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

JIRA

📝 작업 내용

📌 요약

  • 다음 3개를 Mock에서 카카오 로컬 API 기반 실제 구현으로 교체
    • PlaceSearchRepository(키워드 검색)
    • NearbyPlaceRepository(주변 장소)
    • ReverseGeocodingRepository(역지오코딩)
  • API 키 관리, Endpoint/APIClient 공용 네트워크 레이어를 새로 구축
  • 교체 전 Mock으로는 작성되어있지 않던 응답 경쟁, 빈 키워드 요청, 실패 처리를 수정
  • 채팅 위치 공유, 경로 검색 출발지의 "현재 위치" 하드코딩 주소를 실제 주소로 교체

🔍 상세

[1] API 키 / 네트워크 레이어

  • Config.xcconfig에 카카오 REST API 키 추가, Info.plist에 KakaoRestAPIKey 항목으로 연결
  • APIKey: Info.plist 키 조회 캡슐화, 키 미설정 시 assertionFailure로 개발 중 조기 발견
  • NetworkError → AppError 매핑 추가
  • Endpoint 프로토콜: baseURL/path/method/query/header를 선언적으로 표현, makeURLRequest()로 URLRequest 생성
  • APIClient(URLSessionAPIClient): async throws 기반 공용 요청 실행기
    상태 코드 검증 → 디코딩 → NetworkError 매핑까지 한 곳에서 처리

[2] Repository async 전환

  • 3개 Repository 프로토콜과 UseCase를 completion에서 async throws로 전환
  • SearchPlaceCardViewModel, PlaceSelectionViewModel이 새 요청 시작 전 이전 Task를 취소하도록 변경
    • 검색어 연타, 지도 빠른 이동 시 과거 응답이 최신 결과를 덮던 문제 방지

[3] 카카오 로컬 API 연동

  • KakaoLocalEndpoint 다음 3종 정의
    • 키워드 검색(/v2/local/search/keyword.json)
    • 카테고리 검색(/v2/local/search/category.json)
    • 좌표→주소(/v2/local/geo/coord2address.json)
  • KakaoCategoryGroupCode
    • 카카오 카테고리 그룹 코드 전체(18종)를 enum으로 정의
    • 실제 사용하는 카테고리만 참조하도록 해 매직 스트링 제거
  • KakaoPlaceResponse/KakaoAddressResponse
    • 응답 DTO → Place 매핑
    • category_group_code 기반 PlaceType 매핑
  • KakaoNearbyPlaceRepository
    • 카테고리 검색이 요청당 카테고리 1개만 지원하므로, 관심 카테고리(지하철/음식점/카페/병원/마트/편의점) 6개를
      withThrowingTaskGroup으로 병렬 요청 후 가장 가까운 결과 채택
  • AppDIContainer+Register에서 3개 Repository를 Mock에서 실제 구현으로 교체

[4] 현재 위치 표시 개선

  • 채팅 "내 위치 공유" 확인창, 경로 검색 "현재 위치" 출발지 모두 하드코딩된 고정 문자열 대신
    GetNearbyPlaceUseCase로 조회한 실제 장소명/주소 표시

💬 리뷰 노트

[1] 카카오 로컬 API를 선택한 이유

이번에 필요한 기능은 키워드 검색, 좌표 반경 내 POI 조회, 역지오코딩 3가지입니다. 검토한 선택지는 아래와 같습니다.

  • 카카오 로컬 API
    • 3가지 기능을 API 하나로 전부 지원합니다.
    • 키/에러 처리가 하나로 통일되고, 국내 데이터 품질이 높고 무료 쿼터도 넉넉합니다.
  • 네이버(NCP + Developers)
    • 지도 SDK와 같은 회사라는 장점은 있지만,
      역지오코딩(NCP)과 키워드 검색(Developers)이 서로 다른 서비스라 키가 두 벌 필요합니다.
    • 결정적으로 좌표 반경 검색 API 자체가 없어서 주변 장소 조회 기능을 대체 구현해야 합니다.
    • 키워드 검색 결과도 최대 5개로 적습니다.
  • Apple MapKit: 키가 필요 없고 무료지만, 국내 장소 데이터 품질이 상대적으로 낮습니다.
  • Google Places: 국내 데이터가 제한적이고 유료(결제 계정 등록 필요)입니다.

필요한 기능을 가장 적은 통합 비용으로 커버하는 카카오 로컬 API를 선택했습니다.
다만 카카오 로컬 API는 국내 좌표만 지원합니다.
해외 좌표에서는 검색/조회가 모두 실패하고 폴백 문구가 표시됩니다.
이 앱이 국내 사용자만 대상으로 한다는 전제하에 진행했고,
추후 해외 지원이 필요해지면 MapKit 등을 보조로 추가하는 등의 별도 작업이 필요합니다.


[2] 후속 이슈 (별도 이슈 분리)

  • 기능 동작에는 문제없지만 별도 티켓으로 분리해 추적하려 합니다.
    • 1. 키워드 검색 결과 누락
      • "양산" 검색 시 "양산역"처럼 기대하는 장소가 결과에 없음.
      • 카카오 키워드 검색 응답 첫 페이지(size=15, API 자체 최댓값)만 조회하는데,
        광범위한 지명 키워드는 관련도 랭킹에서 원하는 결과가 15위 밖으로 밀릴 수 있음.
    • 2. 주변 장소 조회 시 부속 시설 선택
      • 서울시청 근처에서 "서울시청 구내식당"처럼 대표성 낮은 결과가 선택됨.
      • 카테고리별 거리순 응답에서 가장 가까운 결과 1개만 채택하는데,
        카카오 좌표 데이터가 세분화되어 있어 랜드마크보다 그 안의 작은 상호가 좌표상 더 가깝게 잡히는 경우가 있음.

jira - 정확도 향상이 필요하다는 이슈


[3] 기타 리뷰 포인트

  • 카카오 카테고리 검색 기반 주변 장소 조회는 지도 이동 1회당 카테고리 수(6개)만큼 API를 호출합니다.
    • 카테고리 검색이 요청당 카테고리 1개만 지원하는 API 제약 때문입니다.
    • PlaceSelectionViewModel의 기존 디바운스(300ms)/최소 이동 거리(20m) 필터로 호출량을 억제하고 있지만,
      실사용 패턴에 따라 쿼터 소모가 클 수 있어 모니터링이 필요합니다.
  • 역지오코딩 결과의 name과 address를 같은 값(도로명 주소)으로 채웠습니다.
    • 채팅/경로 검색 화면이 place.name 하나만 참조해도 항상 의미 있는 문구가 나오게 하기 위한 선택입니다.

📸 영상 / 이미지

build.mp4

시뮬레이터 위치 설정(37.5665, 126.9780)

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

🤖 AI 코드 리뷰 - 아키텍처 관점

completion 기반 Repository 시그니처를 async/throws로 전환하고, 카카오 로컬 API 기반 실제 구현체(KakaoNearbyPlaceRepository, KakaoPlaceSearchRepository, KakaoReverseGeocodingRepository)와 공용 네트워크 레이어(APIClient, Endpoint, NetworkError)를 추가하는 PR이다. 계층 분리 원칙은 전반적으로 준수되어 있으나, Data 계층 응답 모델이 Domain 에러 타입(AppError)을 직접 참조하는 부분과, APIClient 프로토콜이 구체 타입(NetworkError)과 결합된 에러 흐름, KakaoLocalEndpoint의 헤더 계산 시 APIKey를 직접 호출하는 구조에 아키텍처적 우려가 있다.

Comment thread WhereAreYou/WhereAreYou/Data/Network/Kakao/KakaoAddressResponse.swift Outdated
Comment thread WhereAreYou/WhereAreYou/Data/Network/Kakao/KakaoLocalEndpoint.swift
Comment thread WhereAreYou/WhereAreYou/Data/KakaoNearbyPlaceRepository.swift

@sangYuLv sangYuLv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

고생 많으셨습니다!
네트워크 레이어 이후에 잘 사용할 수 있게 잘 구축해주셨네요!
코멘트 확인 부탁드립니다 🦦

let place = try? await self.getNearbyPlaceUseCase.execute(coordinate: coordinate)
guard !Task.isCancelled else { return }
completion(coordinate, place?.name ?? "현재 위치")
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

하나의 유즈케이스 안에서 현재 좌표 받고, 그 좌표로 근처 장소 검색한 뒤 결과를 ChatViewModel에게 돌려주는 방향은 어떨까요?

hasSearched = false
errorMessage = nil
allPlaces = []
applyFilter()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

장소 검색 결과가 비도록 처리했는데 applyFilter 호출이 필요한 이유가 궁금합니다!

/// 카카오 카테고리 검색 기반 주변 장소 조회
/// 카테고리 검색은 요청당 카테고리 1개만 지원하므로, 관심 카테고리를 병렬 요청한 뒤 가장 가까운 결과 1개를 선택
/// 지도 이동 1회당 카테고리 수만큼 호출이 발생 — PlaceSelectionViewModel의 디바운스/최소 이동 거리 필터로 호출량을 제한
final class KakaoNearbyPlaceRepository: NearbyPlaceRepository {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

카테고리 검색 기반인 이유가 우리 서비스에서 다루는 장소 타입만 검색해서 가져올 목적인 것으로 이해했습니다.

처음 코드를 읽을 때, 키워드로 장소 검색하는 곳에서 사용하는 레파지토리인 줄 알았습니다.
장소 검색 화면에서 사용자가 장소를 필터링하는 기능 때문에 카테고리 기반이라는 설명이 그 화면에서 사용되는 것이라고 이해됐었습니다.

PlaceType으로 매핑되는 카테고리만 포함하는 것도 중요한 내용이지만, 가까운 장소를 찾아내는 것만 주석에서 강조해도 좋을 듯 합니다!

+) 카테고리 수만큼 호출이 발생하는 게 부담스러워진다면 (1)근처 장소 목록을 조회하고, (2)우리가 다루는 카테고리에 포함되는 장소가 있는지 확인하고 (3)없다면 반경을 넓혀서 다시 조회하는 게 호출 횟수를 줄이는 방향일 수도 있을 듯 합니다! 검토 부탁드려요!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants