2024. 1. 20. 20:54ใBack-End ์์ ์ค/Nest.js
"์ด ํฌ์คํ ์ ์ฟ ํก ํํธ๋์ค ํ๋์ ์ผํ์ผ๋ก, ์ด์ ๋ฐ๋ฅธ ์ผ์ ์ก์ ์์๋ฃ๋ฅผ ์ ๊ณต๋ฐ์ต๋๋ค."
๐ ๋ชฉ์ฐจ
โ
[Nest.js] JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ, ์ธ๊ฐ ๊ทธ๋ฆฌ๊ณ Multi part ์ด์ผ๊ธฐ - โ ์ด๊ธฐ๊ตฌ์ฑ
โ
[Nest.js] JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ, ์ธ๊ฐ ๊ทธ๋ฆฌ๊ณ Multi part ์ด์ผ๊ธฐ - โก ํ์๊ฐ์
๊ณผ ์ธ์ฆ(feat. Access Token & Refresh Token)
โ
[Nest.js] JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ, ์ธ๊ฐ ๊ทธ๋ฆฌ๊ณ Multi part ์ด์ผ๊ธฐ - โข Multer๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
โ
๐ ํ์๊ฐ์ ๊ณผ ์ธ์ฆ(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๊ฐ ํฌํจ๋ ์์ฒญ์๋ง ์ ๊ทผํ ์ ์๋ ๋ณดํธ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ด๋ณด๋ ค๊ณ ํด์.
๐ฝ ์ด๊ธฐ ๊ตฌ์ฑ
๐ฆ Package Download
์ต์ด ์ด์ฉ์ ID์ ์ํธ๋ฅผ ํตํด ์ธ์ฆ ๊ตฌํ์ ์ํ passport-local Package(ํจํค์ง) ์ค์น๋ฅผ ์งํํด๋ณผ๊ฒ์.
์ฐธ๊ณ ๋ก @types/passport-local๋ ์ค์นํ ๊ฑด๋ฐ, ๊ทธ ์ด์ ๋ Type Script ์์ฑ์ ๋์์ ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ด์์.
๐ฝ ํ์ ๊ฐ์
๐ฆ Entity
ํ์๊ฐ์
๊ธฐ๋ฅ ๊ตฌํ์ ์ํด ์ต์ด entity๋ฅผ ๋ง๋ค์ด ๋ณผ๊ฒ์.
์ด์ฉ์์ ์ญํ ์ ๊ตฌ๋ถํ๊ธฐ ์ํด Role ์ด๋ผ๋ Enum์ ๋ง๋ค์ด ์ฃผ๊ณ , ๊ด๋ฆฌ์์ ์ด์ฉ์๋ฅผ ๊ตฌ๋ถํด ์ฃผ์์ด์.
์ฃผ๋๋ ๋ก์ปฌ ํ๊ฒฝ์์๋ SQLite3 Embedded Mode์ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ MariaDB๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ์ค์ ํด ์ฃผ์๋๋ฐ,
SQLite3 ๊ฐ์ ๊ฒฝ์ฐ Entity์ @Column type์ enum์ผ๋ก ์ก์ ๊ฒฝ์ฐ ์ธ์์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ฌธ์์ด๋ก ์ธ์๋ ์ ์๊ฒ varchar๋ก ์ค์ ํด ์ฃผ์์ด์.
๋ง์ฝ MariaDB๋ง์ ์ฌ์ฉํ๋ค๋ฉด type์ enum์ผ๋ก ์ก์๋ ๋ผ์.
๐ฆ Controller
์์ ๊ฐ์ด ํ์ ๊ฐ์
Method(๋ฉ์๋)๋ฅผ ๋ง๋ค์ด ์ฃผ์์ด์.
์ด๋ฒ์๋ UserCreateRequestDto๋ฅผ ๋ง๋ค์ด ๋ณผ๊ฒ์.
๐ฆ RequestDto
๊ฐ๋จํ ํ
์คํธ ์ค์ต์ ์ํด ์์ ๊ฐ์ด Email ์ฃผ์์ ๋น๋ฐ๋ฒํธ, ์ด๋ฆ, ๋์ด๋ง ๋ฐ๋๋ก ๋ง๋ค๊ณ , toEntity()๋ฅผ ๋ง๋ค์ด RequestDto๋ฅผ Entity๋ก ๋ณํํ ์ ์๋ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด ์ฃผ์์ด์.
๐ฆ Service
์ด๋ฒ์ Business Logic์ ๋ด์ Service Logic์ ๊ตฌํํด ๋ณด๋๋ก ํ ๊ฒ์.
์ฃผ๋๋ SOLID 5์์น ์ค์๋ฅผ ์ํด Interface(์ธํฐํ์ด์ค)์ ๊ตฌํ์ฒด๋ฅผ ๋๋์ด ๊ตฌํํด ๋ณผ๊ฑฐ์์.
์ด์ ๋ํ ์์ธํ ๊ฐ๋
์ ๊ณต๋ถํ๊ณ ์ถ์ผ์๋ค๋ฉด ์ด ๊ณณ์ ๊ด์ฌ์ ์ฃผ์ธ์.
์ ์ฝ๋๋ฅผ ๋ถ์ํด ๋ณด๋ฉด ์ต์ด 15๋ฒ์งธ ์ค์ ์ด์ฉ์ ํ์๊ฐ์
์ ๋ณด๊ฐ ๋ด๊ธด DTO ๊ฐ์ด Null์ธ์ง๋ฅผ ์ฒดํฌํด์ฃผ๊ณ , Null์ด๋ผ๋ฉด ๋ฌธ์ ๋ฐ์์ ๋ํ ์๋ต์ ๋ณด๋ด์ฃผ๊ฒ ํ์ด์.
37๋ฒ์งธ ์ค์๋ ๋น๋ฐ๋ฒํธ Hasing ์ํธํ๋ฅผ ์ํด EncryptUtil์ hashingEncrypt()๋ฅผ ํธ์ถํ์ฌ ํด์ฑํ ๊ตฌ๋ถ๊ฐ๊ณผ ํด์ฑ ๋์๊ฐ์ ์ ๋ฌํด ์ฃผ์์ด์.
์ด ๋, 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
์์ ๊ฐ์ด ์ธ์ฆ ๊ด๋ จ Module(๋ชจ๋)์ ๋ง๋ค์ด ์ฃผ์์ด์.
๊ทธ๋ฆฌ๊ณ app.module.ts์ 25๋ฒ์งธ ์ค๊ณผ ๊ฐ์ด ์์์ ๋ง๋ ์ธ์ฆ ๋ชจ๋์ ์ถ๊ฐํด ์ฃผ์์ด์.
๐ฝ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ with.passport - ์๊ฐ
๐ฆ ๊ฐ์
JWT์ ๋ํด ๊ถ๊ธํ์๋ค๋ฉด ๋จผ์ ์ด ๊ณณ์ ๊ด์ฌ์ ์ฃผ์ธ์.
์ด๋ฒ์๋ 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.
๐ฝ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ with.passport - ๊ตฌํ
๐ฆ ์ด๊ธฐ ๊ตฌ์ฑ
์ต์ด ์ ์๊ฐ๋ ๋ชจ๋ ์ค์น๋ถํฐ ์งํํด ๋ณผ๊ฒ์.
์ด๋ฒ์ ์์ ๊ฐ์ด JWT ๊ด๋ จํ์ฌ secret ๊ฐ์ ๋ฃ์ด์ฃผ์์ด์.
ํ๊ฒฝ ๋ณ์ ๊ฐ์ ๋ด์ .env.yml์ ํ๊ฒฝ์ ๋ง์ถ jwt ๊ด๋ จ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ์ค์ ํด ์ฃผ์์ด์.
14๋ฒ์งธ ์ค์ defaultStrategy ๋ฌธ์์ด ๊ฐ์ .env.yml์ ๊ฐ์ ธ์ฌ ๊ฐ์ ์ต์์ ์ด๋ฆ์ ๋ฃ์ด์ฃผ์ด์ผ ํด์.
์ฃผ๋๋ ์์ ๊ฐ์ด 13๋ฒ์งธ ์ค์ jwt๋ฅผ ์ต์์ ์ด๋ฆ์ผ๋ก ์ก์ ์ฃผ์๊ธฐ ๋๋ฌธ์ jwt๋ผ๊ณ ํด ์ค๊ฒ์ด์์.
12๋ฒ์งธ ์ค secret์ Token(ํ ํฐ)์ ๋ง๋ค ๋ ์ด์ฉํ๋ ๋น๋ฐ ๊ฐ์ ๋ฃ์ด์ฃผ๋ ๋ถ๋ถ์ด์์.
ํ๋ฌธ์ผ๋ก ์
๋ ฅํด๋ ๋์ง๋ง, ์ฃผ๋๋ ํ๋ฌธ์ base 64๋ก Encodingํ ๊ฐ์ ๋ฃ์ด์ฃผ์์ด์.
13๋ฒ์งธ ์ค signOption์ ์๋ช
์ ์ด๋ป๊ฒ ๊ตฌํํ ๊ฒ์ธ์ง ์ ์ํด ์ฃผ๋ ๋ถ๋ถ์ด์์.
๊ทธ๋ฆฌ๊ณ , ExpireIn์ Token ๋ง๋ฃ ์๊ฐ์ ๋ฃ์ด์ฃผ๋ ๋ถ๋ถ์ด์์. ์ฃผ๋๋ 7์ผ๋ก ์ค์ ํด ์ฃผ์์ด์.
12๋ฒ์งธ ์ค์ ๋ค๋ฅธ ๋ด์ฉ์ ์ถ๊ฐํด ์ฃผ์๋๋ฐ, ๋ฐ๋ก PassportModule์์ register()๋ฅผ ํธ์ถํ์ฌ ๊ทธ ์์ ๊ธฐ๋ณธ ์ ๋ต ๊ฐ์ผ๋ก jwt๋ผ๋ ๋ฌธ์์ด์ ์ ๋ฌํ์ฌ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ ๊ธฐ๋ฅ ๊ตฌํ์ ํ ์ ์๊ฒ ํด ์ฃผ์์ด์.
๐ฆ Controller
16 ~ 19๋ฒ์งธ ์ค๊ณผ ๊ฐ์ด ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฅผ ์ํ Handler(ํธ๋ค๋ฌ)๋ฅผ ๋ง๋ค์ด ์ฃผ์์ด์.
๋ถ์ํด ๋ณด๋ฉด @Body ๋ฐ์ฝ๋ ์ดํฐ๋ก HTTP ์์ฒญ Body(๋ณธ๋ฌธ)์์ ๋ฐ์ดํฐ๋ฅผ ์ถํ๋๋กํด ์ฃผ์์ด์.
๊ทธ๋ฐ ๋ค ValidationPipe๋ฅผ ์ฌ์ฉํ์ฌ SigninRequestDto ํ์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๊ณ , ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ์งํ๋๋๋ก ํด ์ฃผ์์ด์.
Promise<DefaultResponse<String>>์ ๋น๋๊ธฐ ์์
์ ์ํํ ์ ์๋ ๊ฒ์ ์ ์ ์๊ณ , string์ ๋ด์ ๋ฐํํ๋ ์ด์ ๋ ํ ํฐ์ ๋ฐํํด ์ฃผ๊ธฐ ์ํจ์ด์์.
๐ฆ SignInRequestDto
๋ก๊ทธ์ธํ ๋ ์ฌ์ฉํ ์์ฒญ DTO๋ฅผ ๋ง๋ค์ด ๋ณผ๊ฒ์.
๋ก๊ทธ์ธํ ๋, ID ํ์์ Email๋ก ๋ฐ๊ณ , ๋น๋ฐ๋ฒํธ๋ฅผ ๋ด๊ธฐ ์ํ ์์ฒญ DTO๋ฅผ ๋ง๋ค์ด ์ฃผ์์ด์.
์์ Decorator(๋ฐ์ฝ๋ ์ดํฐ)๋ค์ Pipe๋ฅผ ์ด์ฉํ Validation ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ ์ํด ์์ฑํด ์ฃผ์์ด์.
๐ฆ Service
์ ์ฝ๋๋ฅผ ๋ถ์ํด ๋ณด๋ฉด ์ต์ด ํด๋ผ์ด์ธํธ์์ ๋ค์ด์จ ์์ฒญ DTO์ Email ๊ฐ์ ์ด์ฉํด ๋ฐ์ดํฐ ๋ฒ ์ด์ค์์ ํด๋น ์ด์ฉ์ ์ ๋ณด ์ค Email ๊ฐ๊ณผ ๋น๋ฐ๋ฒํธ ๊ฐ์ ๊บผ๋ด findByUSerInfo ์์ ๋ณ์์ ๋ด๊ฒ ํ์์ด์.
๊ทธ๋ฐ ๋ค 48๋ฒ์งธ ์ค์์ ํด๋น ๊ฐ์ด Null์ด ์๋์ง ํ์ธํ๊ณ , ์์ฒญ์ผ๋ก ๋ค์ด์จ ๋น๋ฐ๋ฒํธ๋ฅผ Hashing(ํด์ฑ)ํ ๋ค ๋ฐ์ดํฐ ๋ฒ ์ด์ค์ ์ ์ฅ๋ ํด์ฑ๋ ๋น๋ฐ๋ฒํธ ๊ฐ๊ณผ ๊ฐ์์ง ํ์ธํ์ฌ ๋ชจ๋ ์ฐธ์ด๋ผ๋ฉด JWT Payload์ ํด๋น ์ด์ฉ์์ Email, ์ด๋ฆ, ๋์ด๋ฅผ ๋ด์
jwtService()๋ฅผ ์ด์ฉํ์ฌ Payload๋ฅผ ์ด์ฉํ์ฌ ์๋ช
ํ ๋ค ๋ฐํํ๊ฒ ํด ์ฃผ์์ด์.
์ด๋ ๊ฒ ๋๋ฉด ํด๋ผ์ด์ธํธ๋ JWT Access Token์ ๋ฐ๊ธ ๋ฐ์ ์ ์์ด์.
๐ฆ Guard
์ ํด๋์ค๋ 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
์ ํด๋์ค๋ Passport๋ฅผ ์ฌ์ฉํ์ฌ JWT๋ฅผ ์ฒ๋ฆฌํ๋ ์ ๋ต์ ์ ์ํ ํด๋์ค๋ก ์ฃผ๋ก ์ธ์ฆ ๋ฏธ๋ค์จ์ด๋ก ์ฌ์ฉ๋๋ ํด๋์ค์์.
์ด ํด๋์ค๋ JWT๋ฅผ ์ถ์ถํ๊ณ , ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ฉฐ, ์ธ์ฆ์ด ์ฑ๊ณตํ๋ฉด ์์ฒญ์ผ๋ก ๋ค์ด์จ ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ฐํํ๋ ์ญํ ์ ํ๊ฒ ๋ผ์.
์ฐธ๊ณ ๋ก validate() ๋งค๊ฐ ๋ณ์์ JwtPayload๋ ์์ ๊ฐ์ด interface๋ก ์ ์ ํด ์ฃผ์๊ณ , ๋ฌธ์์ด ํ์
์ email ๋ณ์๊ฐ ์ ์๋ ๊ฒ์ ์ ์ ์์ด์.
ํ๋ฒ ์์ธํ ๋ถ์ํด ๋ณผ๊น์?
์ต์ด 9๋ฒ์งธ ์ค์ JwtStrategry ํด๋์ค๊ฐ PassportStrategy ํด๋์ค๋ฅผ ์์ํ๊ฑธ ์ ์ ์์ด์. PassportStrategy๋ Passport ๋ชจ๋์ ์ฌ์ฉํ Strategy(์ ๋ต)์ ๊ตฌํํ๊ธฐ ์ํ ๊ธฐ๋ณธ ํด๋์ค์์. Strategy๋ Passport ์ ๋ต์ ๊ธฐ๋ณธ ํด๋์ค๋ก ์ฌ์ฉ๋๋ฉฐ, "jwt"๋ ์ด ์ ๋ต์ ์ด๋ฆ์ ๋ํ๋ด๋๋ฐ, ์ด ๋, ์ฃผ๋๋ .env.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์์ ์ฌ์ฉํ๋ ์ค์ ์ ๋ณด๋ฅผ ํ๋์ ๊ณณ์์ ํตํฉ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ ๊ธฐ๋ฅ ์ ๊ณต.
๐ฝ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ - Refresh Token
์์์ ์งํํ ์ธ์ฆ ๊ธฐ๋ฅ์ JWT์ Access Token๋ง ๋ฐ๊ธํ ์ ์๋๋ก ์ฒ๋ฆฌ๋ ๊ธฐ๋ฅ์ด์์.
JWT์ Access Token๊ณผ Refresh Token ์ด์ผ๊ธฐ๋ ์ด ๊ณณ์ ๊ด์ฌ์ ์ฃผ์ธ์.
์ด๋ฒ์ ์ด์ฉ์๊ฐ ์ ์ ์ธ์ฆ์ ์๋ฃํ๋ฉด Access Token๊ณผ Refresh Token์ด ๊ฐ์ด ๋ฐ๊ธ๋๋๋ก ๊ตฌํํด ๋ณผ๊ฒ์.
๐ฆ Controller
๐ฆ AuthenticationService
์์ ์ฝ๋์์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ๋ํด์ ์ด์ผ๊ธฐ ํด ๋ณผ๊ฒ์.
์ต์ด 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 ๊ฐ์ฒด์์.
์ด์ 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 ๋ง๋ฃ ์ผ์๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ , ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ก์ง์ ๊ตฌํํด ์ฃผ์์ด์.
์ ๋ด์ฉ์ ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ๋ ์๊ฐ์์ jwt.refreshTokenExpireIn ์ค์ ์ ์ ์ํ ์๊ฐ์ ๋ํ ๊ฐ์ ๋ฐํํ๋ ๋ฉ์๋์์. ์ฆ, Refresh Token์ ๋ง๋ฃ ๋ ์ง๋ฅผ ๊ณ์ฐํ๊ธฐ ์ํ private ๋ฉ์๋์์.
154๋ฒ์งธ ์ค์์ ํ์ฌ ์๊ฐ์ ๊ฐ์ ธ์ฌ Date ๊ฐ์ฒด๋ฅผ ์์ฑํด ์ฃผ์์ด์.
๊ทธ๋ฐ๋ค 156๋ฒ์งธ ์ค์์ .env.yml์์ ๊ฐ์ ธ์จ jwt.refreshTokenExpireIn์ ๋ํ ๊ฐ์ ๋ฌธ์์ด๋ก ๊ฐ์ ธ์ค๊ฒ ํ ๋ค parstInt()๋ฅผ ์ด์ฉํด ์ ์๋ก ํ ๋ณํํด ์ฃผ์์ด์. currnetDate.getTime()์ ์ด์ฉํ์ฌ ํ์ฌ ๋ ์ง์ Timestamp(ํ์ ์คํฌํ)๋ฅผ ๋ฐํํ๊ฒ ํด ์ฃผ๊ณ , ์ด ๊ฐ์ ๊ฐ์ ธ์จ Refresh Token ๋ง๋ฃ ๊ธฐ๊ฐ(๋ฐ๋ฆฌ์ด ๋จ์)์ ๋ํ์ฌ ์๋ก์ด Date ๊ฐ์ฒด๋ฅผ ์์ฑํ๋๋ก ํด ์ฃผ์์ด์.
๊ทธ๋ฐ ๋ค ๊ณ์ฐ๋ Refresh Token ๋ง๋ฃ ์ผ์๋ฅผ ๋ฐํํ๊ฒ ํด ์ค ๊ฒ์ด์์.
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์ ๋ด์ ์๋ตํ๋๋ก ์ฒ๋ฆฌํด ์ฃผ์์ด์.
ํ์ ๊ฐ์ ๋ ๋น๋ฐ๋ฒํธ ํด์ฑํ๋ฅผ ์ํด ์ฌ์ฉํ ๋ด์ฉ์ ๋ํด ์ด์ผ๊ธฐ ํ์๋๋ฐ, ์ด๋ฒ์๋ Refresh Token ํด์ฑํ์ ๋ํด ์ด์ผ๊ธฐ ํด๋ณผ๊ฒ์. ์ต์ด ๊ตฌ๋ถ๊ฐ์ด token์ด๋ฉด ๋งค๊ฐ ๋ณ์๋ก ์ ๋ฌ๋ Refresh Token๊ณผ Salt ๊ฐ์ ๋ฐ์ฃฝํ์ฌ ํด์ฑํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด ์ค๊ฑฐ์์.
๐ฆ CookieService
์ ์ฝ๋๋ 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
๊ทธ๋ฆฌ๊ณ , ์์ ๊ฐ์ด CookieService์ ๋ํด 48 ~ 52๋ฒ์งธ ์ค๊น์ง Authentication Module์ ๋ฑ๋กํด ์ฃผ์์ด์.
์์ ๊ฐ์ด HTTP Response Cookie ์ค์ ์ ์ํ ๋ถ๋ถ์ด์์.
๐ฝ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ - Access Token ์ฌ ๋ฐ๊ธ
์ด๋ฒ์๋ Access Token์ ์ฌ ๋ฐ๊ธ ๋ฐ์ ์ ์๋ ๊ธฐ๋ฅ์ ๋ง๋ค์ด ๋ณด๋ ค๊ณ ํด์.
์ผ๋ฐ์ ์ผ๋ก Access Token์ ์ ํจ๊ธฐ๊ฐ์ ์งง๊ฒ ์ก๊ณ , Refresh Token์ ๋ณด๋ค ๊ธธ๊ฒ ์ก์ ์ด์ฉํด์.
์ด์ ๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ง๋ง, ์ด์ฉ์๊ฐ ์ง์์ ์ผ๋ก Login ํด์ผ ํ๋ ๋ถํธํจ์ ์ค์ด๊ธฐ ์ํจ์ด์์.
๐ฆ Controller
์ต์ด ๋ผ์ฐํฐ ํธ๋ค๋ฌ๋ฅผ ์์ ๊ฐ์ด ๋ง๋ค์ด ์ฃผ์์ด์.
์ด ํธ๋ค๋ฌ๋ ๋งค๊ฐ ๋ณ์๋ก ํด๋ผ์ด์ธํธ์์ ์ ์ก๋ UserTokenRequestDto๋ฅผ ๋ฐ๊ณ , ์๋ต๊ฐ์ผ๋ก Refresh Token์ ๋ฐํํด ์ค๊ฑฐ์์.
ํธ๋ค๋ฌ๊ฐ ํธ์ถ ๋๊ธฐ ์ Guard๊ฐ ๋์ํ์ฌ Refresh Token ๊ฒ์ฆ์ ์งํํ๋๋ฐ, ๊ทธ ๋ถ๋ถ์ ๋ํด์๋ ์๋์์ ์ด์ผ๊ธฐ ํด ๋ณผ๊ฒ์.
๐ฆ UserService
๋น์ฆ๋์ค ๋ก์ง์ ์์ ๊ฐ์ด ๋ง๋ค์ด ์ฃผ์์ด์.
์ต์ด 84๋ฒ์งธ ์ค์์ ํด๋ผ์ด์ธํธ์์ ์ ๋ฌํ RequestDto๊ฐ Null์ธ์ง๋ฅผ ํ์ธํ๊ณ , Null์ด ์๋๋ผ๋ฉด ์ด์ฉ์์ Email ์ฃผ์๋ฅผ Parsing(ํ์ฑ)ํ์ฌ JwtPayload ๊ฐ์ฒดํ๋ฅผ ํ์ฌ payload ์์ ๋ณ์๋ฅผ ๋ง๋ค์ด ์ฃผ์์ด์.
๊ทธ๋ฐ ๋ค ์๋ต๊ฐ์ผ๋ก ํด๋น payload์ Access Token ๋น๋ฐํค ๊ทธ๋ฆฌ๊ณ , ์ ํจ ๊ธฐ๊ฐ์ ์ด์ฉํ์ฌ ์๋ช
์ ํ ๋ค Access Token์ ๋ง๋ค์ด ๋ฐํํด ์ฃผ๋ ๋ก์ง์ด์์.
๐ฆ RequestDto
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
์ด ์ฝ๋๋ 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๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ ๋ง๋ฃ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ฒ ํด ์ฃผ์์ด์.
22๋ฒ์งธ ์ค์ JWT ๊ฒ์ฆ ์ ์ฌ์ฉํ ๋น๋ฐํค๋ฅผ ์ค์ ํ๋ ๋ถ๋ถ์ด์์. ์ฃผ๋๋ ์ค์ ํ์ผ์์ ๊ฐ์ ธ์จ .env.yml์ ๋ช
์ํjwt.refreshTokenSecret ์ด๋ผ๋ ๊ฐ์ ์ฌ์ฉํ๋๋ก ํด ์ฃผ์์ด์.
23๋ฒ์งธ ์ค์ ์ฝ๋ฐฑ ํจ์์ HTTP Request ๊ฐ์ฒด ์ ๋ฌ ์ฌ๋ถ๋ฅผ ์ ์ํ๋ ๋ถ๋ถ์ผ๋ก ์ฃผ๋๋ ture๋ก ์ค์ ํ์ฌ ์ฝ๋ฐฑ ํจ์์ HTTP Request ๊ฐ์ฒด๊ฐ ์ ๋ฌ๋๋๋ก ํด ์ฃผ์์ด์.
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
์ ์ฝ๋๋ ๋งค๊ฐ ๋ณ์๋ก ์ ๋ฌ๋ ์ด์ฉ์ ์ ๋ณด์ 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 ํด๋์ค๊น์ง ๋ฑ๋ก ์์ผ ์ฃผ๋ฉด ๋ผ์.
๐ฝ JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ - ํ๋กํ ํ์ธ
๐ฆ Controller
์์ ๊ฐ์ด ์ด์ฉ์๊ฐ ์์ ์ ํ๋กํ์ ํ์ธํ ์ ์๋ ๊ธฐ๋ฅ์ ๊ตฌํํด ๋ณด๋ ค๊ณ ํด์.
URL Parameter๋ก ์ด์ฉ์์ ID๋ฅผ ์ ๋ฌ ๋ฐ์ ๊ฑฐ๊ณ , JWT๋ฅผ ๋ฐ์ ๊ฒ์ฆํ ๋ค ํด๋น ๋ก์ง์ด ์งํ๋ ๊ฑฐ์์.
๐ฆ Service
์ด๋ ๊ฒ ID๋ฅผ ๋ฐ์ดํฐ ๋ฒ ์ด์ค์์ ์ฐพ์ ํด๋น ๊ณ์ ์ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ด์์.
๐ฝJWT๋ฅผ ์ด์ฉํ ์ธ์ฆ - Logout
์ด๋ฒ์ Logout ๊ธฐ๋ฅ์ ๋ํด ๋ง๋ค์ด ๋ณด๋ ค๊ณ ํด์.
์ฌ์ค JWT์ ๊ฐ๋
์์ Logout์ ์์ด์.
์๋ํ๋ฉด ํ ํฐ์ด ์ด์์๋ค๋ฉด ๊ณ์ ์ธ์ฆ์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด์์.
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ Logout ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์๊น์?
์ฃผ๋๋ HTTP Request Header์ HTTP Request Cookie์ ํ ํฐ์ ๋ด์ ๋ณด๋ผ ์ ์๋๋ก ๊ตฌํํด ๋ณด์์์ด์.
๊ทธ๋ ๋ค๋ฉด Hader์ Cookie์ ํ ํฐ์ ์์ ๋ฒ๋ฆฌ๋ ์ ๋ต์ ์ทจํ๋ค๋ฉด Logout ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ง ์์๊น์?
๋ฐ๋ก ๊ตฌํํด ๋ณผ๊ฒ์!
๐ฆ Controller
์ ๋ผ์ฐํฐ ํธ๋ค๋ฌ๋ ๋ถ์ํด ๋ณด๋ฉด ์ต์ด ํธ๋ค๋ฌ๊ฐ ํธ์ถ ๋๊ธฐ ์ ์ JwtAuthenticationGuard๊ฐ ํธ์ถ๋์ด ์ด์ฉ์์ JWT Access Token ๊ฒ์ฆ์ด ์ด๋ค์ง๊ฒ ๋ผ์.
Access Token์ด ์ ์์ด๋ผ๋ฉด ํด๋น ํธ๋ค๋ฌ๊ฐ ๋์ํ๊ฒ ๋๋๋ฐ, ์ด ํธ๋ค๋ฌ๋ UserTokenRequestDto์ Response๋ฅผ ๋งค๊ฐ ๋ณ์๋ก ๋ฐ๊ณ ์์ด์.
๊ทธ๋ฐ ๋ค ์๋ต๊ฐ์ ์ ์ ์ฒ๋ฆฌ์ธ์ง ์๋์ง์ ๋ํ HTTP Response ๊ฐ์ฒด๋ฅผ ๋ฐํํด ์ฃผ๋๋ก ์ฒ๋ฆฌํด ์ฃผ์์ด์.
๐ฆ AuthenticationService
๋น์ฆ๋์ค ๋ก์ง์์ ์ต์ด 121๋ฒ์งธ ์ค์ ๋งค๊ฐ ๋ณ์๋ก ์ ๋ฌ๋ ์ด์ฉ์์ Email ์ฃผ์๋ก ๋ฐ์ดํฐ ๋ฒ ์ด์ค์์ ์ด์ฉ์ ์ ๋ณด๋ฅผ ์กฐํํ๊ณ , 123 ~ 125๋ฒ์งธ ์ค์ ํตํด ํด๋น ์ด์ฉ์ ์ ๋ณด๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฆํ๊ณ ์์ด์.
์ด์ฉ์ ์ ๋ณด๊ฐ ์กด์ฌํ๋ฉด 127 ~ 128๋ฒ์งธ ์ค์ ํตํด Entity์ Refresh Token๊ณผ Refresh Token ์ ํจ ๊ธฐ๊ฐ์ ์ด๊ธฐํ ํด ์ฃผ์์ด์.
๊ทธ๋ฆฌ๊ณ , ๋ฐ์ดํฐ ๋ฒ ์ด์ค์๋ ์ด๊ธฐํ๋ ๊ฐ์ ๋ฃ์ด์ฃผ๊ธฐ ์ํด 144 ~ 150๋ฒ์งธ ์ค ๋ก์ง์ ์์ฑํด ์ฃผ์์ด์.
๊ทธ๋ฐ ๋ค 152๋ฒ์งธ ์ค์ ํตํด HTTP Response Cookie์ Refresh Token์ ๋ฌด๋ ฅํ ์ํค๊ธฐ ์ํ ๋ก์ง์ ๋ฐ๋ก ๊ตฌํํด ์ฃผ์์ด์.
๊ทธ๋ ๊ฒ ํด์ Refresh Token์ด ๋ฌด๋ ฅํ ๋๋ฉด ๋ก๊ทธ ์์์ด ์ฒ๋ฆฌ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ 132๋ฒ์งธ ์ค๊ณผ ๊ฐ์ด ์๋ตํด ์ฃผ๋๋ก ํด ์ฃผ์์ด์.
๐ฆ CookieService
์ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด ๋งค๊ฐ ๋ณ์๋ก ๋ค์ด์จ HTTP Response ๊ฐ์ฒด์ ๋ด๊ธด Cookie์ refreshToken์ด๋ผ๋ ์ด๋ฆ์ Cookie๋ฅผ ์ง์ฐ๊ณ , 26๋ฒ์งธ ์ค์ ํตํด HTTP Response Header์ Authorization ์ด๋ฆ์ ๊ฐ์ ์ง์ฐ๋๋ก ํด ์ฃผ์์ด์.
๐ฝ ์ ๋ง๋ค์๋? ๐ค
๐ฆ Swagger ์์
์ด์ Logic(๋ก์ง)์ ์ด๋์ ๋ ๊ตฌํํ์ผ๋ ์ ๊ตฌํํ๋์ง ํ์ธํด ๋ณด๋ ค๊ณ ํด์.
๊ทธ ์ ์ ํ์ฌ ์ฃผ๋๊ฐ ๊ตฌ์ฑํ Swagger(์ค์จ๊ฑฐ)๋ ์์ฒญ ํค๋๊ฐ์ ๋ฃ์์๊ฐ ์์ด์.
์์ฒญ ํค๋๊ฐ์ ๋ฃ์ ์ ์๋๋ก ์์ ํด๋ณด๋๋ก ํ ๊ฒ์.
11๋ฒ์งธ์์ 19๋ฒ์งธ ์ค๊ณผ ๊ฐ์ด ์ถ๊ฐํด ์ฃผ๋ฉด JWT๋ฅผ ๋ฃ์ ์ ์๋ ๊ณต๊ฐ์ ๋ง๋ค ์ ์์ด์.
์์ ๊ฐ์ด Authorize ๋ฒํผ์ ๋๋ฅด๋ฉด JWT Token์ ์
๋ ฅํ ์ ์๋ ๋ชจ๋ฌ์ฐฝ์ ๋ง๋ ์ ์๋ ๊ฑธ ํ์ธํ ์ ์์ด์.
๐ฆ ํ์ ๊ฐ์
์ค์จ๊ฑฐ๋ฅผ ์ด์ฉํ์ฌ ํ์ ๊ฐ์
์ด ์ ์์ ์ผ๋ก ๋๋์ง ํ์ธํด ๋ณผ๊ฒ์.
์์ ๊ฐ์ด ์ ์์ ์ผ๋ก ๊ฐ์
๋ ๊ฑธ ํ์ธํ ์ ์์ด์.
๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ๊ฐ์ด ์ ๋ค์ด๊ฐ๊ฑธ ํ์ธํ ์ ์์ด์.
๋์ผํ Email ์ฃผ์๋ก ๊ฐ์
์๋๋ฅผ ํ๋ฉด ์ด๋ฏธ ๋ฑ๋ก ๋์๋ค๊ณ ๋์ค๋ ๊ฒ๋ ํ์ธํ ์ ์์ด์.
์ ํจ์ฑ ๊ฒ์ฌ๋ ์ ์งํ๋๋๊ฑธ ์ ์ ์์ด์.
๐ฆ ๋ก๊ทธ์ธ
ํ์ ๊ฐ์
์ด ๋์์ผ๋ ๋ก๊ทธ์ธ์ ์งํํ๊ณ , JWT Token์ด ๋ฐํ๋๋์ง ํ์ธํด ๋ณผ๊ฒ์.
์์ ๊ฐ์ด ์ ์์ ์ผ๋ก ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๊ฐ ๋๊ณ , Access Token๊ณผ Refresh Token๊น์ง ๋ฐ๊ธ ๋๋๊ฑธ ํ์ธํ ์ ์์ด์.
๊ทธ๋ฆฌ๊ณ ์์ ๊ฐ์ด HTTP Response Header์ authorization์ด๋ผ๋ ์ด๋ฆ์ผ๋ก Bearer ๋ฌธ์์ด๊ณผ ,๋ฅผ ๊ธฐ์ค์ผ๋ก Access Token๊ณผ Refresh Token์ด ์ ๋ฌ ๋ ๊ฑธ ํ์ธํ ์ ์์ด์.
๊ทธ๋ฆฌ๊ณ ์์ ๊ฐ์ด HTTP Response Cookie์ Access Token๊ณผ Refresh Token์ด ๋ด๊ฒจ ์จ ๊ฑธ ํ์ธํ ์ ์์ด์.
๐ฆ Refresh Token
Access Token์ด ๋ง๋ฃ ๋์์ ๊ฒฝ์ฐ Refresh Token์ผ๋ก Access Token์ ์ ๋ฐ๊ธ ๋ฐ๋์ง ํ์ธํด ๋ณด๋ ค๊ณ ํด์.
์ต์ด ๋ง๋ฃ ๊ธฐํ์ด ์ง๋๋ฒ๋ฆฐ Access Token์ ์ด์ฉํ์ฌ ํ์ ์ ๋ณด๋ฅผ ์กฐํํ๋ ค๊ณ ํ ๋, ์์ ๊ฐ์ด ์ ๊ทผํ ์ ์๋ ๊ฒ์ ์ ์ ์์ด์.
์ด ๋, ์์ ๊ฐ์ด API๋ฅผ ํธ์ถํด์ฃผ๋ฉด HTTP Request Cookie์ ๋ด๊ธด refreshToken ๊ฐ์ ํตํด Refresh Token์ ๊ฒ์ฆํ๊ณ , ์ ์ Refresh Token์ด๋ฉด ์์ ๊ฐ์ด Access Token์ ๋ค์ ๋ฐ๊ธ ๋ฐ๋ ๊ฑธ ์ ์ ์์ด์.
๐ฆ ํ๋กํ ์กฐํ
์์ ๊ฐ์ด Token๊ณผ ์ด์ฉ์์ ID๋ฅผ URL Parameter๋ก ์ ๋ฌํ์ฌ ์์ฒญ์ ๋ณด๋ด๋
์ ์์ ์ผ๋ก ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ ๊ฑธ ํ์ธํ ์ ์์ด์.
๐ฆ ๋ก๊ทธ ์์
์์ ๊ฐ์ด Access Token์ HTTP Request Header์ Authrozation์ Bearer Token์ผ๋ก ๋ฃ์ด์ฃผ๋ฉด ์ ์์ ์ผ๋ก ๋ก๊ทธ์์์ด ๋๋๊ฑธ ํ์ธํ ์ ์์ด์.
๋ค์ ๊ธ์์ Nest.js Multer๋ฅผ ์ด์ฉํ Image ์ ์ฅ์ ๋ํด ์ค์ตํด ๋ณด๋๋ก ํ ๊ฒ์!
"์ด ํฌ์คํ ์ ์ฟ ํก ํํธ๋์ค ํ๋์ ์ผํ์ผ๋ก, ์ด์ ๋ฐ๋ฅธ ์ผ์ ์ก์ ์์๋ฃ๋ฅผ ์ ๊ณต๋ฐ์ต๋๋ค."
๐ง ์ฐธ๊ณ ์๋ฃ