๐งโ๐ป ์ฌ์ฉ์ ๊ฐ์ด๋
์ด ๊ฐ์ด๋๋ ์นตํ
์ผ ์ฃผ๋ฅ ์ ๋ณด API๋ฅผ ์ฌ์ฉํ๋ ๋ฐ ํ์ํ ๊ธฐ๋ณธ์ ์ธ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ชจ๋ API์ ๊ธฐ๋ณธ URL์ /api/v1
์
๋๋ค.
๐ ์ธ์ฆ (Authentication)
๋ฐ์ดํฐ ๋ฑ๋ก, ์์ , ์ญ์ ๋ฑ ๋ณดํธ๋ ์๋ํฌ์ธํธ์ ์ ๊ทผํ๋ ค๋ฉด ์ธ์ฆ์ด ํ์ํฉ๋๋ค. JWT ๊ธฐ๋ฐ ์ฟ ํค ์ธ์ฆ์ ์ฌ์ฉํฉ๋๋ค.
ํ์๊ฐ์
์ ๊ณ์ ์ ์์ฑํฉ๋๋ค.
- Endpoint:
POST /api/v1/signup
- Content-Type:
application/json
์๋ต: ํ์๊ฐ์ ์ฑ๊ณต ์ ์๋์ผ๋ก ๋ก๊ทธ์ธ๋๋ฉฐ, ์ธ์ฆ ์ฟ ํค๊ฐ ์ค์ ๋ฉ๋๋ค.
๋ก๊ทธ์ธ
๊ธฐ์กด ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํฉ๋๋ค.
- Endpoint:
POST /api/v1/signin
- Content-Type:
application/json
์๋ต: ๋ก๊ทธ์ธ ์ฑ๊ณต ์ accessToken
๊ณผ refreshToken
์ฟ ํค๊ฐ ์ค์ ๋ฉ๋๋ค.
ํ ํฐ ๊ฐฑ์
์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ์๋์ผ๋ก ๊ฐฑ์ ํ ์ ์์ต๋๋ค.
- Endpoint:
POST /api/v1/refresh-token
์ฟ ํค์ refreshToken
์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ์๋ก์ด accessToken
์ ๋ฐ๊ธํฉ๋๋ค.
๋ด ๊ถํ ํ์ธ
ํ์ฌ ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ ๊ถํ์ ํ์ธํฉ๋๋ค.
- Endpoint:
GET /api/v1/my-role
- ์ธ์ฆ: ํ์
๐ฅ ์ฃผ๋ฅ (Spirits)
์ฃผ๋ฅ ๋ฑ๋ก
์๋ก์ด ์ฃผ๋ฅ ์ ๋ณด๋ฅผ ๋ฑ๋กํฉ๋๋ค.
- Endpoint:
POST /api/v1/spirits
- Content-Type:
multipart/form-data
- ์ธ์ฆ: ํ์
ํ์ ํ๋:
- name
(string): ์ฃผ๋ฅ ์ด๋ฆ
- aroma
(array): ํฅ ํน์ฑ (๋ฉํ๋ฐ์ดํฐ ๊ฐ ์ฌ์ฉ)
- taste
(array): ๋ง ํน์ฑ (๋ฉํ๋ฐ์ดํฐ ๊ฐ ์ฌ์ฉ)
- finish
(array): ์ฌ์ด ํน์ฑ (๋ฉํ๋ฐ์ดํฐ ๊ฐ ์ฌ์ฉ)
- kind
(string): ์ฃผ๋ฅ ์ข
๋ฅ (์: Gin, Whiskey, Rum)
- subKind
(string): ์ธ๋ถ ์ข
๋ฅ
- amount
(float): ์ฉ๋ (mL)
- alcohol
(float): ์์ฝ์ฌ ๋์ (%)
- originNation
(string): ์์ฐ์ง ๊ตญ๊ฐ
- originLocation
(string): ์์ฐ์ง ์ง์ญ
- description
(string): ์ค๋ช
- mainImage
(file): ๋ํ ์ด๋ฏธ์ง (์ต๋ 2MB)
์ ํ ํ๋:
- subImage1-4
(file): ๋ณด์กฐ ์ด๋ฏธ์ง๋ค
curl -X POST "http://localhost:8000/api/v1/spirits" \
-F "name=Tanqueray London Dry Gin" \
-F "aroma=juniper" \
-F "aroma=citrus" \
-F "taste=dry" \
-F "taste=botanical" \
-F "finish=clean" \
-F "kind=Gin" \
-F "subKind=London Dry" \
-F "amount=750" \
-F "alcohol=47.3" \
-F "originNation=England" \
-F "originLocation=London" \
-F "description=Classic London Dry Gin" \
-F "mainImage=@tanqueray.jpg"
์ฃผ๋ฅ ๊ฒ์
๋ค์ํ ์กฐ๊ฑด์ผ๋ก ์ฃผ๋ฅ๋ฅผ ๊ฒ์ํฉ๋๋ค.
- Endpoint:
GET /api/v1/spirits
์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ:
- name
(string): ์ด๋ฆ์ผ๋ก ๋ถ๋ถ ๊ฒ์
- aroma
(array): ํฅ ํน์ฑ์ผ๋ก ์ ํํ ์ผ์น
- taste
(array): ๋ง ํน์ฑ์ผ๋ก ์ ํํ ์ผ์น
- finish
(array): ์ฌ์ด ํน์ฑ์ผ๋ก ์ ํํ ์ผ์น
- kind
(string): ์ฃผ๋ฅ ์ข
๋ฅ๋ก ์ ํํ ์ผ์น
- subKind
(string): ์ธ๋ถ ์ข
๋ฅ๋ก ์ ํํ ์ผ์น
- minAlcohol
(float): ์ต์ ์์ฝ์ฌ ๋์
- maxAlcohol
(float): ์ต๋ ์์ฝ์ฌ ๋์
- originNation
(string): ์์ฐ์ง ๊ตญ๊ฐ๋ก ์ ํํ ์ผ์น
- originLocation
(string): ์์ฐ์ง ์ง์ญ์ผ๋ก ๋ถ๋ถ ์ผ์น
- pageNumber
(int): ํ์ด์ง ๋ฒํธ (๊ธฐ๋ณธ๊ฐ: 1)
- pageSize
(int): ํ์ด์ง ํฌ๊ธฐ (๊ธฐ๋ณธ๊ฐ: 10, ์ต๋: 100)
# ์ง ์ข
๋ฅ ๊ฒ์
curl "http://localhost:8000/api/v1/spirits?kind=Gin&pageNumber=1&pageSize=5"
# ๋๋ผ์ดํ๊ณ ๊น๋ํ ์ฃผ๋ฅ ๊ฒ์
curl "http://localhost:8000/api/v1/spirits?taste=dry&finish=clean"
# ์์ฝ์ฌ ๋์ 40-50% ๋ฒ์ ๊ฒ์
curl "http://localhost:8000/api/v1/spirits?minAlcohol=40&maxAlcohol=50"
๋จ์ผ ์ฃผ๋ฅ ์กฐํ
ํน์ ์ฃผ๋ฅ์ ์์ธ ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค.
- Endpoint:
GET /api/v1/spirits/{name}
์ฃผ๋ฅ ์์
๊ธฐ์กด ์ฃผ๋ฅ ์ ๋ณด๋ฅผ ์์ ํฉ๋๋ค.
- Endpoint:
PUT /api/v1/spirits/{document_id}
- Content-Type:
multipart/form-data
- ์ธ์ฆ: ํ์
์ฃผ๋ฅ ์ญ์
์ฃผ๋ฅ ์ ๋ณด๋ฅผ ์ญ์ ํฉ๋๋ค.
- Endpoint:
DELETE /api/v1/spirits/{document_id}
- ์ธ์ฆ: ํ์
๐น ๋ฆฌํ๋ฅด (Liqueur)
๋ฆฌํ๋ฅด ๋ฑ๋ก
์๋ก์ด ๋ฆฌํ๋ฅด ์ ๋ณด๋ฅผ ๋ฑ๋กํฉ๋๋ค.
- Endpoint:
POST /api/v1/liqueur
- Content-Type:
multipart/form-data
- ์ธ์ฆ: ํ์
ํ์ ํ๋:
- name
(string): ๋ฆฌํ๋ฅด ์ด๋ฆ
- brand
(string): ๋ธ๋๋
- taste
(array): ๋ง ํน์ฑ
- kind
(string): ๋ฆฌํ๋ฅด ์ข
๋ฅ
- subKind
(string): ์ธ๋ถ ์ข
๋ฅ
- mainIngredients
(array): ์ฃผ์ฌ๋ฃ ๋ชฉ๋ก
- volume
(float): ์ฉ๋ (mL)
- abv
(float): ์์ฝ์ฌ ๋์ (%)
- originNation
(string): ์์ฐ์ง ๊ตญ๊ฐ
- description
(string): ์ค๋ช
- mainImage
(file): ๋ํ ์ด๋ฏธ์ง
curl -X POST "http://localhost:8000/api/v1/liqueur" \
-F "name=Cointreau" \
-F "brand=Cointreau" \
-F "taste=sweet" \
-F "taste=citrus" \
-F "kind=Triple Sec" \
-F "subKind=Premium Triple Sec" \
-F "mainIngredients=orange peel" \
-F "volume=700" \
-F "abv=40" \
-F "originNation=France" \
-F "description=Premium orange liqueur" \
-F "mainImage=@cointreau.jpg"
๋ฆฌํ๋ฅด ๊ฒ์
- Endpoint:
GET /api/v1/liqueur
- ์ธ์ฆ: ํ์
์ฃผ์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ:
- name
, brand
, taste
, kind
, subKind
- mainIngredients
, minVolume
, maxVolume
- minAbv
, maxAbv
, originNation
- pageNumber
, pageSize
๋จ์ผ ๋ฆฌํ๋ฅด ์กฐํ
- Endpoint:
GET /api/v1/liqueur/{name}
๋ฆฌํ๋ฅด ์์ /์ญ์
- ์์ :
PUT /api/v1/liqueur/{document_id}
(์ธ์ฆ ํ์) - ์ญ์ :
DELETE /api/v1/liqueur/{document_id}
(์ธ์ฆ ํ์)
๐ฟ ๊ธฐํ ์ฌ๋ฃ (Ingredient)
๊ธฐํ ์ฌ๋ฃ ๋ฑ๋ก
์ฃผ์ค, ์๋ฝ ๋ฑ ๊ธฐํ ์ฌ๋ฃ๋ฅผ ๋ฑ๋กํฉ๋๋ค.
- Endpoint:
POST /api/v1/ingredient
- Content-Type:
multipart/form-data
- ์ธ์ฆ: ํ์
ํ์ ํ๋:
- name
(string): ์ฌ๋ฃ ์ด๋ฆ
- kind
(string): ์ฌ๋ฃ ์ข
๋ฅ (์: Juice, Syrup, Bitters)
- description
(string): ์ค๋ช
- mainImage
(file): ๋ํ ์ด๋ฏธ์ง
์ ํ ํ๋:
- brand
(array): ๋ธ๋๋ ๋ชฉ๋ก
curl -X POST "http://localhost:8000/api/v1/ingredient" \
-F "name=Simple Syrup" \
-F "kind=Syrup" \
-F "description=Basic sugar syrup for cocktails" \
-F "mainImage=@simple_syrup.jpg"
๊ธฐํ ์ฌ๋ฃ ๊ฒ์
- Endpoint:
GET /api/v1/ingredient
- ์ธ์ฆ: ํ์
์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ:
- name
, brand
, kind
, description
- pageNumber
, pageSize
๋จ์ผ ๊ธฐํ ์ฌ๋ฃ ์กฐํ
- Endpoint:
GET /api/v1/ingredient/{name}
๊ธฐํ ์ฌ๋ฃ ์์ /์ญ์
- ์์ :
PUT /api/v1/ingredient/{document_id}
(์ธ์ฆ ํ์) - ์ญ์ :
DELETE /api/v1/ingredient/{document_id}
(์ธ์ฆ ํ์)
โ๏ธ ๋ฉํ๋ฐ์ดํฐ (Metadata)
๋ง, ํฅ, ์ข ๋ฅ ๋ฑ ๋ถ๋ฅ์ ์ฌ์ฉ๋๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
๋ฉํ๋ฐ์ดํฐ ์กฐํ
ํน์ ์ข ๋ฅ์ ์นดํ ๊ณ ๋ฆฌ์ ๋ฉํ๋ฐ์ดํฐ ๋ชฉ๋ก์ ์กฐํํฉ๋๋ค.
- Endpoint:
GET /api/v1/metadata/{kind}/{category}
{kind}
:spirits
,liqueur
,ingredient
{category}
:taste
,aroma
,finish
,kind
# ์ฃผ๋ฅ์ ๋ง ๋ฉํ๋ฐ์ดํฐ ์กฐํ
curl "http://localhost:8000/api/v1/metadata/spirits/taste"
# ๋ฆฌํ๋ฅด์ ํฅ ๋ฉํ๋ฐ์ดํฐ ์กฐํ
curl "http://localhost:8000/api/v1/metadata/liqueur/aroma"
๋ฉํ๋ฐ์ดํฐ ๋ฑ๋ก
์๋ก์ด ๋ฉํ๋ฐ์ดํฐ ํญ๋ชฉ์ ๋ฑ๋กํฉ๋๋ค.
- Endpoint:
POST /api/v1/metadata/{kind}/{category}
- Content-Type:
application/json
- ์ธ์ฆ: ๊ด๋ฆฌ์ ๊ถํ ํ์
๋ฉํ๋ฐ์ดํฐ ์ญ์
๋ฉํ๋ฐ์ดํฐ ํญ๋ชฉ์ ์ญ์ ํฉ๋๋ค.
- Endpoint:
DELETE /api/v1/metadata/{id}
- ์ธ์ฆ: ๊ด๋ฆฌ์ ๊ถํ ํ์
๐ ์๋ต ํ์
๋ชจ๋ API ์๋ต์ ๋ค์๊ณผ ๊ฐ์ ํ์คํ๋ ํ์์ ๋ฐ๋ฆ ๋๋ค:
{
"status": "success",
"code": 200,
"data": {
// ์ค์ ๋ฐ์ดํฐ
},
"message": "Successfully retrieved data"
}
ํ์ด์ง๋ค์ด์ ๋ ์๋ต:
{
"status": "success",
"code": 200,
"data": {
"items": [...],
"total_count": 150,
"page_number": 1,
"page_size": 10,
"total_pages": 15
},
"message": "Successfully search spirits"
}
๐จ ์ค๋ฅ ์ฒ๋ฆฌ
์ค๋ฅ ๋ฐ์ ์ RFC 9457 Problem Details ํ์์ผ๋ก ์๋ตํฉ๋๋ค:
{
"type": "https://httpstatuses.com/400",
"title": "Client Error 400",
"detail": "Invalid input data",
"status": 400
}
๋ ์์ธํ ์๋ํฌ์ธํธ ์ ๋ณด์ ์ ์ฒด ์์ฒญ/์๋ต ์คํค๋ง๋ API ๋ ํผ๋ฐ์ค๋ฅผ ์ฐธ๊ณ ํ์ธ์.