[AWS] API Gateway
์ํคํ
์ณ ํจํด์์์ API Gateway ํจํด์ ๊ตฌํ, ์ ๊ณตํ๋ AWS์ ๊ฐํ ์๋น์ค ์ค ํ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก๋ ๋ก๋๋ฐธ๋ฐ์์ฒ๋ผ ๋ค๋ฅธ ์๋ฒ๋ค์ ์๋จ ํ๋ก์ ์ญํ ์ ํ๋๋ฐ, ๋ก๋๋ฐธ๋ฐ์์ ๋นํ๋ฉด ๊ธฐ๋ฅ์ด ์ข๋ ๋ค์ํ ํธ์ด๋ค.
๊ฒฝ๋ก๋ฅผ ๋ค์ํ๊ฒ ์ค์ ํ ์ ์๊ณ , ์ฌ๋ฌ๊ฐ์ง ์ข
๋ฅ์ ์๋ฒ๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ๋ง๋ค ์ ์๋ค.
์ฌ๊ธฐ์์๋ Rest API ๋ชจ๋๋ฅผ ์ฃผ๋ก ๋ค๋ฃฌ๋ค.
Websocket ๋ชจ๋์ ๋ํด์๋ ์๋ ํฌ์คํธ๋ฅผ ์ฐธ๊ณ ํ๋ค.
https://blog.naver.com/sssang97?Redirect=Log&logNo=222923857318&from=postView
์ํคํ
์ณ ํจํด์ผ๋ก์์ API Gateway๋ ๋ค์ ํฌ์คํธ๋ฅผ ์ฐธ๊ณ ํ๋ค.
https://blog.naver.com/sssang97/223042627693
๋น์ฉ
์ฌ์ฉํ ๋งํผ๋ง ๋น์ฉ์ ๋ถ๊ณผํ๋ ์๋ฒ๋ฆฌ์ค ์๋น์ค๋ค.
HTTP ๋ชจ๋๋ ๋ฑ ์ฌ์ฉํ ๋งํผ๋ง ๋น์ฉ์ ๋ด๊ณ , REST API ๋ชจ๋๋ ๊ฑฐ๊ธฐ์ ๋ํด ์บ์ฑ ์๊ฐ๋น ๋น์ฉ์ด ์กฐ๊ธ ์ถ๊ฐ๋๋ค.
https://aws.amazon.com/ko/api-gateway/pricing/
HTTP API๋ ์ด๋ ๊ฒ ์์ฒญ ๋จ์๋ก๋ง ์๊ธ์ ๋ฌผ๊ณ

Reist API์ ์ด๊ฑด ์บ์๊ฐ ๋ ์๋ ์๊ฐ ๋จ์๋ก๋ ์๊ธ์ ๋ธ๋ค.

ํ ์คํธ ํ๊ฒฝ ๊ตฌ์ฑ
์ผ๋ฐ ์๋ณธ ์๋ฒ๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ จํด๋จ๋ค.

์ด๋ฐ๊ฒ ์๋ค.
๊ฒ์ดํธ์จ์ด ๋ง๋ค๊ธฐ
๊ฐ๋จํ ์์ ๋ฅผ ๊ตฌํํด๋๋ก ํ๊ฒ ๋ค.
๋๋ ์ด๋ฏธ ์กด์ฌํ๋ Lambda Function URL ๊ธฐ๋ฐ์ ์๋ฒ๋ฅผ API Gateway๋ก ํ๋ก์ฑํ๋ ค๊ณ ํ๋ค.
์ด๋ฅผ ํตํด rate limit๊ณผ ๋๋ฉ์ธ ์ฐ๋ ๋ฑ์ ๋ฌ์ฑํ๋ ค๋๊ฒ ๋ชฉ์ ์ด๋ค.
์ฐ์ ๊ฒ์ดํธ์จ์ด๋ฅผ ๋ง๋ ๋ค.
์ผ๋ฐ์ ์ธ ์ฌ์ฉ์ฌ๋ก์์๋ ์ธ๋ฒ์งธ์ Rest API๊ฐ ๊ฐ์ฅ ์ ์ ํ ํธ์ด๋ค.
๊ณ ๋ฅด๊ณ
์ด๋ฆ ์ง๊ณ , ์ ์ ํ ๋ง๋ค์ด์ค๋ค.
๊ทธ๋์ ์ด๋ฐ์์ผ๋ก ๋จ๋ฉด ์ฑ๊ณต์ด๋ค.

๋ฆฌ์์ค ๊ด๋ฆฌ
๊ฒ์ดํธ์จ์ด๋ฅผ ๋ง ์์ฑํ ์ฐธ์ด๋ผ๋ฉด, ์์ ๋ค์ด์๋ ๊ฒ ์๋ฌด๊ฒ๋ ์๊ณ ํธ์ถํ ์ ์๋ ๊ฒ๋ ์์ ๊ฒ์ด๋ค.
๊ฐ์ฅ ๋จผ์ "๋ฆฌ์์ค" ๋จ์๋ฅผ ์ถ๊ฐํด์ค์ผ ๋ค๋ฅธ ์๋ฒ๋ฅผ ํ๋ก์ฑํ๊ฒ ํ ์ ์๋ค.
๋๋ ๊ธฐ์กด์ ์กด์ฌํ๋ ์๋ฒ๋ฅผ "api/v1" ๊ฒฝ๋ก์ ํ์ URI๋ก ๋งคํํ๊ณ ์ถ๋ค.
ํ๋ฒ ํด๋ณด๊ฒ ๋ค.
"๋ฆฌ์์ค ์์ฑ"์ ๋๋ฅด๊ณ

๋จผ์ ์์ URI์ธ api๋ฅผ ์์ฑํ๋ค.

๊ทธ๋ฆฌ๊ณ ๊ทธ ์๋์ ๋ v1์ ์์ฑํ๋ค.

๊ทธ๋ฆฌ๊ณ ์์ผ๋ํจํด์ ์ ์ฉํ๊ธฐ ์ํด "proxy" ๋ฆฌ์์ค๋ฅผ ์ถ๊ฐํ๋ค.
์ด๋ ๊ฒ ํด์ผ "/api/v1"๋ก ์์ํ๋ ๋ชจ๋ ํจํด์ ๋งค์นญ์์ผ์ค ์ ์๋ค.
๊ทธ๋ฌ๋ฉด ANY ๋ฉ์๋๊ฐ ์๋์ผ๋ก ์๊ธธํ ๋ฐ

์ ๊ธฐ๋ค๊ฐ ํ๋ก์ฑํ ์๋ณธ ์๋ฒ์ ๊ฒฝ๋ก๋ฅผ
"https://๋๋ฉ์ธ/{proxy}"์ ํํ๋ก ๋ฃ์ด์ค๋ค.
์ฌ๊ธฐ์๋ HTTP ํ๋ก์ ํ์
์ผ๋ก ํด์ URL์ ๊ทธ๋๋ก ๋ฃ์๋๋ฐ, Lambda๋ฅผ ๋ฐ๋ก ๊ฐ๋ฆฌํค๊ฒ ํ ์๋ ์๋ค. ๊ทธ๋์ Lambda ๊ธฐ๋ฐ์ผ๋ก ์๋ฒ๋ฆฌ์ค ์๋ฒ๋ฅผ ์ด์ฉํ ๋ ์ ๊ฑธ ๋ง์ด ์ฌ์ฉํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๊ฒ ๋์ด ์๋๋ค.
"https://๋๋ฉ์ธ/{proxy}" ํํ์ ํ๋ก์ ๋ฆฌ์์ค๋ "https://๋๋ฉ์ธ/foo"๋ "https://๋๋ฉ์ธ/bar" ๊ฐ์ ๊ฒ์๋ ๋งค์นญ์ด ์ ๋๋๋ฐ, "https://๋๋ฉ์ธ/" ๊ฐ์ ๋ฃจํธ URI์๋ ๋งค์นญ๋์ง ์๋๋ค.
๊ทธ๋์ ์์ ๋ ๋ฒจ์ ๋ฉ์๋๋ฅผ ํ๋ ๋ ์ถ๊ฐํด์ค๋ค.

๊ทธ๋ฆฌ๊ณ ์ด๋ฒ์๋ ์๋ํฌ์ธํธ๋ฅผ ์ ๋ ๊ฒ "https://๋๋ฉ์ธ"์ผ๋ก๋ง ๋ฃ์ด์คฌ๋ค.
๋๋ค.
์ด๋ฐ์์ผ๋ก ํ๋์ ๊ฒ์ดํธ์จ์ด์ ์ฌ๋ฌ๊ฐ์ง ์๋ฒ์ API๋ฅผ ๋ผ์ฐํ ํ ์ ์๋ค.
๊ทธ๋์ MSA์์ ๊ฐ์ฅ ์ค์ํ ์ค์ฌ์ถ ์ค ํ๋์ด๊ธฐ๋ ํ๋ค. ์๋ฒ๋ฅผ ์๊ฒ์๊ฒ ์ฐ์ด์ ๊ฒ์ดํธ์จ์ด์ ๋ฌผ๋ฆด ์๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค.
API ๋ฐฐํฌํ๊ธฐ: Stage ๋ง๋ค๊ธฐ
์ ๊ธฐ์ ๋ฆฌ์์ค๋ฅผ ์์ ํ๋๊ฑด ์ฌ์ค ๊ทธ๋ฅ ๊ฐ๋ฐ์ฉ ํธ์ง์ฐฝ์ผ ๋ฟ์ด๊ณ , "์คํ ์ด์ง" ๋จ์๋ก ๋ฐฐํฌ๋ฅผ ํด์ผ ์ค์ง์ ์ธ ๊ธฐ๋ฅ์ ํ๊ธฐ ์์ํ๋ค.
API ๋ฐฐํฌ๋ฅผ ๋๋ฅด๊ณ
์คํ
์ด์ง ์ด๋ฆ์ ์ ์ ํ ๊ณจ๋ผ ๋ฐฐํฌํ๋ค.
๊ทธ๋ผ ์ด๋ ๊ฒ ๋ฐฐํฌ ์คํ
์ด์ง๊ฐ ๋จ๊ณ

์์ฑ๋ ์๋ํฌ์ธํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์๋ ํ์ธํด๋ณผ ์ ์์ ๊ฒ์ด๋ค.
๋๋ฉ์ธ ๋ฌ๊ธฐ
๋๋ฉ์ธ ์ฐ๋์ ๋ณ๋ ํฌ์คํธ๋ฅผ ์ฐธ๊ณ ํ๋ค.
https://blog.naver.com/sssang97/222883393010
canary ๋ฐฐํฌ
API Gateway๋ ์์ ์ ์ธ ๋ฐฐํฌ๋ฅผ ์ํด canary ๋ฐฐํฌ ์ ๋ต์ ์ ๊ณตํ๋ค.
๋ง์ฝ API gateway ๋จ์์ ๋ณ๊ฒฝ์ด ์๊ฒจ์ ํน์ API๊ฐ ๊ฐ๋ฆฌํค๋ ์๋ฒ๋ฅผ ๋ฐ๊พธ๊ณ state ๋ฐฐํฌ๋ฅผ ํด์ผํ๋๋ฐ, ๋ณ๊ฒฝ์ฌํญ์ผ๋ก ์ธํด์ ๊ณผ๋ถํ๋ก ๋ป์ง ์์๊น, ์๋๋ฉด ์ค๋ฅ๊ฐ ํฐ์ง์ง ์์๊น ๊ฑฑ์ ์ค๋ฌ์ธ ์ ์๋ค.
canary๋ ๊ทธ๋ฐ ์คํ
์ด์ง ๋ณ๊ฒฝ์ฌํญ์ ์์ ์ ์ผ๋ก ์ ์ดํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ๋ค.
๋จผ์ ์คํ ์ด์ง์ canary ํญ์์ canary ์ค์ ์ ํ์ฑํํ๋ค.
์ด๋ ๊ฒ ๋จ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์คํ ์ด์ง ๋ณ๊ฒฝ์ฌํญ์ ๋ฐฐํฌํ๋ฉด ๋๋๋ฐ
๊ทธ๋ผ ์ด์ ์ด๋ฐ์์ผ๋ก canary๋ผ๊ณ ๋ฐ ๊ฒ์ด๋ค .
canary๋ฅผ ์ผฐ๊ธฐ ๋๋ฌธ์, ์คํ
์ด์ง๋ฅผ ์๋ก ๋ฐฐํฌํ๋๋ผ๋ ์ค์ ์คํ
์ด์ง์ ๋ฐ๋ก ์ ์ฉ๋์ง ์๋๋ค.
๋์ canary ๋ฒ์ ์๋ง ๋ฐฐํฌ๋๋ค.
๊ทธ๋ฌ๋ฉด ์ ๊ธฐ์
canary๋ก ๋ชฐ๋ฆฌ๋ ํธ๋ํฝ์ ์กฐ๊ธ์ฉ ๋๋ ค๋ณด๋ฉด์ ๋ชจ๋ํฐ๋ง์ ํด๋ณด๊ณ ,
๋ฌธ์ ์๋ค๋ฉด canary๋ฅผ ๋๋ด๋ฉด ๋๋ค.
๊ทธ๋ฌ๋ฉด ์์ ํ ์ ์ฉ์ด ๋๋ ๊ฒ์ด๋ค.
Rate Limit ๊ฑธ๊ธฐ
API Gateway๋ ์์ฒด์ ์ผ๋ก API์ ๋ํ Rate limit ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
์ด๊ฑด ๋ชจ๋ ์ฌ์ฉ์์ ๋ํด ์ ์ญ์ ์ผ๋ก ๋์ํ๊ณ , ์์ฒญ์์ IP๋ณ๋ก rate limit์ ๊ฑฐ๋๊ฑด ๋ฐ๋ก ์๋ค.
๊ทธ๋ฐ๊ฑธ ์ํ๋ค๋ฉด WAF๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
rate limit์ ๊ฒ์ดํธ์จ์ด ์ ์ฒด์ ๋ํด์ ๊ฑธ ์๋ ์๊ณ

ํน์ API ๋จ์๋ก๋ ๊ฑธ ์ ์๋ค.
์ฌ๊ธฐ์ ๋ฒ์คํธ๋ ๋์์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ ์ ์๋ ํ๋๊ณ , ์์จ(rate)์ ์ด๋น ๋ฐ์ ์ ์๋ ์ต๋์น๋ค.
๋ง์ฝ /health API์ ๋ํด์๋ง rate limit์ ๋นก์ธ๊ฒ ๊ฑธ๊ณ ์ถ๋ค๋ฉด, ์ด๋ฐ์์ผ๋ก ํด๋ณผ ์ ์๋ค.
๋ฆฌ์์ค์ ๋ฉ์๋๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ์ถ๊ฐํ ๋ค์, ๋ค์๊ณผ ๊ฐ์ด ๊ฑธ๋ฉด ๋๋ค.
์ด๋ฌ๋ฉด 1์ด์ 2๊ฐ์ฉ๋ง ๋ฐ์ ์ ์๋ค๋ ๋ป์ด๋ค.
๊ทธ๋์ 1์ด์ 2๋ฒ์ ์ด๊ณผํ๊ฒ ๋ฌด์ํ๊ฒ ์ ์์ ๋ฐ๋ณตํด๋ณด๋ฉด
์ผ์์ ์ผ๋ก ์ฐจ๋จ์ ๋จน์ผ ๊ฒ์ด๋ค.
๋ก๊ทธ ๊ตฌ์ฑํ๊ธฐ






๋ฌธ์ํ
์์ ํ์ง๋ ์์ง๋ง, API Gateway๋ Swagger์ import/export ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
๋๋ ๋ฆฌ์์ค ์ ์๋ฅผ ํ๋ก์๋ก ๋๋ ค๋ฐ์์ ์ธ๋งํ ๊ตฌ์ฑ์ ์๋์ง๋ง, ํ๋ํ๋ ๋ฑ๋กํ ๊ฒฝ์ฐ์๋ ์ ์ฉํ ๊ฒ ๊ฐ๋ค.
๊ถํ ๋ถ์ฌ์(Authorizer)
API Gateway๋ ์์ฒด์ ์ผ๋ก ์ธ์ฆ์ ๋ํ ๋ถ๋ถ๋ ์ ์ด๊ฐ ๊ฐ๋ฅํ๋ค.
์ธ์ฆ ์ฒ๋ฆฌ์๋ Cognito์ Lambda๋ฅผ ์ฌ์ฉํ ์ ์๋๋ฐ, ์ฌ๊ธฐ์๋ ํ์ฅ์ฑ์ด ๋์ Lambda๋ฅผ ํ์ฉํด์ ๊ตฌ์ถ์ ํด๋ณด๊ฒ ๋ค.
์ด๋ฐ์์ผ๋ก, ์ต์ข
๋ชฉ์ ์ง ์๋ฒ์ ๋๊ธฐ๊ธฐ ์ ์ Lambda๋ฅผ ํธ์ถํด์ ๋ง์ ์งํํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํ๋ ๊ฒ์ด๋ค.
๋จผ์ ์ ์ฉ๋๋ก ์ฌ์ฉํ ์ธ์ฆ ํจ์๋ฅผ ๊ตฌํํ๊ฒ ๋ค.
์ด๋ฆ์ ์ ๋นํ ์ง๊ณ , ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑํ๋ค.
export const handler = function(event, context, callback) {
console.log('Received event:', JSON.stringify(event, null, 2));
// A simple request-based authorizer example to demonstrate how to use request
// parameters to allow or deny a request. In this example, a request is
// authorized if the client-supplied headerauth1 header, QueryString1
// query parameter, and stage variable of StageVar1 all match
// specified values of 'headerValue1', 'queryValue1', and 'stageValue1',
// respectively.
// Retrieve request parameters from the Lambda function input:
var headers = event.headers;
var queryStringParameters = event.queryStringParameters;
var pathParameters = event.pathParameters;
var stageVariables = event.stageVariables;
// Parse the input for the parameter values
var tmp = event.methodArn.split(':');
var apiGatewayArnTmp = tmp[5].split('/');
var awsAccountId = tmp[4];
var region = tmp[3];
var restApiId = apiGatewayArnTmp[0];
var stage = apiGatewayArnTmp[1];
var method = apiGatewayArnTmp[2];
var resource = '/'; // root resource
if (apiGatewayArnTmp[3]) {
resource += apiGatewayArnTmp[3];
}
// Perform authorization to return the Allow policy for correct parameters and
// the 'Unauthorized' error, otherwise.
var authResponse = {};
var condition = {};
condition.IpAddress = {};
// ์ฌ๊ธฐ์ ์ธ๊ฐ ์ฌ๋ถ๋ฅผ ์ฒ๋ฆฌํจ!!
// ์ธ๊ฐ ์ฑ๊ณต
if (queryStringParameters.key === "q1w2e3r4") {
callback(null, generateAllow('me', event.methodArn));
}
// ์ธ๊ฐ ์คํจ. 401 ์ํ์ฝ๋ ๋ฐํ
else {
callback("Unauthorized");
}
}
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
// Required output:
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17'; // default version
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke'; // default action
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
// Optional output with custom properties of the String, Number or Boolean type.
authResponse.context = {
"stringKey": "stringval",
"numberKey": 123,
"booleanKey": true
};
return authResponse;
}
var generateAllow = function(principalId, resource) {
return generatePolicy(principalId, 'Allow', resource);
}
var generateDeny = function(principalId, resource) {
return generatePolicy(principalId, 'Deny', resource);
}
์ข ๊ธด๋ฐ, ์ค๊ฐ์ ์ด๋ถ๋ถ๋ง ๋ณด๋ฉด ๋๋ค.
// ์ฌ๊ธฐ์ ์ธ๊ฐ ์ฌ๋ถ๋ฅผ ์ฒ๋ฆฌํจ!!
// ์ธ๊ฐ ์ฑ๊ณต
if (queryStringParameters.key === "q1w2e3r4") {
callback(null, generateAllow('me', event.methodArn));
}
// ์ธ๊ฐ ์คํจ. 401 ์ํ์ฝ๋ ๋ฐํ
else {
callback("Unauthorized");
}
์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ์ key๋ ๊ฐ์ด q1w2e3r4์ผ ๋๋ง ํ์ฉ์ ํด์ฃผ๋๋ก ํ๋ค.
์์ ์ ๊ฐ๊ฒฐํจ์ ์ํด์ ํ๋์ฝ๋ฉ์ ํ์ง๋ง, ์ค์ ๋ก๋ ํค๋๊ฐ์ ์ฃผ๋ก ์ฌ์ฉํ ํ
๊ณ , ์ ๋ฐ ์ธ์ฆ ๊ด๋ จ๊ฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฃ๊ณ ์ฌ์ฉํ ๊ฒ์ด๋ค.
ํจ์๋ฅผ ๋ฐฐํฌํ๊ณ , ๋ค์ API Gateway๋ก ๋์์์ "๊ถํ ๋ถ์ฌ์" ํญ์ผ๋ก ์ด๋, ์์ฑ์ ํด์ค๋ค.
์ญํ ์ ํ์์๊ณ , ํจ์๋ฅผ ๊ณ ๋ฅธ๋ค.
๊ทธ๋ฆฌ๊ณ Lambda๋ฅผ ํธ์ถํ ๋ event๋ก ๋๊ธธ ํญ๋ชฉ์ ์ ํด์ค๋ค.
์ด ๊ฒฝ์ฐ์๋ ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ key๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๋์ํ๋์ง ํ ์คํธํด๋ณธ๋ค.
์ด์ํ๊ฑธ ๋ฃ์ผ๋ฉด ํ๊ธฐ๊ณ
์ ๋ฃ์ผ๋ฉด ํต๊ณผ๋์ด์ผ ํ๋ค.
๋ง์ง๋ง์ผ๋ก ์ ๊ฑธ ๋ฆฌ๋ก์ค์ ์ ์ฉ๋ง ํด์ฃผ๋ฉด ๋๋ค.
๋ฆฌ์์ค ํธ์ง์์ ํน์ ๋ฉ์๋ ์ ์๋ฅผ ์ ํํ๊ณ "๋ฉ์๋ ์คํ"์ผ๋ก ์ด๋
์น์ธ์ ๊ทธ๊ฑธ ์ง์ด๋ฃ์ด์ฃผ๊ณ ๋ฐฐํฌ๋ฅผ ํ๋ฉด ๋์ด๋ค.
๊ทธ๋ฌ๋ฉด

๊ธฐ๋ํ๋๋ก ์ ๋์ํ ๊ฒ์ด๋ค.
์ฐธ์กฐ
https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/set-up-logging.html
https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html