2026 Edition

Soomgil

스와이프 취향 데이터를 여행방 추천, 지도 일정 협업, 여행 기록 공유까지 연결한 그룹 여행 웹 앱

Product Screens

Interface Walkthrough

Project Overview

Soomgil은 그룹 여행을 준비하는 과정에서 흩어지기 쉬운 취향 수집, 장소 추천, 지도 일정 편집, 여행 기록 공유를 하나의 흐름으로 묶은 웹 서비스입니다. 사용자는 여행방을 만들고 멤버를 초대한 뒤, 장소 카드를 스와이프하며 LIKE, NOPE, SUPER LIKE 반응을 남깁니다. 이 반응은 단순한 좋아요 기록에 머물지 않고, 여행방 단위 추천과 일정 편집의 입력으로 이어집니다.

서비스의 핵심은 "어디를 갈지"를 한 번에 정답처럼 제시하는 것이 아니라, 각자의 취향을 가볍게 모으고, 그 취향을 그룹 맥락에서 다시 해석해, 실제 일정으로 옮길 수 있게 만드는 데 있습니다. 평소에는 장소를 탐색하고, 여행을 계획할 때는 누적된 반응을 기반으로 후보지를 좁히며, 선택한 장소는 지도와 일정, 여행 기록, 커뮤니티 공유로 자연스럽게 연결됩니다.

Background

여러 명이 함께 여행을 계획하면 의견이 빠르게 흩어집니다. 한 사람은 맛집을 선호하고, 다른 사람은 자연 경관을 보고 싶어 하며, 또 다른 사람은 이동 시간이 짧은 코스를 원합니다. 하지만 일반적인 대화방이나 지도 저장 기능만으로는 누가 어떤 장소를 왜 선호했는지, 그 선호가 실제 일정에 어떻게 반영됐는지 추적하기 어렵습니다.

Soomgil은 이 지점을 스와이프 기반 취향 수집으로 풀어 보려는 프로젝트입니다. 사용자는 장소 후보를 빠르게 넘기며 자신의 반응을 남기고, 서비스는 여행방 멤버들의 반응을 모아 그룹에 맞는 장소 후보를 다시 계산합니다. 특히 SUPER LIKE처럼 강한 선호를 저장 대상으로 삼는 정책을 두어, 사용자의 원시 점수를 그대로 보여주기보다 일정에 활용 가능한 추천 후보로 변환하는 데 초점을 맞췄습니다.

Core Features

  • 여행방 생성, 초대 코드 기반 참여, 멤버 관리
  • 장소 카드 기반 LIKE, NOPE, SUPER LIKE 스와이프
  • 여행방 멤버의 누적 취향을 반영한 장소 추천
  • Mapbox 기반 지도 일정 편집, 장소 배치, 경로 확인
  • STOMP WebSocket 기반 일정 협업과 상태 동기화
  • 여행방 맥락을 활용하는 AI 여행 도우미
  • 사진 업로드, 여행 기록 작성, 기록 상세 조회
  • 커뮤니티 게시글, 댓글, 신고, 관리자 처리, 리트립 흐름

Architecture

루트 저장소는 orchestration repo 역할을 하고, 실제 제품 코드는 프론트엔드와 백엔드 서브모듈로 분리되어 있습니다. 프론트엔드는 Vue 3 SPA로 사용자 화면, 지도 상호작용, 실시간 이벤트 연결을 담당하고, 백엔드는 Spring Boot API로 인증, 여행방, 선호도, 추천, 일정, 기록, 미디어, AI 기능을 처리합니다.

Vue Frontend
  ↓ REST API / STOMP WebSocket
Spring Boot Backend
  ↓
PostgreSQL / Redis / MinIO
  ↓
Mapbox / Google GenAI / KTO tourism data

로컬 개발 환경은 Docker Compose로 PostgreSQL, Redis, Mailpit, MinIO, 백엔드, 프론트엔드를 함께 실행할 수 있도록 구성했습니다. DB 스키마 변경은 Flyway 마이그레이션으로 관리하고, SQL 접근은 MyBatis를 사용해 명시적으로 다뤘습니다. API 계약, DB 모델, 서비스 요구사항 문서를 저장소에 함께 두어 프론트엔드와 백엔드가 같은 도메인 언어를 기준으로 맞물리도록 정리했습니다.

Frontend

프론트엔드는 Vue 3, TypeScript, Vite, Pinia, Vue Router, Tailwind CSS, Mapbox GL JS를 사용했습니다. 주요 화면은 로그인, 홈, 여행방, 장소 스와이프, 추천 장소 목록, 지도 일정 편집, 커뮤니티, 여행 기록, 마이페이지, 관리자 신고 관리로 나뉩니다.

API 클라이언트는 Axios 기반으로 구성해 JWT 토큰 주입과 401 응답 이후 refresh 흐름을 처리합니다. 화면 단에서는 인증 상태와 사용자 정보를 Pinia store로 관리하고, 라우터에서는 서비스의 주요 흐름이 로그인 이후 자연스럽게 이어지도록 경로를 나눴습니다. 여행방, 스와이프, 일정 편집처럼 상태가 자주 바뀌는 화면은 서버 응답과 로컬 UI 상태가 어긋나지 않도록 API 경계를 명확히 두는 것이 중요했습니다.

지도 화면은 Mapbox GL JS를 중심으로 구성했습니다. 장소 후보를 지도 위에 표시하고, 사용자가 선택한 장소를 일정에 배치하며, 경로 정보와 일정 순서를 함께 다룹니다. 지도는 단순한 배경 요소가 아니라 의사결정 화면이기 때문에, 목록에서 고른 장소와 지도 위 위치, 일정 순서가 같은 데이터 기준으로 연결되도록 설계했습니다.

Backend

백엔드는 Java 21과 Spring Boot 3.5 기반으로 구현했습니다. JPA/Hibernate 대신 MyBatis를 사용하고, Flyway로 마이그레이션을 관리해 SQL과 스키마 변경이 명시적으로 드러나도록 했습니다. 도메인은 API, command/query application layer, domain, infrastructure 계층으로 나누어 요청 처리와 비즈니스 규칙, 외부 연동 책임을 분리했습니다.

인증은 JWT와 OAuth2 Resource Server 기반의 stateless 구조로 구성했습니다. 백엔드는 사용자 인증 이후 여행방 권한, 초대, 장소 반응, 추천 조회, 일정 변경, 기록 작성, 미디어 업로드 같은 기능을 도메인별 API로 제공합니다. Redis는 인증/상태성 흐름과 캐싱성 데이터에 활용하고, PostgreSQL은 여행방, 장소, 선호도, 일정, 기록, 커뮤니티 데이터를 저장하는 중심 DB로 사용했습니다.

Preference And Recommendation

Soomgil에서 가장 중요한 데이터는 사용자가 장소에 남긴 반응입니다. LIKE, NOPE, SUPER LIKE는 단순 UI 이벤트가 아니라, 여행방 추천의 입력이 되는 취향 신호입니다. 서비스는 사용자의 원시 취향 점수를 그대로 노출하지 않고, 여행방에서 참고할 수 있는 추천 장소 목록으로 변환합니다.

추천 흐름은 KTO 관광 데이터, 장소 태그, 사용자 반응, 저장 장소, 합성 페르소나와 추천 통계 데이터를 함께 다룹니다. 이 구조는 추천을 단순 검색이나 인기순 정렬로 끝내지 않고, 여행방 멤버들의 누적 반응과 장소 속성을 함께 고려하는 기반이 됩니다. 특히 사용자가 가볍게 스와이프한 행동이 실제 일정 후보로 이어지도록 취향 데이터의 저장 단위와 조회 API를 분리해 관리했습니다.

Itinerary And Realtime

추천된 장소는 다시 지도 일정 편집 화면으로 이어집니다. 사용자는 후보 장소를 일정에 추가하고, 순서를 조정하고, 지도 위에서 위치와 경로를 확인할 수 있습니다. 이때 프론트엔드는 Mapbox를 통해 공간 정보를 보여주고, 백엔드는 일정 상태와 경로 계산에 필요한 데이터를 API로 제공합니다.

여러 명이 같은 여행방을 편집할 수 있기 때문에 실시간 협업 흐름도 필요했습니다. STOMP WebSocket 클라이언트를 통해 일정 변경 이벤트를 연결하고, 서버 상태와 화면 상태가 같은 기준으로 갱신되도록 구성했습니다. 지도 기반 UI에서는 한 사용자의 변경이 다른 사용자 화면에서 늦게 반영되면 일정 순서나 장소 배치가 쉽게 어긋날 수 있기 때문에, 이벤트 경계와 저장 상태를 함께 고려해야 했습니다.

AI And Data

AI 기능은 Spring AI와 Google GenAI/Gemini 설정을 기반으로 구성했습니다. 여행방 단위 AI 세션과 메시지 API를 제공하고, AI 도우미가 여행방 맥락, 장소 정보, 일정 정보를 활용할 수 있도록 도구 호출 구조를 마련했습니다. 이 기능은 별도의 챗봇을 덧붙이는 방식이 아니라, 사용자가 이미 만들고 있는 여행 계획 안에서 필요한 도움을 제공하는 방향에 가깝습니다.

데이터 측면에서는 관광 공공 데이터와 서비스 내부 데이터를 연결하는 작업이 중요했습니다. KTO 관광 데이터는 장소 추천의 기반이 되고, 선호 태그와 사용자 반응은 개인화와 그룹 추천의 입력이 됩니다. 여기에 추천 통계와 저장 장소 데이터를 함께 다루면서, 단순히 많은 장소를 보여주는 것이 아니라 여행방 맥락에 맞는 후보를 좁히는 구조를 만들었습니다.

Media And Community

여행 계획은 일정표에서 끝나지 않기 때문에, Soomgil은 여행 기록과 커뮤니티 공유 흐름도 함께 포함합니다. 사용자는 여행 중 또는 여행 후 사진과 기록을 남길 수 있고, 다른 사용자는 커뮤니티 게시글, 댓글, 신고, 리트립 흐름을 통해 여행 경험을 다시 참고할 수 있습니다.

미디어 업로드는 MinIO/S3 호환 스토리지와 presigned upload 흐름을 기준으로 구성했습니다. 백엔드는 저장소 접근 정보를 직접 노출하지 않고 업로드에 필요한 계약을 제공하며, 프론트엔드는 그 계약을 사용해 사진 기반 기록 작성 흐름을 연결합니다. 이 구조는 로컬 개발에서는 MinIO로 재현하고, 운영 환경에서는 S3 계열 저장소로 확장하기 좋습니다.

Implementation Focus

이 프로젝트에서 특히 흥미로운 지점은 기능이 여러 화면에 흩어져 있어도 데이터 흐름은 하나로 이어져야 한다는 점입니다. 스와이프 화면에서 남긴 반응은 추천 API로 이어지고, 추천된 장소는 지도 일정 편집으로 이동하며, 완성된 일정은 여행 기록과 커뮤니티 공유로 이어집니다.

그래서 구현을 정리할 때도 단일 화면보다 도메인 사이의 연결을 중심으로 봤습니다. 프론트엔드 라우팅, API 클라이언트, STOMP 이벤트, 백엔드 command/query 계층, DB 마이그레이션, 외부 API 연동, 로컬 인프라 구성이 모두 같은 여행방 컨텍스트를 공유해야 했습니다. 이 점이 Soomgil을 단순한 여행 목록 앱보다 더 복합적인 프로젝트로 만든 부분입니다.

Tech Stack

  • Frontend: Vue 3, TypeScript, Vite, Pinia, Vue Router, Tailwind CSS, Mapbox GL JS
  • Backend: Java 21, Spring Boot 3.5, MyBatis, Flyway, Spring Security, PostgreSQL, Redis
  • AI & Data: Spring AI, Google GenAI/Gemini, KTO tourism data, recommendation statistics
  • Realtime & Media: STOMP WebSocket, MinIO/S3 compatible storage, presigned upload
  • Infra & Test: Docker Compose, Mailpit, Vitest, JUnit, Testcontainers

What I Learned

Soomgil을 통해 여행 서비스에서 중요한 것은 장소 데이터를 많이 모으는 것만이 아니라, 사용자의 선택이 다음 행동으로 이어지는 구조라는 점을 다시 확인했습니다. 스와이프 반응, 추천 후보, 지도 일정, 여행 기록이 서로 다른 기능처럼 보이더라도, 실제 사용자 경험에서는 하나의 흐름으로 느껴져야 합니다.

이를 위해서는 프론트엔드 화면 설계만으로는 부족합니다. 취향 데이터의 저장 단위, 추천 API의 응답 형태, 일정 편집의 이벤트 경계, 지도 외부 API의 실패 가능성, 미디어 업로드 계약까지 함께 맞아야 합니다. 특히 실시간 협업과 지도 기반 일정 편집은 UI 상태와 서버 상태가 어긋나기 쉬워, 도메인 경계와 데이터 소유권을 명확히 하는 것이 중요했습니다.

Improvement Points

이후에는 추천 품질을 평가할 수 있는 기준을 더 명확히 만들 필요가 있습니다. 사용자가 스와이프한 반응이 실제 일정 선택에 얼마나 반영됐는지, 추천 장소가 여행방 멤버들에게 충분히 납득 가능한지, SUPER LIKE 같은 강한 신호가 추천 결과에 어떤 영향을 주는지 측정할 수 있어야 합니다.

운영 관점에서는 공개 배포 URL, CI/CD 흐름, 지도 API 실패 처리, WebSocket 재연결 정책, 미디어 업로드 실패 복구, AI 응답 품질 관리가 보완 지점입니다. 또한 커뮤니티와 리트립 흐름이 실제 사용자 기록을 얼마나 잘 재활용하는지 검증하면, 여행 전 계획과 여행 후 기록이 더 단단하게 연결될 수 있습니다.

End of Record
Return to Showcase →