kakao-payment-test icon indicating copy to clipboard operation
kakao-payment-test copied to clipboard

๐Ÿ’ป Create Kakao payment (KakaoPay) FaaS via AWS Serverless

Serverless AWS๋ฅผ ์ด์šฉํ•ด FaaS๋กœ ๊ตฌํ˜„ํ•œ ์นด์นด์˜ค ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ ์„œ๋น„์Šค

what-is-function-as-a-service-serverless-architectures

'์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ' ๋ผ๋Š” ๋ง์ด ์žˆ๋‹ค. ์ด๋Š” ์„œ๋ฒ„๊ฐ€ ์—†๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ์•„๋‹ˆ๋ผ... ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ์„œ๋น„์Šคํ•ด์ฃผ๋Š” ๊ธฐ์—…์—์„œ ์„œ๋ฒ„์— ๋Œ€ํ•ด ์•Œ์•„์„œ ํ”„๋กœ๋น„์ €๋‹ ๋˜๋Š” ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํ•ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ฒ„์— ๋Œ€ํ•ด ๋” ์ด์ƒ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค ๋Š” ์˜๋ฏธ๋กœ ๋ฐ›์•„๋“ค์ด๋ฉด ๋œ๋‹ค. ๋‹ค์‹œ๋งํ•˜์ž๋ฉด, ์„œ๋ฒ„ ์ž์ฒด๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ๊ฑฐ๋‚˜(IaaS, Infrastructure as a Service), ์ ์–ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•ด์•ผ ํ–ˆ๋˜(PaaS, Platform as a Service) ๊ธฐ์กด์˜ ๋ฐฉ์‹์—์„œ ํƒˆํ”ผํ•ด ์ •๋ง ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ๋งŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํด๋ผ์šฐ๋“œ ์ปดํ“จํŒ… ์„œ๋น„์Šค์˜ ์ข…๋ฅ˜๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ์ด์šฉํ•ด ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ์ ์€ ๋น„์šฉ์œผ๋กœ๋„ ์•„์ฃผ ๋น ๋ฅด๊ฒŒ ์ตœ์‹  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋‹ค.

์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋Š” ์„œ๋ฒ„์— ๋Œ€ํ•œ ๊ฒƒ๋“ค(DB, ๊ณ„์ • ๋“ฑ...)์„ API๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” BaaS(Backend as a Service)์™€ ํŠน์ • ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” FaaS(Function as a Service)๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์šฐ๋ฆฌ๋Š” ์ด FaaS ์— ๋Œ€ํ•ด ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

FaaS์˜ ๋™์ž‘ ๋ฐฉ์‹์€ ์ •๋ง ๊ฐ„๋‹จํ•˜๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ํด๋ผ์šฐ๋“œ์— ์–ด๋– ํ•œ ํ•จ์ˆ˜๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ , ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ์—๋งŒ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ ํšŸ์ˆ˜๋งŒํผ ๋น„์šฉ์„ ๋‚ด๋Š” ๋ฐฉ์‹์ด๋‹ค.

์ด FaaS๋Š” ๋Œ€๋‹ค์ˆ˜์˜ ๊ณต๋ฃก ๊ธฐ์—…๋“ค(MS, AWS, Google...)์—์„œ ์ œ๊ณตํ•˜๋ฉฐ, ์šฐ๋ฆฌ๋Š” ์ด๋“ค ์ค‘ AWS์˜ Lambda๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„ํ•ด๋ณด๊ณ , ์นด์นด์˜คํ†ก ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ ๋ชจ๋“ˆ์„ ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋กœ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

AWS Serverless

AWS serverless for microservices

AWS๋Š” Lambda์™€ API Gateway๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ  ์žˆ๋‹ค.

์ฐธ๊ณ ๋กœ AWS๋Š” ํ”„๋ฆฌ ํ‹ฐ์–ด ๋ผ๋Š” ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์—ฌ AWS์˜ ํ”Œ๋žซํผ๊ณผ ์ œํ’ˆ ๋ฐ ์„œ๋น„์Šค๋ฅผ ๋ฌด๋ฃŒ๋กœ ์ฒดํ—˜ํ•ด ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ์—ฌ๊ธฐ์˜ ๋ชจ๋“  ๊ณผ์ •๋“ค์€ (์ž˜๋งŒ ๋”ฐ๋ผ์˜จ๋‹ค๋ฉด) ํ”„๋ฆฌ ํ‹ฐ์–ด์˜ ๋ฒ”์ฃผ ์•ˆ์— ์†ํ•˜๋Š” ๊ฒƒ๋“ค์ด๋‹ˆ ๊ณผ๊ธˆ์˜ ๊ฑฑ์ •์€ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

๊ทธ๋ž˜๋„ ๊ณผ๊ธˆ์ด ๊ฑฑ์ •๋œ๋‹ค๋ฉด ๊ฒฐ์ œ ๋ฐ ๋น„์šฉ ๊ด€๋ฆฌ์—์„œ ๋ชจ๋“  ์ฒญ๊ตฌ ๋น„์šฉ์„ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜๋„๋ก ํ•œ๋‹ค.

AWS Lambda

AWS Lambda

์–ด๋– ํ•œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” AWS serverless computing ์„œ๋น„์Šค์ด๋‹ค.

๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜์ž๋ฉด Lambda์— ์–ด๋– ํ•œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑ(๋˜๋Š” ์—…๋กœ๋“œ)ํ•œ ๋’ค, API Gateway์™€ ๊ฐ™์€ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•ด ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐํ•œ๋‹ค. ์ดํ›„ ์ด๋ฒคํŠธ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š” ์ฆ‰์‹œ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ.

์ฃผ์˜ํ•  ๊ฒƒ์€ Lambda๊ฐ€ ์–ด๋– ํ•œ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” AWS DynamoDB์™€ ๊ฐ™์€ ํƒ€ ์„œ๋น„์Šค๋ฅผ Lambda์— ์—ฐ๊ฒฐํ•ด ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ Lambda๋Š” AWS์˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋“ค๊ณผ๋„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ด๋Š” ๊ธ€์˜ ์ฃผ์ œ์™€ ๋งž์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋” ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด AWS Lambda ๊ฐœ๋ฐœ์ž ์•ˆ๋‚ด์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋„๋ก ํ•œ๋‹ค.

AWS API Gateway

AWS API Gateway architecture

๊ฐœ๋ฐœ์ž๊ฐ€ API๋ฅผ ์ƒ์„ฑ, ๊ฒŒ์‹œ, ์œ ์ง€๊ด€๋ฆฌ, ๋ชจ๋‹ˆํ„ฐ๋ง ๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ๋Š” AWS ์„œ๋น„์Šค์ด๋‹ค. API Gateway๋ฅผ ํ†ตํ•ด Lambda ๋ฟ ์•„๋‹ˆ๋ผ ์œ„์˜ ๊ทธ๋ฆผ์—์„œ์™€ ๊ฐ™์ด DDB(DynamoDB), EC2, S3 ๋“ฑ ์—ฌํƒ€ AWS ์„œ๋น„์Šค๋“ค์— ๋Œ€ํ•ด ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” endpoints๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋กœ์จ ๋‹ค์Œ๊ณผ ๊ฐ™์€ HTTP ์š”์ฒญ๋งŒ์œผ๋กœ Lambda์˜ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

$ curl -X GET https://my-lambda-api-gateway-endpoint.com/

# ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ Lambda์—์„œ ์‹คํ–‰๋œ ํ•จ์ˆ˜์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ์„œ๋น„์Šค๋“ค์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ข€ ๊นŒ๋‹ค๋กœ์šด? ์ง€์‹๋“ค์ด ์„ ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ Serverless Framework ๊ฐ€ ๋‚˜์˜จ๋‹ค.

Serverless Famework

Serverless FW ์ด๋ฏธ์ง€

Serverless๋Š” ์œ„์™€ ๊ฐ™์€ ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ๋ฐฐํฌํ•˜๊ณ  ์šด์˜ํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

Serverless๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์•„์ฃผ ์‰ฝ๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ AWS ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ค์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ํ•˜๋‚˜ํ•˜๋‚˜ ์ง„ํ–‰ํ•ด๋ณด๋„๋ก ํ•˜์ž.

Quick start

Serverless๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด, Hello! ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

0. npm ์„ค์น˜

Serverless๋Š” Node.js ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์—์„œ ์ œ๊ณต๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๊ธฐ ๋•Œ๋ฌธ์—, NPM(Node Package Manager)์„ ํ†ตํ•ด ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ npm์ด ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด ์ด ๋งํฌ๋ฅผ ํ†ตํ•ด ์„ค์น˜๋ฅผ ๋จผ์ € ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜์ž.

์„ค์น˜๋ฅผ ๋งˆ์ณค์œผ๋ฉด ๋‹ค์Œ์˜ ๋ช…๋ น์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋  ๊ฒƒ์ด๋‹ค.

$ node -v
# v12.14.1

$ npm -v
# 6.9.0

๋ฌผ๋ก  ๋ฒ„์ „์€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ € ๋ช…๋ น์ด ์‹คํ–‰๋˜๋ƒ๋Š” ๊ฒƒ.

npm ์„ค์น˜๋ฅผ ๋งˆ์ณค๋‹ค๋ฉด, ๋‹ค์Œ ์ˆœ์„œ๋Œ€๋กœ serverless๋ฅผ ์„ค์น˜ํ•ด๋ณด๋„๋ก ํ•˜์ž.

1. Serverless ์„ค์น˜

๋‹ค์Œ์˜ ๋ช…๋ น์œผ๋กœ serverless๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

$ npm install -g serverless

์ด ๋ช…๋ น์€ globalํ•˜๊ฒŒ serverless๋ฅผ ์„ค์น˜ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์ด ๋ช…๋ น์„ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ปค๋งจ๋“œ ์ฐฝ์—์„œ ํ•ด๋‹น ๋ชจ๋“ˆ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. (SO - what does the "-g" flag do in the command "npm install -g <something>"?)

$ serverless create --template aws-nodejs --path my-service

# ์ข€ ์•„๋ž˜์—์„œ ๋ณด๊ฒ ์ง€๋งŒ, serverless ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ช…๋ น์ด๋‹ค.
# ์•„์ง ์ž…๋ ฅ์€ ํ•˜์ง€ ๋ง๋„๋ก ํ•˜์ž.

์•„๋ฌดํŠผ ์„ค์น˜๋ฅผ ๋งˆ์ณค๋‹ค๋ฉด ์ด์ œ serverless์—์„œ AWS์˜ ๊ถŒํ•œ์„ ์–ป์–ด์•ผ ํ•  ์ฐจ๋ก€์ด๋‹ค.

2. AWS IAM ๋งŒ๋“ค๊ธฐ

๋จผ์ € AWS ์ฝ˜์†”์— ๋กœ๊ทธ์ธํ•œ๋‹ค. AWS ๊ณ„์ •์ด ์—†๋Š” ๊ฒฝ์šฐ ์ƒ์„ฑํ•˜๋„๋ก ํ•˜์ž.

IAM: search IAM

์ฝ˜์†” ์ฐฝ์˜ ์™ผ์ชฝ ์ƒ๋‹จ '์„œ๋น„์Šค(Services)' ํƒญ์„ ํด๋ฆญ ํ›„, ๋‹ค์Œ๊ณผ ๊ฐ™์ด 'IAM' ์„ ๊ฒ€์ƒ‰ํ•ด IAM ํŽ˜์ด์ง€๋กœ ๋“ค์–ด๊ฐ€๋„๋ก ํ•˜์ž.

IAM: Add user

IAM ํŽ˜์ด์ง€์— ๋“ค์–ด๊ฐ”์œผ๋ฉด, ์™ผ์ชฝ ์‚ฌ์šฉ์ž ํƒญ์„ ํด๋ฆญํ•œ ๋’ค '์‚ฌ์šฉ์ž ์ถ”๊ฐ€(Add User)' ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

IAM: Add user 1

์ ์ ˆํ•œ ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ์ž…๋ ฅํ•œ ๋’ค, 'ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹ ์•ก์„ธ์Šค(Programmatic access)'์— ์ฒดํฌํ•œ ๋’ค ๋‹ค์Œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

IAM: Add user 2

'๊ธฐ์กด ์ •์ฑ… ์ง์ ‘ ์—ฐ๊ฒฐ(Attach existing policies directly)' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ ๋’ค, ๋‚˜์˜ค๋Š” ๋ชฉ๋ก์—์„œ 'AdministratorAccess'๋ฅผ ์ฒดํฌํ•œ ๋’ค ๋‹ค์Œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

IAM: Add user 3

ํƒœ๊ทธ๋Š” ๋”ฐ๋กœ ์ถ”๊ฐ€ํ•  ๊ฒƒ ์—†๋‹ค. ๋‹ค์Œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๊ณ„์† ์ง„ํ–‰ํ•ด์ฃผ์ž.

IAM: Add user 4

๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฒ€ํ†  ์ฐฝ์—์„œ ์„ค์ •ํ•œ๋Œ€๋กœ ๊ณ„์ •์ด ๋งŒ๋“ค์–ด์กŒ๋Š”์ง€ ๊ฒ€ํ† ๋ฅผ ํ•œ ๋’ค, '์‚ฌ์šฉ์ž ๋งŒ๋“ค๊ธฐ' ๋ฒ„ํŠผ์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

IAM: Add user 5

์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๋ฉด, ์œ„์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋ฐ˜๊ฒจ์ค„ ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์„œ '์•ก์„ธ์Šค ํ‚ค ID(Access key ID)'์™€ '๋น„๋ฐ€ ์•ก์„ธ์Šค ํ‚ค(Secret access key)'๋ฅผ Serverless ์„ค์ •์—์„œ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. ๊ฐ๊ฐ ๋ณต์‚ฌํ•ด์„œ ๋ฉ”๋ชจ์žฅ์— ์ž ์‹œ ๋ถ™์—ฌ๋„ฃ์–ด์ฃผ๋„๋ก ํ•˜์ž.

์ด ํ‚ค๊ฐ’๋“ค์€ ์œ ์˜ํ•ด์„œ ๋‹ค๋ค„์•ผ ํ•˜๋Š”๋ฐ, ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ AWS ์ž์›์„ ๋ง˜๋Œ€๋กœ ์†Œ๋ชจ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ. ํ˜น์—ฌ ์•…์˜์ ์ธ ๋ชฉ์ ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋กœ ์ธํ•ด ๊ธˆ์ „์ ์ธ ํ”ผํ•ด๋ฅผ ๋ณด๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด ์ด ๊ฐ’๋“ค์„ ์ž˜ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜์ž.

์ฐธ๊ณ ๋กœ Sercret access key๋Š” ๋ฐœ๊ธ‰ ์‹œ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ํžˆ์ž. ๋ฌผ๋ก  ์žƒ์–ด๋ฒ„๋ฆฐ ๊ฒฝ์šฐ Access key ID๋ฅผ ์ƒˆ๋กœ ๋ฐœ๊ธ‰๋ฐ›์•„ ์ƒˆ๋กœ์šด Secret access key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค(๋ฌผ๋ก  ์ด ๊ฒฝ์šฐ๋„ ๋ฐœ๊ธ‰๋ฐ›์„ ๋•Œ ํ•œ ๋ฒˆ๋งŒ ๋ณผ ์ˆ˜ ์žˆ๋‹ค).

3. Serverless credentials ์„ค์ •

Serverless์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•œ AWS ๊ถŒํ•œ์„ ์„ค์ •ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

$ serverless config credentials --provider aws --key xxxxxxxxxx --secret xxxxxxxxxx

์ฒซ ๋ฒˆ์งธ key๋Š” Access key ID๊ฐ€ ๋“ค์–ด๊ฐ€๊ณ , ๋‘ ๋ฒˆ์งธ key๋Š” Secret access key๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ์„ฑ๊ณตํ•˜๊ฒŒ ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

serverless crediental configuration

์ฐธ๊ณ ๋กœ ์ด ๋ช…๋ น์„ ํ†ตํ•ด ~/.aws/credentials ์œ„์น˜์— ํ‚ค๊ฐ’์ด ์ €์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋‘์ž. ์ด๋Š” ๋กœ๊ทธ์—๋„ ์ถœ๋ ฅ๋œ๋‹ค.

์ด๊ฒƒ์œผ๋กœ Serverless์—์„œ AWS๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ค€๋น„๋ฅผ ๋งˆ์ณค๋‹ค. ์ด์ œ serverless project๋ฅผ ์ƒ์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

4. serverless ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•  ํด๋”๋กœ ๋ช…๋ น์ฐฝ์˜ ์œ„์น˜๋ฅผ ์˜ฎ๊ธด ๋‹ค์Œ(cd), ๋‹ค์Œ์˜ ๋ช…๋ น์œผ๋กœ serverless ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

$ cd project-folder

$ serverless create --template aws-nodejs --path quick-start

$ cd quick-start

์ด ๋ช…๋ น์€ AWS์˜ ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ๊ฒƒ์ด๋ฉฐ, Node.js ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์—์„œ Lambda์˜ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•  ๊ฒƒ์ž„์„ ์˜๋ฏธํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ ๋ช…๋ น์„ ์ž…๋ ฅํ•˜๊ธฐ ์ „์— ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์ธ quick-start ๋ผ๋Š” ์ด๋ฆ„์˜ ํด๋”๋Š” ์—†์–ด์•ผ๋งŒ ํ•œ๋‹ค.

์„ฑ๊ณต์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์˜€์„ ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚œ๋‹ค.

serverless create project

์ด๋ ‡๊ฒŒ ์ž˜ ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฉด ์ด์ œ ํ•ด๋‹น ํด๋”์— ์–ด๋– ํ•œ ๊ฒƒ๋“ค์ด ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ๋ณด๋„๋ก ํ•˜์ž.

5. init files

๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒŒ์ผ์ด quick-start ํด๋” ์•„๋ž˜์— ์œ„์น˜ํ•˜๊ฒŒ ๋œ๋‹ค.

serverless create project init files

๊ฐ๊ฐ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. (.gitignore์€ git ๊ด€๋ จ ํŒŒ์ผ)

serverless.yml

์„œ๋น„์Šค์˜ ๋ชจ๋“  ์„ค์ •๋“ค์ด ๋“ค์–ด์žˆ๋Š” ํŒŒ์ผ์ด๋‹ค. ์ฃผ์„์„ ์ œ์™ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

# serverless.yml

service: quick-start # service name

provider:
  name: aws # service provider
  runtime: nodejs10.x # runtime

functions: # function lists
  hello: # function name
    handler: handler.hello # handler function (<file name>.<function name>)
  • service: ์„œ๋น„์Šค์˜ ์ด๋ฆ„
  • provider: ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ณ ์„œ๋น„์Šค ์ œ๊ณต ์—…์ฒด์™€ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ ์ •์˜
  • funcitons: ์„œ๋น„์Šค์˜ ๋ชจ๋“  ํ•จ์ˆ˜๋“ค์— ๋Œ€ํ•œ ๋ฆฌ์ŠคํŠธ

functions ํ”„๋กœํผํ‹ฐ ์•„๋ž˜์—๋Š” ๊ตฌ๋ถ„์„ ์œ„ํ•œ ์ด๋ฆ„๊ณผ ์š”์ฒญ์„ ํ•ธ๋“ค๋งํ•  ํ•จ์ˆ˜๊ฐ€ ๋ช…์‹œ๋˜์–ด์žˆ์œผ๋ฉฐ, ์ด ๊ณณ์— events ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

handler.js

ํŒŒ์ผ์„ ์—ด์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. Lambda ๋ถ€๋ถ„์— ํ•ด๋‹น๋˜๋Š” ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

// handler.js

'use strict';

module.exports.hello = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ // for stringify
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event
    })
  };
};
  • event: ํ—ค๋” ๋“ฑ์˜ ์ •๋ณด
  • context: ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ ๋“ฑ์˜ ์ •๋ณด

serverless.yml์˜ funcitons ํ”„๋กœํผํ‹ฐ์— ์„ค์ •ํ–ˆ๋˜ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜์ธ handler.js ํŒŒ์ผ์˜ hello ํ•จ์ˆ˜์ด๋‹ค. ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ด ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค. (๋งํ–ˆ์ง€๋งŒ ์ด๋ฒคํŠธ(์š”์ฒญ) ๋“ฑ๋ก์€ ์•„๋ž˜์—์„œ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.)

๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ๊ฐ์˜ ์š”์†Œ๋Š” ๋‹ค์Œ์˜ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š”๋‹ค.

  • statusCode: HTTP status code
  • body: response body

์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” Hello! ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ๋กœ ํ–ˆ์œผ๋‹ˆ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด body ๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ์•ผ ํ•  ๊ฒƒ ์ด๋‹ค.

return {
  statusCode: 200,
  body: 'Hello!'
};

์ฐธ๊ณ ๋กœ ์›๋ž˜ ๋‹ค์Œ๊ณผ ๊ฐ™์€ callback ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์˜€์œผ๋‚˜, 1.34.0 ๋ฒ„์ „์—์„œ๋ถ€ํ„ฐ promise ํŒจํ„ด์„ ์ด์šฉํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„๋˜์—ˆ๋‹ค. (Support returning promises from serverless.js)

// handler.js (using callback pattern)

module.exports.hello = (event, context, callback) => {
  const response = { statusCode: 200, body: 'Hello!' };
  callback(null, response);
};

๋ฌผ๋ก  ์›ํ•œ๋‹ค๋ฉด callback ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ๊ฐ„๊ฒฐํ•จ์„ ์œ„ํ•ด promise ํŒจํ„ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.

์•„๋ฌดํŠผ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋Š” ๋ชจ๋“  ์ค€๋น„๋ฅผ ๋งˆ์ณค๋‹ค. ๋‚จ์€ ๊ฑด ์ด์ œ ์ด ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ(์š”์ฒญ)๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ ๋ฟ์ด๋‹ค.

์ด๋Š” serverless.yml ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด events ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ๊ฐ€๋Šฅํ•˜๋‹ค.

# serverless.yml

service: quick-start

provider:
  name: aws
  runtime: nodejs10.x

functions:
  hello:
    handler: handler.hello
    events: # added
      - http:
          path: /
          method: get

๋Œ€๋žต ์˜๋ฏธ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜๋„ ์žˆ์„ํ…๋ฐ, '/' ์œ„์น˜์— 'GET' HTTP์— ๋Œ€ํ•œ ์š”์ฒญ์„ handler.js ํŒŒ์ผ์˜ hello ํ•จ์ˆ˜์—์„œ ํ•ธ๋“ค๋งํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

API Gateway์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฐ›์•„๋“ค์ผ ์š”์ฒญ์„ ์ •์˜ํ•˜๊ณ , ํ•ด๋‹น ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์—ฐ๊ฒฐ๋œ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

6. AWS์— ์—…๋กœ๋“œ

๋ชจ๋“  ์„ค์ •์„ ๋งˆ์ณค๋‹ค๋ฉด ์ด์ œ ๋‚จ์€ ๊ฑด AWS์— ์—…๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ ๋ฟ์ด๋‹ค. ๋‹ค์Œ ๋ช…๋ น ํ•˜๋‚˜๋กœ ๊ฐ€๋Šฅํ•˜๋‹ค.

$ serverless deploy -v

์ •์ƒ์ ์œผ๋กœ deploying ๋˜์—ˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

deploying to AWS

๋์— ๋ณด๋ฉด endpoint ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค. ์ด ๋งํฌ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์„œ๋น„์Šค์˜ API๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๊ฒŒ ๋์ด๋‹ค. ์–ผ๋งˆ๋‚˜ ๊ฐ„ํŽธํ•œ๊ฐ€.

7. Destroy projects

ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค.

$ serverless remove

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธํ™” ํ•จ๊ป˜ aws์— ์—…๋กœ๋“œ ๋œ ๊ฒƒ๋“ค์ด ์ œ๊ฑฐ๋œ๋‹ค.

destroy serverless project in cli

๋‹น์—ฐํžˆ ์‹ค์ œ ํŒŒ์ผ์€ ์ œ๊ฑฐ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฑฑ์ •ํ•˜์ง€ ๋ง์ž. ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ์–ธ์ œ๋“ ์ง€ ๋‹ค์‹œ deploying ํ•  ์ˆ˜ ์žˆ๋‹ค.

$ serverless deploy -v

Relieving the pain

๊ทธ๋Ÿฌ๋‚˜ ๋งค๋ฒˆ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ๋งˆ๋‹ค deploying ํ•  ์ˆ˜๋Š” ์—†์„ ๊ฒƒ์ด๋‹ค. ์ด๋Š” serverless offline ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์„ค์น˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์ฐธ๊ณ ๋กœ npm ๋ง๊ณ  ์ข€ ๋” ํšจ์œจ์ ์ธ yarn์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ด๋Š” ๊ธ€์˜ ์ฃผ์ œ์—์„œ ๋ฒ—์–ด๋‚œ๋‹ค๊ณ  ์ƒ๊ฐ๋˜์–ด ๊ทธ๋ƒฅ ์œ„์—์„œ๋„ ์‚ฌ์šฉํ–ˆ๋˜ npm์œผ๋กœ ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

1. init npm

ํ•ด๋‹น ํ”„๋กœ์ ํŠธ ํด๋”(์œ„์˜ ์˜ˆ์ œ์—์„œ๋Š” quick-start ํด๋”๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.)๋กœ ๋“ค์–ด๊ฐ„ ๋’ค ๋‹ค์Œ์˜ ๋ช…๋ น์œผ๋กœ npm ์‚ฌ์šฉ์„ ์œ„ํ•œ ์ดˆ๊ธฐํ™”๋ฅผ ์ง„ํ–‰ํ•˜์ž.

# yarn์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์œผ๋‚˜, ์—ฌ๊ธฐ์„œ๋Š” npm์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๊ฒ ์Œ
$ npm init

๋ช…๋ น ์ž…๋ ฅ ์‹œ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” ์—ฌ๊ธฐ์— ์ž˜ ์ •๋ฆฌ๋˜์–ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜๋„๋ก ํ•œ๋‹ค.

2. install serverless-offline

๋‹ค์Œ์œผ๋กœ serverless-offline ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•œ๋‹ค.

$ npm install serverless-offline --save-dev

--save-dev ์˜ต์…˜์€ ๊ฐœ๋ฐœ ์‹œ์—๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“ˆ์ž„์„ ๋ช…์‹œํ•œ๋‹ค. ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด build ์‹œ์—๋Š” ํ•ด๋‹น ๋ชจ๋“ˆ์ด ํฌํ•จ๋˜์ง€ ์•Š์•„ ์ข€ ๋” ๊ฐ€๋ฒผ์šด ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. (SO - What is the difference between --save and --save-dev?)

3. Serverless configuration

๋‹ค์Œ์œผ๋กœ serverless.yml์— ํ•ด๋‹น ๋ชจ๋“ˆ์„ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ช…์‹œํ•ด์ค˜์•ผ ํ•œ๋‹ค.

service: quick-start

provider:
  name: aws
  runtime: nodejs10.x

plugins: # added
  - serverless-offline

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: /
          method: get

4. run it locally

์ด์ œ ๋‚จ์€ ๊ฑด ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ ๋ฟ์ด๋‹ค. ๋จผ์ € ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•ด ๋‹ค์Œ์˜ ๋ช…๋ น์„ ์ž…๋ ฅํ•ด์ค€๋‹ค.

$ serverless

์ž…๋ ฅํ•˜๋ฉด ๋‹ค์Œ์˜ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

serverless offline init

์ •์ƒ์ ์œผ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋“ฑ๋ก๋˜์—ˆ์„ ์‹œ, ์œ„์˜ ์‚ฌ์ง„์— ๋‚˜์™€์žˆ๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ Offline ์ด๋ผ๋Š” ๊ธ€์ž๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

์ด์ œ ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ๋กœ์ปฌ์—์„œ serverless๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋„๋ก ํ•˜์ž.

$ serverless offline start

์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉด ๋‹ค์Œ์˜ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋ฉฐ

serverless offline start

http://localhost:3000/ ์œ„์น˜์— ์ ‘์†ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด Hello!๊ฐ€ ์‘๋‹ต์œผ๋กœ ๋Œ์•„์˜ฌ ๊ฒƒ์ด๋‹ค.

serverless offline start: web

์ด๋ ‡๊ฒŒ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ๊ฐœ๋ฐœ์ด ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ deploying ๋ช…๋ น์„ ํ†ตํ•ด AWS๋กœ ์—…๋กœ๋“œํ•˜๋ฉด ๋˜๊ณ ...

Kakao-payment

kakao payment

kakao developers

์นด์นด์˜ค์—์„œ๋Š” ์•ฑ ๊ฐœํŒ” ํ”Œ๋žซํผ ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋“ค์ด ์นด์นด์˜ค์˜ ์„œ๋น„์Šค๋“ค์„ ์ด์šฉํ•œ ๊ฐœ๋ฐœ์„ ์•„์ฃผ ์‰ฝ๊ฒŒ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ  ์žˆ๋‹ค. ๊ฐœ์ค‘์—์„œ๋Š” ์นด์นด์˜คํŽ˜์ด๋ฅผ REST API๋งŒ์œผ๋กœ PC์›น, ๋ชจ๋ฐ”์ผ ์›น, ๋ชจ๋ฐ”์ผ ์•ฑ ๋“ฑ๊ณผ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ ์•„์ฃผ ๊ฐ„๋‹จํžˆ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์„œ๋น„์Šค๋„ ์žˆ์œผ๋ฉฐ, ์šฐ๋ฆฌ๋Š” ์ด ์นด์นด์˜คํŽ˜์ด API๋ฅผ ์ด์šฉํ•ด ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

Create kakao application

์นด์นด์˜คํŽ˜์ด API ์‚ฌ์šฉ์„ ์œ„ํ•ด ๋จผ์ € ์ž์‹ ์˜ ์นด์นด์˜ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋“ฑ๋กํ•ด์•ผ๋งŒ ํ•œ๋‹ค. ์นด์นด์˜ค ๊ฐœ๋ฐœ์ž ํŽ˜์ด์ง€์— ๋จผ์ € ์ ‘์†ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

kakao developers

๋งŒ์•ฝ ์นด์นด์˜ค ๊ณ„์ •์ด ์—†๋‹ค๊ฑฐ๋‚˜ ๋กœ๊ทธ์ธ๋˜์–ด์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋Š” ํšŒ์›๊ฐ€์ž… ๋˜๋Š” ๋กœ๊ทธ์ธ์„ ํ•ด ์ฃผ๋„๋ก ํ•œ๋‹ค.

click 'my applications' button

์—ฌ๊ธฐ์„œ ์ƒ๋‹จ ์šฐ์ธก์— ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด '๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜' ๋ฒ„ํŠผ์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค. ํด๋ฆญํ•ด์ฃผ๋„๋ก ํ•˜์ž.

click 'create appication' button

ํด๋ฆญํ•ด์ฃผ๋ฉด ์™ผ์ชฝ์— '์•ฑ ๋งŒ๋“ค๊ธฐ' ๋ฒ„ํŠผ์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค. ํด๋ฆญํ•ด์ค€๋‹ค.

create application

์ ์ ˆํ•œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•œ ๋’ค, '์•ฑ ๋งŒ๋“ค๊ธฐ' ๋ฒ„ํŠผ์œผ๋กœ ์•ฑ์„ ๋งŒ๋“ค์–ด์ค€๋‹ค.

์ฐธ๊ณ ๋กœ ์ด๋ฆ„์€ ์ •๋ง ์•„๋ฌด๊ฑฐ๋‚˜ ์ƒ๊ด€ ์—†๋‹ค.

application keys

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ์œ„์™€ ๊ฐ™์ด ์นด์นด์˜ค ํ”Œ๋žซํผ ์„œ๋น„์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ‚ค๋„ ํ•จ๊ป˜ ๋ถ€์—ฌ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ Admin ํ‚ค ๋ฅผ ์ด์šฉํ•ด ์นด์นด์˜คํŽ˜์ด API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Before Starting

์นด์นด์˜คํ†ก ๊ฐœ๋ฐœ ํ”Œ๋žซํผ์„ ๋“ฑ๋กํ•ด์ค˜์•ผ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Š” ์นด์นด์˜คํ†ก ๊ฐœ๋ฐœ๊ฐ€์ด๋“œ์˜ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—๋ฅผ ์ฐธ๊ณ ํ•ด ์ž‘์„ฑํ•˜์˜€๋‹ค.

๋จผ์ € ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • ์ฐฝ์œผ๋กœ ์ด๋™ํ•œ ๋‹ค์Œ...

application settings

์„ค์ • > ์ผ๋ฐ˜ ํƒญ์œผ๋กœ ์ด๋™ํ•œ๋‹ค.

๋‚ด๋ฆฌ๋‹ค๋ณด๋ฉด ํ”Œ๋žซํผ ๋ฉ”๋‰ด๊ฐ€ ๋ณด์ด๋Š”๋ฐ, ์ฒ˜์Œ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†์„ ๊ฒƒ์ด๋‹ค.

kakao platform setting

์ด์ œ ํ”Œ๋žซํผ ์ถ”๊ฐ€ > ์›น ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ , ์‚ฌ์ดํŠธ ๋„๋ฉ”์ธ์—๋Š” ๋‹ค์Œ์„ ์ž…๋ ฅํ•ด์ฃผ๋„๋ก ํ•˜์ž.

http://example.com

์˜ˆ์‹œ ์‚ฌ์ดํŠธ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ์ €๋ ‡๊ฒŒ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค. ์ž…๋ ฅ ํ›„์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋œ๋‹ค.

kakao platform setting over

์ด์ œ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ์ง„ํ–‰ํ•˜์ž. ์•„๋ž˜์—์„œ http://example.com์„ ๋งŒ๋‚  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

Kakao payment API

์นด์นด์˜คํŽ˜์ด API๋Š” ์ •๋ง ๊ฐ„๋‹จํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ€๋ น ํ•˜๋‚˜์˜ ๊ฒฐ์ œ ๊ฑด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

curl -v -X POST 'https://kapi.kakao.com/v1/payment/ready' / # ๊ฒฐ์ œ ์š”์ฒญ url
-H 'Authorization: KakaoAK xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' / # 'xxx...' = Admin key
--data-urlencode 'cid=TC0ONETIME' / # ํ…Œ์ŠคํŠธ ์ „์šฉ ๊ฐ€๋งน์  ์ฝ”๋“œ
--data-urlencode 'partner_order_id=partner_order_id' / # ๊ฐ€๋งน์  ์ฃผ๋ฌธ๋ฒˆํ˜ธ
--data-urlencode 'partner_user_id=partner_user_id' / # ๊ฐ€๋งน์  ํšŒ์› ID
--data-urlencode 'item_name=์ดˆ์ฝ”ํŒŒ์ด' / # ์ƒํ’ˆ๋ช…
--data-urlencode 'quantity=1' / # ์ƒํ’ˆ ์ˆ˜๋Ÿ‰
--data-urlencode 'total_amount=2200' / # ์ƒํ’ˆ ์ด์•ก
--data-urlencode 'vat_amount=200' / # ์ƒํ’ˆ ๋ถ€๊ณผ์„ธ ๊ธˆ์•ก
--data-urlencode 'tax_free_amount=0' / # ์ƒํ’ˆ ๋น„๊ณผ์„ธ ๊ธˆ์•ก
--data-urlencode 'approval_url=https://developers.kakao.com/success' / # ๊ฒฐ์ œ ์„ฑ๊ณต ์‹œ redirect url
--data-urlencode 'fail_url=https://developers.kakao.com/fail' / # ๊ฒฐ์ œ ์‹คํŒจ ์‹œ redirect url
--data-urlencode 'cancel_url=https://developers.kakao.com/cancel' # ๊ฒฐ์ œ ์ทจ์†Œ ์‹œ redirect url

์š”์ฒญ์ด ์„ฑ๊ณตํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‘๋‹ต์ด JSON์œผ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.

{
 "tid": "T1234567890123456789", // ๊ฒฐ์ œ ๊ณ ์œ  ๋ฒˆํ˜ธ
 "next_redirect_app_url": "https://mockup-pg-web.kakao.com/v1/xxxxxxxxxx/aInfo", // ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฒฐ์ œ url
 "next_redirect_mobile_url": "https://mockup-pg-web.kakao.com/v1/xxxxxxxxxx/mInfo", // ๋ชจ๋ฐ”์ผ ์›น ๊ฒฐ์ œ url
 "next_redirect_pc_url": "https://mockup-pg-web.kakao.com/v1/xxxxxxxxxx/info", // pc ์›น ๊ฒฐ์ œ url
 "android_app_scheme": "kakaotalk://kakaopay/pg?url=https://mockup-pg-web.kakao.com/v1/xxxxxxxxxx/order", // ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฒฐ์ œ ์•ฑ scheme
 "ios_app_scheme": "kakaotalk://kakaopay/pg?url=https://mockup-pg-web.kakao.com/v1/xxxxxxxxxx/order", // IOS ๊ฒฐ์ œ ์•ฑ scheme
 "created_at": "2016-11-15T21:18:22" // ๊ฒฐ์ œ ์ค€๋น„ ์š”์ฒญ ์‹œ๊ฐ„
}

request, response ์˜ต์…˜์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์ด๋‚˜ ๋‹ค๋ฅธ ์˜ต์…˜์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.

๋ฐ˜ํ™˜๋˜๋Š” URL๋กœ ์ ์ ˆํžˆ ์ด๋™ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์ด ๋ฐ˜๊ฒจ์ฃผ๋ฉฐ, ํ…Œ์ŠคํŠธ ๊ฒฐ์ œ๊ฐ€ ์ง„ํ–‰๋œ๋‹ค.

kakao payment process

์ฐธ๊ณ ๋กœ ํ•œ ๋ฒˆ ์‚ฌ์šฉ๋œ URL์€ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ํ•„์š”์‹œ๋งˆ๋‹ค ๊ฐ€์ ธ์™€์ค˜์•ผ ํ•œ๋‹ค.

Development of Kakao-payment using Serverless

nodejs๋ฅผ ์ด์šฉํ•ด ๊ฒฐ์ œ์š”์ฒญ์„ ๋ณด๋‚ธ ๋’ค, ๋งํฌ๋ฅผ ๋ฐ›์•„์™€ ๊ฒฐ์ œ ํŽ˜์ด์ง€(PC ์›น)๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ค๋Š” ์„œ๋น„์Šค๋ฅผ serverless AWS๋ฅผ ์ด์šฉํ•œ FaaS๋กœ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

Installation

๋จผ์ € ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ์„ ์œ„ํ•ด serverless-offline ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ axios nodejs ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

$ serverless create --template aws-nodejs --path kakao-payment-test

$ cd kakao-payment-test

$ npm i serverless-offline --save-dev
$ npm i axios # request๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•  nodejs ๋ชจ๋“ˆ

Configuration

serverless-offline ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ํ•ธ๋“ค๋งํ•  ์š”์ฒญ. ๊ทธ๋ฆฌ๊ณ  ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋ช…์‹œํ•œ๋‹ค.

# serverless.yml

service: kakao-payment-test

provider:
  name: aws
  runtime: nodejs10.x

plugins:
  - serverless-offline

functions:
  payment:
    handler: handler.payment
    events:
      - http:
          path: /
          method: get

์„ค์ • ํŒŒ์ผ์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์œ„์—์„œ ์ถฉ๋ถ„ํžˆ ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐ๋˜๊ธฐ์—, ๊ตณ์ด ํ•˜์ง€ ์•Š์•„๋„ ์ƒ๊ด€์€ ์—†์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐ๋œ๋‹ค.

Development

๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰๋  ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ด์ฃผ๋„๋ก ํ•˜์ž. ๋™์ž‘์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. https://kapi.kakao.com/v1/payment/ready๋กœ ํ•„์š” ์ •๋ณด์™€ ํ•จ๊ป˜ ๊ฒฐ์ œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
  2. ๋ฐ˜ํ™˜๋˜๋Š” JSON ๊ฐ’ ์ค‘, PC์›น ๊ฒฐ์ œ์— ํ•ด๋‹น๋˜๋Š” URL์„ ๊ฐ€์ ธ์˜จ๋‹ค.
  3. HTTP 301 ์ฝ”๋“œ์™€ Location ํ—ค๋”๋ฅผ ์ด์šฉํ•ด redirect ์‘๋‹ต์„ ๋ณด๋‚ธ๋‹ค.

์ด๋กœ์จ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค ๋งํฌ(endpoint)๋กœ ์ ‘์†ํ•  ์‹œ ์นด์นด์˜ค ๊ฒฐ์ œ ์ฐฝ์œผ๋กœ ์ด๋™(redirect)ํ•˜๊ฒŒ ๋œ๋‹ค.

์ด๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// handler.js

'use strict';

const axios = require('axios'); // using 'axios' node module for HTTP request

module.exports.payment = async () => { // meaning 'async' is 'asynchronous function'

  // set variables
  const item_name = '์ดˆ์ฝ”ํŒŒ์ด';
  const quantity = 1;
  const total_amount = 2200;
  const vat_amount = 200;
  const tax_free_amount = 0;

  const approval_url = 'http://example.com/success';
  const fail_url = 'http://example.com/fail';
  const cancel_url = 'http://example.com/cancel';

  // set data
  const data = [
    'cid=TC0ONETIME',
    'partner_order_id=partner_order_id',
    'partner_user_id=partner_user_id',
    `item_name=${item_name}`,
    `quantity=${quantity}`,
    `total_amount=${total_amount}`,
    `vat_amount=${vat_amount}`,
    `tax_free_amount=${tax_free_amount}`,
    `approval_url=${approval_url}`,
    `fail_url=${fail_url}`,
    `cancel_url=${cancel_url}`
  ].join('&'); // encode data (application/x-www-form-urlencoded)

  // send request (kakao payment)
  const req = await axios.post('https://kapi.kakao.com/v1/payment/ready', data, {
    headers: {
      'Authorization': 'KakaoAK xxxxxxxxxx', // 'xxx...' = admin key
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

  const pc_url = req.data.next_redirect_pc_url; // get pc url

  const response = {
    statusCode: 301, // redirect
    headers: {
      'Cache-Control': 'no-cache, no-store, must-revalidate',
      'Pragma': 'no-cache',
      'Expires': '0',
      Location: pc_url
    },
    body: ''
  };

  return response;
};

ํ—ค๋”์— ๋ณด๋ฉด Cache-Control, Pragma, Expires๋ฅผ ๋ณด๋‚ด๋Š”๋ฐ, ์ด๋Š” chacing์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒ๋˜๋Š” ์ด์Šˆ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. ์ด๋Š” ์•„๋ž˜์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ๋‹ค.

์—ฌ๊ธฐ์„œ axios ๋ชจ๋“ˆ์ด ์‚ฌ์šฉ๋˜์—ˆ๋Š”๋ฐ, ์ด๋Š” ์ฝ”๋“œ์—์„œ๋„ ๋‚˜์™€์žˆ๋“ฏ์ด 'POST' ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ ๋ชจ๋“ˆ์ด๋‹ค.

Test

$ serverless offline start # http://localhost:3000/

$ curl -I -HEAD http://localhost:3000/ # ์ƒˆ๋กœ์šด ์ฐฝ์—์„œ ์‹คํ–‰ํ•ด์•ผ๋งŒ ํ•œ๋‹ค

์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์—ˆ์„ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ—ค๋”๊ฐ€ ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.

kakao payment response

http://localhost:3000/ ์—๋„ ์ ‘์†ํ•ด๋ณด๋„๋ก ํ•˜์ž(PC ์›น). ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์นด์นด์˜ค ๊ฒฐ์ œ ํŽ˜์ด์ง€๋กœ redirect ๋  ๊ฒƒ์ด๋‹ค.

kakao payment page

Development with Parameter modification

์•ž์„œ ๋ณธ ๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์ง„ํ–‰ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

// set params
const item_name = '์ดˆ์ฝ”ํŒŒ์ด';
const quantity = 1;
const total_amount = 2200;
const vat_amount = 200;
const tax_free_amount = 0;

์—ฌ๊ธฐ์„œ๋Š” serverless.yml์„ ์ˆ˜์ •ํ•ด POST Request๋ฅผ ํ†ตํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›๊ณ , ์ด๋ฅผ ํ†ตํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

GET์˜ Body๋Š” query stringํ˜•ํƒœ๋กœ ์ „์†กํ•  ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด serverless.yml์„ ์ˆ˜์ •ํ•˜๊ณ ...

# ...

functions:
  payment:
    handler: handler.payment
    events:
      - http:
          path: /
          method: get
          request: # add
            parameters:
              querystrings:
                url: true

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณด์ž

module.exports.payment = async (evt) => {
  // get parmas
  console.log(evt.multiValueQueryStringParameters);

  return {
    statusCode: 200,
    body: 'hello~',
  };

  /*
  // set variables
  const item_name = '์ดˆ์ฝ”ํŒŒ์ด';
  const quantity = 1;
  const total_amount = 2200;
  const vat_amount = 200;
  const tax_free_amount = 0;

  const approval_url = 'http://example.com/success';
  const fail_url = 'http://example.com/fail';
  const cancel_url = 'http://example.com/cancel';

  // set data
  const data = [
    'cid=TC0ONETIME',
    'partner_order_id=partner_order_id',
    'partner_user_id=partner_user_id',
    `item_name=${item_name}`,
    `quantity=${quantity}`,
    `total_amount=${total_amount}`,
    `vat_amount=${vat_amount}`,
    `tax_free_amount=${tax_free_amount}`,
    `approval_url=${approval_url}`,
    `fail_url=${fail_url}`,
    `cancel_url=${cancel_url}`
  ].join('&'); // encode data (application/x-www-form-urlencoded)

  // send request (kakao payment)
  const req = await axios.post('https://kapi.kakao.com/v1/payment/ready', data, {
    headers: {
      'Authorization': 'KakaoAK xxxxxxxxxx', // 'xxx...' = admin key
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

  const pc_url = req.data.next_redirect_pc_url; // get pc url

  const response = {
    statusCode: 301, // redirect
    headers: {
      'Cache-Control': 'no-cache, no-store, must-revalidate',
      'Pragma': 'no-cache',
      'Expires': '0',
      Location: pc_url
    },
    body: ''
  };

  return response;
  */
}

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด Request๋ฅผ ๋‚ ๋ฆฌ๊ฒŒ ๋˜๋ฉด... ์ฐธ๊ณ ๋กœ curl ๋‚ ๋ฆฌ๊ธฐ ์–ด๋ ค์šฐ๋ฉด curl tool ๋˜๋Š” postman์„ ์• ์šฉํ•˜์ž. (๋ณธ์ธ์€ postman์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ)

$ curl -XGET 'http://localhost:3000/?item_name=์ดˆ์ฝ”ํŒŒ์ด&quantity=1&total_amount=2200&vat_amount=200&tax_free_amount=0'

serverless ์ฝ˜์†”์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚  ๊ฒƒ์ด๋‹ค.

get-result

์ฐธ๊ณ ๋กœ, ๊ฒฐ๊ณผ ํ™”๋ฉด์ด ๊ผญ ๊ฐ™์„ ํ•„์š”๋Š” ์—†๋‹ค. ๊ทธ๋ƒฅ ์œ„์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค. ์ฐธ๊ณ ๋กœ ํ•œ๊ธ€์ด ๊นจ์ ธ๋ณด์ด๋Š” ๊ฑด postman์œผ๋กœ ๋‚ ๋ฆฌ๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋‚˜์˜จ๋‹ค.

get-result-utf-8

์ž ์ด์ œ ๋‹ค์‹œ ๊ฐœ๋ฐœ์„ ํ•ด ๋ณผ ์‹œ๊ฐ„. ๋‹ค์Œ๊ณผ ๊ฐ™์ด body๋กœ ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์ž.

module.exports.payment = async ({ multiValueQueryStringParameters: params }) => {
  // get parmas
  const {
    item_name,
    quanity,
    total_amount,
    vat_amount,
    tasx_free,amount,
  } = params;

  /*
  // set variables
  const item_name = '์ดˆ์ฝ”ํŒŒ์ด';
  const quantity = 1;
  const total_amount = 2200;
  const vat_amount = 200;
  const tax_free_amount = 0;
  */

  const approval_url = 'http://example.com/success';
  const fail_url = 'http://example.com/fail';
  const cancel_url = 'http://example.com/cancel';

  // set data
  const data = [
    'cid=TC0ONETIME',
    'partner_order_id=partner_order_id',
    'partner_user_id=partner_user_id',
    `item_name=${item_name}`,
    `quantity=${quantity}`,
    `total_amount=${total_amount}`,
    `vat_amount=${vat_amount}`,
    `tax_free_amount=${tax_free_amount}`,
    `approval_url=${approval_url}`,
    `fail_url=${fail_url}`,
    `cancel_url=${cancel_url}`
  ].join('&'); // encode data (application/x-www-form-urlencoded)

  // send request (kakao payment)
  const req = await axios.post('https://kapi.kakao.com/v1/payment/ready', data, {
    headers: {
      'Authorization': 'KakaoAK xxxxxxxxx', // 'xxx...' = admin key
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

  const pc_url = req.data.next_redirect_pc_url; // get pc url

  const response = {
    statusCode: 301, // redirect
    headers: {
      'Cache-Control': 'no-cache, no-store, must-revalidate',
      'Pragma': 'no-cache',
      'Expires': '0',
      Location: pc_url
    },
    body: ''
  };

  return response;
}

๊ฐœ๋ฐœ ๋! ์ด์ œ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ˆ˜์ •ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Watch out!

๊ฐœ๋ฐœ ์‹œ ๋ฐœ์ƒ๋˜์—ˆ๋˜ ๋ฌธ์ œ ๋˜๋Š” ์ฃผ์˜ํ•  ๊ฒƒ๋“ค

application/x-www-form-urlencoded

๊ฒฐ์ œ ์š”์ฒญ ์‹œ body๋Š” application/x-www-form-urlencoded ์ปจํ…์ธ  ํƒ€์ž…์œผ๋กœ ๋ณด๋‚ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ body๋ฅผ ์ „์†กํ•ด์•ผ๋งŒ ํ•œ๋‹ค.

cid=TC0ONETIME&partner_order_id=partner_order_id&partner_user_id=partner_user_id&item_name=์ดˆ์ฝ”ํŒŒ์ด&quantity=1&total_amount=2200&vat_amount=200&tax_free_amount=0&approval_url=http://example.com&fail_url=http://example.com&cancel_url=http://example.com

๋งŒ์ผ ์ด๋Ÿฌํ•œ ํ˜•ํƒœ๋กœ ๋ณด๋‚ด์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ, ํ•„์š”ํ•œ ๊ฒฐ์ œ ๊ด€๋ จ ์ •๋ณด๊ฐ€ ๋ณด๋‚ด์ง€์ง€ ์•Š์•˜๋‹ค๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์—๋Ÿฌ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋  ๊ฒƒ์ด๋‹ค.

์ด๋Ÿฐ POST ๋ฉ”์„œ๋“œ์˜ ์ฝ˜ํ…์ธ  ํƒ€์ž…์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜๋„๋ก ํ•œ๋‹ค.

Admin key

์นด์นด์˜คํŽ˜์ด ๊ฒฐ์ œ ์š”์ฒญ ์‹œ Authorization ํ—ค๋”์˜ ๊ฐ’์— Admin key๋ฅผ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค. ์ด ๋•Œ, Admin key ๊ฐ’ ์•ž์— 'KakaoAK '๋ฅผ ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•˜์ž.

'Authorization': 'KakaoAK xxxxxxxxxx'

๋„์–ด์“ฐ๊ธฐ๋„ ํฌํ•จ์ด๋‹ค.

redirect cache

HTTP 301๋กœ ๋„˜์–ด๊ฐ€๋Š” redirect response๋Š” Crome ๋“ฑ ํ˜„๋Œ€์ ์ธ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํšจ์œจ์„ ์œ„ํ•ด caching ๋œ๋‹ค.(SO - How long do browsers cache HTTP 301s?)

๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด cachingํ•˜์ง€ ๋ง๋ผ๋Š” ํ—ค๋”๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๋ณด๋‚ด์ค˜์•ผ๋งŒ ํ•œ๋‹ค.

'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'

๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ cache๋กœ ์ธํ•ด ๊ฐ™์€ ๋งํฌ๋กœ ๊ณ„์† redirect ๋  ๊ฒƒ์ด๋ฉฐ, ์ด๋Š” ๊ฐ™์€ ๊ฒฐ์ œ ๊ฑด์— ๋Œ€ํ•ด์„œ๋งŒ redirect ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ ์ค‘๋ณต ๊ฑฐ๋ž˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๋งŒ์„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

์ฐธ๊ณ