kakao-payment-test
kakao-payment-test copied to clipboard
๐ป Create Kakao payment (KakaoPay) FaaS via AWS Serverless
Serverless AWS๋ฅผ ์ด์ฉํด FaaS๋ก ๊ตฌํํ ์นด์นด์ค ํ ์คํธ ๊ฒฐ์ ์๋น์ค
'์๋ฒ๋ฆฌ์ค ์ํคํ ์ณ' ๋ผ๋ ๋ง์ด ์๋ค. ์ด๋ ์๋ฒ๊ฐ ์๋ค๋ ์๋ฏธ๊ฐ ์๋๋ผ... ์๋ฒ๋ฆฌ์ค ์ํคํ ์ณ๋ฅผ ์๋น์คํด์ฃผ๋ ๊ธฐ์ ์์ ์๋ฒ์ ๋ํด ์์์ ํ๋ก๋น์ ๋ ๋๋ ์ ์ง๋ณด์๋ฅผ ํด ์ฃผ๊ธฐ ๋๋ฌธ์, ์๋ฒ์ ๋ํด ๋ ์ด์ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ค ๋ ์๋ฏธ๋ก ๋ฐ์๋ค์ด๋ฉด ๋๋ค. ๋ค์๋งํ์๋ฉด, ์๋ฒ ์์ฒด๋ฅผ ๊ตฌํํด์ผ ํ๊ฑฐ๋(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๋ Lambda์ API Gateway๋ฅผ ํตํด ์๋ฒ๋ฆฌ์ค ์ํคํ ์ณ๋ฅผ ๊ตฌํํ ์ ์๋๋ก ํ๊ณ ์๋ค.
์ฐธ๊ณ ๋ก AWS๋ ํ๋ฆฌ ํฐ์ด ๋ผ๋ ์๋น์ค๋ฅผ ์ ๊ณตํ์ฌ AWS์ ํ๋ซํผ๊ณผ ์ ํ ๋ฐ ์๋น์ค๋ฅผ ๋ฌด๋ฃ๋ก ์ฒดํํด ๋ณผ ์ ์๋๋ก ํ๋ค. ์ฌ๊ธฐ์ ๋ชจ๋ ๊ณผ์ ๋ค์ (์๋ง ๋ฐ๋ผ์จ๋ค๋ฉด) ํ๋ฆฌ ํฐ์ด์ ๋ฒ์ฃผ ์์ ์ํ๋ ๊ฒ๋ค์ด๋ ๊ณผ๊ธ์ ๊ฑฑ์ ์ ํ์ง ์์๋ ๋๋ค.
๊ทธ๋๋ ๊ณผ๊ธ์ด ๊ฑฑ์ ๋๋ค๋ฉด ๊ฒฐ์ ๋ฐ ๋น์ฉ ๊ด๋ฆฌ์์ ๋ชจ๋ ์ฒญ๊ตฌ ๋น์ฉ์ ํ์ธํ ์๋ ์์ผ๋ ์ฐธ๊ณ ํ๋๋ก ํ๋ค.
AWS Lambda
์ด๋ ํ ์ด๋ฒคํธ์ ๋ํด ์ฝ๋๋ฅผ ์คํํ๊ณ ์ปดํจํ ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ๋ AWS serverless computing ์๋น์ค์ด๋ค.
๊ฐ๋จํ ์ค๋ช ํ์๋ฉด Lambda์ ์ด๋ ํ ํจ์๋ฅผ ์์ฑ(๋๋ ์ ๋ก๋)ํ ๋ค, API Gateway์ ๊ฐ์ ์๋น์ค๋ฅผ ์ด์ฉํด ์ด๋ฒคํธ์ ์ฐ๊ฒฐํ๋ค. ์ดํ ์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋ ์ฆ์ ํด๋น ํจ์๋ฅผ ์คํํ๋ ๊ฒ.
์ฃผ์ํ ๊ฒ์ Lambda๊ฐ ์ด๋ ํ ์ํ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ ์๋๊ธฐ ๋๋ฌธ์, ๊ฐ์ ์ ์ฅํ๊ธฐ ์ํด์๋ AWS DynamoDB์ ๊ฐ์ ํ ์๋น์ค๋ฅผ Lambda์ ์ฐ๊ฒฐํด ์ฌ์ฉํด์ผ ํ๋ค. ์ด๋ ๊ฒ Lambda๋ AWS์ ๋ค๋ฅธ ์๋น์ค๋ค๊ณผ๋ ์ฐ๊ฒฐํ ์ ์๋ค. ๋ค๋ง ์ด๋ ๊ธ์ ์ฃผ์ ์ ๋ง์ง ์๊ธฐ ๋๋ฌธ์, ๋ ์๊ณ ์ถ๋ค๋ฉด AWS Lambda ๊ฐ๋ฐ์ ์๋ด์๋ฅผ ์ฐธ๊ณ ํ๋๋ก ํ๋ค.
AWS API Gateway
๊ฐ๋ฐ์๊ฐ 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๋ ์์ ๊ฐ์ ์๋ฒ๋ฆฌ์ค ์ํคํ ์ณ๋ฅผ ๋ฐฐํฌํ๊ณ ์ด์ํ๊ธฐ ์ํ ํ๋ ์์ํฌ์ด๋ค.
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 ๊ณ์ ์ด ์๋ ๊ฒฝ์ฐ ์์ฑํ๋๋ก ํ์.
์ฝ์ ์ฐฝ์ ์ผ์ชฝ ์๋จ '์๋น์ค(Services)' ํญ์ ํด๋ฆญ ํ, ๋ค์๊ณผ ๊ฐ์ด 'IAM' ์ ๊ฒ์ํด IAM ํ์ด์ง๋ก ๋ค์ด๊ฐ๋๋ก ํ์.
IAM ํ์ด์ง์ ๋ค์ด๊ฐ์ผ๋ฉด, ์ผ์ชฝ ์ฌ์ฉ์ ํญ์ ํด๋ฆญํ ๋ค '์ฌ์ฉ์ ์ถ๊ฐ(Add User)' ๋ฒํผ์ ๋๋ฅธ๋ค.
์ ์ ํ ์ฌ์ฉ์ ์ด๋ฆ์ ์ ๋ ฅํ ๋ค, 'ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์ ์ก์ธ์ค(Programmatic access)'์ ์ฒดํฌํ ๋ค ๋ค์ ๋ฒํผ์ ๋๋ฅธ๋ค.
'๊ธฐ์กด ์ ์ฑ ์ง์ ์ฐ๊ฒฐ(Attach existing policies directly)' ๋ฒํผ์ ํด๋ฆญํ ๋ค, ๋์ค๋ ๋ชฉ๋ก์์ 'AdministratorAccess'๋ฅผ ์ฒดํฌํ ๋ค ๋ค์ ๋ฒํผ์ ๋๋ฅธ๋ค.
ํ๊ทธ๋ ๋ฐ๋ก ์ถ๊ฐํ ๊ฒ ์๋ค. ๋ค์ ๋ฒํผ์ ๋๋ฌ ๊ณ์ ์งํํด์ฃผ์.
๋ง์ง๋ง์ผ๋ก ๊ฒํ ์ฐฝ์์ ์ค์ ํ๋๋ก ๊ณ์ ์ด ๋ง๋ค์ด์ก๋์ง ๊ฒํ ๋ฅผ ํ ๋ค, '์ฌ์ฉ์ ๋ง๋ค๊ธฐ' ๋ฒํผ์ผ๋ก ์ฌ์ฉ์๋ฅผ ์ถ๊ฐํด์ฃผ๋๋ก ํ์.
์ ์์ ์ผ๋ก ์ฌ์ฉ์๊ฐ ์ถ๊ฐ๋์๋ค๋ฉด, ์์ ๊ฐ์ ํ๋ฉด์ด ๋ฐ๊ฒจ์ค ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ '์ก์ธ์ค ํค 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๊ฐ ๋ค์ด๊ฐ๊ฒ ๋๋ค. ์ฑ๊ณตํ๊ฒ ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ค.
์ฐธ๊ณ ๋ก ์ด ๋ช
๋ น์ ํตํด ~/.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
๋ผ๋ ์ด๋ฆ์ ํด๋๋ ์์ด์ผ๋ง ํ๋ค.
์ฑ๊ณต์ ์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ์์ ๊ฒฝ์ฐ, ๋ค์๊ณผ ๊ฐ์ด ๋ํ๋๋ค.
์ด๋ ๊ฒ ์ ์์ฑ๋์์ผ๋ฉด ์ด์ ํด๋น ํด๋์ ์ด๋ ํ ๊ฒ๋ค์ด ์์ฑ๋์๋์ง ๋ณด๋๋ก ํ์.
5. init files
๋ค์๊ณผ ๊ฐ์ ํ์ผ์ด quick-start
ํด๋ ์๋์ ์์นํ๊ฒ ๋๋ค.
๊ฐ๊ฐ ๋ค์๊ณผ ๊ฐ๋ค. (.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 ๋์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ค.
๋์ ๋ณด๋ฉด endpoint ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ด ๋ณด์ผ ๊ฒ์ด๋ค. ์ด ๋งํฌ๋ฅผ ํตํด ํด๋น ์๋น์ค์ API๋ก ์ ๊ทผํ ์ ์๋ค.
์ด๊ฒ ๋์ด๋ค. ์ผ๋ง๋ ๊ฐํธํ๊ฐ.
7. Destroy projects
ํ๋ก์ ํธ๋ฅผ ์ญ์ ํ๊ณ ์ถ์ ๋๋ ๋ค์๊ณผ ๊ฐ์ ๋ช ๋ น๋ง ์ ๋ ฅํ๋ฉด ๋๋ค.
$ serverless remove
๋ค์๊ณผ ๊ฐ์ ๋ก๊ทธํ ํจ๊ป aws์ ์ ๋ก๋ ๋ ๊ฒ๋ค์ด ์ ๊ฑฐ๋๋ค.
๋น์ฐํ ์ค์ ํ์ผ์ ์ ๊ฑฐ๋์ง ์๊ธฐ ๋๋ฌธ์ ๊ฑฑ์ ํ์ง ๋ง์. ๋ค์ ๋ช ๋ น์ผ๋ก ์ธ์ ๋ ์ง ๋ค์ 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
์ ๋ ฅํ๋ฉด ๋ค์์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ค.
์ ์์ ์ผ๋ก ํ๋ฌ๊ทธ์ธ์ด ๋ฑ๋ก๋์์ ์, ์์ ์ฌ์ง์ ๋์์๋ ๊ฒ ์ฒ๋ผ Offline
์ด๋ผ๋ ๊ธ์๊ฐ ์ถ๋ ฅ๋๋ค.
์ด์ ๋ค์ ๋ช ๋ น์ผ๋ก ๋ก์ปฌ์์ serverless๋ฅผ ์คํํด๋ณด๋๋ก ํ์.
$ serverless offline start
์ ์์ ์ผ๋ก ์คํ๋๋ฉด ๋ค์์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋ฉฐ
http://localhost:3000/
์์น์ ์ ์ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด Hello!
๊ฐ ์๋ต์ผ๋ก ๋์์ฌ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ ๊ฐ๋ฐ์ ์งํํ๋ฉด ๋๋ค. ๊ฐ๋ฐ์ด ์๋ฃ๋๋ฉด ๋ค์ deploying ๋ช ๋ น์ ํตํด AWS๋ก ์ ๋ก๋ํ๋ฉด ๋๊ณ ...
Kakao-payment
์นด์นด์ค์์๋ ์ฑ ๊ฐํ ํ๋ซํผ ์๋น์ค๋ฅผ ํตํด ๊ฐ๋ฐ์๋ค์ด ์นด์นด์ค์ ์๋น์ค๋ค์ ์ด์ฉํ ๊ฐ๋ฐ์ ์์ฃผ ์ฝ๊ฒ ์งํํ ์ ์๋๋ก ํ๊ณ ์๋ค. ๊ฐ์ค์์๋ ์นด์นด์คํ์ด๋ฅผ REST API๋ง์ผ๋ก PC์น, ๋ชจ๋ฐ์ผ ์น, ๋ชจ๋ฐ์ผ ์ฑ ๋ฑ๊ณผ ๊ฐ์ด ๋ค์ํ ํ๊ฒฝ์์ ์์ฃผ ๊ฐ๋จํ ๊ฒฐ์ ๋ฅผ ์งํํ ์ ์๋๋ก ํ๋ ์๋น์ค๋ ์์ผ๋ฉฐ, ์ฐ๋ฆฌ๋ ์ด ์นด์นด์คํ์ด API๋ฅผ ์ด์ฉํด ํ ์คํธ ๊ฒฐ์ ์๋น์ค๋ฅผ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ๋ค.
Create kakao application
์นด์นด์คํ์ด API ์ฌ์ฉ์ ์ํด ๋จผ์ ์์ ์ ์นด์นด์ค ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํด์ผ๋ง ํ๋ค. ์นด์นด์ค ๊ฐ๋ฐ์ ํ์ด์ง์ ๋จผ์ ์ ์ํด์ฃผ๋๋ก ํ์.
๋ง์ฝ ์นด์นด์ค ๊ณ์ ์ด ์๋ค๊ฑฐ๋ ๋ก๊ทธ์ธ๋์ด์์ง ์์ ๊ฒฝ์ฐ์๋ ํ์๊ฐ์ ๋๋ ๋ก๊ทธ์ธ์ ํด ์ฃผ๋๋ก ํ๋ค.
์ฌ๊ธฐ์ ์๋จ ์ฐ์ธก์ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด '๋ด ์ ํ๋ฆฌ์ผ์ด์ ' ๋ฒํผ์ด ๋ณด์ผ ๊ฒ์ด๋ค. ํด๋ฆญํด์ฃผ๋๋ก ํ์.
ํด๋ฆญํด์ฃผ๋ฉด ์ผ์ชฝ์ '์ฑ ๋ง๋ค๊ธฐ' ๋ฒํผ์ด ๋ณด์ผ ๊ฒ์ด๋ค. ํด๋ฆญํด์ค๋ค.
์ ์ ํ ์ด๋ฆ์ ์ ๋ ฅํ ๋ค, '์ฑ ๋ง๋ค๊ธฐ' ๋ฒํผ์ผ๋ก ์ฑ์ ๋ง๋ค์ด์ค๋ค.
์ฐธ๊ณ ๋ก ์ด๋ฆ์ ์ ๋ง ์๋ฌด๊ฑฐ๋ ์๊ด ์๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑํ๊ฒ ๋๋ฉด ์์ ๊ฐ์ด ์นด์นด์ค ํ๋ซํผ ์๋น์ค์ ์ ๊ทผํ ์ ์๋ ํค๋ ํจ๊ป ๋ถ์ฌ๋๋๋ฐ, ์ฌ๊ธฐ์ Admin ํค ๋ฅผ ์ด์ฉํด ์นด์นด์คํ์ด API๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
Before Starting
์นด์นด์คํก ๊ฐ๋ฐ ํ๋ซํผ์ ๋ฑ๋กํด์ค์ผ ์ ์์ ์ผ๋ก ์๋ํ๊ฒ ๋๋ค. ์ด๋ ์นด์นด์คํก ๊ฐ๋ฐ๊ฐ์ด๋์ ์์ํ๊ธฐ ์ ์๋ฅผ ์ฐธ๊ณ ํด ์์ฑํ์๋ค.
๋จผ์ ์ ํ๋ฆฌ์ผ์ด์ ์ค์ ์ฐฝ์ผ๋ก ์ด๋ํ ๋ค์...
์ค์ > ์ผ๋ฐ
ํญ์ผ๋ก ์ด๋ํ๋ค.
๋ด๋ฆฌ๋ค๋ณด๋ฉด ํ๋ซํผ ๋ฉ๋ด๊ฐ ๋ณด์ด๋๋ฐ, ์ฒ์์๋ ์๋ฌด๊ฒ๋ ์์ ๊ฒ์ด๋ค.
์ด์ ํ๋ซํผ ์ถ๊ฐ > ์น
๋ฒํผ์ ๋๋ฅด๊ณ , ์ฌ์ดํธ ๋๋ฉ์ธ์๋ ๋ค์์ ์
๋ ฅํด์ฃผ๋๋ก ํ์.
http://example.com
์์ ์ฌ์ดํธ๊ฐ ์๋๋ค. ์ค์ ์ ๋ ๊ฒ ์ ๋ ฅํ๋ฉด ๋๋ค. ์ ๋ ฅ ํ์๋ ์๋์ ๊ฐ์ ํ๋ฉด์ด ๋ํ๋๊ฒ ๋๋ค.
์ด์ ๋ค์ ๋จ๊ณ๋ฅผ ์งํํ์. ์๋์์ 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๋ก ์ ์ ํ ์ด๋ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ํ๋ฉด์ด ๋ฐ๊ฒจ์ฃผ๋ฉฐ, ํ ์คํธ ๊ฒฐ์ ๊ฐ ์งํ๋๋ค.
์ฐธ๊ณ ๋ก ํ ๋ฒ ์ฌ์ฉ๋ 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
๋ง์ง๋ง์ผ๋ก ์คํ๋ ํจ์๋ฅผ ์ ์ํด์ฃผ๋๋ก ํ์. ๋์์ ๋ค์๊ณผ ๊ฐ๋ค.
-
https://kapi.kakao.com/v1/payment/ready
๋ก ํ์ ์ ๋ณด์ ํจ๊ป ๊ฒฐ์ ์์ฒญ์ ๋ณด๋ธ๋ค. - ๋ฐํ๋๋ JSON ๊ฐ ์ค, PC์น ๊ฒฐ์ ์ ํด๋น๋๋ URL์ ๊ฐ์ ธ์จ๋ค.
- 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/ # ์๋ก์ด ์ฐฝ์์ ์คํํด์ผ๋ง ํ๋ค
์ ์์ ์ผ๋ก ์คํ๋์์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ํค๋๊ฐ ์๋ต์ผ๋ก ๋ฐํ๋๋ค.
http://localhost:3000/
์๋ ์ ์ํด๋ณด๋๋ก ํ์(PC ์น). ๋ค์๊ณผ ๊ฐ์ด ์นด์นด์ค ๊ฒฐ์ ํ์ด์ง๋ก redirect ๋ ๊ฒ์ด๋ค.
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 ์ฝ์์ ๋ค์๊ณผ ๊ฐ์ด ๊ฒฐ๊ณผ๊ฐ ๋ํ๋ ๊ฒ์ด๋ค.
์ฐธ๊ณ ๋ก, ๊ฒฐ๊ณผ ํ๋ฉด์ด ๊ผญ ๊ฐ์ ํ์๋ ์๋ค. ๊ทธ๋ฅ ์์ ๊ฐ์ด ์ถ๋ ฅ๋๊ธฐ๋ง ํ๋ฉด ๋๋ค. ์ฐธ๊ณ ๋ก ํ๊ธ์ด ๊นจ์ ธ๋ณด์ด๋ ๊ฑด postman์ผ๋ก ๋ ๋ฆฌ๋ฉด ์ ์์ ์ผ๋ก ๋์จ๋ค.
์ ์ด์ ๋ค์ ๊ฐ๋ฐ์ ํด ๋ณผ ์๊ฐ. ๋ค์๊ณผ ๊ฐ์ด 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 ํ๋ค๋ ์๋ฏธ์ด๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ์ค๋ณต ๊ฑฐ๋๋ผ๋ ๋ฉ์์ง๋ง์ ๋ณด๊ฒ ๋ ๊ฒ์ด๋ค.