데이터 수집 → 정제 → 저장까지의 설계와 시행착오
FLIK은 “2025 관광데이터 공모전” 참가 조건에 따라
한국관광공사 TourAPI (공공데이터포털 제공) 를 반드시 활용해야 했다.
그래서 개발 초기엔 다음 순서로 데이터 수집 인프라를 준비했다.
1️⃣ API 키 발급
2️⃣ 응답 구조 분석
addr1, addr2, cat1, cat2, cat3 … 같은 다단계 필드 존재3️⃣ content_type_id 별 데이터 분리 수집
TourAPI는 content_type_id에 따라 관광지 유형이 다르다.
FLIK에서는 이를 내부적으로 도메인별 테이블에 매핑했다.
| content_type_id | 수집 데이터 | 내부 저장 테이블 |
|---|---|---|
| 12 | 관광지 | fetched_tourist_attractions |
| 14 | 문화시설 | fetched_cultural_facilities |
| 15 | 축제/공연/행사 | fetched_festivals_events |
| 28 | 레포츠 | fetched_sports_recreation |
| 32 | 숙박 | fetched_accommodations |
| 38 | 쇼핑 | fetched_shopping |
| 39 | 음식점 | fetched_restaurants |
초기엔 FLIK 메인 서버에서 API 호출을 직접 처리했지만,
트래픽 제어와 스케줄링 효율을 위해 별도 배치 서버를 구성했다.
🎯 목적: 외부 API를 주기적으로 호출 → 가공 → MySQL 저장
(추후 Redis 캐시 및 검색엔진 확장 고려)
Scheduler (Spring Batch)
↓
TourAPIClient (RestTemplate)
↓
Raw Data (JSON/XML)
↓
DataParser (정제 로직)
↓
SpotEntity (도메인 매핑)
↓
MySQL 저장
@Scheduled(cron="0 0 1 * * ?")
→ 매일 새벽 1시에 수집 시작RestTemplate 기반 외부 호출
→ content_type_id별로 endpoint 반복 호출label_depth 매핑, 주소 정제fetched_* 임시테이블 → 정제 후 spot 본테이블로 마이그레이션TourAPI 응답은 단순하지 않다.
카테고리(cat1, cat2, cat3)와 지역정보(areacode, sigungucode)를
FLIK의 자체 비즈니스 분류(역사문화, 자연, 카페 등)로 매핑해야 했다.
| label_depth1 | label_depth2 | FLIK 카테고리 |
|---|---|---|
| A02 | A0201 | 역사문화 |
| A05 | A0502 | 전통시장 |
| C01 | C0112 | 카페 |
| A03 | A0302 | 테마파크 |
| A01 | A0101 | 자연 |
| A04 | A0401 | 실내여행지 |
| A06 | A0601 | 액티비티 |
📌 최종적으로
spot테이블에는category컬럼이 FLIK 내부 분류로 저장됨.
API 응답의 복잡한 코드 체계를 사람이 이해할 수 있는 형태로 단순화.
cat3가 없는 데이터
→ Optional.ofNullable() + 기본값 처리로 대응contentid를 기준으로 중복 필터링Thread.sleep) 적용fetch_log 테이블)StringEscapeUtils.unescapeHtml4() 사용최종적으로 다음과 같은 형태로 정제 데이터가 저장된다.
| 컬럼 | 설명 |
|---|---|
| id | PK |
| title | 장소명 |
| address | 주소 |
| category | 비즈니스 카테고리 |
| content_type_id | 원본 분류코드 |
| image_urls | 대표 이미지 |
| description | 설명 |
| phone | 연락처 |
| lat/lng | 좌표 |
| reg_date | 등록일 |
데이터를 가져와서 저장했다면,
이제 그걸 “사용자에게 어떻게 보여줄지”가 남았다.
다음 편에서는 데이터 검색 및 추천 시스템 설계를 다룬다.
(사용자 스와이프 패턴 + 카테고리 벡터 기반 추천 로직)
🔜 4편 예고: “나에게 맞는 여행지는 무엇일까?”