Skip to content
SMBDHTeamPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

196 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FastAPI AI / Schedule Server

data/ 는 부산 투어 프로젝트의 Python FastAPI 서버입니다.

현재 이 서버가 담당하는 기능은 크게 3가지입니다.

  • 장소 카테고리 / 테마 분류
  • 일정 미리보기 / 일정 생성 / 일정 조회 / 일정 수정 / 지도 응답
  • 즉흥 여행 목적지 추천 / 즉흥 코스 생성

이 문서는 2026년 9월 4일 기준 현재 구조와 운영 상태를 빠르게 파악하기 위한 요약 문서입니다.

구조

  • server/
    • 프론트가 직접 호출하는 공개 API 서버
    • 인증, 에러 포맷, 기존 API 계약 유지
    • 일정 API는 FastAPI(data-ai)에 위임 가능
  • data/
    • FastAPI 서버
    • 분류 모델, 일정 생성 로직, 교통 응답 조립, 즉흥 여행 추천 담당

현재 운영 흐름은 아래와 같습니다.

  1. 프론트는 기본적으로 server API를 호출합니다.
  2. server 는 일정 관련 요청을 data-ai FastAPI로 위임할 수 있습니다.
  3. data/ 는 개발 DB와 외부 교통 API를 사용해 응답을 만듭니다.

제공 API

분류 API

  • GET /health
  • POST /predict
  • POST /predict/batch

역할:

  • 장소 텍스트 기반 1차 카테고리 분류
  • Spring 호환 응답 필드 제공
  • 일정/추천용 보조 테마 정보 생성

주요 응답 필드:

  • primary_category
  • theme_answer_id
  • model_category
  • decision_source
  • primary_theme
  • secondary_themes
  • semantic_tags
  • mood_tags
  • is_meal_place
  • is_low_mobility_friendly

일정 API

  • POST /api/v1/schedule-previews
  • GET /api/v1/schedule-previews/{preview_id}
  • POST /api/v1/schedules
  • GET /api/v1/schedules
  • GET /api/v1/schedules/{schedule_id}
  • PATCH /api/v1/schedules/{schedule_id}
  • GET /api/v1/schedules/{schedule_id}/map

현재 상태:

  • preview 생성 / 조회 가능
  • preview 기반 일정 생성 가능
  • 직접 일정 생성 가능
  • 일정 목록 / 단건 조회 가능
  • 일정 수정 가능
  • 지도 응답 생성 가능

즉흥 여행 API

  • POST /api/v1/spontaneous-trips/destinations
  • POST /api/v1/spontaneous-trips/course
  • POST /api/v1/spontaneous-trips/schedules

현재 상태:

  • 출발 위치/시간/희망 테마 기준 목적지 추천 가능
  • 선택한 목적지 기준 즉흥 코스 미리보기 생성 가능
  • 사용자가 저장을 확정한 미리보기를 공통 일정 구조로 저장 가능
  • 교통수단별 이동 가능 여부 반영
  • stop별 장소·이동 구간·지도선과 마지막 복귀 시간 포함

/course는 계산 전용이다. schedules, schedule_*, transit_*, places, place_images 등 비즈니스 테이블을 쓰지 않으며, 응답의 previewToken에 서버가 계산한 코스 스냅샷을 HMAC으로 서명한다. Spring이 전달한 현재 사용자 ID도 서명 범위에 포함된다.

클라이언트가 저장 버튼을 누를 때만 /spontaneous-trips/schedules를 호출한다. 요청 본문은 previewId, previewToken, 헤더는 Idempotency-Key와 Spring 내부 X-Auth-User-Id를 사용한다. 저장은 장소 식별, 공통 일정/정류장/이동 경로 저장, 멱등성 완료 처리를 한 DB 트랜잭션으로 커밋한다. 저장 과정에서 코스 생성기를 다시 실행하지 않는다.

운영 환경의 모든 data 인스턴스에는 같은 고엔트로피 SPONTANEOUS_PREVIEW_SECRET을 설정해야 한다. 기본 만료 시간은 20분이며 SPONTANEOUS_PREVIEW_TTL_SECONDS로 조정한다. 설정하지 않은 로컬 실행은 프로세스별 임시 키를 사용하므로 재시작 또는 다른 인스턴스를 거친 저장은 의도적으로 실패한다.

일정 생성 동작 방식

장소 후보 소스

일정 생성은 아래 우선순위로 후보 장소를 읽습니다.

  1. SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD
  2. LOCAL_POSTGRES_*
  3. candidate_places.json fallback

즉, 정상 env가 있으면 JSON이 아니라 개발 DB를 우선 사용합니다.

/health 확인값:

  • schedule_candidate_source
  • schedule_candidate_count
  • schedule_db_enabled
  • schedule_db_host

교통 / 경로

현재 교통 경로 생성은 아래 공급자를 사용합니다.

  • ODSAY 대중교통 경로
  • TMAP 보행 경로
  • 내부 도보 fallback

관련 env:

  • ODSAY_ENABLED
  • ODSAY_API_KEY
  • ODSAY_BASE_URL
  • TMAP_WALKING_ENABLED
  • TMAP_BASE_URL
  • SKT_API_KEY

참고:

  • 사용자 응답에서는 TMAP, INTERNAL_WALK, FASTAPI_MIGRATION 같은 공급자명을 최대한 숨기도록 정리했습니다.
  • 기존 DB에 저장된 일정 조회도 같은 기준으로 정규화합니다.
  • ODSAY_ENABLED=false 이거나 ODSAY_API_KEY가 없으면 대중교통 경로를 호출하지 않습니다.
  • TMAP_WALKING_ENABLED=false 이거나 SKT_API_KEY가 실제 값이 아니면 TMAP 보행 경로를 호출하지 않습니다.
  • ODSAY/TMAP을 모두 사용할 수 없거나 외부 API가 실패하면 내부 거리 기반 도보 fallback이 사용됩니다.
  • 긴 구간이 도보 90분처럼 보이면 우선 data-ai 컨테이너 env와 transit odsay_skipped, transit tmap_skipped, transit fallback=estimated_walk 로그를 확인해야 합니다.

일정 채우기 보정

현재 일정 생성은 하루가 너무 일찍 끝나는 경우를 줄이기 위해 아래 보정을 사용합니다.

  • 목표 방문지 수 계산
  • underfilled day 판정
  • 방문지 추가 재배치 시도
  • 일부 방문지 체류 시간 연장

주의:

  • 10:00 ~ 20:00 는 활동 가능 시간 창입니다.
  • 항상 20시까지 꽉 차는 것은 아닙니다.
  • 다만 긴 일정은 방문지 수와 체류시간을 더 적극적으로 채우도록 보정하고 있습니다.

AI 재정렬

현재 사용하지 않습니다.

이전 실험용 후보 재정렬 경로는 제거했고, 지금 일정 생성은 규칙 기반 점수와 시간/이동 가능 여부 검증으로 동작합니다.

운영 조건

Spring -> FastAPI 위임 env

Spring 서버가 일정 API를 FastAPI에 위임하려면 아래 값이 필요합니다.

SCHEDULE_FASTAPI_ENABLED=true
SCHEDULE_FASTAPI_BASE_URL=http://data-ai:8010

주의:

  • SCHEDULE_FASTAPI_ENABLED 가 없으면 위임이 비활성화될 수 있습니다.
  • SCHEDULE_FASTAPI_BASE_URL 은 끝에 슬래시 없이 넣는 것을 권장합니다.
  • EC2에서 컨테이너를 수동 재실행할 때 env 누락 시 preview/create가 실패할 수 있습니다.

FastAPI 컨테이너 env

FastAPI 컨테이너는 최소 아래 값들이 필요합니다.

SPRING_DATASOURCE_URL=...
SPRING_DATASOURCE_USERNAME=...
SPRING_DATASOURCE_PASSWORD=...
ODSAY_ENABLED=true
ODSAY_API_KEY=...
TMAP_WALKING_ENABLED=true
SKT_API_KEY=...

현재 deploy-dev.yml 은 위 값을 .env.dev 로 만든 뒤 EC2의 data-ai 컨테이너를 --env-file /opt/hackathon-dev/.env.dev 로 실행합니다.

즉흥여행 시간 입력 정책

POST /api/v1/spontaneous-trips/destinations와 POST /api/v1/spontaneous-trips/course는 같은 시간 검증 함수를 사용한다. 기존 요청·응답 필드는 바꾸지 않는다.

  • startAt과 returnBy는 +09:00, Z 등 timezone offset이 있는 datetime이어야 한다.
  • 요청 시점의 서버 시각을 한 번 읽고, 모든 날짜·시간 비교는 ZoneInfo("Asia/Seoul")로 변환해서 수행한다.
  • startAt의 KST 날짜는 서버 요청 시점의 KST 오늘과 같아야 한다. 00:00~08:59 자체는 금지하지 않는다.
  • startAt >= now - 5분을 허용한다. 정확히 5분 전은 허용하지만, 자정 직후라도 전날 출발은 허용하지 않는다.
  • returnBy > startAt이어야 하며, 상한은 출발일의 다음날 03:00:00 KST다. 03:00:00 초과는 거부한다.
  • now는 유효성 검증에만 사용한다. 추천 프로필은 사용자가 보낸 startAt, 코스 방문 프로필은 예상/실제 방문시간을 사용한다. 예를 들어 now=15:00, startAt=19:00이면 NIGHT이고, 다음날 00:30 방문은 LATE_NIGHT다.
  • 검증은 각 요청에서 다시 수행한다. 목적지 조회 후 시간이 지나 허용 오차나 KST 날짜 경계를 넘으면 동일한 출발시간도 더 이상 유효하지 않을 수 있다.

시간 정책 위반은 HTTP 422를 유지하면서 detail을 SPONTANEOUS_TIMEZONE_REQUIRED, SPONTANEOUS_START_DATE_NOT_TODAY, SPONTANEOUS_START_TIME_IN_PAST, SPONTANEOUS_RETURN_TIME_BEFORE_START, SPONTANEOUS_RETURN_TIME_TOO_LATE로 구분한다. 시간대 변환 자체가 불가능한 예외 상황은 기존 INVALID_TIME_RANGE를 유지한다. SERVER는 각 detail을 사용자 행동 지침이 포함된 공개 오류 코드로 변환한다.

호환성 변경: DATA의 기존 Pydantic datetime 필드는 naive 입력도 파싱했지만, 이제 두 엔드포인트에서 이를 명시적으로 거부한다. DATA 직접 호출자는 offset을 포함해야 한다. Spring 요청 DTO는 이미 OffsetDateTime을 사용한다. API 필드 구조는 유지하며 SERVER 코드는 수정하지 않는다.

테스트는 validate_spontaneous_time_window(..., now=fixed_now) 또는 spontaneous.time_window.current_kst_time의 고정 시각으로 실제 현재시간 의존성을 제거한다.

즉흥 여행 실패 코드

목적지 추천

POST /api/v1/spontaneous-trips/destinations

  • SPONTANEOUS_DESTINATION_ROUTE_NOT_FOUND: 모든 라우팅 후보가 NO_ROUTE로 탈락
  • SPONTANEOUS_DESTINATION_TIME_TOO_SHORT: 모든 라우팅 후보가 최소 체류시간 부족으로 탈락
  • SPONTANEOUS_DESTINATION_TRANSPORT_CONSTRAINT: 경로 없음과 체류시간 부족이 함께 발생
  • SPONTANEOUS_DESTINATION_CANDIDATES_NOT_FOUND: 장소 또는 테마 조건을 만족하는 라우팅 후보가 없음
  • DESTINATIONS_NOT_FOUND: 분류되지 않은 기존 추천 실패

즉흥 코스

POST /api/v1/spontaneous-trips/course

  • DESTINATION_NOT_FOUND
  • TRANSPORT_MODE_UNAVAILABLE
  • PLACE_SEARCH_FAILED
  • COURSE_CANDIDATES_NOT_FOUND
  • COURSE_GENERATION_EMPTY
  • COURSE_SEGMENT_ROUTE_UNAVAILABLE
  • COURSE_RETURN_ROUTE_UNAVAILABLE

프론트 문구 예시:

  • TRANSPORT_MODE_UNAVAILABLE
    • 선택한 이동수단으로 이동 가능한 코스를 찾지 못했습니다.
  • COURSE_CANDIDATES_NOT_FOUND
    • 주변에서 추천할 장소를 찾지 못했습니다.
  • COURSE_SEGMENT_ROUTE_UNAVAILABLE
    • 추천 코스의 이동 경로를 계산하지 못했습니다. 다시 시도해 주세요.
  • COURSE_RETURN_ROUTE_UNAVAILABLE
    • 복귀 경로를 계산하지 못했습니다. 다른 이동수단으로 다시 시도해 주세요.

로컬 실행

cd /Users/miju/test_1/data
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --reload --host 0.0.0.0 --port 8010

빠른 확인 명령

health

curl -s http://127.0.0.1:8010/health

카테고리 분류

curl -s -X POST http://127.0.0.1:8010/predict \
  -H 'Content-Type: application/json' \
  --data '{
    "title": "광안리해수욕장",
    "overview": "부산 대표 해변과 야경 명소",
    "contenttypeid": "12",
    "cat3": "A01011100",
    "addr1": "부산 수영구 광안해변로 219",
    "mapx": 129.1186,
    "mapy": 35.1532
  }'

일정 미리보기

curl -s -X POST http://127.0.0.1:8010/api/v1/schedule-previews \
  -H 'Content-Type: application/json' \
  -d '{
    "startDate": "2026-08-10",
    "endDate": "2026-08-11",
    "startLocation": {
      "name": "부산역",
      "longitude": 129.0403,
      "latitude": 35.1151
    },
    "lodgingPlan": {
      "mode": "UNDECIDED",
      "nightStays": []
    },
    "selectedAnswers": [
      { "questionId": "THEME", "answerIds": ["THEME_NATURE"] },
      { "questionId": "COMPANION", "answerIds": ["COMPANION_FRIENDS"] },
      { "questionId": "MOBILITY", "answerIds": ["MOBILITY_NORMAL"] },
      { "questionId": "PACE", "answerIds": ["PACE_RELAXED"] },
      { "questionId": "TRANSIT", "answerIds": ["TRANSIT_SIMPLE"] }
    ],
    "mustVisitPlaceIds": [],
    "fixedEvents": [],
    "dayOverrides": []
  }'

파일 역할

현재 한 줄 요약

현재 data/ 는 단순 분류 서버가 아니라, 분류 + 일정 preview/create/list/get/update/map + 즉흥 여행 추천/코스를 담당하는 FastAPI 서버입니다.

다만 공개 API의 최종 진입점은 아직 Spring이 일부 맡고 있으므로, 현재 단계는 하이브리드 운영 상태입니다.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages