백엔드(Node)와의 계약 문서다. 이 파일만 전달하면 된다.
Base URL: http://<서버IP>:8000
{ "x": 129.1187, "y": 35.1532, "state": "바다", "mood": "시원한", "k_tracks": 10 }| 필드 | 타입 | 필수 | 뜻 |
|---|---|---|---|
x |
float | O | 경도 (lng) — x가 경도다, 헷갈리기 쉬움 |
y |
float | O | 위도 (lat) |
state |
string|null | X | 여행 상태: 바다/드라이브/산책/카페/야경 |
mood |
string|null | X | 무드: 잔잔한/신나는/시원한/설레는/감성적인 |
k_tracks |
int | X | 곡 수 (기본 10) |
{
"tracks": [ { "artist": "적재", "title": "나랑 같이 걸을래" }, ... ],
"backgroundImageUrl": "http://tong.visitkorea.or.kr/.../3311245_image2",
"poi": {
"title": "광안리해수욕장", "type": "관광지", "dist": 25.3,
"name": "광안리해수욕장", "addr": "부산광역시 수영구 광안해변로 219",
"sido": "부산", "collapsedFrom": null,
"source": "catalog", "desc": "부산을 대표하는 해수욕장으로...",
"homepage": "...", "tel": "051-..."
},
"meta": {
"elapsedMs": 850, "poiResolved": true, "returned": 10,
"place": { "axis": "sea", "place_applied": true, "...": "..." },
"photo": { "source": "poi_image", "gate": null }, "poiSource": "catalog"
}
}{
"tracks": [ ...10곡, 무드만으로... ],
"backgroundImageUrl": null,
"poi": null,
"meta": { "poiResolved": false, "photo": { "source": null, "gate": null }, "...": "..." }
}POI를 못 구해도 500을 내지 않는다. 곡은 항상 나간다.
/recommend의 앞부분만 탄다: POI 확보(카탈로그 → 관광공사 폴백 → detailCommon2 보강) → 사진 선택. 곡 매칭이 없어서 더 빠르고, 같은 좌표면 /recommend가 주는 배경과 같은 사진이 나온다 — 파이프라인이 하나다.
쓰는 곳: Node 리캡 배경 추천. 기획서 "관광사진갤러리 → Recap 배경화면 제공" 항목의 구현이다.
{ "x": 129.1187, "y": 35.1532, "state": "바다", "mood": "시원한" }state·mood는 선택이다 — 갤러리 폴백의 질의문에만 쓰이고, POI 대표사진 (poi_image)이 있으면 영향이 없다. 리캡처럼 상태가 없는 호출은 좌표만 보내면 된다.
{
"backgroundImageUrl": "https://tong.visitkorea.or.kr/...jpg",
"poi": { "title": "광안리해수욕장", "...": "— /recommend의 poi와 같은 모양" },
"meta": {
"elapsedMs": 12,
"poiResolved": true,
"photo": { "source": "poi_image", "gate": null },
"poiSource": "catalog"
}
}/recommend 응답에서 tracks와 meta.place·meta.returned만 뺀 부분집합이다.
backgroundImageUrl 계약(키 항상 존재, null 가능)과 POI 없음 계약(500 없이
poiResolved: false)은 아래 /recommend 것과 동일하다.
내보내는 사진 URL은 항상 https://다. 원본 photos_ready.csv는 5,043장 중
3,711장(73.6%)이 http://tong.visitkorea.or.kr로 들어와 있어서, 같은 호스트의
https로 승격해 내보낸다(photo_recommend.to_https). POI 대표사진 211건도 같다.
이유 — iOS는 ATS 때문에 http 이미지 로드를 기본 차단한다. 그대로 내보내면 앱에서 배경이 통째로 안 뜬다. 호스트를 목록으로 제한하는 이유는, 모르는 호스트를 https로 바꿔놓고 그 호스트가 https를 안 받으면 원래 보이던 사진까지 죽기 때문이다.
- 키가 항상 존재한다. 값은 URL 문자열 또는
null. - 백엔드는
data?.backgroundImageUrl을 읽어 playlist의 배경 필드에 넣는다 — 현재undefined하드코딩 자리를 이 값으로 교체하면 된다. null이면 프론트가 기본 그라데이션 배경을 쓴다.- 키 자체가 없다면 그건 구버전 서버다 (아래 헬스로 판별 가능).
| 필드 | 뜻 |
|---|---|
tracks[] |
{artist, title} — 형태 불변. artist 빈 문자열 없음(로드 시 제거) |
backgroundImageUrl |
배경 사진. POI 대표 사진 → 관광공사 갤러리 → null 순 폴백 |
poi.title / type / dist |
대표 장소 이름·분류·미터 거리 (기존과 동일 키) |
poi.name / addr / sido |
카탈로그 원 이름·주소·시도 — 표시/디버깅용 신규 |
poi.collapsedFrom |
값이 있으면 내부 시설→대표 장소 교체됨 (예: "근정전") |
meta.place |
장소 축 판정 전문. place_applied가 위치 반영 여부 |
meta.photo.source |
"poi_image"(대표 사진) / "gallery"(갤러리 매칭) / null |
poi.source |
"catalog"(자체 카탈로그) / "tourapi"(관광공사 폴백) |
poi.desc / homepage / tel |
detailCommon2 보강 결과. 그 POI 첫 방문에는 비어 있다 — 개요는 백그라운드로 받아 캐시에 넣고 다음 요청부터 붙는다(응답을 막지 않으려고). 없으면 키 자체가 빠질 수 있으니 poi.desc ?? "" 로 읽을 것 |
meta.poiSource |
이 요청이 관광공사 폴백을 탔는지 |
meta.photo.gate |
gallery일 때만: place / place+sgg / place+sido / sigungu / sido / nationwide |
meta.elapsedMs |
서버 처리 시간. CPU 임베딩 포함 수백 ms대가 정상 |
모르는 키는 무시해도 안전하다 — 추가 키는 하위호환으로만 붙는다.
서버 생존 + 어떤 데이터를 물고 있는지. 배포 확인은 이걸로 한다.
{
"status": "ok",
"catalog": { "uniqueTracks": 3149, "sources": 695, "channels": 25,
"axes": { "...": "..." }, "corrections": { "...": "..." } },
"poi": { "count": 512, "file": "poi_catalog_v3.csv",
"aliases": 12, "parents": 13, "maxRadiusM": 5000 },
"photos": { "count": 5043, "embMatched": true, "sido": 16, "sigungu": 207 }
}| 항목 | 이전 | 지금 |
|---|---|---|
backgroundImageUrl |
없음 | 항상 존재 (null 가능) |
poi.hasOverview |
있음 | 제거 — 개요를 더 안 가져옴 |
poi.name/addr/sido/collapsedFrom |
없음 | 추가 |
meta.photo |
없음 | 추가 |
헬스 poiCache |
있음 | 제거 — 외부 API가 사라져 캐시도 없음. 대신 poi/photos |
| POI 출처 | 관광공사 API (요청당 최대 2회) | 자체 카탈로그 (외부 호출 0회) |
tracks 배열 형태는 바뀐 적이 없다 — normalizeMlTracks는 그대로 둔다.