[Android/공공데이터앱] 외부 검색 API와 내부 관리자 API를 함께 사용할 때의 경계
Android 앱을 만들다 보면 외부 검색 API와 내부 관리자 API를 함께 사용해야 하는 경우가 있습니다. 예를 들어 장소 검색은 외부 검색 API를 사용하고, 앱에서 보여줄 공지, 카테고리, 지역별 설정은 내부 관리자 API에서 가져오는 식입니다.
API 호출 코드만 빠르게 붙이면 처음에는 동작하는 것처럼 보일 수 있습니다. 다만 외부 API와 내부 API의 역할을 분리하지 않으면 응답 모델이 섞이고, 실패 처리도 복잡해집니다. 나중에는 어떤 데이터가 외부에서 온 것인지, 어떤 데이터가 내부 운영 데이터인지 구분하기 어려워질 수 있습니다.
작업을 진행하다 보면 API를 많이 붙이는 것보다 각 API의 책임을 분명히 나누는 일이 더 중요해집니다. 이 글에서는 Android 앱에서 외부 검색 API와 내부 관리자 API를 함께 사용할 때 경계를 나누는 기준을 정리해보겠습니다.
외부 API와 내부 API는 목적이 다릅니다
외부 API는 보통 지도, 장소 검색, 주소 검색, 이미지 검색처럼 앱 밖의 플랫폼이 제공하는 데이터를 가져오는 데 사용합니다. 내부 API는 앱 운영자가 관리하는 데이터, 앱 설정, 공지, 카테고리, 지역별 노출 여부처럼 서비스 운영에 필요한 정보를 다루는 경우가 많습니다.
| 구분 | 주요 역할 | 예시 데이터 | 주의할 점 |
|---|---|---|---|
| 외부 검색 API | 외부 플랫폼의 검색 결과 조회 | 장소명, 주소, 좌표, 외부 링크 | 키 노출, 호출 제한, 응답 변경 가능성을 고려한다. |
| 내부 관리자 API | 서비스 운영 데이터 제공 | 공지, 카테고리, 지역 설정, 앱 안내 문구 | 관리자 URL, 권한, 내부 코드를 공개하지 않는다. |
| 공공데이터 API | 공공기관 데이터 조회 | 기관 데이터, 행정 정보, 지역별 목록 | 최신성, 제공 범위, 오류 응답을 확인한다. |
역할이 다른 API를 하나의 client나 하나의 응답 모델로 처리하면 코드가 적어 보일 수 있습니다. 운영 관점에서는 API마다 client, DTO, 에러 처리를 분리하는 편이 더 안전합니다. 응답 변경이나 장애가 발생했을 때 영향 범위를 좁힐 수 있기 때문입니다.
API client는 역할별로 분리하기
외부 API와 내부 API는 base URL, 인증 방식, timeout, 에러 메시지, 응답 구조가 다를 가능성이 큽니다. 그래서 하나의 API client에 모두 넣기보다 역할별로 나누는 것이 좋습니다.
- 외부 검색 API client는 검색어, 좌표, 페이지 같은 검색 조건을 중심으로 구성한다.
- 내부 관리자 API client는 앱 설정, 카테고리, 공지, 지역 상태를 중심으로 구성한다.
- 공공데이터 API client는 기관 응답 형식과 오류 코드를 별도로 처리한다.
- 공통 네트워크 설정은 분리하되, 응답 모델까지 억지로 합치지 않는다.
예를 들어 Retrofit을 사용한다면 service interface를 나눠두는 방식이 이해하기 쉽습니다. 아래 코드는 구조 설명을 위한 예시이며 실제 API 주소나 인증 값은 포함하지 않았습니다.
interface ExternalSearchApi {
suspend fun searchPlace(
keyword: String,
page: Int
): ExternalSearchResponse
}
interface AdminApi {
suspend fun getAppNotice(): NoticeResponse
suspend fun getRegionConfig(): RegionConfigResponse
}
이렇게 나누면 외부 검색 API의 응답이 바뀌어도 내부 관리자 API 코드에 영향을 덜 줍니다. 반대로 관리자 API의 공지 구조가 바뀌어도 검색 기능을 직접 건드리지 않아도 됩니다.
코드를 나눠보다 보면 client 분리는 단순한 파일 정리가 아니라 장애 범위를 줄이는 기준이 됩니다. 검색이 실패했을 때 공지까지 같이 깨지지 않도록, API의 목적에 맞춰 책임을 나누는 편이 좋습니다.
응답 모델과 화면 모델을 바로 연결하지 않기
API 응답 DTO를 화면에서 바로 사용하는 방식은 빠르게 구현할 수 있습니다. 그러나 외부 API와 내부 API를 함께 쓰는 앱에서는 화면 모델을 따로 두는 편이 좋습니다. 외부 API 응답에는 앱에서 쓰지 않는 필드가 많고, 내부 API 응답은 운영 정책에 따라 바뀔 수 있기 때문입니다.
| 모델 | 역할 | 변경 원인 | 화면 사용 여부 |
|---|---|---|---|
| 외부 API DTO | 외부 검색 API 응답을 그대로 받음 | 외부 플랫폼 응답 변경 | 직접 사용하지 않는 편이 좋음 |
| 내부 API DTO | 관리자 API 응답을 그대로 받음 | 운영 데이터 구조 변경 | 직접 사용을 피함 |
| Domain Model | 앱 내부에서 의미 있는 데이터로 변환 | 앱 기능 변경 | ViewModel에서 사용하기 좋음 |
| UI Model | 화면 표시용 텍스트와 상태를 담음 | 화면 디자인 변경 | 화면에서 사용 |
화면 모델을 따로 두면 외부 API 응답 필드명이 바뀌거나 내부 API에서 값이 추가되어도 화면 전체가 흔들리지 않습니다. 실제로 구현 흐름을 따라가다 보면 이 분리 하나가 테스트와 디버깅에도 도움이 됩니다.
mapper로 화면 모델 만들기
외부 API와 내부 API의 응답을 화면에서 바로 쓰지 않으려면 중간에 mapper를 두는 것이 좋습니다. mapper는 API 응답을 앱 내부에서 쓰기 좋은 모델로 바꿔주는 역할을 합니다.
data class PlaceUiModel(
val title: String,
val subtitle: String,
val sourceLabel: String
)
fun ExternalSearchResponse.toPlaceUiModel(): PlaceUiModel {
return PlaceUiModel(
title = title.orEmpty(),
subtitle = address.orEmpty(),
sourceLabel = "외부 검색 결과"
)
}
이 예시는 단순하지만 방향은 분명합니다. 화면은 외부 API 응답 구조를 직접 알지 않고, 이미 정리된 UI model만 받습니다. 외부 응답 필드가 바뀌면 mapper를 중심으로 수정하면 되기 때문에 화면 코드가 덜 흔들립니다.
fallback 기준을 미리 정하기
외부 API는 호출 제한, 네트워크 오류, 응답 형식 변경, 일시적인 장애가 발생할 수 있습니다. 내부 API도 서버 배포나 관리자 설정 문제로 실패할 수 있습니다. API별로 실패했을 때 앱이 어떻게 동작할지 미리 정해두는 것이 좋습니다.
| 실패 위치 | 사용자 영향 | fallback 방향 |
|---|---|---|
| 외부 검색 API 실패 | 검색 결과를 가져오지 못함 | 다시 시도, 검색 조건 확인, 네트워크 안내 |
| 내부 공지 API 실패 | 공지 미노출 | 기본값 사용, 공지 영역 숨김 |
| 지역 설정 API 실패 | 지원 지역 판단 어려움 | 안전한 기본 상태, 최신 확인 안내 |
| 공공데이터 API 실패 | 행정 정보 조회 실패 | 일시 오류 안내, 공식 안내 확인 문구 |
모든 실패를 오류가 발생했다는 문구 하나로 처리하면 사용자는 다음 행동을 알기 어렵습니다. 검색 API 실패와 공지 API 실패는 사용자 영향이 다르기 때문에 메시지도 다르게 구성하는 편이 좋습니다.
timeout과 에러 메시지는 API별로 다르게 보기
외부 검색 API는 사용자가 직접 기다리는 흐름에 들어가는 경우가 많습니다. 검색 버튼을 누른 뒤 몇 초 동안 결과가 없으면 앱이 멈춘 것처럼 느껴질 수 있습니다. 반면 내부 공지 API는 화면 진입 시 백그라운드로 받아오고 실패 시 숨겨도 되는 경우가 있습니다.
- 검색 API는 응답 지연이 UX에 바로 영향을 준다.
- 공지 API는 실패해도 앱 사용을 막지 않는 방향을 검토한다.
- 지역 설정 API는 기본값을 잘못 잡으면 잘못된 안내로 이어질 수 있다.
- 결제나 신고 같은 민감한 흐름과 연결된다면 실패 시 진행을 막고 안내한다.
따라서 timeout 값과 에러 문구도 API 성격에 맞춰 다르게 잡는 것이 좋습니다. 특히 행정 정보나 신고 안내처럼 정확성이 필요한 화면에서는 대체 데이터를 무리하게 보여주기보다 확인이 필요하다는 메시지를 제공하는 편이 안전합니다.
보안 기준은 클라이언트 노출을 기준으로 잡기
Android 앱은 배포 후 사용자의 기기에 설치됩니다. 앱 안에 들어간 문자열, URL, 일부 설정값은 완전히 숨긴다고 보기 어렵습니다. 그래서 클라이언트에 넣어도 되는 값과 서버 또는 관리자 영역에만 있어야 하는 값을 구분해야 합니다.
| 항목 | 클라이언트 포함 여부 | 권장 처리 |
|---|---|---|
| 공개 가능한 검색 API 식별자 | 정책상 허용되는 경우만 제한적으로 포함 | 도메인 제한, 패키지 제한, 호출량 모니터링 |
| 관리자 API URL | 가능하면 직접 노출하지 않음 | 공개 API와 관리자 API를 분리 |
| 관리자 토큰 | 포함 금지 | 서버에서 처리 |
| 내부 지역 코드 | 필요 최소한만 사용 | 화면에는 사용자 친화적 이름만 표시 |
| 사용자 검색어 로그 | 공개 예시로 사용하지 않음 | 개인 식별 가능성을 제거 |
보안 관점에서 가장 위험한 패턴은 외부 API 키와 내부 관리자 권한을 같은 레벨로 취급하는 것입니다. 외부 검색 API 호출에 필요한 값과 관리자 기능에 필요한 값은 성격이 다르므로 반드시 분리해서 봐야 합니다.
추천 구조
작은 앱이라도 아래 정도의 구조로 나누면 API 경계를 이해하기 쉬워집니다. 새 패턴을 억지로 만들기보다, 프로젝트의 기존 구조에 맞춰 service, repository, mapper 정도의 역할만 분리해도 충분합니다.
data/
api/
ExternalSearchApi
AdminApi
dto/
ExternalSearchResponse
NoticeResponse
RegionConfigResponse
mapper/
SearchResultMapper
NoticeMapper
repository/
SearchRepository
AppConfigRepository
ui/
search/
notice/
region/
중요한 것은 폴더 이름 자체가 아니라 책임의 방향입니다. 외부 API 응답을 화면에서 바로 쓰지 않고, 내부 API의 운영 데이터도 화면 표시용 모델로 한 번 정리해주는 흐름이 있으면 유지보수가 훨씬 수월해집니다.
테스트 기준
외부 API와 내부 API를 함께 사용할 때는 정상 응답만 확인하면 부족합니다. API별 실패, fallback, 메시지, 민감 정보 노출 여부를 함께 확인하는 편이 좋습니다.
- 외부 검색 API와 내부 관리자 API client가 분리되어 있는지 확인한다.
- API 응답 DTO를 화면에서 직접 사용하지 않는지 확인한다.
- 검색 API 실패와 내부 설정 API 실패의 사용자 메시지가 다른지 확인한다.
- 호출 제한, timeout, fallback 기준을 API별로 나눠 정리한다.
- 정책이나 API 응답 형식이 바뀔 수 있는 부분은 공식 문서 확인 문구를 남긴다.
자주 하는 실수
1. 외부 API와 내부 API를 하나의 client에 모두 넣는 경우가 있습니다. base URL, 인증 방식, timeout, 오류 메시지가 달라지면 하나의 client가 금방 복잡해질 수 있습니다.
2. API 응답 DTO를 화면에서 바로 사용하는 경우도 있습니다. 외부 응답 구조나 내부 운영 데이터가 바뀌면 화면 코드까지 같이 흔들릴 수 있습니다.
3. 모든 API 실패를 같은 오류 문구로 처리하는 경우가 있습니다. 검색 API 실패와 공지 API 실패는 사용자 영향이 다르기 때문에 안내 문구도 달라야 합니다.
4. 관리자 URL이나 내부 토큰을 클라이언트 코드에 넣는 경우도 있습니다. Android 앱 안에 들어간 값은 완전히 숨기기 어렵기 때문에 민감 정보는 서버에서 처리하는 편이 안전합니다.
마무리
Android 앱에서 외부 검색 API와 내부 관리자 API를 함께 사용하는 것은 흔한 구조입니다. 중요한 것은 API를 많이 붙이는 것이 아니라, 각 API가 어떤 책임을 가지는지 분명히 나누는 것입니다.
외부 API는 검색과 외부 데이터 조회에 집중하고, 내부 API는 앱 운영 데이터와 설정을 담당하도록 분리하면 코드 흐름이 단순해집니다. 응답 모델, fallback, 에러 메시지, 보안 기준까지 함께 나눠두면 나중에 API가 바뀌거나 기능이 늘어났을 때 수정 범위를 줄일 수 있습니다.
참고 자료
- Android Developers, Guide to app architecture
- Android Developers, App security best practices
- Retrofit 공식 문서
- 각 외부 검색 API 제공사의 인증 및 호출 제한 문서