NOLDA (놀다)
한 줄 설명: 질문에 일일이 답하지 않아도 사진첩·유튜브 기록으로 취향을 분석하고, 서울 마포구 실제 장소로 걸어서 따라갈 수 있는 여가 코스를 GPT로 설계해 주는 서비스 (PWA)
| 항목 |
링크 |
| 데모 (영상/GIF/배포 URL) |
https://frontend-sigma-one-39.vercel.app (Vercel 프론트). 2026-10-08 확인: 이 주소는 https://nolda.is-cool.dev/로 넘어가는데, 그 도메인은 Vercel이 아닌 서버를 가리켜 인증서 오류 + 404. 백엔드는 Render(render.yaml) |
| 코드 레포 |
nolda-app/nolda (팀 레포, 공개) |
| 로컬 경로 |
sojeong-dev/nolda |
| 기준 커밋 |
8d32544 (2026-09-20) |
| 상세 리포트 |
report.md |
| 모델 카드 |
없음 (만들면 models/<YYYYMMDD>-<과제>-<모델>/card.md에 level: project · project: nolda. NOLDA는 gpt-5-mini를 API로 쓰고 학습한 모델은 아직 없음) |
| 실험 추적 (W&B / MLflow) |
없음 (코드 레포에서 확인 못 함) |
| LLM 트레이싱 (Langfuse / LangSmith) |
없음 (코드 레포에서 확인 못 함) |
슬롯: 미정 · 상태: 진행 중 · 기간: 2026.09 ~ · 역할: 팀 (인원·담당 범위 TODO)
핵심 수치 3개
- AI 코스 생성 응답 시간 약 50초 → 14.5초 (gpt-5-mini,
reasoning_effort 기본 → minimal, 망원 테스트 기준. 근거: backend/courses.py 주석, README "AI 코스 생성 API")
- TODO (예: 코스 검증 통과율, 사용자 평가)
- TODO
1. 문제 정의
- 사용자 / 상황: 주말·저녁에 무엇을 하며 놀지 정하려는 사람. MVP 지역은 서울 마포구 한정
- 해결하려는 문제: 기존 서비스는 예산·시간·위치·취향을 매번 클릭해서 입력해야 하고, 서로 차별점이 없음 (2026-09-04 회의 피드백,
docs/meeting-2026-09-04.md)
- 기존 방식의 한계: 조건 클릭 → 조건 클릭 → 추천. 사용자가 자기 취향을 직접 말해야 함
- 지향점: 사용자 데이터(사진첩·유튜브, 카드 내역 검토) 분석 → 취향 자동 파악 → 실제로 걸을 수 있는 코스 추천 + 길안내 (
docs/service-flow.md)
- 성공 기준 (수치로): TODO
2. 데이터 / 지식 소스
| 소스 |
규모 |
형식 |
수집 방법 |
라이선스·이용 조건 |
| 마포구 일반 장소 |
TODO |
CSV → place 스키마 |
네이버 검색 API 지역검색 (동 × 업종 순회, 쿼리당 5건 상한이라 sort 이중 호출로 보강) |
네이버 검색 API 이용 약관 (TODO 확인) |
| 마포구 팝업스토어 |
TODO |
CSV |
네이버 블로그 검색 + 본문 스크래핑 |
TODO |
| 사용자 사진첩 |
1회 최대 12장 분석 |
이미지 + EXIF |
<input type="file">로 사용자가 직접 선택 → EXIF(시간·위치) + GPT Vision |
사용자 업로드 (개인정보 안내문 미적용, 서비스 흐름 문서 "아직 안 된 것") |
| 사용자 유튜브 기록 |
TODO |
API 응답 |
유튜브 OAuth 연동 |
Google API 약관 |
| 도보 경로 |
코스 구간별 |
거리·시간·경로선 |
TMAP 보행자 경로 API 또는 OpenRouteService |
각 API 약관 |
- 근거:
backend/PLACE_DATA_PIPELINE.md, docs/data-strategy-2026-09-07.md, backend/taste.py
- 카드 내역(스크린샷 → GPT Vision OCR)은 수집 전략으로 정했지만 오픈뱅킹·마이데이터 연동은 금융위 인가가 필요해 사용자 업로드 방식으로 확정 (
docs/data-strategy-2026-09-07.md). 구현 여부 TODO
3. 전처리
- 선택: 병원·약국·편의점·미용·학원 등 코스에 맞지 않는 업종과 기간이 끝난 팝업 제외 (
nolda-장소가공.ipynb, backend/scripts/build_places_geo.py)
- 정제: 지역검색 응답을
place 스키마로 매핑(좌표 mapx/mapy ÷ 1e7), 좌표 없는 행은 주소로 네이버 Geocoding해 채우고 결과를 캐시
- 가공: 장소를 종류별(식사/카페/한잔/체험/문화/산책/운동)로 정리하고, 고정 코스 구간의 실제 도보 거리·시간·경로선을 스크립트로 생성 (
frontend/src/planner/geo.ts, routes.ts)
4. 모델 · 프롬프트 · 에이전트 설계
- 모델: OpenAI
gpt-5-mini (코스 생성 OPENAI_MODEL, 취향 분석 OPENAI_TASTE_MODEL), reasoning_effort: minimal 기본 (backend/courses.py, backend/taste.py)
- 후보 비교:
reasoning_effort minimal 14.5초 / low 32.5초 / 기본 약 50초, 결과 품질은 비슷 (망원 테스트, 코드 주석)
- 취향 분석 (
POST /taste/analyze): "숫자는 코드가, 문장은 LLM이"
- 사진 읽기(Vision,
detail=low): 사진마다 장소 종류·태그·동행 수
- 가중 합산(코드): 사진 0.6 : 유튜브 0.4, 근거가 적은 쪽은 가중치를 깎음(사진 10장·유튜브 20개가 모여야 100%)
- LLM: 확정된 값은 바꾸지 않고 설명 문장과 이 사람에게 맞는 질문 주제 4개만 생성
- 코스 생성 (
POST /courses): 후보 목록 안에서만 고르고 서버가 검증
- 동네 반경 안에서 종류별로 가까운 장소 약 60~125곳을 샘플링해
p1, p2… ref로만 전달 → 장소를 지어내지 못하게 함
- 6개를 요청해 검증: 없는 ref, 장소 반복, 같은 종류 3연속, 도보 2km 초과, 시간·예산 초과, 근거 없는 평가 표현(인기·맛집·한적 등)이면 버림. 통과가 4개 미만이면 1회 재요청
- 선택한 시간 창에 맞춰 체류 시간을 비율대로 조정(1곳 30~240분)
- 검색 / 리랭킹: 해당 없음 (동네 반경 + 종류별 샘플링)
- README의 "scikit-learn 이탈도(anomaly) 분석"은 기준 커밋 코드에서 사용처를 찾지 못함 (계획 단계로 보임, TODO 확인)
5. 평가
| 항목 |
내용 |
| 테스트셋 |
TODO |
| 지표 |
응답 시간(측정됨, 위 4절), 검증 통과율·코스 품질 TODO |
| 평가 방법 |
TODO |
개선 전후
| 버전 |
변경 사항 |
응답 시간 |
지표 2 |
지표 3 |
| baseline |
gpt-5-mini, reasoning_effort 기본 |
약 50초 |
|
|
| v1 |
reasoning_effort minimal |
14.5초 (망원) |
|
|
6. 아키텍처 · DB · 운영
flowchart LR
U[사용자 · PWA] --> FE[프론트엔드<br/>React + Vite · Vercel]
FE --> API[FastAPI · Render]
API --> LLM[OpenAI gpt-5-mini<br/>취향 분석 · 코스 생성]
API --> DB[(Supabase Postgres)]
API --> WALK[TMAP / ORS<br/>도보 경로]
FE --> MAP[네이버 지도 API]
API --> AUTH[카카오 · 구글 OAuth<br/>유튜브 연동]
- DB / 스토리지: Supabase(Postgres), 스키마는
docs/db-schema.md. 분석 결과·도보 경로 캐시는 서버 메모리에만 있어 재시작 시 사라짐 (README "상태")
- 인증: 카카오·구글 OAuth2 + 자체 발급 JWT(30일), 네이버 예정
- 배포 환경: 프론트 Vercel(소정 개인 계정, MVP 이후 팀 계정 이전 예정), 백엔드 Render 무료 플랜. CORS는
render.yaml에서 프론트 주소와 Vercel 미리보기 주소 패턴만 허용
- 모니터링 / 로깅: TODO
- 비용 (월 예상치): TODO (사진은
detail=low로 1장 약 85토큰, 1회 최대 12장으로 상한)
7. 회고: 결정과 트레이드오프
| 결정 |
고려한 대안 |
선택 이유 |
대가 (트레이드오프) |
| 취향 수치는 코드로 계산, LLM은 문장만 |
LLM이 취향을 바로 판단 |
근거가 적은 쪽이 결과를 뒤집지 않게 가중치를 통제 |
가중치·항목을 사람이 정해야 함 |
| 후보 장소 ref 목록 안에서만 코스 구성 + 서버 검증 |
LLM이 자유롭게 장소 제안 |
장소를 지어내지 않게, 시간·도보 거리·예산을 규칙으로 보장 |
재요청으로 응답이 길어질 수 있음, 후보 밖 장소는 추천 불가 |
reasoning_effort: minimal |
low, 기본 |
응답 약 50초 → 14.5초, 품질 비슷 |
복잡한 조건에서 품질 저하 가능성 (평가 TODO) |
| 카드 내역은 사용자 업로드(스크린샷 OCR) |
오픈뱅킹·마이데이터 API |
금융위 인가 없이 개인 프로젝트로 가능 |
사용자 수고, OCR 오류 |
- 다시 한다면: TODO
- 다음 단계: 개인정보 안내문, 분석 결과·코스 저장 서버 연동(Supabase), 마포구 외 지역 (
docs/service-flow.md "아직 안 된 것")