API๋ ๊ณต๊ฐ ์์ค์ ๋ฐ๋ผ ์๋์ 3๊ฐ์ง๋ก ๊ตฌ๋ถํ๋ฉฐ, ๊ฐ๋ฅํ ๋์ ๊ณต๊ฐ ์์ค์ ์ง์ํด์ผ ํ๋ค.
-
Public API
- ์๋ํํฐ ํน์ ์์์ ๊ฐ์ธ์ด ์ฌ์ฉ ๊ฐ๋ฅ
- FQDN:
api.ridibooks.com
- ๋๋ฉ์ธ์ API Gateway์ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉฐ, ๋ณ๊ฒฝ ํ์์ @ridi/performance ํ์ ์์ฒญ
- ๋ณด์ ํ๋กํ ์ฝ(TLS)์ ์ฌ์ฉํด์ผ ํจ
- ์ธ์ฆ์๋ OAuth 2.0์ ์ฌ์ฉ
- ์ธ์ฆ ์๋ฒ ๊ตฌํ์ฒด ๋ฐ Python, PHP ๋ฏธ๋ค์จ์ด ์ฐธ๊ณ
- ์ฒซ ๋ฒ์งธ Path Segment๋ ์๋น์ค๋ช
์ผ๋ก ์์ํ๋ค.
- ์) ๊ฒ์ ์๋น์ค:
api.ridibooks.com/search/
- ์) ์ฑ
์์ธ ์๋น์ค:
api.ridibooks.com/books/
- ์) ๊ฒ์ ์๋น์ค:
-
Protected API
- ์ธ์ปจ๋ํํฐ ํน์ ๋ด๋ถ ์ง์๋ง ์ฌ์ฉ ๊ฐ๋ฅ
- FQDN:
- ๊ณต์ธ IP๊ฐ ์๋ค๋ฉด
{team}-api.ridibooks.com
- ๊ณต์ธ IP๊ฐ ์๋ค๋ฉด
api.{team}.ridi.io
- ๊ณต์ธ IP๊ฐ ์๋ค๋ฉด
- ๊ณต์ธ IP๋ฅผ ๊ฐ์ง ๊ฒฝ์ฐ ๋ณด์ ํ๋กํ ์ฝ(TLS)์ ์ฌ์ฉํด์ผ ํจ
- ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ ํ์์ ์ง์ ์์ฑ
-
Private API
- ํ ๋ด๋ถ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅ
- ๋๋ฉ์ธ ๊ท์น ์์
- ํฌ๋งท, ๋ฌธ์ํ๋ ํ ๋ด ๊ท์ฝ์ ๋ฐ๋ผ ์์ ๋กญ๊ฒ ๊ด๋ฆฌ
- ํ์ํธํ ์ ์งํ ํ์ ์์
- HTTP 1.1 ์ด์์ ์ง์ํด์ผ ํ๋ค.
- REST ํน์ GraphQL ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋์ด์ผ ํ๋ค.
- OpenAPI ํ์์ ์คํ๋ฌธ์๋ฅผ ์ ๊ณตํด์ผ ํ๋ค.
- ํตํฉํ ์คํธ๋ฅผ ๊ตฌ์ถํ๊ณ ์๋ํํด์ผ ํ๋ค.
๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ์ด์ํ๋ค๋ณด๋ฉด ๋ด๋ถ ์๋ฒ๊ฐ, ์ฆ ์ ๋ขฐํ ์ ์๋ ์๋ฒ๊ฐ์ API ํต์ ์ด ํ์ํ ์ํฉ์ด ๋ฐ์ํ๋ค. ์ด ๋ ์ธ๊ฐ๋ JWT(JSON Web Tokens)๋ฅผ ํตํด ์ด๋ฃจ์ด์ ธ์ผ ํ๋ฉฐ, ์ฌ์ฉ๋๋ ํ ํฐ์ ์๋์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํ๋ค.
- iss(๋ฐ๊ธ์)์ aud(์์ ์)์ ์๋น์ค๋ช
์
๋ ฅ
- ์๋น์ค๋ช ์ ์ฌ์ ์ ์ฝ์๋ ๋ฌธ์์ด์ ์ฌ์ฉ
- ์๋น์ค๋ช ์ kebab-case๋ก ํ๊ธฐ
- sub(์ฃผ์ )๋ ํ์์ ๋ฐ๋ผ ์ ํ์ ์ผ๋ก ์ฌ์ฉ
- RS256(RSA Signature with SHA-256) ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ์๋ช
- iss์ ๋ฐ๋ผ ๊ตฌ๋ถ๋๋ ๋น๋์นญํค๋ฅผ ์ฌ์ฉ
- ํ ํฐ์ ์๋ช ์๋ iss์ ๋น๋ฐํค๋ฅผ, ํ ํฐ์ ๊ฒ์ฆ์๋ ์ฌ์ ์ aud์ธก์ผ๋ก ์ ๋ฌ๋ iss์ ๊ณต๊ฐํค๋ฅผ ์ฌ์ฉ
- exp(๋ง๋ฃ์๊ฐ)๋ฅผ ๋ฐ๋์ ํฌํจ
- nbf, iat ๋ฐ leeway๋ ์ ํ์ ์ผ๋ก ์ฌ์ฉ
Authorization
ํค๋์Bearer
ํ ํฐ์ผ๋ก ์ ๋ฌ
์) ๊ตฌ๋งค๋ชฉ๋ก ์๋น์ค์์ ์ฑ ์๋น์ค์ API๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ
{ // header
"typ": "JWT",
"alg": "RS256"
}
{ // payload
"iss": "library",
"aud": "book",
"exp": 1531819973
}
๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ์์ ์ต์ข ์ฌ์ฉ์์ SSO(Single Sign-On)๋ฅผ ์ง์ํ๊ธฐ ์ํด OAuth2๋ฅผ ์ฌ์ฉํ๋ค. ์ด ๋ access token์ JWT(JSON Web Tokens) ํํ๋ก ๋ฐ๊ธ๋๋ฉฐ, ์ฌ์ฉ๋๋ ํ ํฐ์ ์๋์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํ๋ค.
- ES256(ECDSA using P-256 curve and SHA-256) ํน์ RS256(RSA Signature with SHA-256) ์ผ๋ก ์๋ช
- ๋ฆฌ์์ค ์๋ฒ๋ค์ RS256 ๋ฐ ES256 ์๊ณ ๋ฆฌ์ฆ์ ์ง์ํด์ผ ํ๋ค.
- ์๋์ claim๋ค์ด payload์ ์ ๊ณต๋์ด์ผ ํจ
- u_idx: ๋ฆฌ์์ค ์์ ์์ ๊ณ ์ ์๋ณ์(user_idx)
- sub: ๋ฆฌ์์ค ์์ ์์ ๋ฆฌ๋๋ถ์ค ID
- u_idx๊ฐ ์์ผ๋ฉด sub์ ๋ฌด์ํ ์ ์๋ค.
- exp: ์ ๋์ค ์๊ฐ์ผ๋ก ํํ๋ ํ ํฐ ๋ง๋ฃ์๊ฐ
- client_id: ์ฌ์ ์ ์ฝ์๋ OAuth2 ํด๋ผ์ด์ธํธ ID
- scope: ํ ํฐ์ด ํ์ฉํ๋ ์ธ๊ฐ ๋ฒ์ (๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ)
์)
{ // header
"typ": "JWT",
"alg": "ES256"
}
{ // payload
ย "u_idx": 12312233,
"sub": "antiline",
"exp": 1518505258,
"client_id": "**given_client_id**",
"scope": "all"
}
RSA ํน์ ECDSA ๊ธฐ๋ฐ์ผ๋ก JWT๋ฅผ ์๋ช ํ๋ ๊ฒฝ์ฐ JSON Web Key Set (JWKS) ํ์์ผ๋ก ๊ณต๊ฐํค ์งํฉ์ ์ ๋ฌํ๋ค. JWKS๋ ํค ๋กํ ์ด์ ์ ์ํ ํ์คํ๋ ์๋จ์ ์ ๊ณตํ๋ฏ๋ก, ๊ธฐ์กด์ ์ฌ์ฉํ๊ณ ์๋ PEM ํ์์ ํ์ผ ์ ๋ฌ ๋ฐฉ์์ ๋์ฒดํ๋ค.
-
JWK ๊ฐ์ฒด๋ ์๋์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํ๋ค.
- kid(Key ID)๋ ๋ฐ๋์ ์์ฑ
- kty(Key Type)์ ๋ฐ๋์
"RSA"
ํน์"EC"
- kty๊ฐ
"RSA"
์ธ ๊ฒฝ์ฐ alg(Algorithm)๋ ๋ฐ๋์"RS256"
- kty๊ฐ
"EC"
์ธ ๊ฒฝ์ฐ crv(Curve)๋ ๋ฐ๋์"P-256"
- kty๊ฐ
- use(Public Key Use)๋ ๋ฐ๋์
"sig"
์)
{ "keys": [{ "kid":"1234example=", "kty":"EC", "crv":"P-256", "use":"sig", "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM" }, { "kid": "5678example=", "kty": "RSA", "alg": "RS256", "use": "sig", "e": "AQAB", "n": "987654321" }] }
-
์๋ช ํ๋ ์ธก์ ํค ์ ๋ณด๋ฅผ ๋ด์ JSON ํ์ผ์ ์๋ฒ์ ์ ๋ก๋ํ๊ณ ํด๋น URL์ ํตํด ๊ณต์ ํ๋ค.
- ์:
https://www.googleapis.com/oauth2/v3/certs
- ์:
-
์๋ช ํ๋ ์ธก์ ์ธ์ฆํ๋ ์ธก์ด JWK ํ์์ ์ง์ํ๋์ง ์ฌ์ ์ ํ์ธํด์ผ ํ๋ค.
- ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ PEM ํ์์ ์ ์ ํ์ผ์ ์ ๋ฌํ๋ค.
MSA ๊ธฐ๋ฐ์ ์๋น์ค๋ค์ ๊ฐ๋ฐ์ ํ์ํ ํ ์คํธ ํ๊ฒฝ์ด ์ํํ๊ฒ ์ ๊ณตํ ์ ์์ด์ผ ํ๋ฉฐ ์๋ ๊ท์น์ ์ค์ํด์ผ ํ๋ค.
- ์ฟ ํค ๊ณต์ ๋ฅผ ์ํด ์์ ๋๋ฉ์ธ์
.ridi.io
๋ก ์ค์ ํด์ผ ํ๋ค. - ๊ณต์ธ์ธ์ฆ์๋ก ์ํธํ๋ HTTPS ํ๋กํ ์ฝ์ ์ง์ํด์ผ ํ๋ค.
- ์๊ธฐ์น ์์ ์ ๋ณด ์ ์ถ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ธ๋ถ ์ ๊ทผ์ ์ฐจ๋จํด์ผ ํ๋ค.
- ํฌ๋กค๋ง์ ์ฐจ๋จํ๋ robots.txt ํ์ผ๋ ๋ฐ๋์ ์ถ๊ฐํ ๊ฒ
ํ ์คํธ ํ๊ฒฝ์ ๋ฌด์ค๋จ์ผ๋ก ์ด์๋์ง ์์ ์ ์์ผ๋ฏ๋ก ์ฌ์ ์ ์ด์ฉ์ ํ์ํ๋ ๊ฒ์ ์์น์ผ๋ก ํ๋ค.
-
Path Segments๋ฅผ ํํํ ๋์๋ kebab-case ๋ฅผ ์ฌ์ฉํ ๊ฒ
- ์)
/reading-notes/{b_id}
- ์)
-
Query Parameters์๋ snake_case ๋ฅผ ์ฌ์ฉํ ๊ฒ
-
Trailing Slashes ๋ฅผ ์ฌ์ฉํ์ง ๋ง ๊ฒ
- slash ๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด์ผ ํ๊ณ , / ๋ฅผ ๋ถ์ด์ง ์๋ ๊ฒ์ ์์น์ผ๋ก ํ๋ค.
-
API์ ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ์ํด Path Segment์
/v1
,/v2
๋ฐ์๋ฅผ ํฌํจํ์ง ๋ง ๊ฒ- ๋ค์ํ ๋ฒ์ ์ ๊ด๋ฆฌํ๋ ๊ฒ์ ํ ์คํธ ๋ฐ ์ ์ง๋ณด์ ์ธก๋ฉด์์ ๋งค์ฐ ๋ณต์กํ๊ณ ์ด๋ ค์ด ์ผ์ด๋ค.
- ๊ฐ์ฅ ์ข์ ๊ฒ์ API ๋ฒ์ ์ ๊ด๋ฆฌํ์ง ์๋ ๊ฒ์ด๋ฉฐ, ์๋์ ๋ฐฉ๋ฒ๋ค ์ค ํ๋๋ฅผ ์ฐ์ ์ ์ผ๋ก ๊ณ ๋ คํ๋ค.
- ๊ธฐ์กด ๋ฆฌ์์ค์ ํธํ์ ์ ์งํ๋ฉฐ ํ์ฅ
- ๊ธฐ์กด ํ๋ ์ญ์ ๊ธ์ง
- ์ ๊ท ํ๋ ์ถ๊ฐ๋ง ๊ฐ๋ฅ
- ์๋ก์ด ๋ฆฌ์์ค๋ฅผ ์ ์
- ์๋ก์ด ์๋น์ค ์๋ํฌ์ธํธ๋ฅผ ์ ์
- ๊ธฐ์กด ๋ฆฌ์์ค์ ํธํ์ ์ ์งํ๋ฉฐ ํ์ฅ
- ๋ถ๋์ด ๋ฒ์ ์ ๊ด๋ฆฌํด์ผ ํ๋ค๋ฉด HTTP
Content-Type
ํค๋๋ฅผ ํตํ Media Type ๋ฒ์ ๋์ ํ ๊ฒ- ์)
Accept: application/vnd.ridibooks.cart+json;version=2
- ์)
-
Level 2 ์ด์์ REST Maturity Model์ ๊ตฌํํ ๊ฒ
-
์์ฒญ๊ณผ ์๋ต์ ์ฌ์ฉ๋๋ Payload๋ JSON ํ์์ ๋ฐ๋ฅผ ๊ฒ
- MIME ํ์์ผ๋ก๋
application/json
์ ์ฌ์ฉํ ๊ฒ - ํ๋กํผํฐ ์ด๋ฆ์๋ snake_case ๋ฅผ ์ฌ์ฉํ ๊ฒ
- MIME ํ์์ผ๋ก๋
-
๋ ์ง์ ์๊ฐ ํ๊ธฐ๋ ISO 8601 ํ์ค์ ๋ฐ๋ฅผ ๊ฒ
๋ค์ํ response code๋ฅผ ์ฌ์ฉํ๋ฉด ๊ทธ ์์ฒด๋ก ๋ช ์์ ์ด์ง๋ง ์ฝ๋ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง๋ค๋ ๋จ์ ์ด ์๋ค. ๋ฐ๋ผ์ ์๋ ๋ช ์๋ ๋ณดํธ์ ์ธ response code๋ง์ ์ฌ์ฉํ๊ณ ๋ ์์ธํ ๋ด์ฉ์ message-body์์ ์ ๊ณตํ๋ค.
์ํ ์ฝ๋ | ์๋ฏธ | ์ฉ๋ |
---|---|---|
200 OK | ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ | ์๋ฌ ์๋ต์ ๋ํด์ ์ฌ์ฉํ์ง ์๋๋ก ํ๋ค |
201 Created | ๋ฆฌ์์ค ์์ฑ ์๋ฃ | ๊ฐ๋ฅํ๋ฉด ์๋ต ํค๋ Locationํ๋์ ๋ฆฌ์์ค๋ฅผ ์ ๊ทผํ ์ ์๋ URI๋ฅผ ํฌํจ์ํจ๋ค. |
204 No Content | ๋ด์ฉ์์ | ์์ฒญ ์ฒ๋ฆฌ ์ฑ๊ณต ํ ํน๋ณํ message body์ ํฌํจ์ํฌ ๋ด์ฉ์ด ์๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ค. |
301 Moved Permanently | ์์ฒญํ ๋ฆฌ์์ค๊ฐ ์๋ก์ด URI๋ฅผ ๋ถ์ฌ๋ฐ์์ | ์ URI๋ฅผ ์๋ต ํค๋ Locationํ๋์ ๋ช ์ํ๋ค. |
302 Found | URI๊ฐ ์์๋ก ๋ณ๊ฒฝ๋จ | 301๊ณผ ๋น์ทํ์ง๋ง ์ผ์์ ์ผ๋ก ์ฎ๊ฒจ์ง ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๋ค. |
304 Not Modified | ์์ฒญํ ๋ฆฌ์์ค๊ฐ ๋ณ๊ฒฝ๋์ง ์์ | If-Modified-Since ์์ฒญ ํค๋์ ๋ํ ์๋ต์ผ๋ก ํ์ฉ๋ ์ ์๋ค. |
400 Bad Request | ์๋ชป๋ ์์ฒญ | ์ ์๋์ง ์์ ํ์์ด๋ ๋ณด๋ด๋ฉด ์๋๋ ์์ฒญ์ ๋ํ ์๋ต. |
401 Unauthorized | ๋ฆฌ์์ค ์ ๊ทผ ๊ถํ์ด ์์ | ๊ฐ๋ฅํ๋ฉด ์๋ต์ ์ธ์ฆ์ ๊ดํ ์ ๋ณด๋ฅผ ํฌํจ์์ผ ํ์ํ ๊ฒฝ์ฐ ํด๋ผ์ด์ธํธ์์ ์ถ๊ฐ ์์ฒญ์ ๋ณด๋ผ ์ ์๋๋ก ํ๋ค. |
403 Forbidden | ์จ๊ฒจ์ง ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ค ํจ | ํ์ํ๋ค๋ฉด ๊ฑฐ๋ถ๋ ์ด์ ๋ฅผ ์๋ต์ ํฌํจ์ํค๊ณ ,์์ ๊ณต๊ฐํ ์์ฌ๊ฐ ์๋ค๋ฉด 404: Not Found๋ฅผ ์ฌ์ฉํ๋ค. |
404 Not Found | ๋งค์นญ๋๋ URI๊ฐ ์์ | ์ค์ ๋ฆฌ์์ค๊ฐ ์กด์ฌํ๋๋ผ๋ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๋ ธ์ถ์ํค์ง ์๊ณ ์ถ์ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์๋ค. |
429 Too Many Requests | ๋๋ฌด ๋ง์ ์์ฒญ | ๊ฐ๋ฅํ๋ฉด ์ผ๋ง๋ ๊ธฐ๋ค๋ฆฐ ํ์ ์๋ก์ด ์์ฒญ์ ๋ฐ์ ์ ์๋์ง ์๋ต ํค๋์ Retry-Afterํ๋์ ๋ช ์ํ๋ค. |
500 Internal Server Error | ์๋ฒ ์๋ฌ | ์์ฒญ์ ์ ์์ ์ผ๋ก ๋ฐ์์ง๋ง ์๋ฒ ์ฒ๋ฆฌ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ์ ๋ฐ์ ์ฌ์ฉํ๋ค. |
502 Bad Gateway | ๊ฒ์ดํธ์จ์ด ์๋ฌ | ์๋ฒ๊ฐ ๊ฒ์ดํธ์จ์ด๋ ํ๋ก์๋ก ์ฌ์ฉ ์ค์ธ ๊ฒฝ์ฐ upstream์๋ฒ์ ์ด์์ด ์์ ๋ ์ฌ์ฉํ๋ค. |
503 Service Unavailable | ์๋น์ค ์ด์ฉ ๋ถ๊ฐ | ๋๊ธฐ ์๊ฐ์ ์ ์ ์๋ค๋ฉด ์๋ต ํค๋์ Retry-Afterํ๋์ ๋ช ์ํ๋ค. |