fairy
서비스
여행 일정 생성 과정에 피로함을 느끼는 사람들을 위한
여행 일정 AI 플래너 서비스, fairy
주요 기능
여행 일정 생성
목적지, 기간, 인원, 교통편, 기호 등의 여행 기본 정보를 입력으로 받아 기상, 교통, 숙소, 엔터테인먼트, 음식점, 카페 등의 정보를 수집하고 여행 일정 포맷에 맞춰 조합하여 사용자가 희망하는 여행 일정 계획을 제안합니다.
장소 추천
사용자의 질문을 입력으로 받아 음식점, 숙소, 카페 등의 장소를 추천해줍니다.
서비스 설명
AX/DX 전환이 더딘 여행 산업 분야에서, 여행에 필요한 정보 수집 과정이 매우 불편하며 시간을 많이 소모한다는 문제점을 AI Agent를 통해 자동화하여 해결하고자 한 서비스입니다.
사용자는 가고자 하는 여행지와 인원 등 여행에 필요한 정보를 입력하면 AI Agent를 사용하여 숙박, 엔터테인먼트, 음식점, 카페 등의 정보를 수집하여 여행 기획을 작성하여 전달합니다.
프로젝트 기간
2025.09. - 2025.12. (3개월)
2025.04. ~ (리펙토링)
사용 기술 스택
React + Typescript + Vite
- 엄격한 타입 관리를 통한 오류 발생 가능성 억제 + 빠른 개발
- AI 친화적인 구성
Python + FastAPI + LangChain/LangGraph
- 비선형적 LLM Chain 구조를 사용한 에이전트 구축
- 빠른 개발과 비동기 처리에 용이
- AI 친화적인 구성
AWS
- AWS 프리티어를 사용한 서버 운영 비용 최소화 목적
- 다양한 클라우드 서비스를 제공하여 람다, ECR 등을 쉽고 저비용으로 사용 가능
DynamoDB
- AI 서비스 중 변경 사항에 대해 스키마 변경 없이 유연하게 대응
- AWS 관리형 서비스 + auto scailing에 이점
- 다른 RDS 대비 프리티어 혜택을 통한 유지 비용 낮음
구현 및 기여 내용
infra
- AWS 인프라 아키텍처 설계 및 구성
AI Engineer
- Agent 시스템 설계 및 구현
Backend
- CI/CD 파이프라인 구축
- AWS DynamoDB 스키마 설계 및 구현
시스템 아키텍처
사용자 인증/인가
AWS Cognito를 사용하여 사용자를 등록 및 관리하며 Cognito로 발급된 JWT 토큰을 사용하여 사용자 인증이 필요한 경우에 대한 인증/인가를 구현했습니다.
람다가 아닌 EC2
최초에는 가격적으로 유지 비용이 압도적으로 유리한 람다로 구성하려 했으나 테스트 중 람다의 무상태 특성과 실행 시간 제한 조건에 부딪혔습니다. 그래프 실행 중 state를 메모리에 유지할 수 없는 람다보단 EC2로 서버를 띄워 이러한 문제가 발생하지 않도록 했습니다.
github Actions와 Docker image를 사용한 CI/CD 파이프라인 구축
K-PaaS 공모전에도 참가하여 Kubernetes 환경과 AWS EC2 환경에서 동시에 운영할 수 있도록 관리의 부담을 줄이기 위해 docker image로 관리하도록 하고 EC2로의 CI/CD를 구축했습니다.
Subgraph를 활용한 Multi-Agent Hierarchical Team 패턴 구현
사용자의 입력 내용에 따라 입력 요청을 범주화하고 main supervisor가 범주에 맞는 서브 그래프로 라우팅하도록 하여 사용자의 질문 패턴에 맞는 서브 그래프가 실행되도록 처리했습니다.
DynamoDB 주요 구성
sessions - 채팅 세션을 저장할 테이블
| 속성 | type | 설명 |
|---|---|---|
| sessionID(PK) | string | 채팅 세션 ID |
| title | string | AI로 요약한 채팅 세션 제목 |
| createdAt | datetime | 세션 생성 시각 |
| updatedAt | datetime | 세션 업데이트 시각 |
chats - 채팅 내용을 저장할 테이블
| 속성 | type | 설명 |
|---|---|---|
| chatID(PK) | string | 챗 ID |
| sessionID(SK) | string | 세션 ID |
| content | string | 챗 내용 |
| role | string | USER | AI |
| timestamp | datetime | 챗 생성 시각 |
itinerary - 생성된 여행 일정을 저장할 테이블
| 속성 | type | 설명 |
|---|---|---|
| itineraryID(PK) | string | 여행 일정 ID |
| userID(SK) | string | 생성한 사용자 |
| sessionID | string | 세션 ID |
| content | string | md 형식의 일정 계획 |
| timestamp | datetime | 생성 시각 |
users - 사용자 정보를 관리할 테이블
| 속성 | type | 설명 |
|---|---|---|
| email(PK) | string | 사용자 email |
| username | string | 사용자 이름 |
| createdAt | datetime | 생성 시각 |
| updatedAt | datetime | 업데이트 시각 |
프로젝트 수행 중 부딪힌 문제
크롤링 실패로 인한 데이터 확보 실패
여행 정보 수집을 위해 크롤링 기반 데이터 수집을 설계했으나, WAF과 동적 스크립팅 기술로 크롤링을 차단하고 있는 서비스들의 보안 정책을 간과하여 설계가 무너졌습니다. AI와 크롤링 봇의 확산으로 인해 주요 여행 플랫폼들이 데이터 무단 유출 방지를 강화하고 있다는 점을 사전에 충분히 검토하지 못한 것이 원인이었습니다.
Tavily 엔진을 사용하여 키워드 기반 검색으로 정보를 수집하고, 그 중 장소 이름 등을 추출하여 추가 웹 검색을 통해 장소 오브젝트를 생성하는 방식으로 전환했습니다. 단순 LLM 웹 검색 기반 에이전트의 정보 정확도 40% 대비 Tavily 기반 검색 프로세스 도입 후 58%로, 약 20초의 실행 시간 증가와 정보 정확도 18% 향상을 trade-off로 수용했습니다.
근본적인 해결을 위해서는 네이버 지도, 카카오맵, 트립어드바이저, 정부 오픈 API 서비스 등 신뢰할 수 있는 플랫폼의 공식 API 연동 또는 자체 데이터 수집 파이프라인 구축이 필요하며, 이를 통해 데이터 정확도를 58% 그 이상으로 개선할 수 있을 것으로 예상합니다.
DynamoDB batch 쓰기 한도 초과 이슈와 SQS 도입
단위 테스트 환경에서는 발생하지 않던 문제로, 실제 서비스 형태의 통합 테스트에서 채팅 기록이 DynamoDB batch 쓰기 한도인 25건을 초과하는 경우 DB 저장에 실패하는 문제를 확인했습니다. 단위 테스트와 통합 테스트 환경의 데이터 규모 차이를 사전에 고려하지 못한 것이 원인이었습니다.
DB 저장 시점을 그래프 종료 시점으로 일원화하고, SQS를 도입하여 채팅 기록을 최대 25건 단위로 분할하여 큐에 전달한 뒤 Lambda가 순차적으로 처리하도록 구성하여 쓰기 실패 시에도 메시지가 큐에 보존되어 데이터 누락을 방지하도록 조치했습니다.
Agent 그래프의 실행 시간 개선
초기 설계에서 메인 관리자 노드가 작업 계획과 각 노드의 지시 사항을 생성하여 위임하는 단일 그래프 구조로 구현했으나, 2박 3일 기준 여행 일정 생성에 평균 약 3분이 소요되어 기대 수준을 초과했습니다.
LangSmith와 자체 구축한 그래프 state 스냅샷 추적을 통해 병목 지점을 분석한 결과, 실제 작업 흐름이 "데이터 수집 → 여행 일정 생성 → 포매팅" 순서로 진행되고 있었으며 데이터 수집 단계에서 API 호출이 동기적으로 이루어져 idle 시간이 대부분을 차지하고 있음을 확인했습니다.
이에 두 가지를 병행하여 개선하고자 했습니다.
첫째, 단일 그래프에서 서브그래프 기반 계층 구조로 전환하여 메인 관리자 노드의 작업 지시 사항 생성 과정을 제거해 추론 시간을 단축했습니다. 둘째, 데이터 수집 단계의 API 호출을 동기에서 비동기로 전환하여 날씨, 음식점, 카페, 숙소, 엔터테인먼트, 교통 정보를 병렬로 수집하도록 재구성했습니다. 결과적으로 수집 시간을 60% 이상 단축하여 평균 2분 23초로 기존보다 약 30초 단축하여 개선할 수 있었습니다.
섣부른 라이브러리 도입의 실패
JSON 형태로 여행 일정을 출력하여 프론트엔드에서 interactive한 일정 수정 경험을 제공하는 방향으로 설계했습니다. 하지만 답변 생성 중 빈 화면이 노출되는 UX 문제가 있었습니다. 이를 해결하고자 live-streaming을 지원하는 assistant-ui 라이브러리를 도입했으나 해당 라이브러리가 JSON 형식의 최종 출력을 지원하지 않아 기존 설계와 충돌이 발생했습니다. 기존 설계와 신규 라이브러리의 호환성을 사전에 충분히 검토하지 않은 채 도입을 결정한 것이 원인이었습니다.
결과적으로 5주치 작업량을 재작성해야 했고, streaming은 지원하되 interactive한 일정 수정 UX는 구현하지 못한 채 마무리되었습니다.
이 경험을 통해 새로운 기술 도입 전 기존 데이터 흐름과의 호환성을 먼저 검증하는 것을 기준으로 삼게 되었습니다. 향후에는 assistant-ui 라이브러리를 걷어내고 LangChain/LangGraph에서 지원하는 기능으로 메시지 streaming과 JSON 출력을 동시에 처리할 수 있는 구조로 개선할 계획입니다.



