프로젝트

여행지 추천 기능 구현 중 (TourAPI 서비스 분류 코드 검색)

TMJeti 2025. 4. 9. 14:39

이제부터 FB에 저장할 정보들이 조금 중요해진다. 순서를 대략 구성해보면 이렇다.

 

1. TourAPI 엔드포인트에서 내가 원하는 관광지 정보들을 꺼내온다.

2. Firebase에 저장

3. FB에 저장된 정보들을 가지고 openAI가 추천 ("이 장소는 힐링에 어울린다!")

 

처음써본 GPT 도식화 신기해서 넣어봄

 

...벌써부터 머리가 어지러워지는데 일단 TourAPI 활용 문서부터 살펴보고 저장은 추후에 하고 텍스트만 가져와보자.

 

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:dotenv/dotenv.dart' as dotenv;

Future<void> main() async {
  final env = dotenv.DotEnv()..load();

  final tourApiKey = env['TOUR_API_KEY'];
  final areaCode = 3; // 대전

  final tourUrl = Uri.parse(
    'https://apis.data.go.kr/B551011/KorService1/areaBasedList1'
    '?serviceKey=$tourApiKey'
    '&numOfRows=1'
    '&pageNo=1'
    '&MobileOS=ETC'
    '&MobileApp=AITrip'
    '&_type=json'
    '&areaCode=$areaCode',
  );

  final response = await http.get(tourUrl);

  if (response.statusCode != 200) {
    print('❌ TourAPI 요청 실패: ${response.statusCode}');
    return;
  }

  // ✅ UTF-8로 수동 디코딩 (깨짐 방지 핵심!)
  final decodedBody = utf8.decode(response.bodyBytes);
  final jsonData = json.decode(decodedBody);

  final items = jsonData['response']['body']['items']['item'];

  if (items == null || items.isEmpty) {
    print('📭 가져온 장소 데이터 없음');
    return;
  }

  // ✅ 단일 객체 또는 리스트 처리 (numOfRows=1일 때 방어코드)
  final List<dynamic> places = items is List ? items : [items];

  for (final item in places) {
    final title = item['title'] ?? '제목 없음';
    final address = item['addr1'] ?? '주소 없음';
    final image = item['firstimage'] ?? '이미지 없음';
    final lat = item['mapy'] ?? '위도 없음';
    final lng = item['mapx'] ?? '경도 없음';
    final contentTypeId = item['contenttypeid'];
    final cat1 = item['cat1'];
    final cat2 = item['cat2'];
    final cat3 = item['cat3'];

    print('---');
    print('📍 제목: $title');
    print('📫 주소: $address');
    print('🖼 이미지: $image');
    print('📍 좌표: 위도=$lat / 경도=$lng');
    print('📦 contentTypeId: $contentTypeId');
    print('📁 cat1: $cat1 / cat2: $cat2 / cat3: $cat3');
  }
}

 

 제목: 가나샤브샤브
 주소: 대전광역시 유성구 신성로84번길 49
 이미지: http://tong.visitkorea.or.kr/cms/resource/30/3062830_image2_1.JPG
 좌표: 위도=36.3890161659 / 경도=127.3512700000
 contentTypeId: 39
 cat1: A05 / cat2: A0502 / cat3: A05020100

 

음 잘 저장된다. 이미지까지는 기본 장소 정보이고 좌표는 여행의 반경을 정하는데 쓰이고 contentTypeId와 cat 분류는 우리 프로젝트에서 사용자에게 물어보는 여행  스타일, 테마를 매핑하는데 쓰일거다. (생각이 여기에 도달하는데까지 오전시간이 꼬박 지나가버렸다...)

 

cat 대분류 중분류 소분류 기준에 대한 설명을 찾는 방법이 두가지 있다.

TourAPI 사이트 -> openAPI -> 서비스 목록 -> 국문 관광정소 서비스 검색 후 매뉴얼 다운받으면 그 안에 상세하게 적혀있는 엑셀 파일이있고

 

 

가이드란에 서비스 분류코드 검색으로 넘어가서 아무것도 입력안하고 검색 버튼만 눌러도 해당 코드가 어떤 기준으로 분류되었나 알 수 있다. 기쁘게도 이걸 이제 매핑해야 한다. 꺄하~

final Map<String, List<String>> tagMap = {
  // contentTypeId
  '12': ['관광지', '힐링'],
  '14': ['문화시설', '배움이 있는'],
  '28': ['레저', '스포츠'],
  // ...
};

 

TourAPI에서 분류코드를 가져오고, 이런식으로 매핑 후 firestore에는 분류코드가 아닌 해당 분류코드에 있는 한글 태그 부분들을 저장하게 되는 것이다.