API Reference
์ด ๋ฌธ์๋ ์นตํ ์ผ ๋ฉ์ด์ปค ํ๋ก์ ํธ์ ์ฃผ์ API ์๋ํฌ์ธํธ ๋ฐ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ํ ๊ธฐ์ ์ฐธ์กฐ ๋ฌธ์์ ๋๋ค.
Base URL: http://localhost:8000/api/v1
๐ ๋ชฉ์ฐจ
- ์ธ์ฆ (Authentication)
- ์ฃผ๋ฅ (Spirits)
- ๋ฆฌํ๋ฅด (Liqueur)
- ๊ธฐํ ์ฌ๋ฃ (Ingredient)
- ๋ฉํ๋ฐ์ดํฐ (Metadata)
- ์ํ ํ์ธ (Health Check)
์ธ์ฆ (Authentication)
POST /signup
์์ฝ: ํ์๊ฐ์
์ธ์ฆ: ๋ถํ์
์์ฒญ ๋ณธ๋ฌธ:
์๋ต:
- 204 No Content
: ํ์๊ฐ์
์ฑ๊ณต, ์๋ ๋ก๊ทธ์ธ ๋ฐ ์ฟ ํค ์ค์
- 409 Conflict
: ์ด๋ฏธ ์กด์ฌํ๋ ์ฌ์ฉ์
POST /signin
์์ฝ: ๋ก๊ทธ์ธ
์ธ์ฆ: ๋ถํ์
์์ฒญ ๋ณธ๋ฌธ:
์๋ต:
- 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 ํค ๋ฐ๊ธ
์ธ์ฆ: ๊ด๋ฆฌ์ ๊ถํ ํ์
์์ฒญ ๋ณธ๋ฌธ:
์๋ต:
{
"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
์๋ต:
๋ฆฌํ๋ฅด (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}
์์ฝ: ๋ฉํ๋ฐ์ดํฐ ๋ฑ๋ก
์ธ์ฆ: ๊ด๋ฆฌ์ ๊ถํ ํ์
์์ฒญ ๋ณธ๋ฌธ:
DELETE /metadata/{id}
์์ฝ: ๋ฉํ๋ฐ์ดํฐ ์ญ์
์ธ์ฆ: ๊ด๋ฆฌ์ ๊ถํ ํ์
๊ฒฝ๋ก ํ๋ผ๋ฏธํฐ:
- id
(int): ๋ฉํ๋ฐ์ดํฐ ID
์ํ ํ์ธ (Health Check)
GET /health
์์ฝ: ์๋น์ค ์ํ ํ์ธ
์ธ์ฆ: ๋ถํ์
์๋ต:
๐จ ๊ณตํต ์ค๋ฅ ์๋ต
๋ชจ๋ ์๋ํฌ์ธํธ๋ ์ค๋ฅ ๋ฐ์ ์ 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