코스 자동 생성 알고리즘 설계 & 벡터 기반 개인화 로직
이전 편까지는 관광지 데이터를 수집하고 정제해 Spot DB로 저장하는 과정을 다뤘다.
이제 FLIK의 핵심 기능 — “사용자 맞춤형 여행 코스 생성” — 의 설계와 구현을 소개한다.
FLIK의 여행 코스 생성 기능은 단순한 랜덤 추천이 아니라,
사용자의 취향 + 장소 특성 + 이동 동선을 함께 고려해 코스를 구성한다.
기본 플로우는 다음과 같다 👇
1️⃣ 사용자가 선호하는 카테고리를 선택한다.
2️⃣ 시스템이 해당 카테고리에 맞는 장소 후보를 추천한다.
3️⃣ 사용자는 스와이프를 통해 마음에 드는 장소를 저장한다.
4️⃣ 특정 조건이 충족되면 자동으로 코스가 생성된다.
사용자는 다음 카테고리 중 2개 이상, 4개 이하를 선택한다.
🌿 자연
☕ 카페
🏛️ 문화역사
🧗 액티비티
🏰 테마파크
🏮 축제
🏠 전통시장
🏚️ 실내여행지
서버는 사용자가 선택한 카테고리에
자동으로 식당(restaurant)과 숙박(accommodation)을 추가하고,
카테고리별로 20개씩 장소를 검색한다.
검색 결과는 라운드 로빈(round robin) 방식으로 섞어 카드 UI로 사용자에게 노출된다.
자연 → 문화 → 카페 → 전통시장 → 자연 → 문화 → …
사용자는 마음에 드는 장소를 스와이프 저장한다.
→ 이때 저장된 장소는 이후 사용자 선호 벡터(User Preference Vector) 계산에 활용된다.
사용자의 “취향”을 수치화하기 위해 각 장소를 키워드 임베딩 벡터로 표현했다.
예:
{"자연": [0.11, 0.27, 0.34, ...], "카페": [0.42, 0.09, 0.18, ...]}
사용자가 특정 카테고리의 장소를 여러 개 저장하면,
그 장소들의 키워드 벡터 평균값을 해당 카테고리의 사용자 벡터로 정의한다.
user_vector["자연"] = mean([벡터1, 벡터2, 벡터3, 벡터4, 벡터5])
→ 이후 새 장소가 저장될 때마다 자동 업데이트된다.
코스 생성 시 사용자가 선택한 카테고리의 장소를 탐색할 때,
해당 사용자 벡터와 유사한 장소(코사인 유사도 기준) 를 우선적으로 추천한다.
즉, 단순히 “자연 카테고리” 안에서 무작위 선택이 아니라
“이 사용자가 좋아할 법한 자연” 장소를 고르는 것이다.
RAG(Retrieval-Augmented Generation)를 바로 사용하지는 않았다.
현재는 벡터 검색 + 알고리즘 기반 코스 생성으로 충분하지만,
향후에는 다음과 같은 확장을 고려 중이다 👇
특정 테마 기반 코스 (“벚꽃 시즌 감성여행”, “비 오는 날 indoor 코스”)
→ RAG를 활용해 사용자 선호 + 외부 지식베이스에서 컨셉 코스 생성
즉, RAG의 Generate 단계는 향후 “콘셉트 기반 생성형 추천”에 도입 예정이다.
HashMap에 저장기본 규칙
accommodation) 추가[DAY1] [카페][Spot][식당][Spot][식당][숙박]
[DAY2] [카페][Spot][Spot][식당][Spot][숙박]
[DAY3] [카페][Spot][식당][Spot][식당][빈칸]
특별 처리 카테고리
| 시나리오 | 기대결과 |
|---|---|
| 자연+카페+문화 선택 | 각 날짜의 첫 슬롯에 카페 존재 |
| 숙박 1박 이상 | 숙박시설이 마지막 슬롯에 자동 배치 |
| 축제 포함 | 해당 날짜의 일부 슬롯 비활성화 |
| 스와이프 빈도 높은 카테고리 | 우선 배치됨 |
@Test
void shouldPlaceCafeInFirstSlot() {
CourseGenerator generator = new CourseGenerator(userPreferences);
Course course = generator.generate(3); // 3일 여행
for (DaySchedule day : course.getDays()) {
assertEquals("CAFE", day.getSlots().get(0).getCategory());
}
}
public Course generateCourse(User user, List<String> selectedCategories) {
Map<String, Double> preference = preferenceService.getUserVector(user);
List<Spot> candidates = spotService.findTopByCategorySimilarity(preference, selectedCategories);
return courseAssembler.build(candidates, user.getTripDays());
}
이번 편에서는 “사용자 벡터 + 규칙 기반 코스 자동 생성 알고리즘” 을 설계했다.
🔜 5편 예고: “추천은 어떻게 진화하는가 – 벡터 기반 개인화와 LLM 결합의 가능성”