Skip to content

API Reference

์ด ๋ฌธ์„œ๋Š” ์นตํ…Œ์ผ ๋ฉ”์ด์ปค ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” API ์—”๋“œํฌ์ธํŠธ ๋ฐ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์— ๋Œ€ํ•œ ๊ธฐ์ˆ  ์ฐธ์กฐ ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค.

Base URL: http://localhost:8000/api/v1

๐Ÿ“‹ ๋ชฉ์ฐจ

์ธ์ฆ (Authentication)

POST /signup

์š”์•ฝ: ํšŒ์›๊ฐ€์ž…
์ธ์ฆ: ๋ถˆํ•„์š”

์š”์ฒญ ๋ณธ๋ฌธ:

{
  "userId": "string",
  "password": "string"
}

์‘๋‹ต: - 204 No Content: ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต, ์ž๋™ ๋กœ๊ทธ์ธ ๋ฐ ์ฟ ํ‚ค ์„ค์ • - 409 Conflict: ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์‚ฌ์šฉ์ž

POST /signin

์š”์•ฝ: ๋กœ๊ทธ์ธ
์ธ์ฆ: ๋ถˆํ•„์š”

์š”์ฒญ ๋ณธ๋ฌธ:

{
  "userId": "string", 
  "password": "string"
}

์‘๋‹ต: - 204 No Content: ๋กœ๊ทธ์ธ ์„ฑ๊ณต, ์ธ์ฆ ์ฟ ํ‚ค ์„ค์ • - 401 Unauthorized: ์ž˜๋ชป๋œ ์ธ์ฆ ์ •๋ณด

POST /refresh-token

์š”์•ฝ: ์•ก์„ธ์Šค ํ† ํฐ ๊ฐฑ์‹ 
์ธ์ฆ: refreshToken ์ฟ ํ‚ค ํ•„์š”

์‘๋‹ต: - 204 No Content: ํ† ํฐ ๊ฐฑ์‹  ์„ฑ๊ณต - 401 Unauthorized: ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ˆ„๋ฝ ๋˜๋Š” ๋งŒ๋ฃŒ

GET /my-role

์š”์•ฝ: ํ˜„์žฌ ์‚ฌ์šฉ์ž ๊ถŒํ•œ ํ™•์ธ
์ธ์ฆ: ํ•„์š”

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": {
    "roles": ["admin", "user"]
  },
  "message": "Successfully get user roles"
}

POST /publish-api-key

์š”์•ฝ: API ํ‚ค ๋ฐœ๊ธ‰
์ธ์ฆ: ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ํ•„์š”

์š”์ฒญ ๋ณธ๋ฌธ:

{
  "domain": "string"
}

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": {
    "api_key": "generated_api_key"
  },
  "message": "API key generated successfully"
}

์ฃผ๋ฅ˜ (Spirits)

POST /spirits

์š”์•ฝ: ์ฃผ๋ฅ˜ ์ •๋ณด ๋“ฑ๋ก
์ธ์ฆ: ํ•„์š”
Content-Type: multipart/form-data

ํผ ํ•„๋“œ: - name (string, ํ•„์ˆ˜): ์ฃผ๋ฅ˜ ์ด๋ฆ„ - aroma (array[string], ํ•„์ˆ˜): ํ–ฅ ํŠน์„ฑ ๋ชฉ๋ก - taste (array[string], ํ•„์ˆ˜): ๋ง› ํŠน์„ฑ ๋ชฉ๋ก - finish (array[string], ํ•„์ˆ˜): ์—ฌ์šด ํŠน์„ฑ ๋ชฉ๋ก - kind (string, ํ•„์ˆ˜): ์ฃผ๋ฅ˜ ์ข…๋ฅ˜ - subKind (string, ํ•„์ˆ˜): ์„ธ๋ถ€ ์ข…๋ฅ˜ - amount (float, ํ•„์ˆ˜): ์šฉ๋Ÿ‰ (mL) - alcohol (float, ํ•„์ˆ˜): ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ (%) - originNation (string, ํ•„์ˆ˜): ์›์‚ฐ์ง€ ๊ตญ๊ฐ€ - originLocation (string, ํ•„์ˆ˜): ์›์‚ฐ์ง€ ์ง€์—ญ - description (string, ํ•„์ˆ˜): ์„ค๋ช… - mainImage (file, ํ•„์ˆ˜): ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ (์ตœ๋Œ€ 2MB) - subImage1-4 (file, ์„ ํƒ): ๋ณด์กฐ ์ด๋ฏธ์ง€๋“ค

์‘๋‹ต:

{
  "status": "success",
  "code": 201,
  "data": "document_id", 
  "message": "Successfully register spirits"
}

GET /spirits

์š”์•ฝ: ์ฃผ๋ฅ˜ ๊ฒ€์ƒ‰
์ธ์ฆ: ๋ถˆํ•„์š”

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ: - name (string): ์ด๋ฆ„ ๋ถ€๋ถ„ ์ผ์น˜ ๊ฒ€์ƒ‰ - aroma (array[string]): ํ–ฅ ํŠน์„ฑ ์ •ํ™• ์ผ์น˜ - taste (array[string]): ๋ง› ํŠน์„ฑ ์ •ํ™• ์ผ์น˜
- finish (array[string]): ์—ฌ์šด ํŠน์„ฑ ์ •ํ™• ์ผ์น˜ - kind (string): ์ฃผ๋ฅ˜ ์ข…๋ฅ˜ ์ •ํ™• ์ผ์น˜ - subKind (string): ์„ธ๋ถ€ ์ข…๋ฅ˜ ์ •ํ™• ์ผ์น˜ - minAlcohol (float): ์ตœ์†Œ ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ - maxAlcohol (float): ์ตœ๋Œ€ ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ - originNation (string): ์›์‚ฐ์ง€ ๊ตญ๊ฐ€ ์ •ํ™• ์ผ์น˜ - originLocation (string): ์›์‚ฐ์ง€ ์ง€์—ญ ๋ถ€๋ถ„ ์ผ์น˜ - pageNumber (int, ๊ธฐ๋ณธ๊ฐ’: 1): ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ - pageSize (int, ๊ธฐ๋ณธ๊ฐ’: 10, ์ตœ๋Œ€: 100): ํŽ˜์ด์ง€ ํฌ๊ธฐ

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": {
    "items": [...],
    "total_count": 150,
    "page_number": 1,
    "page_size": 10,
    "total_pages": 15
  },
  "message": "Successfully search spirits"
}

GET /spirits/{name}

์š”์•ฝ: ๋‹จ์ผ ์ฃผ๋ฅ˜ ์กฐํšŒ
์ธ์ฆ: ๋ถˆํ•„์š”

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: - name (string): ์ฃผ๋ฅ˜ ์ด๋ฆ„ (์ •ํ™•ํ•œ ์ผ์น˜)

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": {
    "name": "Tanqueray London Dry Gin",
    "aroma": ["juniper", "citrus"],
    "taste": ["dry", "botanical"],
    "finish": ["clean"],
    "kind": "Gin",
    "sub_kind": "London Dry",
    "amount": 750.0,
    "alcohol": 47.3,
    "origin_nation": "England",
    "origin_location": "London",
    "description": "Classic London Dry Gin",
    "created_at": "2024-01-01T00:00:00Z"
  },
  "message": "Successfully get spirits"
}

PUT /spirits/{document_id}

์š”์•ฝ: ์ฃผ๋ฅ˜ ์ •๋ณด ์ˆ˜์ •
์ธ์ฆ: ํ•„์š”
Content-Type: multipart/form-data

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: - document_id (string): MongoDB ๋ฌธ์„œ ID

ํผ ํ•„๋“œ: POST /spirits์™€ ๋™์ผ

์‘๋‹ต: - 204 No Content: ์ˆ˜์ • ์„ฑ๊ณต

DELETE /spirits/{document_id}

์š”์•ฝ: ์ฃผ๋ฅ˜ ์ •๋ณด ์‚ญ์ œ
์ธ์ฆ: ํ•„์š”

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: - document_id (string): MongoDB ๋ฌธ์„œ ID

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": null,
  "message": "Successfully delete spirits"
}

๋ฆฌํ๋ฅด (Liqueur)

POST /liqueur

์š”์•ฝ: ๋ฆฌํ๋ฅด ์ •๋ณด ๋“ฑ๋ก
์ธ์ฆ: ํ•„์š”
Content-Type: multipart/form-data

ํผ ํ•„๋“œ: - name (string, ํ•„์ˆ˜): ๋ฆฌํ๋ฅด ์ด๋ฆ„ - brand (string, ํ•„์ˆ˜): ๋ธŒ๋žœ๋“œ - taste (array[string], ํ•„์ˆ˜): ๋ง› ํŠน์„ฑ ๋ชฉ๋ก - kind (string, ํ•„์ˆ˜): ๋ฆฌํ๋ฅด ์ข…๋ฅ˜ - subKind (string, ํ•„์ˆ˜): ์„ธ๋ถ€ ์ข…๋ฅ˜ - mainIngredients (array[string], ํ•„์ˆ˜): ์ฃผ์žฌ๋ฃŒ ๋ชฉ๋ก - volume (float, ํ•„์ˆ˜): ์šฉ๋Ÿ‰ (mL) - abv (float, ํ•„์ˆ˜): ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ (%) - originNation (string, ํ•„์ˆ˜): ์›์‚ฐ์ง€ ๊ตญ๊ฐ€ - description (string, ํ•„์ˆ˜): ์„ค๋ช… - mainImage (file, ํ•„์ˆ˜): ๋Œ€ํ‘œ ์ด๋ฏธ์ง€

GET /liqueur

์š”์•ฝ: ๋ฆฌํ๋ฅด ๊ฒ€์ƒ‰
์ธ์ฆ: ํ•„์š”

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ: - name (string): ์ด๋ฆ„ ๋ถ€๋ถ„ ์ผ์น˜ - brand (string): ๋ธŒ๋žœ๋“œ ์ •ํ™• ์ผ์น˜ - taste (array[string]): ๋ง› ํŠน์„ฑ ์ •ํ™• ์ผ์น˜ - kind (string): ์ข…๋ฅ˜ ์ •ํ™• ์ผ์น˜ - subKind (string): ์„ธ๋ถ€ ์ข…๋ฅ˜ ์ •ํ™• ์ผ์น˜ - mainIngredients (array[string]): ์ฃผ์žฌ๋ฃŒ ์ •ํ™• ์ผ์น˜ - minVolume (float): ์ตœ์†Œ ์šฉ๋Ÿ‰ - maxVolume (float): ์ตœ๋Œ€ ์šฉ๋Ÿ‰ - minAbv (float): ์ตœ์†Œ ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ - maxAbv (float): ์ตœ๋Œ€ ์•Œ์ฝ”์˜ฌ ๋„์ˆ˜ - originNation (string): ์›์‚ฐ์ง€ ๊ตญ๊ฐ€ ์ •ํ™• ์ผ์น˜ - pageNumber (int): ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ - pageSize (int): ํŽ˜์ด์ง€ ํฌ๊ธฐ

GET /liqueur/{name}

์š”์•ฝ: ๋‹จ์ผ ๋ฆฌํ๋ฅด ์กฐํšŒ
์ธ์ฆ: ๋ถˆํ•„์š”

PUT /liqueur/{document_id}

์š”์•ฝ: ๋ฆฌํ๋ฅด ์ •๋ณด ์ˆ˜์ •
์ธ์ฆ: ํ•„์š”

DELETE /liqueur/{document_id}

์š”์•ฝ: ๋ฆฌํ๋ฅด ์ •๋ณด ์‚ญ์ œ
์ธ์ฆ: ํ•„์š”

๊ธฐํƒ€ ์žฌ๋ฃŒ (Ingredient)

POST /ingredient

์š”์•ฝ: ๊ธฐํƒ€ ์žฌ๋ฃŒ ๋“ฑ๋ก
์ธ์ฆ: ํ•„์š”
Content-Type: multipart/form-data

ํผ ํ•„๋“œ: - name (string, ํ•„์ˆ˜): ์žฌ๋ฃŒ ์ด๋ฆ„ - kind (string, ํ•„์ˆ˜): ์žฌ๋ฃŒ ์ข…๋ฅ˜ - description (string, ํ•„์ˆ˜): ์„ค๋ช… - mainImage (file, ํ•„์ˆ˜): ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - brand (array[string], ์„ ํƒ): ๋ธŒ๋žœ๋“œ ๋ชฉ๋ก

GET /ingredient

์š”์•ฝ: ๊ธฐํƒ€ ์žฌ๋ฃŒ ๊ฒ€์ƒ‰
์ธ์ฆ: ํ•„์š”

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ: - name (string): ์ด๋ฆ„ ๋ถ€๋ถ„ ์ผ์น˜ - brand (array[string]): ๋ธŒ๋žœ๋“œ ์ •ํ™• ์ผ์น˜ - kind (string): ์ข…๋ฅ˜ ์ •ํ™• ์ผ์น˜ - description (string): ์„ค๋ช… ๋ถ€๋ถ„ ์ผ์น˜ - pageNumber (int): ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ - pageSize (int): ํŽ˜์ด์ง€ ํฌ๊ธฐ

GET /ingredient/{name}

์š”์•ฝ: ๋‹จ์ผ ๊ธฐํƒ€ ์žฌ๋ฃŒ ์กฐํšŒ
์ธ์ฆ: ๋ถˆํ•„์š”

PUT /ingredient/{document_id}

์š”์•ฝ: ๊ธฐํƒ€ ์žฌ๋ฃŒ ์ˆ˜์ •
์ธ์ฆ: ํ•„์š”

DELETE /ingredient/{document_id}

์š”์•ฝ: ๊ธฐํƒ€ ์žฌ๋ฃŒ ์‚ญ์ œ
์ธ์ฆ: ํ•„์š”

๋ฉ”ํƒ€๋ฐ์ดํ„ฐ (Metadata)

GET /metadata/{kind}/{category}

์š”์•ฝ: ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐํšŒ
์ธ์ฆ: ๋ถˆํ•„์š”

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: - kind: spirits, liqueur, ingredient - category: taste, aroma, finish, kind

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": [
    {
      "id": 1,
      "value": "sweet"
    },
    {
      "id": 2, 
      "value": "dry"
    }
  ],
  "message": "Successfully get metadata"
}

POST /metadata/{kind}/{category}

์š”์•ฝ: ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋“ฑ๋ก
์ธ์ฆ: ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ํ•„์š”

์š”์ฒญ ๋ณธ๋ฌธ:

{
  "items": ["sweet", "dry", "fruity"]
}

DELETE /metadata/{id}

์š”์•ฝ: ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์‚ญ์ œ
์ธ์ฆ: ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ํ•„์š”

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: - id (int): ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ID

์ƒํƒœ ํ™•์ธ (Health Check)

GET /health

์š”์•ฝ: ์„œ๋น„์Šค ์ƒํƒœ ํ™•์ธ
์ธ์ฆ: ๋ถˆํ•„์š”

์‘๋‹ต:

{
  "status": "success",
  "code": 200,
  "data": {
    "status": "ok"
  },
  "message": "Service is running"
}

๐Ÿšจ ๊ณตํ†ต ์˜ค๋ฅ˜ ์‘๋‹ต

๋ชจ๋“  ์—”๋“œํฌ์ธํŠธ๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ RFC 9457 Problem Details ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค:

{
  "type": "https://httpstatuses.com/400",
  "title": "Client Error 400",
  "detail": "Detailed error message",
  "status": 400
}

์ฃผ์š” HTTP ์ƒํƒœ ์ฝ”๋“œ: - 200: ์„ฑ๊ณต - 201: ์ƒ์„ฑ ์„ฑ๊ณต - 204: ์„ฑ๊ณต (์‘๋‹ต ๋ณธ๋ฌธ ์—†์Œ) - 400: ์ž˜๋ชป๋œ ์š”์ฒญ - 401: ์ธ์ฆ ํ•„์š” - 403: ๊ถŒํ•œ ์—†์Œ - 404: ๋ฆฌ์†Œ์Šค ์—†์Œ - 409: ์ค‘๋ณต ๋ฆฌ์†Œ์Šค - 422: ๊ฒ€์ฆ ์˜ค๋ฅ˜ - 500: ์„œ๋ฒ„ ์˜ค๋ฅ˜

๐Ÿ’ก ์ถ”๊ฐ€ ์ •๋ณด

์ฝ”๋“œ๋ฅผ ์ง์ ‘ ํƒ์ƒ‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด: - ๋ผ์šฐํŠธ ์ •์˜: app/main.py - ๋ฐ์ดํ„ฐ ๋ชจ๋ธ: app/model/ - CRUD ์ž‘์—…: app/query/ - ์ธ์ฆ ๋กœ์ง: app/auth/

์ƒํ˜ธ์ž‘์šฉ ๋ฌธ์„œ: - Swagger UI: /api/docs - ReDoc: /api/redoc