[Nest.js] JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ, ์ธ๊ฐ€ ๊ทธ๋ฆฌ๊ณ  Multi part ์ด์•ผ๊ธฐ - โ‘ก ํšŒ์›๊ฐ€์ž…๊ณผ ์ธ์ฆ(feat. Access Token & Refresh Token)

2024. 1. 20. 20:54ใ†Back-End ์ž‘์—…์‹ค/Nest.js

728x90
๋ฐ˜์‘ํ˜•

 

 

์นด์นด์˜คํŽ˜์ด | ๋งˆ์Œ ๋†“๊ณ  ๊ธˆ์œตํ•˜๋‹ค

์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ ๋งํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

qr.kakaopay.com

 

 

 

 

 

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 




๐Ÿ—‚ ๋ชฉ์ฐจ

โœ… [Nest.js] JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ, ์ธ๊ฐ€ ๊ทธ๋ฆฌ๊ณ  Multi part ์ด์•ผ๊ธฐ - โ‘  ์ดˆ๊ธฐ๊ตฌ์„ฑ
โœ… [Nest.js] JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ, ์ธ๊ฐ€ ๊ทธ๋ฆฌ๊ณ  Multi part ์ด์•ผ๊ธฐ - โ‘ก ํšŒ์›๊ฐ€์ž…๊ณผ ์ธ์ฆ(feat. Access Token & Refresh Token)
โœ… [Nest.js] JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ, ์ธ๊ฐ€ ๊ทธ๋ฆฌ๊ณ  Multi part ์ด์•ผ๊ธฐ - โ‘ข Multer๋ฅผ ์ด์šฉํ•œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
โœ… 

 

 

 

GitHub - junyharang/nestTs-jwt-multer: Nest.ts๋ฅผ ์ด์šฉํ•œ JWT ์ธ์ฆ, ์ธ๊ฐ€์™€ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์—ฐ์Šต

Nest.ts๋ฅผ ์ด์šฉํ•œ JWT ์ธ์ฆ, ์ธ๊ฐ€์™€ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์—ฐ์Šต. Contribute to junyharang/nestTs-jwt-multer development by creating an account on GitHub.

github.com

 

 

 

๐Ÿš€ ํšŒ์›๊ฐ€์ž…๊ณผ ์ธ์ฆ(feat. Access Token & Refresh Token)

    ๐Ÿ”ฝ ์†Œ๊ฐœ

        ๐Ÿ“ฆ ๊ฐœ์š”

์ธ์ฆ, ์ธ๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ผ๋ จ์˜ ์ž‘์—… ์ค‘ ์ฒซ๋ฒˆ์งธ๋กœ Passport๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

Passport๋Š” ์ธ์ฆ Middleware(๋ฏธ๋“ค์›จ์–ด)๋กœ ๋‹ค์–‘ํ•œ ์ธ์ฆ ์ „๋žต๊ณผ ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ๋ฒ•์ด ์žฅ์ ์ด๊ณ , ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ณ , ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด์š”. ๋˜ํ•œ, OAuth, OpenID ๋“ฑ ๋‹ค์–‘ํ•œ ์ธ์ฆ Protocol(ํ”„๋กœํ† ์ฝœ)์„ ์ง€์›ํ•˜๋ฉฐ, ์†์‰ฝ๊ฒŒ ๋กœ๊ทธ์ธ ๋ฐ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” Package(ํŒจํ‚ค์ง€)์—์š”.

์ฃผ๋‹ˆ๊ฐ€ ๊ตฌํ˜„ํ•œ ์ธ์ฆ ์ ˆ์ฐจ๋Š” ์•„๋ž˜์™€ ๊ฐ™์•„์š”.

1๏ธโƒฃ ์ด์šฉ์ž์˜ ์ž๊ฒฉ์ฆ๋ช…(ID/Password, JWT ๋“ฑ)์„ ์ด์šฉ ๋ฐ ํ™•์ธํ•˜์—ฌ ์ธ์ฆ ์ฒ˜๋ฆฌ.
2๏ธโƒฃ ์ธ์ฆ ์ƒํƒœ ๊ด€๋ฆฌ (JWT์™€ ๊ฐ™์€ ํœด๋Œ€์šฉ Token ๋ฐœํ–‰)
3๏ธโƒฃ Request ๊ฒฝ๋กœ ์ฒ˜๋ฆฌ๊ธฐ์—์„œ ๋‚˜์ค‘์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธ์ฆ ๋œ ์ด์šฉ์ž์— ๋Œ€ํ•œ ์ •๋ณด ์ฒจ๋ถ€.

์ด๋ฆ„ ๋ฐ ์•”ํ˜ธ ์ธ์ฆ ๊ตฌํ˜„์€ ์ด์šฉ์ž์˜ ์ด๋ฆ„(ID)๊ณผ ์•”ํ˜ธ๋กœ ์ธ์ฆ(๋กœ๊ทธ์ธ)์„ ์ง„ํ–‰ํ• ๊ฑด๋ฐ, ์ธ์ฆ์ด ๋˜๋ฉด Server(์„œ๋ฒ„)๋Š” ์ธ์ฆ์„ ์ฆ๋ช…ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ Request(์š”์ฒญ)์—์„œ Authentication Header(์ธ์ฆ ํ—ค๋”)์— ์ „๋‹ฌ์ž Token(ํ† ํฐ)์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” JWT(Json Web Token)๋ฅผ ๋ฐœํ–‰ํ•˜๊ฒŒ ํ• ๊ฑฐ์—์š”. ๋˜ํ•œ, ์œ ํšจํ•œ JWT๊ฐ€ ํฌํ•จ๋œ ์š”์ฒญ์—๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ณดํ˜ธ ๊ฒฝ๋กœ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋ ค๊ณ  ํ•ด์š”.

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

 

    ๐Ÿ”ฝ ์ดˆ๊ธฐ ๊ตฌ์„ฑ

        ๐Ÿ“ฆ Package Download

์ตœ์ดˆ ์ด์šฉ์ž ID์™€ ์•”ํ˜ธ๋ฅผ ํ†ตํ•ด ์ธ์ฆ ๊ตฌํ˜„์„ ์œ„ํ•œ passport-local Package(ํŒจํ‚ค์ง€) ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ•ด๋ณผ๊ฒŒ์š”.
์ฐธ๊ณ ๋กœ @types/passport-local๋„ ์„ค์น˜ํ• ๊ฑด๋ฐ, ๊ทธ ์ด์œ ๋Š” Type Script ์ž‘์„ฑ์— ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.

npm i @nestjs/passport passport passport-local

 

npm i @types/passport-local --save-dev





 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

    ๐Ÿ”ฝ ํšŒ์› ๊ฐ€์ž…

        ๐Ÿ“ฆ Entity

ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด ์ตœ์ดˆ entity๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š”.

src/api/user/model/entity/user.entity.ts

 

src/api/user/model/entity/Role.ts


์ด์šฉ์ž์˜ ์—ญํ• ์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด Role ์ด๋ผ๋Š” Enum์„ ๋งŒ๋“ค์–ด ์ฃผ๊ณ , ๊ด€๋ฆฌ์ž์™€ ์ด์šฉ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ด ์ฃผ์—ˆ์–ด์š”.
์ฃผ๋‹ˆ๋Š” ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ๋Š” SQLite3 Embedded Mode์™€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” MariaDB๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •ํ•ด ์ฃผ์—ˆ๋Š”๋ฐ,
SQLite3 ๊ฐ™์€ ๊ฒฝ์šฐ Entity์— @Column type์„ enum์œผ๋กœ ์žก์„ ๊ฒฝ์šฐ ์ธ์‹์ด ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด๋กœ ์ธ์‹๋  ์ˆ˜ ์žˆ๊ฒŒ varchar๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.

๋งŒ์•ฝ MariaDB๋งŒ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด type์„ enum์œผ๋กœ ์žก์•„๋„ ๋ผ์š”.



        ๐Ÿ“ฆ Controller

src/api/authentication/controller/authentication.controller.ts


์œ„์™€ ๊ฐ™์ด ํšŒ์› ๊ฐ€์ž… Method(๋ฉ”์„œ๋“œ)๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

์ด๋ฒˆ์—๋Š” UserCreateRequestDto๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š”.



        ๐Ÿ“ฆ RequestDto

src/api/authentication/model/dto/request/signup-request.dto.ts


๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์‹ค์Šต์„ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์ด Email ์ฃผ์†Œ์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ด๋ฆ„, ๋‚˜์ด๋งŒ ๋ฐ›๋„๋ก ๋งŒ๋“ค๊ณ , toEntity()๋ฅผ ๋งŒ๋“ค์–ด RequestDto๋ฅผ Entity๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.







        ๐Ÿ“ฆ Service

์ด๋ฒˆ์—” Business Logic์„ ๋‹ด์„ Service Logic์„ ๊ตฌํ˜„ํ•ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”.
์ฃผ๋‹ˆ๋Š” SOLID 5์›์น™ ์ค€์ˆ˜๋ฅผ ์œ„ํ•ด Interface(์ธํ„ฐํŽ˜์ด์Šค)์™€ ๊ตฌํ˜„์ฒด๋ฅผ ๋‚˜๋ˆ„์–ด ๊ตฌํ˜„ํ•ด ๋ณผ๊ฑฐ์—์š”.

์ด์— ๋Œ€ํ•œ ์ƒ์„ธํ•œ ๊ฐœ๋…์„ ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์ด ๊ณณ์— ๊ด€์‹ฌ์„ ์ฃผ์„ธ์š”. 

 

[SOLID][Nest.js][Java + Spring] Interface๋ฅผ ํ™œ์šฉํ•œ ๊ฒฐํ•ฉ๋„ ๋ถ„๋ฆฌ (Interface๋ฅผ ์ด์šฉํ•œ Dependency Injection - DI)

๊ฐ์ฒด์ง€ํ–ฅ์˜ ์‚ฌ์‹ค๊ณผ ์˜คํ•ด : ์—ญํ•  ์ฑ…์ž„ ํ˜‘๋ ฅ ๊ด€์ ์—์„œ ๋ณธ ๊ฐ์ฒด์ง€ํ–ฅ COUPANG www.coupang.com "์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค." GitHub - junyharang-cod

junyharang.tistory.com



src/api/authentication/service/authentication.service.ts

 

npm i bcrypt @types/bcrypt

 

common/config/environment/.env.local.yml

 

src/api/authentication/service/authentication.service-impl.ts


์œ„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณด๋ฉด ์ตœ์ดˆ 15๋ฒˆ์งธ ์ค„์— ์ด์šฉ์ž ํšŒ์›๊ฐ€์ž… ์ •๋ณด๊ฐ€ ๋‹ด๊ธด DTO ๊ฐ’์ด Null์ธ์ง€๋ฅผ ์ฒดํฌํ•ด์ฃผ๊ณ , Null์ด๋ผ๋ฉด ๋ฌธ์ œ ๋ฐœ์ƒ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋ณด๋‚ด์ฃผ๊ฒŒ ํ–ˆ์–ด์š”.

37๋ฒˆ์งธ ์ค„์—๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ Hasing ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•ด EncryptUtil์˜ hashingEncrypt()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด์‹ฑํ•  ๊ตฌ๋ถ„๊ฐ’๊ณผ ํ•ด์‹ฑ ๋Œ€์ƒ๊ฐ’์„ ์ „๋‹ฌํ•ด ์ฃผ์—ˆ์–ด์š”.

src/common/util/encrypt.util.ts

728x90


์ด ๋•Œ, division(๊ตฌ๋ถ„)๊ฐ’์ด password์ด๋ฉด 9 ~ 11๋ฒˆ์งธ ์ค„์ด ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ข€ ๋” ์•ˆ์ „ํ•œ ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•œ Salt ๊ฐ’์„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.
์ด๋ฅผ ์œ„ํ•ด common/config/environment/.env.local.yml ์— bcrypt.salt์— ๋ฌธ์ž์—ด๋กœ Salt๊ฐ’์„ ๋„ฃ์–ด์คฌ๊ณ , configuration().brypt.salt๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ฌธ์ž์—ด์„ ๋ถˆ๋Ÿฌ์™€์„œ Salt ๊ฐ’์ด bcrypt.hash()์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

์ฐธ๊ณ ๋กœ bcrypt์˜ hash()์˜ salt ๊ฐ’์€ ์ •์ˆ˜๋งŒ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์— parseInt()๋กœ ๋ฌธ์ž์—ด์„ ์ •์ˆ˜๋กœ ๋ฐ”๊พธ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.


๊ทธ๋ฆฌ๊ณ , 10๋ฒˆ์งธ ์ค„์—์„œ ์ด์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ Salt๊ฐ’๊ณผ ๋ฐ˜์ฃฝํ•˜์—ฌ bcrypt ์•”ํ˜ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด์šฉํ•œ Hasing ์•”ํ˜ธํ™”๋ฅผ ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋ฆฌ๊ณ , ์š”์ฒญ DTO์— ๋‹ด๊ธด ์ด์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ Email ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํ•ด๋‹น ๊ฐ’์ด ์žˆ๋Š”์ง€ ์กฐํšŒํ•˜๊ณ , ์žˆ๋‹ค๋ฉด ์ด๋ฏธ ๋“ฑ๋ก๋œ Email์ด๋ผ๊ณ  ๋ฌธ์ œ ๋ฐœ์ƒ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ฃผ๊ฒŒ ํ•˜์˜€์–ด์š”.

45๋ฒˆ์งธ ์ค„์—์„œ ์œ„์— ๋ชจ๋“  ๋ฌธ์ œ ๋ฐœ์ƒ ์ƒํ™ฉ์„ ํ†ต๊ณผํ•˜์˜€๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ด๋„ ๋˜๊ฒ ๋‹ค๊ณ  ํŒ๋‹จ๋œ ๋ฐ” ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— Entity๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ Promise<User> ์ž๋ฃŒํ˜• ํƒ€์ž…์„ ๊ฐ–์€ saveUserResult ๋ณ€์ˆ˜์˜ ๊ฐ’์ด Null์ธ์ง€๋ฅผ ํ™•์ธํ•œ ๋’ค Null์ด ์•„๋‹ˆ๋ผ๋ฉด ํ•ด๋‹น ๋ณ€์ˆ˜์— ๋‹ด๊ธด ์ด์šฉ์ž ID(๊ณ ์œ  ๋ฒˆํ˜ธ)๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ •์ƒ ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ๊ฐ€ ๋œ ๊ฒƒ์„ ์‘๋‹ตํ•˜๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

 

 

 

 

        ๐Ÿ“ฆ Module

src/api/authentication/module/authentication.module.ts


์œ„์™€ ๊ฐ™์ด ์ธ์ฆ ๊ด€๋ จ Module(๋ชจ๋“ˆ)์„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

 

src/app.module.ts


๊ทธ๋ฆฌ๊ณ  app.module.ts์— 25๋ฒˆ์งธ ์ค„๊ณผ ๊ฐ™์ด ์œ„์—์„œ ๋งŒ๋“  ์ธ์ฆ ๋ชจ๋“ˆ์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ์–ด์š”.

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

    ๐Ÿ”ฝ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ with.passport - ์†Œ๊ฐœ

        ๐Ÿ“ฆ ๊ฐœ์š”

JWT์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•˜์‹œ๋‹ค๋ฉด ๋จผ์ € ์ด ๊ณณ์— ๊ด€์‹ฌ์„ ์ฃผ์„ธ์š”.

 

[์ •๋ณด๋ณด์•ˆ] JWT(JSON Web Token) ์ด๋ž€?

๐Ÿš€ JWT(JSON Web Token) ๐Ÿ”ฝ ๊ฐœ์š” ์ด์šฉ์ž๊ฐ€ Login์„ ํ• ๋•Œ, ํ•ด๋‹น ํšŒ์›์˜ ๊ณ ์œ ํ•œ Token์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ๋•Œ `JWT`๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด์—์š”. ์ธ์ฆ์—๋Š” ๋Œ€ํ‘œ์  3์ธ๋ฐฉ์ด ์žˆ๋Š”๋ฐ, `Cookie, Session, Token` ๋ฐฉ์‹์ด ์žˆ๋Š”

junyharang.tistory.com


์ด๋ฒˆ์—๋Š” JWT๋ฅผ ์ด์šฉํ•œ Login(๋กœ๊ทธ์ธ) ๊ตฌํ˜„์„ ํ•ด๋ณด๋ ค๊ณ  ํ•˜๋Š”๋ฐ, Passport๋ผ๋Š” Module(๋ชจ๋“ˆ)์„ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

๊ทธ ์ด์œ ๋Š” ํ•ด๋‹น ๋ชจ๋“ˆ์ด JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ, ์ธ๊ฐ€ ์ฒ˜๋ฆฌ ๋“ฑ์˜ ๊ณผ์ •์„ ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.




        ๐Ÿ“ฆ Module ์†Œ๊ฐœ

โ— @nestjs/jwt : Nest.js์—์„œ JWT๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” Module.
โ— @nestjs/passport : Nest.js์—์„œ Passport๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” Module.
โ— Passport : Passport Module.
โ— passport-jwt : JWT Module.

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

    ๐Ÿ”ฝ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ with.passport - ๊ตฌํ˜„

        ๐Ÿ“ฆ ์ดˆ๊ธฐ ๊ตฌ์„ฑ

์ตœ์ดˆ ์œ„ ์†Œ๊ฐœ๋œ ๋ชจ๋“ˆ ์„ค์น˜๋ถ€ํ„ฐ ์ง„ํ–‰ํ•ด ๋ณผ๊ฒŒ์š”.

npm install @nestjs/jwt @nestjs/passport passport passport-jwt --save



common/config/environment/.env.local.yml


์ด๋ฒˆ์—” ์œ„์™€ ๊ฐ™์ด JWT ๊ด€๋ จํ•˜์—ฌ secret ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์—ˆ์–ด์š”.


src/api/authentication/module/authentication.module.ts


ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ฐ’์„ ๋‹ด์€ .env.yml์— ํ™˜๊ฒฝ์— ๋งž์ถ˜ jwt ๊ด€๋ จ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.
14๋ฒˆ์งธ ์ค„์— defaultStrategy ๋ฌธ์ž์—ด ๊ฐ’์€ .env.yml์— ๊ฐ€์ ธ์˜ฌ ๊ฐ’์˜ ์ตœ์ƒ์œ„ ์ด๋ฆ„์„ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•ด์š”.

common/config/environment/.env.local.yml

์ฃผ๋‹ˆ๋Š” ์œ„์™€ ๊ฐ™์ด 13๋ฒˆ์งธ ์ค„์— jwt๋ฅผ ์ตœ์ƒ์œ„ ์ด๋ฆ„์œผ๋กœ ์žก์•„ ์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— jwt๋ผ๊ณ  ํ•ด ์ค€๊ฒƒ์ด์—์š”.

12๋ฒˆ์งธ ์ค„ secret์€ Token(ํ† ํฐ)์„ ๋งŒ๋“ค ๋•Œ ์ด์šฉํ•˜๋Š” ๋น„๋ฐ€ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”.
ํ‰๋ฌธ์œผ๋กœ ์ž…๋ ฅํ•ด๋„ ๋˜์ง€๋งŒ, ์ฃผ๋‹ˆ๋Š” ํ‰๋ฌธ์„ base 64๋กœ Encodingํ•œ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์—ˆ์–ด์š”.

13๋ฒˆ์งธ ์ค„ signOption์€ ์„œ๋ช…์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ๊ฒƒ์ธ์ง€ ์ •์˜ํ•ด ์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”.
๊ทธ๋ฆฌ๊ณ , ExpireIn์€ Token ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๋„ฃ์–ด์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”. ์ฃผ๋‹ˆ๋Š” 7์ผ๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.

src/api/authentication/module/authentication.module.ts


12๋ฒˆ์งธ ์ค„์— ๋‹ค๋ฅธ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ๋Š”๋ฐ, ๋ฐ”๋กœ PassportModule์•ˆ์— register()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ทธ ์•ˆ์— ๊ธฐ๋ณธ ์ „๋žต ๊ฐ’์œผ๋กœ jwt๋ผ๋Š” ๋ฌธ์ž์—ด์„ ์ „๋‹ฌํ•˜์—ฌ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.






 

        ๐Ÿ“ฆ Controller

src/api/authentication/controller/authentication.controller.ts


16 ~ 19๋ฒˆ์งธ ์ค„๊ณผ ๊ฐ™์ด ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ Handler(ํ•ธ๋“ค๋Ÿฌ)๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

๋ถ„์„ํ•ด ๋ณด๋ฉด @Body ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ HTTP ์š”์ฒญ Body(๋ณธ๋ฌธ)์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœํ•˜๋„๋กํ•ด ์ฃผ์—ˆ์–ด์š”.
๊ทธ๋Ÿฐ ๋’ค ValidationPipe๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SigninRequestDto ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ณ , ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ง„ํ–‰๋˜๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

Promise<DefaultResponse<String>>์€ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๊ณ , string์„ ๋‹ด์•„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ด์œ ๋Š” ํ† ํฐ์„ ๋ฐ˜ํ™˜ํ•ด ์ฃผ๊ธฐ ์œ„ํ•จ์ด์—์š”.

 

 

 

        ๐Ÿ“ฆ SignInRequestDto

๋กœ๊ทธ์ธํ•  ๋•Œ ์‚ฌ์šฉํ•  ์š”์ฒญ DTO๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š”.

src/api/authentication/model/dto/request/signin-request.dto.ts


๋กœ๊ทธ์ธํ•  ๋•Œ, ID ํ˜•์‹์€ Email๋กœ ๋ฐ›๊ณ , ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•œ ์š”์ฒญ DTO๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.
์œ„์— Decorator(๋ฐ์ฝ”๋ ˆ์ดํ„ฐ)๋“ค์„ Pipe๋ฅผ ์ด์šฉํ•œ Validation ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.



 

 

 

        ๐Ÿ“ฆ Service

src/api/authentication/service/authentication.service.ts




src/api/authentication/service/authentication.service-impl.ts


์œ„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณด๋ฉด ์ตœ์ดˆ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋“ค์–ด์˜จ ์š”์ฒญ DTO์— Email ๊ฐ’์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„œ ํ•ด๋‹น ์ด์šฉ์ž ์ •๋ณด ์ค‘ Email ๊ฐ’๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ’์„ ๊บผ๋‚ด findByUSerInfo ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๋‹ด๊ฒŒ ํ•˜์˜€์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค 48๋ฒˆ์งธ ์ค„์—์„œ ํ•ด๋‹น ๊ฐ’์ด Null์ด ์•„๋‹Œ์ง€ ํ™•์ธํ•˜๊ณ , ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜จ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ Hashing(ํ•ด์‹ฑ)ํ•œ ๋’ค ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ํ•ด์‹ฑ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ’๊ณผ ๊ฐ™์€์ง€ ํ™•์ธํ•˜์—ฌ ๋ชจ๋‘ ์ฐธ์ด๋ผ๋ฉด JWT Payload์— ํ•ด๋‹น ์ด์šฉ์ž์˜ Email, ์ด๋ฆ„, ๋‚˜์ด๋ฅผ ๋‹ด์•„ 
jwtService()๋ฅผ ์ด์šฉํ•˜์—ฌ Payload๋ฅผ ์ด์šฉํ•˜์—ฌ ์„œ๋ช…ํ•œ ๋’ค ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” JWT Access Token์„ ๋ฐœ๊ธ‰ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

        ๐Ÿ“ฆ Guard

src/api/authentication/guard/jwt.authentication.guard.ts


์œ„ ํด๋ž˜์Šค๋Š” Nest.js์—์„œ ์‚ฌ์šฉ๋˜๋Š” Guard ํด๋ž˜์Šค์—์š”.
Guard๋Š” ์š”์ฒญ์ด Router(๋ผ์šฐํ„ฐ) Handler(ํ•ธ๋“ค๋Ÿฌ)์—๊ฒŒ ์ „๋‹ฌ๋˜๊ธฐ ์ „์— ์ž‘๋™ํ•˜์—ฌ ํŠน์ • ์กฐ๊ฑด์„ ๊ฒ€์‚ฌํ•˜๊ณ , ์š”์ฒญ์„ ๊ณ„์† ์ง„ํ–‰ํ• ์ง€ ๋ง‰์„์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•ด์š”.

๋˜ํ•œ, Guard๋Š” Nest.js Application(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜)์—์„œ ์ธ์ฆ, ๊ถŒํ•œ ๋ถ€์—ฌ, ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด ํด๋ž˜์Šค๋Š” AuthGuard๋ฅผ ์ƒ์† ๋ฐ›๊ฒŒ ๋˜๋Š”๋ฐ, AuthGuard("jwt")๋ฅผ ํ†ตํ•ด ๋ถ€๋ชจ ๊ฐ์ฒด์— ๋ฌธ์ž์—ด jwt๋ฅผ ์ „๋‹ฌํ•˜์—ฌ JWT ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋Š” Guard๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ๊ฑฐ์—์š”. AuthGuard ํด๋ž˜์Šค๋Š” Nest.js์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ Guard ์ค‘ ํ•˜๋‚˜์ด๊ณ , ํŠน์ • ์ „๋žต์„ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. JWT ์ „๋žต์€ JWT๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•œ ๋ถ€๋ถ„์ด์—์š”.

๊ฒฐ๊ตญ ์ด ํด๋ž˜์Šค๋Š” ์ฃผ์–ด์ง„ ์š”์ฒญ์— ๋Œ€ํ•ด JWT ํ† ํฐ์˜ ์œ ํšจ์„ฑ์„ ํ™•์ธํ•˜๊ณ , ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•˜์—ฌ ํ•ด๋‹น ์š”์ฒญ์„ ๊ณ„์† ์ง„ํ–‰ํ• ์ง€ ๋ง‰์„์ง€ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค์—์š”.

๋งŒ์•ฝ ํ† ํฐ์ด ์œ ํšจํ•˜๋‹ค๋ฉด ์š”์ฒญ ์ฒ˜๋ฆฌ๋Š” ๊ณ„์† ์ง„ํ–‰๋˜๊ณ , ์•„๋‹ˆ๋ผ๋ฉด ํ•ด๋‹น ์š”์ฒญ์€ ๊ฑฐ๋ถ€๋  ๊ฑฐ์—์š”.

Guard๋Š” ์ฃผ๋กœ ๋ผ์šฐํ„ฐ ํ•ธ๋“ค๋Ÿฌ์˜ @UseGuards() Decorator(๋ฐ์ฝ”๋ ˆ์ดํ„ฐ)๋ฅผ ๋ถ™ํ˜€ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ต๋‹ˆ๋‹ค.

 

 

 

        ๐Ÿ“ฆ strategy

src/api/authentication/jwt/strategy/jwt.strategy.ts

 

src/api/authentication/jwt/jwt.payload.ts



์œ„ ํด๋ž˜์Šค๋Š” Passport๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JWT๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ „๋žต์„ ์ •์˜ํ•œ ํด๋ž˜์Šค๋กœ ์ฃผ๋กœ ์ธ์ฆ ๋ฏธ๋“ค์›จ์–ด๋กœ ์‚ฌ์šฉ๋˜๋Š” ํด๋ž˜์Šค์—์š”.
์ด ํด๋ž˜์Šค๋Š” JWT๋ฅผ ์ถ”์ถœํ•˜๊ณ , ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋ฉฐ, ์ธ์ฆ์ด ์„ฑ๊ณตํ•˜๋ฉด ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜จ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ผ์š”.

์ฐธ๊ณ ๋กœ validate() ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ JwtPayload๋Š” ์œ„์™€ ๊ฐ™์ด interface๋กœ ์ •์˜ ํ•ด ์ฃผ์—ˆ๊ณ , ๋ฌธ์ž์—ด ํƒ€์ž…์˜ email ๋ณ€์ˆ˜๊ฐ€ ์ •์˜๋œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.

ํ•œ๋ฒˆ ์ž์„ธํžˆ ๋ถ„์„ํ•ด ๋ณผ๊นŒ์š”?

์ตœ์ดˆ 9๋ฒˆ์งธ ์ค„์€ JwtStrategry ํด๋ž˜์Šค๊ฐ€ PassportStrategy ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•œ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์ด์š”. PassportStrategy๋Š” Passport ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ Strategy(์ „๋žต)์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ํด๋ž˜์Šค์—์š”. Strategy๋Š” Passport ์ „๋žต์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ, "jwt"๋Š” ์ด ์ „๋žต์˜ ์ด๋ฆ„์„ ๋‚˜ํƒ€๋‚ด๋Š”๋ฐ, ์ด ๋•Œ, ์ฃผ๋‹ˆ๋Š” .env.yml์— ์ตœ์ƒ์œ„ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด ์ฃผ์—ˆ์–ด์š”.

common/config/environment/.env.local.yml


์ฆ‰, ์œ„ ์ฝ”๋“œ์˜ 13๋ฒˆ์งธ ์ค„์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด ๋‘” ๊ฒƒ์ด์—์š”.

14 ~ 19๋ฒˆ์งธ ์ค„์€ ์ƒ์„ฑ์ž์ธ๋ฐ, ์—ฌ๊ธฐ์„œ UserService ๋ฐ ConfigService๋ฅผ ์ฃผ์ž…๋ฐ›๊ณ , super()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ถ€๋ชจ ํด๋ž˜์Šค์ธ PassportStrategy์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๊ณ , ์ด ๋•Œ, JWT ์ „๋žต์— ํ•„์š”ํ•œ ์„ค์ •์„ ์ œ๊ณตํ•˜๋„๋ก ํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.

โœ… jwtFromRequest: JWT๋ฅผ ์–ด๋–ป๊ฒŒ ์ถ”์ถœํ• ์ง€ ์ •์˜. 15๋ฒˆ์งธ ์ค„์—์„œ๋Š” Bearer Token์„ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•ด fromAuthHeaderAsBearerToken() ์‚ฌ์šฉ.

โœ… ignoreExpiration: ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ๋ฌด์‹œํ• ์ง€ ์—ฌ๋ถ€ ์ •์˜.

โœ… secretOrKey: JWT์˜ Secret Key(๋น„๋ฐ€ํ‚ค) ์ œ๊ณต. 17๋ฒˆ์งธ ์ค„์—์„œ๋Š” configService๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์„ค์ •์—์„œ ๋น„๋ฐ€ํ‚ค๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ์ •์˜.

21 ~ 24๋ฒˆ์งธ ์ค„ validate()๋Š” Passport ์ „๋žต์—์„œ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๋ฉ”์„œ๋“œ validate ๋ฉ”์„œ๋“œ์—์š”.
์ด ๋ฉ”์„œ๋“œ๋Š” JWT Access Token์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ , ์œ ์š”ํ•œ ๊ฒฝ์šฐ ํ•ด๋‹น ์š”์ฒญ ์ด์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•ด์š”.

โœ… jwtPayload: JWT์—์„œ ์ถ”์ถœํ•œ Payload(Clam ์ •๋ณด)
โœ… done: Passport์—์„œ ์ œ๊ณตํ•˜๋Š” Call back(์ฝœ๋ฐฑ) ํ•จ์ˆ˜๋กœ ์ธ์ฆ ์„ฑ๊ณต ์‹œ ํ•ด๋‹น ์š”์ฒญ ์ด์šฉ์ž ์ •๋ณด ์ „๋‹ฌ.

validate()๋Š” ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜จ Email ์ฃผ์†Œ๋ฅผ ์ด์šฉํ•˜์—ฌ UserService๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์ด์šฉ์ž ์ •๋ณด๋ฅผ ์ฐพ๊ฒŒ ๋˜๊ณ , ์ด์šฉ์ž ์ •๋ณด๊ฐ€ ์—†์œผ๋ฉด UnauthorizedException์„ throw(๋˜์ง€๋‹ค)ํ•˜์—ฌ ์ธ์ฆ ์‹คํŒจ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‘๋‹ตํ•ด ์ฃผ๊ฒŒ ๋ผ์š”. ๋งŒ์•ฝ ์ธ์ฆ ์„ฑ๊ณต ์‹œ ์ด์šฉ์ž ์ •๋ณด๋ฅผ done()์„ ํ†ตํ•ด ์ „๋‹ฌํ•˜๊ฒŒ ํ•ด ์ค€๋‹ต๋‹ˆ๋‹ค.

 

๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
ConfigService๋ž€?

ConfigService๋Š” Nest.js์—์„œ ํ™˜๊ฒฝ ์„ค์ • ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” Service.
์ฃผ๋กœ .env File์ด๋‚˜, ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์ •๋œ ๊ฐ’๋“ค์„ ์ฝ์–ด์˜ค๊ณ , Application ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ ์ œ๊ณต.

์ผ๋ฐ˜์ ์œผ๋กœ ConfigService๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์ • ๊ฐ’์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด @nestjs/config Module์„ ์„ค์น˜ํ•˜๊ณ , ์„ค์ • ํ•„์š”.
์ฐธ๊ณ  ์ž๋ฃŒ: https://junyharang.tistory.com/537   

ConfigService๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๋ณต๋˜๋Š” ์„ค์ • ๊ฐ’์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋‚˜ .env File์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ์„ค์ •์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด Service๋Š” Application์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์„ค์ • ์ •๋ณด๋ฅผ ํ•˜๋‚˜์˜ ๊ณณ์—์„œ ํ†ตํ•ฉ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ธฐ๋Šฅ ์ œ๊ณต.

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

    ๐Ÿ”ฝ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ - Refresh Token

์œ„์—์„œ ์ง„ํ–‰ํ•œ ์ธ์ฆ ๊ธฐ๋Šฅ์€ JWT์˜ Access Token๋งŒ ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌ๋œ ๊ธฐ๋Šฅ์ด์—์š”.

JWT์˜ Access Token๊ณผ Refresh Token ์ด์•ผ๊ธฐ๋Š” ์ด ๊ณณ์— ๊ด€์‹ฌ์„ ์ฃผ์„ธ์š”.

 

[์ •๋ณด๋ณด์•ˆ] JWT(JSON Web Token) ์ด๋ž€?

๐Ÿš€ JWT(JSON Web Token) ๐Ÿ”ฝ ๊ฐœ์š” ์ด์šฉ์ž๊ฐ€ Login์„ ํ• ๋•Œ, ํ•ด๋‹น ํšŒ์›์˜ ๊ณ ์œ ํ•œ Token์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ๋•Œ `JWT`๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด์—์š”. ์ธ์ฆ์—๋Š” ๋Œ€ํ‘œ์  3์ธ๋ฐฉ์ด ์žˆ๋Š”๋ฐ, `Cookie, Session, Token` ๋ฐฉ์‹์ด ์žˆ๋Š”

junyharang.tistory.com


์ด๋ฒˆ์—” ์ด์šฉ์ž๊ฐ€ ์ •์ƒ ์ธ์ฆ์„ ์™„๋ฃŒํ•˜๋ฉด Access Token๊ณผ Refresh Token์ด ๊ฐ™์ด ๋ฐœ๊ธ‰๋˜๋„๋ก ๊ตฌํ˜„ํ•ด ๋ณผ๊ฒŒ์š”.

        ๐Ÿ“ฆ Controller

src/api/authentication/controller/authentication.controller.ts



 

 

 

 

        ๐Ÿ“ฆ AuthenticationService

src/api/authentication/service/authentication.service-impl.ts


์œ„์— ์ฝ”๋“œ์—์„œ ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐ ํ•ด ๋ณผ๊ฒŒ์š”.

์ตœ์ดˆ 20, 21, 25, 26๋ฒˆ์งธ ์ค„ ๋‚ด์šฉ์ด ์ถ”๊ฐ€ ๋˜์—ˆ์–ด์š”.
20๋ฒˆ์งธ ์ค„์€ .env ํŒŒ์ผ์˜ ๊ฐ€์ ธ์˜ฌ ๊ฐ’๋“ค์˜ ์ตœ์ƒ์œ„ ์ด๋ฆ„์„ ์ •์˜ํ•ด ์ฃผ์—ˆ์–ด์š”.

๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
JwtConfig๋ž€?
JwtConfig๋Š” JWT ์„ค์ •์„ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด.

// jwt.config.ts

export interface JwtConfig {
  accessTokenSecret: string;
  accessTokenExpireIn: number;
  refreshTokenSecret: string;
  refreshTokenExpireIn: number;
  saltOrRounds: number;
}

export default (): JwtConfig => ({
  accessTokenSecret: process.env.JWT_ACCESS_TOKEN_SECRET,
  accessTokenExpireIn: parseInt(process.env.JWT_ACCESS_TOKEN_EXPIRE_IN, 10),
  refreshTokenSecret: process.env.JWT_REFRESH_TOKEN_SECRET,
  refreshTokenExpireIn: parseInt(process.env.JWT_REFRESH_TOKEN_EXPIRE_IN, 10),
  saltOrRounds: parseInt(process.env.JWT_SALT_OR_ROUNDS, 10),
});

โœ… JwtConfig๋Š” Interface์ด๋ฉฐ, Access Token ๋ฐ Refresh Token์— ๋Œ€ํ•œ ๋น„๋ฐ€ ํ‚ค, ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„, ์•”ํ˜ธํ™” ๊ฐ•๋„ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ๋ณด๊ด€.

โœ… export default () => ({ ... }): ์„ค์ • ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜. ์ด๋Š” Nest.js์˜ ConfigModule์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์„ค์ • Loading์„ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ตฌ์กฐ.

โœ… process.env๋ฅผ ์ด์šฉํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ฝ๊ธฐ: process.env๋ฅผ ์ด์šฉํ•˜์—ฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ JWT ์„ค์ • ๊ฐ’ ์ฝ์Œ.

โœ… partInt๋ฅผ ์ด์šฉํ•œ ์ˆซ์ž ํ˜•๋ณ€ํ™˜: ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ์ฝ์€ ๊ฐ’๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌธ์ž์—ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ˆซ์ž๋กœ ํ˜•๋ณ€ํ™˜์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์‚ฌ์šฉ.

JwtConfig๋Š” Applicaion์—์„œ JWT๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•„์š”ํ•œ ์—ฌ๋Ÿฌ ์„ค์ •๋“ค์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ConfigModule์„ ํ†ตํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ๋ถ€ํ„ฐ ํ•ด๋‹น ์„ค์ • Load.

์ด๋ฅผ ํ†ตํ•ด Application ๋™์ž‘์„ ๋™์ ์œผ๋กœ ์กฐ์ •.

JwtService๋ž€?

JwtService๋Š” Nest.js์—์„œ ์ œ๊ณตํ•˜๋Š” JWT ๊ด€๋ จ ๊ธฐ๋Šฅ ๋‹ด๋‹น Service.
์ฃผ๋กœ ์ด์šฉ์ž ํ˜น์€ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ์— ์‚ฌ์šฉ๋˜๋ฉฐ, JWT ์ƒ์„ฑ, ๊ฒ€์ฆ ๋“ฑ์˜ ์ž‘์—… ์ˆ˜ํ–‰.
์—ฌ๋Ÿฌ ์„ค์ • Option์„ ์ œ๊ณตํ•˜๋ฉฐ, JWT์˜ ๋™์ž‘ ์กฐ์ • ๊ฐ€๋Šฅ.


20๋ฒˆ์งธ ๋‚ด์šฉ์€ .env ํŒŒ์ผ์˜ JWT ๊ด€๋ จ ์„ค์ • ๋‚ด์šฉ์„ ์ฝ์–ด ๋“œ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋‚ด์šฉ์œผ๋กœ ์ตœ์ƒ์œ„ ์ด๋ฆ„์„ get()์— ์ „๋‹ฌํ•ด์คŒ์œผ๋กœ ํ•ด๋‹น ๋‚ด์šฉ์˜ ๋ชจ๋“  ์„ค์ •๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.

21๋ฒˆ์งธ ๋‚ด์šฉ์€ 20๋ฒˆ์งธ์—์„œ ๋ฐ›์•„์˜จ ์„ค์ •๊ฐ’ ์ค‘ SaltOrRouds๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์™€ ์ƒ์ˆ˜ํ˜• ๋ณ€์ˆ˜์— ๋‹ด์•„์ค€ ๋ถ€๋ถ„์ด์—์š”.

23 ~ 27๋ฒˆ์งธ ์ค„์€ ์ƒ์„ฑ์ž๋กœ ํ•ด๋‹น ์„œ๋น„์Šค ๋กœ์ง์—์„œ ์‚ฌ์šฉํ•  ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•ด ๋ช…์‹œํ•ด ์ฃผ์—ˆ๊ณ , ์ถ”๊ฐ€๋œ ๊ฐ์ฒด๋Š” ConfigService์™€ JwtService ๊ฐ์ฒด์—์š”.

src/api/authentication/service/authentication.service-impl.ts


์ด์ œ signIn()์— ๋Œ€ํ•ด ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ๋ถ„์„ํ•ด ๋ณผ๊ฒŒ์š”.

59๋ฒˆ์งธ ์ค„์—์„œ ๋กœ๊ทธ์ธ ์š”์ฒญ ์ด์šฉ์ž์˜ ์ •๋ณด๊ฐ€ ์ฐธ์ด๋ผ๋ฉด Access Token์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด 62 ~ 65๋ฒˆ์งธ ์ค„์„ ์ž‘์„ฑํ•ด ์ฃผ์—ˆ๊ณ , Refresh Token์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด 67 ~ 70๋ฒˆ์งธ ์ค„์„ ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

์ด ๋•Œ, ๊ฐ๊ฐ JWT ์„œ๋ช…์„ ํ•˜๋Š”๋ฐ, ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ์ด์šฉ์ž Email ์ฃผ์†Œ๋กœ ๋งŒ๋“  JwtPayload ๊ฐ์ฒด์™€ Access Token, Refresh Token ๋น„๋ฐ€ ํ‚ค ๊ทธ๋ฆฌ๊ณ , ์œ ํšจ ๊ธฐ๊ฐ„์„ ๊ฐ์ฒด๋กœ ๋„ฃ์–ด ์„œ๋ช…์„ ํ•œ ๊ฒƒ์„ accessToken, refreshToken ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๋‹ด์•„ ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค 72๋ฒˆ์งธ ์ค„์—์„œ ํ•ด๋‹น ๊ฐ’์„ ํ•ด์‹ฑํ•˜์—ฌ User Entity์— setRefreshToken()์„ ํ˜ธ์ถœํ•˜์—ฌ refreshToken ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด ์ฃผ๊ณ , ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์ˆ˜์ •์„ ์œ„ํ•ด 71๋ฒˆ์งธ ์ค„๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋ฆฌ๊ณ  73๋ฒˆ์งธ ์ค„์—๋Š” Refresh Token ๋งŒ๋ฃŒ ์ผ์‹œ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด ์ฃผ์—ˆ์–ด์š”.

๋˜ํ•œ, 74 ~ 80๋ฒˆ์งธ ์ค„์— Refresh Token๊ณผ Refresh Token ๋งŒ๋ฃŒ ์ผ์‹œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ณ , ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด ์ฃผ์—ˆ์–ด์š”.

src/api/authentication/service/authentication.service-impl.ts


์œ„ ๋‚ด์šฉ์€ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ ์‹œ๊ฐ์—์„œ jwt.refreshTokenExpireIn ์„ค์ •์— ์ •์˜ํ•œ ์‹œ๊ฐ„์„ ๋”ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์š”. ์ฆ‰, Refresh Token์˜ ๋งŒ๋ฃŒ ๋‚ ์งœ๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•œ private ๋ฉ”์„œ๋“œ์—์š”.

154๋ฒˆ์งธ ์ค„์—์„œ ํ˜„์žฌ ์‹œ๊ฐ์„ ๊ฐ€์ ธ์˜ฌ Date ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ๋’ค 156๋ฒˆ์งธ ์ค„์—์„œ .env.yml์—์„œ ๊ฐ€์ ธ์˜จ jwt.refreshTokenExpireIn์— ๋Œ€ํ•œ ๊ฐ’์„ ๋ฌธ์ž์—ด๋กœ ๊ฐ€์ ธ์˜ค๊ฒŒ ํ•œ ๋’ค parstInt()๋ฅผ ์ด์šฉํ•ด ์ •์ˆ˜๋กœ ํ˜• ๋ณ€ํ™˜ํ•ด ์ฃผ์—ˆ์–ด์š”. currnetDate.getTime()์„ ์ด์šฉํ•˜์—ฌ ํ˜„์žฌ ๋‚ ์งœ์˜ Timestamp(ํƒ€์ž„ ์Šคํƒฌํ”„)๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•ด ์ฃผ๊ณ , ์ด ๊ฐ’์„ ๊ฐ€์ ธ์˜จ Refresh Token ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„(๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„)์™€ ๋”ํ•˜์—ฌ ์ƒˆ๋กœ์šด Date ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค ๊ณ„์‚ฐ๋œ Refresh Token ๋งŒ๋ฃŒ ์ผ์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

src/api/authentication/service/authentication.service-impl.ts


75, 77๋ฒˆ์งธ ์ค„์€ ์ด์šฉ์ž๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ๊ฒฝ์šฐ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก๋˜๋Š” JWT Token๊ณผ ๊ด€๋ จ๋œ ์‘๋‹ต์„ ๋‚ด๋ ค์ฃผ๊ธฐ ์œ„ํ•œ ๋ถ€๋ถ„์ด์—์š”.

75๋ฒˆ์งธ ์ค„์€ HTTP Reponse Header์— Authorization์ด๋ผ๋Š” Key๊ฐ’์œผ๋กœ JWT ์ „์†ก์„ ์œ„ํ•˜์—ฌ ๋งŒ๋“  ๊ฒƒ์ธ๋ฐ, ์ด ๋•Œ, Bearer scheme(์Šคํ‚ด)์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ณ , Access Token๊ณผ Refresh Token์„ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด ,(์‰ผํ‘œ)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฌธ์ž์—ด๋กœ ํ•ฉ์นœ ๋’ค ์ ‘๋‘์‚ฌ(Prefix)์— Bearer ์ด๋ผ๋Š” ๋ฌธ์ž์—ด๊ณผ ํ•จ๊ป˜ Value๋กœ ์ „๋‹ฌ๋˜๋„๋ก ํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.

77๋ฒˆ์งธ ์ค„์€ HTTP Response Cookie๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ cookieService์˜ setRefreshToken()์„ ํ˜ธ์ถœํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ฃผ์—ˆ์–ด์š”.

์œ„ 73 ~ 75๋ฒˆ์งธ ์„ค์ •์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ Authorization Header๋‚˜, Cookie๋ฅผ ํ†ตํ•ด ํ† ํฐ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”. ์ด๋ ‡๊ฒŒ ์ „๋‹ฌ ๋ฐ›์€ ํ† ํฐ์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋‹ค์Œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉ๋˜์–ด ์งˆ ๊ฒƒ์ด๊ณ , ์„œ๋ฒ„๋Š” ์ด๋ฅผ ํ†ตํ•ด ์ด์šฉ์ž์— ๋Œ€ํ•œ ์ธ์ฆ์„ ์‹œ๋„ํ•˜๊ฒŒ ๋  ๊ฑฐ์—์š”. JWT์˜ ๊ฒฝ์šฐ Authorization Header์— ํ† ํฐ์„ ๋„ฃ๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด์ง€๋งŒ, Cookie๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์ด ์žˆ์–ด์š”.

์ฃผ๋‹ˆ๋Š” ์—ฐ์Šต์„ ์œ„ํ•ด ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•ด ๋ณด์•˜์–ด์š”.

์ด๋ ‡๊ฒŒ ํ•œ ๋’ค 79๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด Access Token์„ ์ƒ์„ฑํ•˜๊ณ , Refresh Token์„ ๋‹ด์•„ ์‘๋‹ตํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•ด ์ฃผ์—ˆ์–ด์š”.

 

src/common/util/encrypt.util.ts

 

ํšŒ์› ๊ฐ€์ž… ๋•Œ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑํ™”๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ–ˆ์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ์—๋Š” Refresh Token ํ•ด์‹ฑํ™”์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ•ด๋ณผ๊ฒŒ์š”. ์ตœ์ดˆ ๊ตฌ๋ถ„๊ฐ’์ด token์ด๋ฉด ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ Refresh Token๊ณผ Salt ๊ฐ’์„ ๋ฐ˜์ฃฝํ•˜์—ฌ ํ•ด์‹ฑํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ค„๊ฑฐ์—์š”.

 

 

 

        ๐Ÿ“ฆ CookieService

src/api/common/cookie/service/cookie.service-impl.ts

์œ„ ์ฝ”๋“œ๋Š” Cookie ๊ด€๋ จ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๋งŒ๋“  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด์—์š”.

์ตœ์ดˆ 8๋ฒˆ์งธ ์ค„์€ configService๋ฅผ ํ†ตํ•ด ์„ค์ • ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜จ cookie.domain ๊ฐ’์„ DOMAIN ์ด๋ผ๋Š” ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๋„ฃ๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.
์ด๋ ‡๊ฒŒ ํ•ด ์คŒ์œผ๋กœ์จ ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž์—์„œ ์ดˆ๊ธฐํ™” ๋˜์–ด ํด๋ž˜์Šค ๋‚ด์—์„œ this.DOMAIN์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ๋˜์—ˆ์–ด์š”.

10๋ฒˆ์งธ ์ค„์€ ์ƒ์„ฑ์ž์ธ๋ฐ, ConfigService๋ฅผ ์ฃผ์ž… ๋ฐ›์•„ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜ configService์— ํ• ๋‹นํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ๋ช…์‹œํ•ด ์ฃผ์—ˆ์–ด์š”.

12 ~ 18๋ฒˆ์งธ setRefreshToken()์€ HTTP Response Cookie์— Refresh Token์„ ๋‹ด๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ์—์š”.
ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ๋‘ ๊ฐœ์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๊ฒŒ ๋˜๋Š”๋ฐ, ์ฒซ๋ฒˆ์งธ๋Š” HTTP Response ๊ฐ์ฒด์ด๊ณ , ๋‘๋ฒˆ์งธ๋Š” Refresh Token ๊ฐ’์ด์—์š”.

13๋ฒˆ์งธ ์ค„์— ์ด ์ž‘์—…์„ ์œ„ํ•ด response.cookie()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Cookie๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”. ์ด ๋•Œ, ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ Cookie ์ด๋ฆ„ ("refreshToken") ๊ณผ Cookie Value (refreshToken - Refresh Token ๊ฐ’)์„ ๋‹ด์•„์ฃผ๊ณ , Cookie Option์„ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.

14๋ฒˆ์งธ ์ค„ httpOnly๋ฅผ ture๋กœ ํ•ด ์ค€ ๋ถ€๋ถ„์€ JavaScript๋กœ Cookie ์ ‘๊ทผ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์„ค์ •ํ•œ ๋ถ€๋ถ„์ด๋ฉฐ, ์ด๋ ‡๊ฒŒ ํ•ด์•ผ ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ JavaScript๋ฅผ ์ด์šฉํ•˜์—ฌ Cookie์— ์ ‘๊ทผํ•ด์„œ ์•…์˜์ ์ธ ํ–‰์œ„๋ฅผ ํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ํ•ด ์ฃผ์–ด์•ผ ํ•ด์š”.

16๋ฒˆ์งธ ์ค„์€ Cookie์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ฃผ๋‹ˆ๋Š” 7์ผ๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.

17๋ฒˆ์งธ ์ค„์€ ์„ค์ •์—์„œ ๊ฐ€์ ธ์˜จ ๋„๋ฉ”์ธ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ Cookie์˜ ๋„๋ฉ”์ธ ์„ค์ •์„ ํ•ด ์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”.

 

 

 

        ๐Ÿ“ฆ AuthenticationModule

src/api/authentication/module/authentication.module.ts


๊ทธ๋ฆฌ๊ณ , ์œ„์™€ ๊ฐ™์ด CookieService์— ๋Œ€ํ•ด 48 ~ 52๋ฒˆ์งธ ์ค„๊นŒ์ง€ Authentication Module์— ๋“ฑ๋กํ•ด ์ฃผ์—ˆ์–ด์š”.

 

 

Postman

 

Postman


์œ„์™€ ๊ฐ™์ด HTTP Response Cookie ์„ค์ •์„ ์œ„ํ•œ ๋ถ€๋ถ„์ด์—์š”.

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

 

    ๐Ÿ”ฝ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ - Access Token ์žฌ ๋ฐœ๊ธ‰

์ด๋ฒˆ์—๋Š” Access Token์„ ์žฌ ๋ฐœ๊ธ‰ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด ๋ณด๋ ค๊ณ  ํ•ด์š”.
์ผ๋ฐ˜์ ์œผ๋กœ Access Token์€ ์œ ํšจ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์žก๊ณ , Refresh Token์€ ๋ณด๋‹ค ๊ธธ๊ฒŒ ์žก์•„ ์ด์šฉํ•ด์š”.

์ด์œ ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ, ์ด์šฉ์ž๊ฐ€ ์ง€์†์ ์œผ๋กœ Login ํ•ด์•ผ ํ•˜๋Š” ๋ถˆํŽธํ•จ์„ ์ค„์ด๊ธฐ ์œ„ํ•จ์ด์—์š”.

        ๐Ÿ“ฆ Controller

src/api/authentication/controller/authentication.controller.ts


์ตœ์ดˆ ๋ผ์šฐํ„ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

์ด ํ•ธ๋“ค๋Ÿฌ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „์†ก๋œ UserTokenRequestDto๋ฅผ ๋ฐ›๊ณ , ์‘๋‹ต๊ฐ’์œผ๋กœ Refresh Token์„ ๋ฐ˜ํ™˜ํ•ด ์ค„๊ฑฐ์—์š”.

ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ ๋˜๊ธฐ ์ „ Guard๊ฐ€ ๋™์ž‘ํ•˜์—ฌ Refresh Token ๊ฒ€์ฆ์„ ์ง„ํ–‰ํ•˜๋Š”๋ฐ, ๊ทธ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜์—์„œ ์ด์•ผ๊ธฐ ํ•ด ๋ณผ๊ฒŒ์š”.

 

 

 

 

        ๐Ÿ“ฆ UserService

src/api/authentication/service/authentication.service-impl.ts


๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

์ตœ์ดˆ 84๋ฒˆ์งธ ์ค„์—์„œ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „๋‹ฌํ•œ RequestDto๊ฐ€ Null์ธ์ง€๋ฅผ ํ™•์ธํ•˜๊ณ , Null์ด ์•„๋‹ˆ๋ผ๋ฉด ์ด์šฉ์ž์˜ Email ์ฃผ์†Œ๋ฅผ Parsing(ํŒŒ์‹ฑ)ํ•˜์—ฌ JwtPayload ๊ฐ์ฒดํ™”๋ฅผ ํ•˜์—ฌ payload ์ƒ์ˆ˜ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค ์‘๋‹ต๊ฐ’์œผ๋กœ ํ•ด๋‹น payload์™€ Access Token ๋น„๋ฐ€ํ‚ค ๊ทธ๋ฆฌ๊ณ , ์œ ํšจ ๊ธฐ๊ฐ„์„ ์ด์šฉํ•˜์—ฌ ์„œ๋ช…์„ ํ•œ ๋’ค Access Token์„ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ด ์ฃผ๋Š” ๋กœ์ง์ด์—์š”.

 

 

 

 

        ๐Ÿ“ฆ RequestDto

src/api/authentication/model/dto/request/user-token-request.dto.ts


Access Token์„ ์žฌ ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ„์™€ ๊ฐ™์ด email, ์ด์šฉ์ž ์ด๋ฆ„, ์ด์šฉ์ž ๋‚˜์ด, ํ•ด๋‹น ๊ณ„์ •์˜ ์—ญํ• ์„ ๋ณด๋‚ด์ค˜์•ผ ํ•ด์š”.


์ฃผ๋‹ˆ๋Š” Custom(์ปค์Šคํ…€) Decorator(๋ฐ์ฝ”๋ ˆ์ดํ„ฐ)๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.
์ด ๋ถ€๋ถ„์€ 11 ~ 15๋ฒˆ์งธ ์ค„ ๋‚ด์šฉ์ธ๋ฐ, ์ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” HTTP Request์—์„œ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ๋กœ ๋งŒ๋“ค์–ด ์ค€ ๊ฒƒ์ด์—์š”.

์ตœ์ดˆ 11๋ฒˆ์งธ ์ค„์„ ๋ถ„์„ํ•ด ๋ณผ๊ฒŒ์š”.
์—ฌ๊ธฐ์„œ createParamDecorator() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋Š” Nest.js์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ •์˜ํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ ์ž ํ•  ๋•Œ, ์‚ฌ์šฉํ•ด์š”.

๋งค๊ฐœ ๋ณ€์ˆ˜ ๋ถ€๋ถ„์€ ์ƒ์„ฑ๋œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ๋กœ์ง์ด ๋“ค์–ด๊ฐ€๋Š” ์ต๋ช… ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ด ํ•จ์ˆ˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์ ์šฉ๋œ ๊ณณ์—์„œ ์‹คํ–‰ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ๋ฅผ ๊ฐ–๊ณ  ์ž‡์–ด์š”. ๋งค๊ฐœ ๋ณ€์ˆ˜ ExecutionContext ๊ฐ์ฒด๋Š” ์‹คํ–‰ Context(์ปจํ…์ŠคํŠธ)๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜์—์š”.

12๋ฒˆ์งธ ์ค„์€ HTTP Request ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ request๋ž€ ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๊ฐ’์„ ๋‹ด์•„ ์ฃผ๊ธฐ ์œ„ํ•œ ๋ถ€๋ถ„์ด์—์š”.
์ด ๋•Œ, switchToHttp()๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๋ฅผ HTTP ์ปจํ…์ŠคํŠธ๋กœ ์ „ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๊ณ , getRequest()๋Š” HTTP Request ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์š”.

14๋ฒˆ์งธ ์ค„์€ HTTP Request ๊ฐ์ฒด์—์„œ user๋ฅผ ์ถ”์ถœํ•˜๋Š”๋ฐ, ์ด ์†์„ฑ์€ ์ด์šฉ์ž ์ธ์ฆ ํ›„์— ์ €์žฅ๋œ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด์š”. 
์ถ”์ถœํ•œ user ๊ฐ์ฒด๋ฅผ UserReissueAccessTokenRequestDto Type์œผ๋กœ Casting(์บ์ŠคํŒ… - ํ˜• ๋ณ€ํ™˜)ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“  ๋ถ€๋ถ„์ด์—์š”.

์ด๋ ‡๊ฒŒ ์ƒ์„ฑ๋œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ฃผ๋กœ Nest.js Controller(์ปจํŠธ๋กค๋Ÿฌ) ๋ฉ”์„œ๋“œ์˜ Parameter(ํŒŒ๋ผ๋ฏธํ„ฐ)๋กœ ์‚ฌ์šฉ๋˜๊ณ , ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰ ๋˜์—ˆ์„ ๋•Œ, ์ด์šฉ์ž ์ •๋ณด์— ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์—์š”.
์ด์šฉ์ž ์ •๋ณด๋Š” ์ฃผ๋กœ ์ธ์ฆ๋œ ์ด์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • ์ž‘์—…์ด๋‚˜, ๊ถŒํ•œ ํ™•์ธ ๋“ฑ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”.

 

 

 

        ๐Ÿ“ฆ strategy

src/api/authentication/jwt/strategy/jwt.refresh-access-token.strategy.ts


์ด ์ฝ”๋“œ๋Š” Passport๋ฅผ ์ด์šฉํ•˜์—ฌ JWT Refresh Token ์ „๋žต์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์—์š”.

์ตœ์ดˆ 11๋ฒˆ์งธ ์ค„์„ ๋ถ„์„ํ•ด ๋ณผ๊ฒŒ์š”.

์—ฌ๊ธฐ์„œ PassportStrategy๋ฅผ ์ƒ์† ๋ฐ›๋Š”๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ์ •์˜๋œ JWT Refresh Token ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•จ์ด์—์š”.
๋˜ํ•œ, jwt-refresh-token์ด๋ผ๊ณ  ๋ช…๋ช…ํ•œ ๋ถ€๋ถ„์€ ์ „๋žต์˜ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉ๋˜๊ณ , Passport์—์„œ ์ „๋žต ๋“ฑ๋ก ์‹œ ์‚ฌ์šฉ๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๋ฌธ์ž์—ด์ด์—์š”.

12 ~ 16๋ฒˆ์งธ ์ค„ ์ƒ์„ฑ์ž ๋ถ€๋ถ„์—์„œ 13 ~ 14๋ฒˆ์งธ ์ค„์€ UserService์™€ AuthenticationService๋ฅผ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•œ ๋ถ€๋ถ„์ด๊ณ , ์ฃผ๋‹ˆ๋Š” SOLID 5์›์น™ ์ค€์ˆ˜๋ฅผ ์œ„ํ•ด ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌํ˜„์ฒด๋ฅผ ๋‚˜๋ˆด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ์ •์˜ํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

15๋ฒˆ์งธ ์ค„์€ ์„ค์ • ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ConfigService๋ฅผ ์ •์˜ํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.

19 ~24๋ฒˆ์งธ ์ค„์€ ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ, PassportStrategy์˜ ์ƒ์„ฑ์ž์— ํ•„์š”ํ•œ Option์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๋ถ€๋ถ„์ด์—์š”.

20๋ฒˆ์งธ ์ค„์€ JWT๋ฅผ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ด ์ค€ ๋ถ€๋ถ„์ธ๋ฐ, ์ฃผ๋‹ˆ๋Š” HTTP Request์˜ Cookie์—์„œ refreshToken ๊ฐ’์„ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

21๋ฒˆ์งธ ์ค„์€ ๋ง๋ฃŒ ์—ฌ๋ถ€๋ฅผ ๋ฌด์‹œํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ฃผ๋‹ˆ๋Š” false๋กœ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

common/config/environment/.env.local.yml



22๋ฒˆ์งธ ์ค„์€ JWT ๊ฒ€์ฆ ์‹œ ์‚ฌ์šฉํ•  ๋น„๋ฐ€ํ‚ค๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด์—์š”. ์ฃผ๋‹ˆ๋Š” ์„ค์ • ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜จ .env.yml์— ๋ช…์‹œํ•œjwt.refreshTokenSecret ์ด๋ผ๋Š” ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

23๋ฒˆ์งธ ์ค„์€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— HTTP Request ๊ฐ์ฒด ์ „๋‹ฌ ์—ฌ๋ถ€๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ฃผ๋‹ˆ๋Š” ture๋กœ ์„ค์ •ํ•˜์—ฌ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— HTTP Request ๊ฐ์ฒด๊ฐ€ ์ „๋‹ฌ๋˜๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.


src/api/authentication/jwt/strategy/jwt.refresh-access-token.strategy.ts


validate()๋Š” Passport ์ „๋žต์˜ validate()๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ๋กœ ์ด ๋ฉ”์„œ๋“œ๋Š” Passport๊ฐ€ JWT ๊ฒ€์ฆ ์‹œ, ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์—์š”.
๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ ๋ฐ›๋Š” Request๋Š” HTTP Request ๊ฐ์ฒด๋กœ 28๋ฒˆ์งธ ์ค„์—์„œ ํ•ด๋‹น ๊ฐ์ฒด์— ๋“ค์–ด ์žˆ๋Š” Cookie์— Refresh Token์„ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด์š”.

JwtPayload๋Š” JWT์˜ ๋ณตํ˜ธํ™”๋œ Payload ๊ฐ์ฒด์—์š”.

done()์€ Passport์—๊ฒŒ ๊ฒ€์ฆ ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—์š”.

30๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด ๋ณตํ˜ธํ™”๋œ JWT Payload ๊ฐ์ฒด ์•ˆ์— ๋‹ด๊ธด ์ด์šฉ์ž Email ์ฃผ์†Œ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๋‹น ์ด์šฉ์ž๊ฐ€ ์‹ค์ œ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , 32๋ฒˆ์งธ ์ค„์—์„œ ํ•ด๋‹น ์ด์šฉ์ž๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์น˜๊ณ , ์กด์žฌํ•œ๋‹ค๋ฉด 36๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด Refresh Token์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋„๋ก ๊ตฌ์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋๋‚˜๋ฉด 37๋ฒˆ์งธ ์ค„์˜ done()์„ ํ˜ธ์ถœํ•˜์—ฌ Passport์—๊ฒŒ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ด ์ฃผ๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

์ด๋ ‡๊ฒŒ ํ•˜์—ฌ Passport๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JWT Refresh Token ์ „๋žต์„ ๊ตฌํ˜„ํ•˜๊ณ , ์ด์šฉ์ž์˜ JWT Refresh Token ๊ฒ€์ฆ์„ ํ•˜๊ณ , ์œ ํšจํ•œ ๊ฒฝ์šฐ ์ด์šฉ์ž ์ •๋ณด๋ฅผ Passport์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ค€ ๊ฒƒ์ด์—์š”.

์ด๋Ÿฌํ•œ ์ „๋žต์€ ์ฃผ๋กœ ์ด์šฉ์ž์˜ Session(์„ธ์…˜)์„ ์œ ์ง€ํ•˜๊ณ , ํ† ํฐ์„ ์ด์šฉํ•˜์—ฌ ์ด์šฉ์ž ์ธ์ฆ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ด์š”.

 

 

 

 

        ๐Ÿ“ฆ AuthenticationService

src/api/authentication/service/authentication.service-impl.ts


์œ„ ์ฝ”๋“œ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ ์ด์šฉ์ž ์ •๋ณด์™€ Refresh Token์ด ์œ ํšจํ•œ์ง€ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ์—์š”.

์ตœ์ดˆ 102๋ฒˆ์งธ ์ค„์—์„œ bcrypt.compare()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ Refresh Token๊ณผ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ํ•ด๋‹น ์ด์šฉ์ž์˜ Refresh Token ๊ฐ’์ด ํ•ด์‹ฑํ•˜์—ฌ ๊ฐ™์€์ง€๋ฅผ ํ™•์ธํ•ด ์ฃผ๋Š” ๋ถ€๋ถ„์ด์—์š”.

๋งŒ์•ฝ ๊ฐ™์ง€ ์•Š๋‹ค๋ฉด compare()๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฑด๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ํ† ํฐ์ด ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๋Š” ์‘๋‹ต์„ ์ „๋‹ฌํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๊ณ , if ๋ฌธ์— NOT(!)๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— false์ผ ๊ฒฝ์šฐ ์ฐธ์ด ๋˜์–ด 103๋ฒˆ์งธ ์ค„์— ๋‚ด์šฉ์ด ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์—์š”.

ํ•ด๋‹น ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ Promise<void>๋กœ ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.

์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ด์šฉ์ž์˜ Refresh Token์ด ์œ ํšจํ•œ์ง€ ๊ฒ€์ฆํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, bcrypt.compare()๋ฅผ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ํ˜•ํƒœ๋กœ ์ €์žฅ๋œ Refresh Token๊ณผ ์ „๋‹ฌ ๋ฐ›์€ Refresh Token์ด ๊ฐ™์€์ง€ ๋น„๊ตํ•˜๊ณ , ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๋ฐฉ์‹์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.




 

        ๐Ÿ“ฆ AuthenticationModule

import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "../../user/model/entity/user.entity";
import { AuthenticationController } from "../controller/authentication.controller";
import { AuthenticationServiceImpl } from "../service/authentication.service-impl";
import { JwtModule } from "@nestjs/jwt";
import configuration from "../../../../common/config/environment/configuration";
import { PassportModule } from "@nestjs/passport";
import { JwtStrategy } from "../jwt/strategy/jwt.strategy";
import { UserServiceImpl } from "../../user/service/user.service-impl";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { JwtConfig } from "../../../../common/config/jwt.config";
import { CookieServiceImpl } from "../../common/cookie/service/cookie.service-impl";
import { JwtRefreshAccessTokenStrategy } from "../jwt/strategy/jwt.refresh-access-token.strategy";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configuration],
    }),
    TypeOrmModule.forFeature([User]),
    // UserModule,
    JwtModule.registerAsync({
      useFactory: (configService: ConfigService) => {
        const jwt = configService.get<JwtConfig>("jwt");

        return {
          secret: jwt.accessTokenSecret,
          signOptions: { expiresIn: jwt.accessTokenExpireIn },
        };
      },
      inject: [ConfigService],
    }),
    PassportModule.register({ defaultStrategy: "jwt" }),
  ],
  controllers: [AuthenticationController],
  providers: [
    AuthenticationServiceImpl,
    {
      provide: "AuthenticationService",
      useClass: AuthenticationServiceImpl,
    },
    UserServiceImpl,
    {
      provide: "UserService",
      useClass: UserServiceImpl,
    },
    CookieServiceImpl,
    {
      provide: "CookieService",
      useClass: CookieServiceImpl,
    },
    JwtStrategy,
    JwtRefreshAccessTokenStrategy,
  ],
  exports: [
    AuthenticationServiceImpl,
    {
      provide: "AuthenticationService",
      useClass: AuthenticationServiceImpl,
    },
  ],
})
export class AuthenticationModule {}

src/api/authentication/module/authentication.module.ts


์ด์ œ ๋ชจ๋“ˆ์—๋Š” JwtRefreshAccessTokenStrategy ํด๋ž˜์Šค๊นŒ์ง€ ๋“ฑ๋ก ์‹œ์ผœ ์ฃผ๋ฉด ๋ผ์š”.

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

 

    ๐Ÿ”ฝ JWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ - ํ”„๋กœํ•„ ํ™•์ธ

        ๐Ÿ“ฆ Controller

src/api/user/controller/user.controller.ts


์œ„์™€ ๊ฐ™์ด ์ด์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ํ”„๋กœํ•„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

URL Parameter๋กœ ์ด์šฉ์ž์˜ ID๋ฅผ ์ „๋‹ฌ ๋ฐ›์„ ๊ฑฐ๊ณ , JWT๋ฅผ ๋ฐ›์•„ ๊ฒ€์ฆํ•œ ๋’ค ํ•ด๋‹น ๋กœ์ง์ด ์ง„ํ–‰๋  ๊ฑฐ์—์š”.





 

 

 

        ๐Ÿ“ฆ Service

src/api/user/service/user.service-impl.ts


์ด๋ ‡๊ฒŒ ID๋ฅผ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„œ ์ฐพ์•„ ํ•ด๋‹น ๊ณ„์ •์˜ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์—์š”.





 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

๐Ÿ”ฝJWT๋ฅผ ์ด์šฉํ•œ ์ธ์ฆ - Logout

์ด๋ฒˆ์—” Logout ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ๋งŒ๋“ค์–ด ๋ณด๋ ค๊ณ  ํ•ด์š”.
์‚ฌ์‹ค JWT์˜ ๊ฐœ๋…์—์„œ Logout์€ ์—†์–ด์š”.

์™œ๋ƒํ•˜๋ฉด ํ† ํฐ์ด ์‚ด์•„์žˆ๋‹ค๋ฉด ๊ณ„์† ์ธ์ฆ์„ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ Logout ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

์ฃผ๋‹ˆ๋Š” HTTP Request Header์™€ HTTP Request Cookie์— ํ† ํฐ์„ ๋‹ด์•„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ด ๋ณด์•˜์—ˆ์–ด์š”.
๊ทธ๋ ‡๋‹ค๋ฉด Hader์™€ Cookie์— ํ† ํฐ์„ ์—†์•  ๋ฒ„๋ฆฌ๋Š” ์ „๋žต์„ ์ทจํ•œ๋‹ค๋ฉด Logout ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ์š”?

๋ฐ”๋กœ ๊ตฌํ˜„ํ•ด ๋ณผ๊ฒŒ์š”!

        ๐Ÿ“ฆ Controller

src/api/authentication/controller/authentication.controller.ts

์œ„ ๋ผ์šฐํ„ฐ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ถ„์„ํ•ด ๋ณด๋ฉด ์ตœ์ดˆ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ ๋˜๊ธฐ ์ „์— JwtAuthenticationGuard๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ์ด์šฉ์ž์˜ JWT Access Token ๊ฒ€์ฆ์ด ์ด๋ค„์ง€๊ฒŒ ๋ผ์š”.

Access Token์ด ์ •์ƒ์ด๋ผ๋ฉด ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ํ•ธ๋“ค๋Ÿฌ๋Š” UserTokenRequestDto์™€ Response๋ฅผ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ  ์žˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค ์‘๋‹ต๊ฐ’์€ ์ •์ƒ ์ฒ˜๋ฆฌ์ธ์ง€ ์•„๋‹Œ์ง€์— ๋Œ€ํ•œ HTTP Response ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ฃผ๋„๋ก ์ฒ˜๋ฆฌํ•ด ์ฃผ์—ˆ์–ด์š”.












        ๐Ÿ“ฆ AuthenticationService

src/api/authentication/service/authentication.service-impl.ts


๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ตœ์ดˆ 121๋ฒˆ์งธ ์ค„์— ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ ์ด์šฉ์ž์˜ Email ์ฃผ์†Œ๋กœ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„œ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ณ , 123 ~ 125๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด ํ•ด๋‹น ์ด์šฉ์ž ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ณ  ์žˆ์–ด์š”.


์ด์šฉ์ž ์ •๋ณด๊ฐ€ ์กด์žฌํ•˜๋ฉด 127 ~ 128๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด Entity์˜ Refresh Token๊ณผ Refresh Token ์œ ํšจ ๊ธฐ๊ฐ„์„ ์ดˆ๊ธฐํ™” ํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋ฆฌ๊ณ , ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—๋„ ์ดˆ๊ธฐํ™”๋œ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ธฐ ์œ„ํ•ด 144 ~ 150๋ฒˆ์งธ ์ค„ ๋กœ์ง์„ ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.


๊ทธ๋Ÿฐ ๋’ค 152๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด HTTP Response Cookie์— Refresh Token์„ ๋ฌด๋ ฅํ™” ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋กœ์ง์„ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ Refresh Token์ด ๋ฌด๋ ฅํ™” ๋˜๋ฉด ๋กœ๊ทธ ์•„์›ƒ์ด ์ฒ˜๋ฆฌ๋œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— 132๋ฒˆ์งธ ์ค„๊ณผ ๊ฐ™์ด ์‘๋‹ตํ•ด ์ฃผ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.









        ๐Ÿ“ฆ CookieService

src/api/common/cookie/service/cookie.service-impl.ts


์œ„ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ๋“ค์–ด์˜จ HTTP Response ๊ฐ์ฒด์— ๋‹ด๊ธด Cookie์— refreshToken์ด๋ผ๋Š” ์ด๋ฆ„์˜ Cookie๋ฅผ ์ง€์šฐ๊ณ , 26๋ฒˆ์งธ ์ค„์„ ํ†ตํ•ด HTTP Response Header์˜ Authorization ์ด๋ฆ„์˜ ๊ฐ’์„ ์ง€์šฐ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

 

 

 

 

    ๐Ÿ”ฝ ์ž˜ ๋งŒ๋“ค์—ˆ๋‚˜? ๐Ÿค”

        ๐Ÿ“ฆ Swagger ์ˆ˜์ •

์ด์ œ Logic(๋กœ์ง)์„ ์–ด๋Š์ •๋„ ๊ตฌํ˜„ํ–ˆ์œผ๋‹ˆ ์ž˜ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

๊ทธ ์ „์— ํ˜„์žฌ ์ฃผ๋‹ˆ๊ฐ€ ๊ตฌ์„ฑํ•œ Swagger(์Šค์›จ๊ฑฐ)๋Š” ์š”์ฒญ ํ—ค๋”๊ฐ’์„ ๋„ฃ์„์ˆ˜๊ฐ€ ์—†์–ด์š”.

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp


์š”์ฒญ ํ—ค๋”๊ฐ’์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ • ํ•ด๋ณด๋„๋ก ํ• ๊ฒŒ์š”.

common/config/document/swagger.config.ts

 

11๋ฒˆ์งธ์—์„œ 19๋ฒˆ์งธ ์ค„๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด JWT๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”.

 

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp

 

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp


์œ„์™€ ๊ฐ™์ด Authorize ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด JWT Token์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋‹ฌ์ฐฝ์„ ๋งŒ๋‚  ์ˆ˜ ์žˆ๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

        ๐Ÿ“ฆ ํšŒ์› ๊ฐ€์ž…

์Šค์›จ๊ฑฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ํšŒ์› ๊ฐ€์ž…์ด ์ •์ƒ์ ์œผ๋กœ ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณผ๊ฒŒ์š”.

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp


์œ„์™€ ๊ฐ™์ด ์ •์ƒ์ ์œผ๋กœ ๊ฐ€์ž… ๋œ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


Embedded SQLite


๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋„ ๊ฐ’์ด ์ž˜ ๋“ค์–ด๊ฐ„๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp


๋™์ผํ•œ Email ์ฃผ์†Œ๋กœ ๊ฐ€์ž… ์‹œ๋„๋ฅผ ํ•˜๋ฉด ์ด๋ฏธ ๋“ฑ๋ก ๋˜์—ˆ๋‹ค๊ณ  ๋‚˜์˜ค๋Š” ๊ฒƒ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signUp


์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋„ ์ž˜ ์ง„ํ–‰๋˜๋Š”๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.






 

        ๐Ÿ“ฆ ๋กœ๊ทธ์ธ

ํšŒ์› ๊ฐ€์ž…์ด ๋˜์—ˆ์œผ๋‹ˆ ๋กœ๊ทธ์ธ์„ ์ง„ํ–‰ํ•˜๊ณ , JWT Token์ด ๋ฐ˜ํ™˜๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณผ๊ฒŒ์š”.

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signIn


์œ„์™€ ๊ฐ™์ด ์ •์ƒ์ ์œผ๋กœ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๊ฐ€ ๋˜๊ณ , Access Token๊ณผ Refresh Token๊นŒ์ง€ ๋ฐœ๊ธ‰ ๋˜๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

http://localhost:8181/swagger-ui/index.html#/default/AuthenticationController_signIn

 

Postman



๊ทธ๋ฆฌ๊ณ  ์œ„์™€ ๊ฐ™์ด HTTP Response Header์— authorization์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ Bearer ๋ฌธ์ž์—ด๊ณผ ,๋ฅผ ๊ธฐ์ค€์œผ๋กœ Access Token๊ณผ Refresh Token์ด ์ „๋‹ฌ ๋œ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


postman


๊ทธ๋ฆฌ๊ณ  ์œ„์™€ ๊ฐ™์ด HTTP Response Cookie์— Access Token๊ณผ Refresh Token์ด ๋‹ด๊ฒจ ์˜จ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

        ๐Ÿ“ฆ Refresh Token

Access Token์ด ๋งŒ๋ฃŒ ๋˜์—ˆ์„ ๊ฒฝ์šฐ Refresh Token์œผ๋กœ Access Token์„ ์ž˜ ๋ฐœ๊ธ‰ ๋ฐ›๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

Postman

๋ฐ˜์‘ํ˜•


์ตœ์ดˆ ๋งŒ๋ฃŒ ๊ธฐํ•œ์ด ์ง€๋‚˜๋ฒ„๋ฆฐ Access Token์„ ์ด์šฉํ•˜์—ฌ ํšŒ์› ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ์œ„์™€ ๊ฐ™์ด ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.


Postman


์ด ๋•Œ, ์œ„์™€ ๊ฐ™์ด API๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด HTTP Request Cookie์— ๋‹ด๊ธด refreshToken ๊ฐ’์„ ํ†ตํ•ด Refresh Token์„ ๊ฒ€์ฆํ•˜๊ณ , ์ •์ƒ Refresh Token์ด๋ฉด ์œ„์™€ ๊ฐ™์ด Access Token์„ ๋‹ค์‹œ ๋ฐœ๊ธ‰ ๋ฐ›๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

 

 

        ๐Ÿ“ฆ ํ”„๋กœํ•„ ์กฐํšŒ


์œ„์™€ ๊ฐ™์ด Token๊ณผ ์ด์šฉ์ž์˜ ID๋ฅผ URL Parameter๋กœ ์ „๋‹ฌํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋‹ˆ
์ •์ƒ์ ์œผ๋กœ ์ด์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

        ๐Ÿ“ฆ ๋กœ๊ทธ ์•„์›ƒ

Postman


์œ„์™€ ๊ฐ™์ด Access Token์„ HTTP Request Header์˜ Authrozation์— Bearer Token์œผ๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋กœ๊ทธ์•„์›ƒ์ด ๋˜๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.



Login ์‹œ HTTP Response Cookies ์ •๋ณด

 

Login ์‹œ HTTP Response Headers ์ •๋ณด



Logout ์‹œ&nbsp;HTTP Response Cookies ์ •๋ณด

 

Logout ์‹œ&nbsp;HTTP Response Headers ์ •๋ณด



๋‹ค์Œ ๊ธ€์—์„  Nest.js Multer๋ฅผ ์ด์šฉํ•œ Image ์ €์žฅ์— ๋Œ€ํ•ด ์‹ค์Šตํ•ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”!

 

 

 

 

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

 

GitHub - junyharang/nestTs-jwt-multer: Nest.ts๋ฅผ ์ด์šฉํ•œ JWT ์ธ์ฆ, ์ธ๊ฐ€์™€ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์—ฐ์Šต

Nest.ts๋ฅผ ์ด์šฉํ•œ JWT ์ธ์ฆ, ์ธ๊ฐ€์™€ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์—ฐ์Šต. Contribute to junyharang/nestTs-jwt-multer development by creating an account on GitHub.

github.com

 

 

 

๐Ÿง ์ฐธ๊ณ  ์ž๋ฃŒ

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

 

 

 

request dto์˜ ๋ฉ”์„œ๋“œ ๋™์ž‘ ์˜ค๋ฅ˜

์—”ํ‹ฐํ‹ฐ ๋ณ€ํ™˜๋กœ์ง ์˜ค๋ฅ˜

velog.io

 

 

 

์นด์นด์˜คํŽ˜์ด | ๋งˆ์Œ ๋†“๊ณ  ๊ธˆ์œตํ•˜๋‹ค

์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ ๋งํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

qr.kakaopay.com

 

 

 

728x90
๋ฐ˜์‘ํ˜•