여행 가서 관광 서비스 프롬프톤 진행하기
결선 진출이라는 유의미한 결과를 얻어낸 2024년 원티드 프롬프톤에 이어, 2025년 원티드 프롬프톤의 소식을 전해 들으며 1년 만에 다시 개인으로 참가하게 되었습니다. 이번 프롬프톤은 한국관광공사의 TourAPI를 활용하여 차별화된 AI 관광 서비스를 만드는 것인데요.
이번에도 bubble.io로 직접 웹 서비스 개발까지 진행하며 TourAPI 외에도 여러 API를 연동하여 보다 고도화된 서비스를 제공하기 위해 고군분투를 했습니다. 그 덕에 전보다 더 재미있는 시도도 많이 하고 여러모로 성장할 수 있는 경험이 되었습니다. 개발한 AI 서비스에 대한 소개와 프롬프톤을 진행하며 마주친 이슈들, 그리고 이를 어떻게 해결했는지에 대해 이 글에 담아보았습니다.
(1) 서비스 소개
‘Sight-See Zip’은 명소라는 뜻의 'sight’와 관광하다는 뜻의 'sightsee' 그리고 시집의 발음을 영문으로 쓴 'see zip'을 합성하여 네이밍 하였습니다. 명소를 깊이 음미할 수 있도록 내 주변 또는 지도 주변에서 문화와 역사 이야기가 있는 명소 주제를 선정하고 이를 시로 작성하여 감성적으로 풍부한 여행 경험을 제공하는 '나만의 명소 시 생성 AI 서비스'입니다.
서비스에서는 크게 두 가지 기능을 제공하고 있습니다. 1) 내 주변의 명소와 관련된 주제로 시를 쓰거나 2) 지도에서 위치를 선택하고 주변 명소의 주제를 선정하여 시를 쓸 수도 있습니다. 이때 공통적으로 현재 날씨와 시간 정보를 활용하여 시를 작성하기 때문에 여행이 끝나고도 그 순간의 기억이 오래 지속될 수 있도록 했습니다. 또한, 시 하단에는 API에서 참고한 내용을 시 해설문처럼 답변하도록 하여 사용자가 시 내용을 보다 쉽게 이해하고 해석할 수 있도록 했습니다.
(2) 서비스 배경
초반 아이데이션은 '맞춤 여행지 추천'을 중심으로 진행되었는데요. 차별화된 추천 아이디어를 현실화하기에는 기술이나 데이터의 한계가 있었고, 효용 있는 추천이 아닌 그저 구색만 맞춘 추천에 그치는 한계가 있었습니다.
그래서 다시 백지상태로 돌아와 이번 프롬프톤의 목적인 '다양하고 참신한 관광서비스 개발'과 '국내 관광산업의 발전'에 집중하였는데요. 현재 활성화된 관광이 뭐가 있을까 생각하다가 문득 '드라마 촬영지 명소'가 뇌리를 스쳤습니다. '선업튀 솔이네 집', '우영우 팽나무', '도깨비 바다'처럼 드라마에 나온 촬영지, 특히 서사가 담겨있는 촬영지는 꾸준히 많은 사람들의 발길을 이끌고 기억되는 관광지가 되곤 합니다. 도깨비 방영이 9년이나 지났는데도 아직까지 도깨비 촬영지로 기억되고 불리는 것처럼 말이죠.
역사와 문화 이야기가 있는 명소에 서사와 문학 요소를 가미하면 단순히 정보를 전달하는 것을 넘어서 감정을 나누고 보다 친숙하게 접근할 수 있을 것이라고 보았습니다. 아이디어를 실현할 수 있는 적합한 데이터도 마침 한국관광공사에서 제공되었는데요. ‘관광지 오디오 가이드 API’는 오디오 데이터뿐만 아니라 해당 내용의 스크립트도 제공하기 때문에 AI에게 이를 기반으로 문학 콘텐츠의 생성을 요청할 수 있었습니다. 이러한 배경으로 시작하여 프롬프트 내용 보강과 서비스 고도화 작업을 거쳐 최종적으로는 아래와 같이 서비스 방향 및 범위가 확정되었습니다.
이번 프롬프톤은 작년과 비교하여 개인적으로 고된 일정을 소화해야 했습니다. 이직 준비 중인 백수에서 어느새 왕복 3시간 출퇴근러가 되었고, 관광 프롬프톤 일정 중에 우연찮게 2박 3일 주말여행도 예정되어 있었거든요.
오롯이 프롬프톤에 할애할 수 있는 시간이 별로 없었기 때문에 최대한 초기에 기본 구조를 빠르게 잡아 놓아야 했습니다. 그리고 이후에는 퇴근 후 1-3시간 정도 틈틈이 시간을 들였고, 심지어 여행 전날까지도 서비스 개발을 진행하고 여행 중에는 서비스 테스트를 진행해야 하는 요란스러운 상황이었습니다.
* 프롬프톤 진행 일정 (6월 11일 ~ 27일)
4 DAYS : 아이데이션 및 TourAPI 활용 방안 고민 + 프롬프트 초안 작성 + API 사전 세팅
6 DAYS : 프롬프트 내용 보강 + 웹 서비스 개발 (Bubble.io)
5 DAYS : 테스트 + 서비스 및 프롬프트 고도화 + 제출 자료 준비 및 초안 작성 (+ 브런치 작성)
2 DAYS : 제출 자료 작성 및 마무리 + 제출
(1) 할루시네이션
단순히 TourAPI 연동에서 끝나는 것이 아니라 명확하게 프롬프트를 작성해서 정확한 정보에 기반한 답변을 내놓는 것이 이번 프롬프톤의 핵심이라고 보았습니다. 그렇지 않으면 API와 관련 없는 정보를 마치 API를 참고한 것처럼 꾸며내기 십상이었거든요.
예를 들면, 주변에 명소가 없음에도 불구하고 명소를 지어내거나, 관광지의 이미지 링크가 없는데 이미지 링크를 임의로 생성하거나, 가이드 스크립트 내용이 아닌 출처를 알 수 없는 정보를 추가하는 것처럼 말이죠. 이를 개선하기 위해 다음 내용을 프롬프트에 추가하여 답변의 신뢰도를 높였습니다.
오디오 가이드 API 응답에 기반하여 답변합니다.
사용자가 입력한 OO 값을 오디오 가이드 API의 OO값으로 입력합니다.
사용자가 입력한 OO이 null 인 경우, OO을 우선하여 선정합니다. 사용자가 입력한 OO 값이 있는 경우, 오디오 가이드 API 응답 중 해당 값과 동일한 <OO>의 <item>을 선정합니다.
반드시 오디오 가이드 API의 응답에 기반해야 하며, 문화나 역사 등의 정보가 왜곡되지 않도록 유의합니다.
만약 API의 응답 결과가 없거나 해당 항목 값이 없는 경우, 각 항목을 "null"로 답변합니다. 임의로 답변을 지어내어 만들지 마세요.
아래와 같은 JSON 형식으로 동일하게 답변합니다.
{
"title" : "25자 이내의 시 제목",
"tour title" : "오디오 가이드 API에서 선정한 <item>의 <title>",
...
"refer" : "오디오 가이드 api에서 선정한 <item>의 <script>에서 참고한 내용 (...)
...
}
또한, TourAPI 응답에서 참고한 내용을 “refer” 값으로 답변하도록 하여 어떤 내용을 참고했는지, 해당 값이 API의 스크립트에서 나온 내용이 맞는지에 대해 검증을 용이하게 하고 정보 기반의 답변을 생성할 수 있도록 했습니다.
(2) AI 모델 선정
* API 연동 모델
LaaS에서 API를 연동할 수 있는 여러 AI 모델 중 요청한 답변 형식에 적합한 응답이 생성되는 모델은 gemini와 gpt였는데요. 다만, gpt는 XML 응답에 대해 할루시네이션이 과하게 발생했고, gemini는 정상적으로 답변을 생성했으나, 프롬프트에서 json 구조로 답변을 요청하면 꼭 답변 앞뒤에 '''json {답변} '''을 붙여서 답변을 생성했습니다.
* gpt vs gemini
해당 이슈를 해결하기 위한 두 가지 방안은 1) gpt의 과도한 할루시네이션을 해결하거나 2) gemini의 답변 형식을 완벽한 json으로 받는 것이었습니다. 우선 gpt의 이슈는 TourAPI의 응답 형식을 json으로 지정하여 호출하니 안개가 걷히듯 할루시네이션이 개선되었습니다. gemini는 프롬프트에 온갖 방법으로 요청을 해도 끝내 이슈가 해결되지 않았습니다. 이를 감안하고 버블에서 해당 키워드('''json, ''')를 제거하더라도 간헐적으로 나오는 다른 답변 구조에 대해서는 제어하기 어려울 것이라고 판단하였습니다. 따라서 gpt를 모델로 선정하게 되었고, 그중에서도 어떤 gpt 모델이 서비스에 적합한지 2차 검증을 진행했습니다.
* gpt vs gpt
우선, 정보 이해와 창작 능력이 중요하기 때문에, nano나 mini와 같은 경량화 모델은 제외했습니다. 동일한 프롬프트 기준으로 나머지 두 모델을 비교했을 때, 정보 전달뿐만 아니라 감성적이고 다채로운 표현을 잘 활용한 4.1의 결과물들이 더 적합한 것으로 최종 결정을 하였습니다.
다양한 API의 연동을 통해 기능 확장이 가능하고, 디자인 및 플로우 등의 커스터마이징이 가능하여 활용성 측면에서 우수한 bubble.io를 이용해서 이번에도 웹 서비스의 개발을 진행했습니다.
(1) 텍스트 형태의 응답 → json 전환
원티드 API를 버블에서 호출하면, message content 안에 AI 답변 내용이 모두 묶여서 제공됩니다. 즉, 하나의 텍스트로 처리가 되는 것인데요. image url, 좌표 등의 각 응답 항목을 서비스에서 원활하게 활용 및 제공하기 위해 json으로 전환하는 API를 추가하는 작업을 진행했습니다. 처음에는 어떻게 처리해야 할지 감도 안 잡혔지만 다행히도 유튜브에 유사한 버블 케이스들을 참고할 수 있었기에 적절하게 대응하여 처리했습니다.
(2) "null"의 처리
* AI 응답의 "null" 처리
버블에서도 결과값으로 받는 "null"에 대해 적절하게 대응을 해주어야 했는데요. TourAPI의 관광지명인 {tour title} 값이 없는 경우, 즉 응답 결과가 없는 케이스에 대해서 alert message("주변에 등록된 명소 주제를 찾지 못했어요. 다른 위치로 다시 시도해 보세요.")가 표출되도록 했습니다. 또, TourAPI의 이미지 링크 값인 {image}가 없는 경우에는 기본 이미지가 뜰 수 있도록 대응했습니다.
* 현재 위치의 "null" 처리
기기나 웹 브라우저에서 현재 위치 값을 허용하지 않는 경우, '내 주변 명소'에서는 alert message("내 현재 위치 정보를 알 수 없어요. 지도 주변 명소를 선택해 보세요.")가 표출되도록 했습니다. 또, 좌표 값이 없으면 날씨 API 호출도 불가능하기 때문에 '내 주변 명소' 메뉴를 선택하자마자 현재 위치 값의 유무를 확인하는 플로우도 추가했습니다. 한편, '지도 주변 명소'에서는 현재 위치 값이 없는 경우, 한국 명소인 경복궁의 위치를 지도의 기본값으로 지정하여 탐색하기 용이하도록 했습니다. 그렇지 않으면 기본값이 미국이기 때문이죠.
(3) AI의 필터링
드롭다운에 리스트업 된 지도 주변의 명소 주제를 직접 선택하는 경우, 해당 명소의 좌표 값을 기준으로 API가 호출되는 구조인데요. 그래서 동일한 좌표 값의 명소가 여러 개인 경우, 선택한 명소의 주제와 다른 결과값이 생성되는 이슈가 발생할 수 있습니다. 실제로 울산 여행에서 테스트를 해보면서 알게 된 사실로, 언양읍성과 알프스 산맥이 그러한 케이스였습니다.
연동한 TourAPI(위치정보 기반)로는 드롭다운에서 선택한 값을 별도 구분하여 처리하기 어려운 상황이었습니다. 그래서 대안으로 각 <item>의 고유값 중 하나인 <stlid>(이야기언어아이디)를 AI 가변값으로 추가하고 드롭다운에서 선택한 <item>의 <stlid> 값과 동일한 <item>을 선정하도록 하여 해결할 수 있었습니다.
다만, 내 주변 관광지는 사용자가 직접 명소를 선택하지 않기 때문에 ${stlid}의 null 값에 따른 응답 기준을 별도로 지정했습니다.
한 문장으로 말하자면 생각보다 더 재미있었고 생각보다 훨씬 어려웠습니다. 물론 원티드 LaaS는 이전에 사용한 경험이 있어서 비교적 수월했지만, LaaS에 API를 연동하고 AI가 데이터를 어떻게 잘 활용하도록 만들어야 할지 방향을 잡기 위해 초반에는 적응이 필요했습니다. 이후에는 LaaS에서 실행할 때마다 API 커넥터 응답을 바로 확인할 수 있어서 답변 신뢰성을 검증하기가 용이했던 것 같습니다.
한편, bubble.io로 앱을 만들면서 원하는 기능과 플로우를 적용하기 위해 복잡한 워크 플로우 설정과 데이터 소스 설정이 필요했는데요. 유튜브와 gpt의 도움을 받아 겨우 이슈 하나 해결하면, 또 하나의 이슈가 생기는 요지경 상황이었습니다. 그래서인지 다시 만들라고 하면 더 잘 만들 수는 있어도 절대 똑같이는 못 만들 것 같습니다.
빠듯한 일정으로 더 어려운 도전이었지만 어느 정도 구상했던 수준까지 만들고 작년보다 더 완성도 높은 결과물을 보니 성취감이 큰 활동이었습니다. 최근에 번아웃 비슷하게 노잼시기가 왔었는데 역시 새로운 도전은 재밌는 것 같습니다. 내년에 프롬프톤이 있다면 또 참가하고 싶고 그때는 서비스 상용화를 목표로 도전해 봐야겠습니다!
/ 썸네일: CANVA (AI)