Skip to content

About

🌍BeTrip의 Backendμž…λ‹ˆλ‹€πŸŒ ν•΄λ‹Ή μ„œλΉ„μŠ€λŠ” μ—¬ν–‰μ§€/인원/μ—¬ν–‰ μŠ€νƒ€μΌ 기반으둜 AIκ°€ 일정을 μžλ™ μƒμ„±ν•΄μ£ΌλŠ” μ—¬ν–‰ 일정 ν”Œλž˜λ„ˆ μ›Ή μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

🌍 BeTrip Backend

여행지·인원·여행 μŠ€νƒ€μΌμ„ μž…λ ₯ν•˜λ©΄ 일정을 μžλ™μœΌλ‘œ λ§Œλ“€μ–΄ μ£Όκ³ , λŒ€ν™”λ‘œ μ·¨ν–₯에 λ§žλŠ” μž₯μ†Œλ₯Ό μΆ”μ²œλ°›μ•„ λ°”λ‘œ 일정에 담을 수 μžˆλŠ” μ—¬ν–‰ 일정 ν”Œλž˜λ„ˆ μ›Ή μ„œλΉ„μŠ€μ˜ λ°±μ—”λ“œμž…λ‹ˆλ‹€.

Python FastAPI PostgreSQL SQLAlchemy Gemini Docker


πŸ“Œ λͺ©μ°¨


πŸ’‘ μ£Όμš” κΈ°λŠ₯

κΈ°λŠ₯ μ„€λͺ…
일정 μžλ™ 생성 λ‹΄μ•„ λ‘” μž₯μ†Œλ₯Ό μœ„μΉ˜ κΈ°μ€€μœΌλ‘œ μΌμžλ³„λ‘œ λ‚˜λˆ•λ‹ˆλ‹€. (k-means)
ν•˜λ£¨ μ•ˆμ—μ„œλŠ” 이동 거리가 μ΅œμ†Œκ°€ λ˜λŠ” μˆœμ„œλ‘œ μ •λ ¬ν•˜κ³  μ˜€μ „, 점심, 저녁에 λ°°μΉ˜ν•©λ‹ˆλ‹€.
μž₯μ†Œ κ°„ 이동 μ‹œκ°„μ€ 카카였λͺ¨λΉŒλ¦¬ν‹° API둜 κ³„μ‚°ν•©λ‹ˆλ‹€.
λŒ€ν™”ν˜• μž₯μ†Œ μΆ”μ²œ Gemini function calling으둜 카카였맡과 ν•œκ΅­κ΄€κ΄‘κ³΅μ‚¬ TourAPIλ₯Ό μ—°κ²°ν•©λ‹ˆλ‹€.
μ‹€μ œ 검색 결과에 κ·Όκ±°ν•΄ μž₯μ†Œλ₯Ό μΆ”μ²œν•˜λ©°, μΆ”μ²œ μΉ΄λ“œμ—μ„œ λ°”λ‘œ 일정에 담을 수 μžˆμŠ΅λ‹ˆλ‹€.
μ·¨ν–₯ νƒœκ·Έ 검색 λΈ”λ‘œκ·Έ ν›„κΈ°λ₯Ό LLM으둜 감성, μ‘°μš©ν•¨, λ·°λ§›μ§‘ λ“± 11μ’…μ˜ κ³ μ • νƒœκ·Έλ‘œ λΆ„λ₯˜ν•©λ‹ˆλ‹€.
배치 μ›Œμ»€κ°€ 미리 μˆ˜ν–‰ν•œ λΆ„λ₯˜ κ²°κ³Όλ₯Ό ν™œμš©ν•΄ "μ‘°μš©ν•œ 카페" 같은 질의λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
지도 검색 카카였맡 ν‚€μ›Œλ“œ, μ’Œν‘œ, μ˜μ—­ 검색을 μ§€μ›ν•©λ‹ˆλ‹€.
μž₯μ†Œ 상세 정보와 썸넀일(og:image)을 μˆ˜μ§‘ν•˜κ³ , μž₯μ†Œ κ°„ 이동 μ‹œκ°„μ„ κ³„μ‚°ν•©λ‹ˆλ‹€.
일정 νŽΈμ§‘ μž₯μ†Œ 담기와 μ‚­μ œ, λ‚ μ§œ 및 μ‹œκ°„λŒ€ 이동, 같은 λ‚ μ˜ λ°©λ¬Έ μˆœμ„œ 변경을 μ§€μ›ν•©λ‹ˆλ‹€.
λ³€κ²½ μ‹œ ν•΄λ‹Ή 일자의 λ°©λ¬Έ μˆœμ„œμ™€ μ‹œμž‘ μ‹œκ°μ„ λ‹€μ‹œ κ³„μ‚°ν•©λ‹ˆλ‹€.
인증 JWT Access Tokenκ³Ό νšŒμ „(rotation)λ˜λŠ” Refresh Token(HttpOnly μΏ ν‚€)을 μ‚¬μš©ν•©λ‹ˆλ‹€.
λ™μ‹œ κ°±μ‹  μš”μ²­μ— λŒ€μ‘ν•˜κΈ° μœ„ν•΄ μ›μžμ  폐기와 μž¬μ‚¬μš© 유예 μ‹œκ°„μ„ μ μš©ν–ˆμŠ΅λ‹ˆλ‹€.

πŸ› οΈ 기술 μŠ€νƒ

λΆ„λ₯˜ μ‚¬μš© 기술
Language Python 3.12
Framework FastAPI, Pydantic v2, Uvicorn
Database PostgreSQL 16, SQLAlchemy 2.0 (async) + asyncpg, Alembic
LLM Google Gemini (google-genai): function calling, structured output
μ™ΈλΆ€ API 카카였맡/카카였λͺ¨λΉŒλ¦¬ν‹°, ν•œκ΅­κ΄€κ΄‘κ³΅μ‚¬ TourAPI, 넀이버 검색(λΈ”λ‘œκ·Έ)
비동기 μž‘μ—… APScheduler (νƒœκ·Έ λΆ„λ₯˜ 배치)
인증 PyJWT, bcrypt
ν…ŒμŠ€νŠΈ 및 ν’ˆμ§ˆ pytest, pytest-asyncio, pytest-cov, Ruff, pre-commit, SonarCloud
배포 Docker, GitHub Actions, GitHub Container Registry

🐟 μ‹œμŠ€ν…œ ꡬ쑰

전체 ꡬ쑰

image

Backend ꡬ쑰

flowchart LR
    FE[Frontend] -->|REST /api/v1| API[FastAPI Router]
 
    subgraph Backend
        API --> SVC[Service]
        SVC --> REPO[Repository]
        REPO --> DB[(PostgreSQL)]
        SVC --> CONV[ConversationService]
        SCHED[APScheduler<br/>νƒœκ·Έ λΆ„λ₯˜ 배치] --> TAG[TagService]
        TAG --> REPO
    end
 
    CONV <-->|function calling| GEMINI[Gemini]
    CONV -->|search_places| KAKAO[카카였맡]
    CONV -->|search_tour_places| TOUR[TourAPI]
    SVC --> MOBILITY[카카였λͺ¨λΉŒλ¦¬ν‹°]
    TAG --> NAVER[넀이버 λΈ”λ‘œκ·Έ 검색]
    TAG -->|νƒœκ·Έ λΆ„λ₯˜| GEMINI
Loading
  1. Router / Service / Repository 3κ³„μΈ΅μœΌλ‘œ λ‚˜λˆ„κ³  μ˜μ‘΄μ„± μ£Όμž…(Depends)으둜 μ—°κ²°ν•©λ‹ˆλ‹€.
    • μ„œλΉ„μŠ€λŠ” λͺ¨ν‚Ήν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ‘œ,
    • λ ˆν¬μ§€ν† λ¦¬λŠ” μ‹€μ œ PostgreSQL 톡합 ν…ŒμŠ€νŠΈλ‘œ κ²€μ¦ν•©λ‹ˆλ‹€.
  2. 카카였 검색 κ²°κ³ΌλŠ” places ν…Œμ΄λΈ”μ— μΊμ‹±λ˜κ³ , 배치 μ›Œμ»€κ°€ νƒœκ·Έκ°€ μ—†κ±°λ‚˜ 90일이 μ§€λ‚œ μž₯μ†Œλ₯Ό 골라 νƒœκ·Έλ₯Ό μ±„μ›λ‹ˆλ‹€.
    • μ‚¬μš©μž μš”μ²­ κ²½λ‘œμ—μ„œλŠ” LLM λΆ„λ₯˜λ₯Ό ν˜ΈμΆœν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

πŸ“„ API

λͺ¨λ“  경둜의 μ ‘λ‘μ‚¬λŠ” /api/v1μž…λ‹ˆλ‹€. μ„œλ²„ μ‹€ν–‰ ν›„ Swaggerμ—μ„œ μš”μ²­Β·μ‘λ‹΅ μŠ€ν‚€λ§ˆλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 인증이 ν•„μš”ν•œ APIλŠ” Authorization: Bearer <access_token> 헀더λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Auth

Method Path μ„€λͺ… 인증
POST /auth/signup νšŒμ›κ°€μž…
POST /auth/login 둜그인, Access Token λ°œκΈ‰ 및 Refresh Token μΏ ν‚€ μ„€μ •
POST /auth/refresh Refresh Token으둜 토큰 μž¬λ°œκΈ‰ (rotation) μΏ ν‚€
POST /auth/logout Refresh Token 폐기 μΏ ν‚€

Itineraries

Method Path μ„€λͺ… 인증
POST /itineraries/conditions μ—¬ν–‰ 쑰건(μ§€μ—­, λ‚ μ§œ, λͺ©μ , μŠ€νƒ€μΌ λ“±)으둜 일정 생성 βœ…
GET /itineraries λ‚΄ 일정 λͺ©λ‘ βœ…
GET /itineraries/{itinerary_id} 일정 상세 (μΌμžΒ·μ‹œκ°„λŒ€λ³„ μž₯μ†Œ) βœ…
DELETE /itineraries/{itinerary_id} 일정 μ‚­μ œ βœ…
POST /itineraries/{itinerary_id}/plans/generate 담은 μž₯μ†Œλ‘œ 일정 μžλ™ 배치 βœ…
POST /itineraries/{itinerary_id}/plans/save 배치된 일정 μ €μž₯ βœ…

Itinerary Places

Method Path μ„€λͺ… 인증
GET /itineraries/{itinerary_id}/places/recommend μ§€μ—­Β·λͺ©μ Β·μŠ€νƒ€μΌ 기반 μž₯μ†Œ μΆ”μ²œ (category ν•„ν„°)
POST /itineraries/{itinerary_id}/places μž₯μ†Œ λ‹΄κΈ°
DELETE /itineraries/{itinerary_id}/places/{itinerary_place_id} 담은 μž₯μ†Œ μ‚­μ œ
PATCH /itineraries/{itinerary_id}/places/reorder 같은 λ‚  μ•ˆμ—μ„œ μˆœμ„œ λ³€κ²½
PATCH /itineraries/{itinerary_id}/places/{place_id} λ‹€λ₯Έ λ‚ μ§œΒ·μ‹œκ°„λŒ€λ‘œ 이동

Map

Method Path μ„€λͺ… 인증
GET /map/search μž₯μ†Œ 검색 (q, xΒ·yΒ·radius, rect, category, page)
GET /map/search/by-tags μΊμ‹±λœ μž₯μ†Œλ₯Ό μ·¨ν–₯ νƒœκ·Έλ‘œ 검색 (tags, category, xΒ·yΒ·radius)
GET /map/places/{place_id} μž₯μ†Œ 상세
GET /map/transit 두 지점 κ°„ 이동 μ‹œκ°„ 계산

Chat

Method Path μ„€λͺ… 인증
POST /itineraries/{itinerary_id}/chat λŒ€ν™”ν˜• μž₯μ†Œ μΆ”μ²œ.
응닡에 λ‹΅λ³€ ν…μŠ€νŠΈμ™€ μž₯μ†Œ μΉ΄λ“œ(μ’Œν‘œ, νƒœκ·Έ 포함)λ₯Ό ν•¨κ»˜ λ°˜ν™˜

πŸ“Š λ°μ΄ν„°λ² μ΄μŠ€

erDiagram
    users ||--o{ refresh_tokens : has
    users ||--o{ itineraries : owns
    itineraries ||--o{ itinerary_places : contains
    places ||--o{ itinerary_places : "referenced by"
    itineraries ||--o{ conversation_messages : has
 
    users {
        uuid user_id PK
        varchar email UK
        varchar password_hash
        varchar provider "LOCAL | KAKAO"
    }
    refresh_tokens {
        uuid token_id PK
        uuid user_id FK
        varchar token_hash "SHA-256"
        timestamptz expires_at
        timestamptz revoked_at
    }
    itineraries {
        uuid itinerary_id PK
        uuid user_id FK
        varchar status "DRAFT | GENERATED | SAVED"
        varchar region
        date start_date
        date end_date
        jsonb styles
    }
    places {
        varchar place_id PK "카카였 ID λ˜λŠ” tour_ 접두사"
        varchar name
        varchar category "RESTAURANT | CAFE | ACTIVITY"
        float lat
        float lng
        jsonb tags "GIN jsonb_path_ops"
        timestamp tags_synced_at
    }
    itinerary_places {
        uuid itinerary_place_id PK
        uuid itinerary_id FK
        varchar place_id FK
        smallint day "NULL이면 λ―Έλ°°μ •"
        varchar time_slot
        smallint order_in_day
        varchar start_time
    }
    conversation_messages {
        uuid message_id PK
        uuid itinerary_id FK
        varchar role
        text content
    }
Loading

섀계 ν¬μΈνŠΈλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 담기와 배정을 ν•œ ν…Œμ΄λΈ”μ—μ„œ: itinerary_places의 day / time_slot / order_in_dayλŠ” nullable이고, λ°°μ •λœ μŠ¬λ‘―μ—λ§Œ κ±Έλ¦¬λŠ” λΆ€λΆ„ μœ λ‹ˆν¬ 인덱슀(WHERE day IS NOT NULL)둜 쀑볡을 λ§‰μŠ΅λ‹ˆλ‹€.
  • νƒœκ·Έ 검색: places.tags(JSONB)에 GIN(jsonb_path_ops) 인덱슀λ₯Ό 두어 @> 포함 검색을 μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • 토큰 λ³΄μ•ˆ: Refresh Token은 원문 λŒ€μ‹  SHA-256 ν•΄μ‹œλ§Œ μ €μž₯ν•©λ‹ˆλ‹€.
  • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜: μŠ€ν‚€λ§ˆ 변경은 Alembic으둜 κ΄€λ¦¬ν•˜λ©°, μ»¨ν…Œμ΄λ„ˆ μ‹œμž‘ μ‹œ alembic upgrade headκ°€ μžλ™ μ‹€ν–‰λ©λ‹ˆλ‹€.

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

app/
β”œβ”€β”€ api/v1/
β”‚   β”œβ”€β”€ router.py            # 도메인별 λΌμš°ν„° 등둝
β”‚   └── endpoints/           # auth, itineraries, itinerary_place, map, chat
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ config.py            # ν™˜κ²½λ³€μˆ˜ (pydantic-settings)
β”‚   β”œβ”€β”€ security.py          # JWT, λΉ„λ°€λ²ˆν˜Έ ν•΄μ‹±
β”‚   β”œβ”€β”€ scheduler.py         # νƒœκ·Έ λΆ„λ₯˜ 배치 (APScheduler)
β”‚   └── *_client.py          # 카카였, 넀이버, TourAPI ν΄λΌμ΄μ–ΈνŠΈ
β”œβ”€β”€ db/                      # async μ—”μ§„Β·μ„Έμ…˜
β”œβ”€β”€ models/                  # SQLAlchemy λͺ¨λΈ
β”œβ”€β”€ repositories/            # DB μ ‘κ·Ό
β”œβ”€β”€ schemas/                 # μš”μ²­Β·μ‘λ‹΅ μŠ€ν‚€λ§ˆ (Pydantic)
β”œβ”€β”€ services/                # λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
└── utils/
    └── itinerary_planner.py # 일자 ν΄λŸ¬μŠ€ν„°λ§, 동선 μ •λ ¬, μ‹œκ°„ 배치
alembic/                     # λ§ˆμ΄κ·Έλ ˆμ΄μ…˜
scripts/
β”œβ”€β”€ seed.sql                 # μ‹œλ“œ 데이터
└── eval_tool_calling.py     # LLM tool calling 평가 슀크립트
tests/
β”œβ”€β”€ unit/                    # μ„œλΉ„μŠ€ λ‹¨μœ„ ν…ŒμŠ€νŠΈ (λͺ¨ν‚Ή)
└── integration/             # μ‹€μ œ PostgreSQL λŒ€μƒ 톡합 ν…ŒμŠ€νŠΈ

πŸš€ μ‹œμž‘ν•˜κΈ°

1. ν™˜κ²½λ³€μˆ˜

cp .env.example .env
λ³€μˆ˜ μ„€λͺ… ν•„μˆ˜
DATABASE_URL postgresql+asyncpg://user:password@localhost:5432/betrip βœ…
JWT_SECRET_KEY JWT μ„œλͺ… ν‚€ βœ…
CORS_ORIGINS ν—ˆμš©ν•  ν”„λ‘ νŠΈμ—”λ“œ μ£Όμ†Œ (κΈ°λ³Έ http://localhost:5173)
COOKIE_SECURE 둜컬 httpλŠ” false, 배포(https)λŠ” true
KAKAO_REST_API_KEY 카카였맡·카카였λͺ¨λΉŒλ¦¬ν‹° βœ…
NAVER_CLIENT_ID, NAVER_CLIENT_SECRET 넀이버 λΈ”λ‘œκ·Έ 검색 (νƒœκ·Έ λΆ„λ₯˜μš©) βœ…
GEMINI_API_KEY λŒ€ν™”ν˜• μΆ”μ²œ, νƒœκ·Έ λΆ„λ₯˜ βœ…
TOUR_API_KEY ν•œκ΅­κ΄€κ΄‘κ³΅μ‚¬ TourAPI βœ…
OPENAI_API_KEY ν˜„μž¬ μ½”λ“œμ—μ„œ μ‚¬μš©ν•˜μ§€ μ•Šμ§€λ§Œ μ„€μ • 검증에 ν•„μš” βœ…

2-A. 둜컬 μ‹€ν–‰

python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install -r requirements-dev.txt
pre-commit install                # 컀밋 μ‹œ ruff μžλ™ μ‹€ν–‰
 
alembic upgrade head              # μŠ€ν‚€λ§ˆ 적용
psql "postgresql://user:password@localhost:5432/betrip" -f scripts/seed.sql   # μ‹œλ“œ (선택)
 
uvicorn app.main:app --reload

2-B. Docker μ‹€ν–‰

μ΄λ―Έμ§€λŠ” main 브랜치 ν‘Έμ‹œ μ‹œ GHCR에 μžλ™μœΌλ‘œ μ˜¬λΌκ°‘λ‹ˆλ‹€. μ»¨ν…Œμ΄λ„ˆλŠ” μ‹œμž‘ν•  λ•Œ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜κ³Ό μ‹œλ“œ 적용 ν›„ μ„œλ²„λ₯Ό λ„μ›λ‹ˆλ‹€.

docker pull ghcr.io/hyun7931/betrip-backend:latest
docker run --env-file .env -p 8000:8000 ghcr.io/hyun7931/betrip-backend:latest

βœ… ν…ŒμŠ€νŠΈμ™€ 평가

pytest tests/unit            # λ‹¨μœ„ ν…ŒμŠ€νŠΈ (DB λΆˆν•„μš”)
pytest                       # 톡합 ν…ŒμŠ€νŠΈ 포함 (PostgreSQL ν•„μš”)

LLM tool calling 평가

λŒ€ν™”ν˜• μΆ”μ²œμ΄ μ§ˆμ˜μ— λ§žλŠ” 도ꡬ와 인자λ₯Ό κ³ λ₯΄λŠ”μ§€ ν‰κ°€μ…‹μœΌλ‘œ μΈ‘μ •ν•©λ‹ˆλ‹€. 도ꡬλ₯Ό μ‹€μ œλ‘œ μ‹€ν–‰ν•˜μ§€ μ•Šμ•„ DB 없이 GEMINI_API_KEY만으둜 μ‹€ν–‰ν•  수 있고, ν”„λ‘¬ν”„νŠΈλ‚˜ 도ꡬ μ„€λͺ…을 λ°”κΏ€ λ•Œ νšŒκ·€ ν™•μΈμš©μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.

python scripts/eval_tool_calling.py                     # 기본 질의 24개
python scripts/eval_tool_calling.py \
    --cases scripts/eval_tool_calling_cases_hard.json \
    --repeat 5                                          # 경계 사둀 15개, 5회 반볡
평가셋 κ²°κ³Ό
기본 질의 24개 24/24
경계 사둀 15개 Γ— 5회
(λͺ©λ‘ λ°– μ·¨ν–₯, μ˜€νƒ€Β·μ˜μ–΄, 도ꡬ 2개 ν•„μš”, 지원 μ™Έ μš”μ²­, 닀쀑 ν„΄ μ •μ •)
74/75 β†’ 도ꡬ μ„€λͺ… μˆ˜μ • ν›„ μ‹€νŒ¨ μΌ€μ΄μŠ€
10/10

πŸ”— CI/CD

μ›Œν¬ν”Œλ‘œ 트리거 λ‚΄μš©
ci.yml main, developλŒ€μƒ
push, PR
Postgres μ„œλΉ„μŠ€ μ»¨ν…Œμ΄λ„ˆ β†’ Ruff lint & format 검사
β†’ pytest(컀버리지) β†’ SonarCloud ν’ˆμ§ˆ 게이트
docker-build.yml main λŒ€μƒ
push
Docker 이미지 λΉŒλ“œ ν›„ GHCR ν‘Έμ‹œ

forkμ—μ„œ 올라온 PR은 μ‹œν¬λ¦Ώμ΄ μ „λ‹¬λ˜μ§€ μ•ŠμœΌλ―€λ‘œ SonarCloud λ‹¨κ³„λ§Œ μ‘°κ±΄λΆ€λ‘œ κ±΄λ„ˆλœλ‹ˆλ‹€.


😺 ν˜‘μ—… κ·œμΉ™

λΈŒλžœμΉ˜μ™€ PR

이슈 생성 β†’ μ΄μŠˆλ³„ 브랜치 β†’ PR(ν…œν”Œλ¦Ώ μ‚¬μš©) β†’ CI 톡과 ν›„ develop에 λ¨Έμ§€ν•©λ‹ˆλ‹€.

컀밋 λ©”μ‹œμ§€

type: subject
 
- 무엇을 μ™œ λ³€κ²½ν–ˆλŠ”μ§€ (ν•œ 쀄 72자 이내)
 
Closes #이슈번호
컀밋 νƒ€μž…κ³Ό μž‘μ„± κ·œμΉ™ | νƒ€μž… | μ„€λͺ… | | --- | --- | | `feat` | μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ | | `fix` | 버그 μˆ˜μ • | | `refactor` | κΈ°λŠ₯ λ³€ν™” μ—†λŠ” μ½”λ“œ ꡬ쑰 κ°œμ„  | | `docs` | λ¬Έμ„œ μˆ˜μ • | | `style` | μ½”λ“œ μŠ€νƒ€μΌ μˆ˜μ • | | `test` | ν…ŒμŠ€νŠΈ μ½”λ“œ μΆ”κ°€ 및 μˆ˜μ • | | `chore` | μ„€μ •Β·λΉŒλ“œΒ·νŒ¨ν‚€μ§€ λ“± μœ μ§€λ³΄μˆ˜ | | `hotfix` | 운영 ν™˜κ²½ κΈ΄κΈ‰ μˆ˜μ • |
  • 제λͺ©μ€ 50자 이내, 끝에 λ§ˆμΉ¨ν‘œ κΈˆμ§€
  • 제λͺ©κ³Ό λ³Έλ¬Έ 사이 ν•œ 쀄 곡백
  • λͺ…λ Ήλ¬Έ ν˜•νƒœ μ‚¬μš© (예: μˆ˜μ •ν•œλ‹€ X β†’ μˆ˜μ • O)
  • ν•˜λ‚˜μ˜ μ»€λ°‹μ—λŠ” ν•˜λ‚˜μ˜ μž‘μ—…λ§Œ 포함
  • 컀밋 λ‚΄μš©μ€ ν•œκ΅­μ–΄, 이슈 λ²ˆν˜ΈλŠ” Footer에 μž‘μ„±

μ½”λ“œ μŠ€νƒ€μΌ

컀밋 μ‹œ pre-commit이 Ruffλ₯Ό μžλ™ μ‹€ν–‰ν•©λ‹ˆλ‹€. μˆ˜λ™μœΌλ‘œ μ‹€ν–‰ν•˜λ €λ©΄:

ruff check . --fix && ruff format .     # μžλ™ μˆ˜μ •
ruff check . && ruff format --check .   # κ²€μ‚¬λ§Œ

About

🌍BeTrip의 Backendμž…λ‹ˆλ‹€πŸŒ ν•΄λ‹Ή μ„œλΉ„μŠ€λŠ” μ—¬ν–‰μ§€/인원/μ—¬ν–‰ μŠ€νƒ€μΌ 기반으둜 AIκ°€ 일정을 μžλ™ μƒμ„±ν•΄μ£ΌλŠ” μ—¬ν–‰ 일정 ν”Œλž˜λ„ˆ μ›Ή μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages