2022. 4. 11. 01:37ใProgramming Project ์์ ์ค/๋ด์ฉ ์ ๋ฆฌ
Project Git Hub
๐ ๋ชฉ์ฐจ
โ [BackEnd][Node.js][Nest.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - ํ์ ๊ฐ์
โ [BackEnd][Node.js][Nest.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - JWT๋ฅผ ์ด์ฉํ Login
โ [BackEnd][Node.js][Nest.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - Custom Decorator
๐ ๋ถ๋ก
โ [Node.js] Node.js ๊ธฐ์ด
โ [JavaScript] ๊ธฐ๋ณธ ๋ฌธ๋ฒ
โ [BackEnd][Node.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - ์ด๊ธฐ ๊ตฌ์ฑ
โ [BackEnd][Node.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - ์ด๊ธฐ ๊ตฌ์ฑ : DB ์ฐ๊ฒฐ
โ [BackEnd][Node.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - ์ด๊ธฐ ๊ตฌ์ฑ : DB ์ฐ๊ฒฐ(Sequelize)
โ [Web] URL์ ์ดํด
โ [Spring] Spring์ด๋?
โ[Node.js][Nest.js] ์ ํจ์ฑ ๊ฒ์ฌ์ Data ํ์ ๋ณํํ๊ธฐ - Pipe
โ[Node.js][Nest.js] Configuration (์ค์ )
โ[Node.js][Nest.js]TypeORM
โ [Node.js][Nest.js] Logger ๊ธฐ๋ฅ
โ[JavaScript] Promise์ async ๊ทธ๋ฆฌ๊ณ await
โ [Nest.js] API ์ค๊ณ - ์์ฒญ, ์๋ต ๊ฐ์ฒด (Request, Response Object)
โ [์ ๋ณด๋ณด์] JWT(JSON Web Token) ์ด๋?
โ [Nest.js] Middle Ware(๋ฏธ๋ค์จ์ด)
๐ค ๋ด๊ฐ ๋ง๋ Error
โ [Node.js][Error] Cannot find module '.dotenv'
๐ ํ์ ๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ
๐ฝ ์ด๊ธฐ ๊ตฌ์ฑ
์ด๋ฒ `Nest.js`๋ฅผ ํตํ Project๋ ๊ธฐ์กด์ `Spring Boot`๋ก ๊ตฌ์ฑํ๊ณ ์๋ Project๋ฅผ ๋ฐ๋ผํด๋ณด๋ฉด์ ๊ณต๋ถํด ๋ณด๋๋ฐ ๊ทธ ์ด์ ์ด ๋ง์ถฐ์ง Project์ธ ๊ฒ์ด์์.
๋ฐ๋ผ์ ์ด๋ฏธ Table์ด ์กด์ฌํ๊ณ , ์ด Table์ ์ด์ฉํด์ Project๋ฅผ ์งํํด๋ณด๊ณ ์ ํ๋ ๊ฒ์ด์์.
๐ฆ Nest CLI๋ฅผ ํตํ ์ด๊ธฐ ๊ตฌ์ฑ - Module
๋ช ๋ น์ด
nest g module {Module-Name}
`Nest CLI`๋ฅผ ํตํด ์์ ๊ฐ์ด Module์ ์์ฑํ๋ฉด Directory๋ฅผ ๋ง๋ค์ง ์์๋ ์์์ ๋ง๋ค์ด ์ง๋ ๊ฒ์ด์์.
๐ฆ Nest CLI๋ฅผ ํตํ ์ด๊ธฐ ๊ตฌ์ฑ - Controller
๋ช ๋ น์ด
nest g controller {Contorller-Name} {--no-spec}
๐ฆ Nest CLI๋ฅผ ํตํ ์ด๊ธฐ ๊ตฌ์ฑ - Service
๋ช ๋ น์ด
nest g service {Service-Name} {--no-spec}
๋ช ๋ น์ด์ ๋ํ ๋ด์ฉ์ `์ด ๊ณณ`์ ์ค๋น ํด ๋์์ด์!
๐ฆ User Entity ๋ง๋ค๊ธฐ
์ต์ด `User Entity`๋ ์์ ๊ฐ์ด ์์ฑํด ์ค ๊ฒ์ด์์.
๐ฆ Reposiroty ๋ง๋ค๊ธฐ
`Repository`๋ ์์ ๊ฐ์ด ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
์ฐธ๊ณ ๋ก `Repository`์ `Eentity`๋ ๋ช
๋ น์ด๊ฐ ์๋ ์ง์ `TypeScript File`์ ๋ง๋ค์ด์ ์งํํ ๊ฒ์ด์์.
์ด๋ฒ์๋ ์์ฑ๋ `User Repository`๊ฐ ๋ค๋ฅธ ๊ณณ์์๋ ์ด์ฉ๋ ์ ์๊ฒ ํ๊ธฐ ์ํด์ `auth Module`์ `import`๋ฅผ ์์ผ์ค ๊ฒ์ด์์.
9๋ฒ์งธ ์ค๊ณผ ๊ฐ์ด `TypeOrmModule forFeture()`๋ฅผ ์ด์ฉํ์ฌ `auth Module`์์ ๋งค๊ฐ ๋ณ์๋ก `UserRepository`์ธ์คํด์ค๋ฅผ ์ ๋ฌํ์ฌ ๋ฑ๋กํด ์ค ๊ฒ์ด์์.
์ฐธ๊ณ ๋ก ๋ช ๋ น์ด๋ก ๋ง๋ค์๋ `Controller`์ `Service`๋ ์๋์ผ๋ก ๋ฑ๋ก ๋์ด ์๋ ๊ฒ์ด์์.
์ด๋ฒ์๋ `User Repository`๋ฅผ `auth Service`์์ ์ด์ฉํ๊ธฐ ์ํด `DI(Dependency Injection)` ํด ์ค ๊ฒ์ด์์.
์์ ๊ฐ์ด `์์ฑ์ ์ฃผ์ `์ ํตํด `User Repositoy`๋ฅผ `DI(Dependency Injection)` ํด ์ค ๊ฒ์ด์์.
๐ฝ ๊ธฐ๋ฅ ๊ตฌํ
๐ฆ DTO ๋ง๋ค๊ธฐ
์ต์ด `Client`๊ฐ `Request(์์ฒญ)`์ ๋ณด๋ผ ๋ ๊ทธ๊ฒ์ ๋ด์ ๊ทธ๋ฆ์ ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
์์ ๊ฐ์ด `Pipe`๋ฅผ ์ด์ฉํ `์ ํจ์ฑ ๊ฒ์ฌ(Validation)` ๊น์ง ์งํํ ์ ์๋ DTO๋ฅผ ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
`Pipe`์ ๋ํด ์๊ณ ์ถ์ผ์ ๋ถ๋ค์ ์ํด `์ด ๊ณณ`์ ์ค๋น ํด ๋ ๊ฒ์ด์์.
โบ ๋ณ๊ฒฝ ์ฌํญ
๋ณ๊ฒฝ ์ฌํญ์ ๋ํด ์์๋ณด๋ฉด ๋จผ์ `@ApiProperty` Decorator(JAVA = Annotaion)์ ์ถ๊ฐ ํด ์คฌ๋๋ฐ,
Swagger์์ ์์ ๊ฐ์ด ๊ฐ Property(JAVA = Member ๋ณ์)์ ๋ํ ์ค๋ช ์ด ๋์ฌ ์ ์๋๋ก ํ๊ธฐ ์ํจ์ด์์.
๊ทธ๋ฆฌ๊ณ , `Pipe`๋ฅผ ์ด์ฉํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํด `@IsNotEmpty() = ๋น์ด ์๋ ๊ฐ์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ`, `@IsString() = ์ ๋ ฅ๊ฐ์ด ๋ฌธ์์ด์ธ์ง ๊ฒ์ฌ`, `@IsEmail = ์ ๋ ฅ๊ฐ์ด Mail ํํ์ธ์ง ๊ฒ์ฌ`๋ฅผ ์ฌ์ฉํด ์ค ๊ฒ์ด์์.
๐ฆ Repository ๊ตฌํ
ํ์๊ฐ์ ์ด๋ ๊ธฐ๋ฅ์ `CRUD`์์ `C(Create)`์ ํด๋นํ๊ธฐ ๋๋ฌธ์ `Repository`์์ Data Base์๊ฒ Insert Query๋ฅผ ๋ ๋ฆด ์ ์๋๋ก ํด ์ค์ผ ํ๋ ๊ฒ์ด์์.
28๋ฒ์งธ ์ค์ ์์ฒญ ๋ด์ฉ์ ๋ด์ DTO์ ๋ค์ด ์์ ๋ด์ฉ๋ค์ ๊ฐ๊ฐ์ ์์ํ ๋ณ์์ ๋ด์์ฃผ๋ ๊ฒ์ด์์.
๊ทธ๋ฐ ๋ค `create()`์ ์ด์ฉํด์ ๊ทธ ๊ฐ๊ฐ์ ๋ณ์๋ค์ ์์ํ ๊ฐ์ฒด user๋ฅผ ์๋กญ๊ฒ ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
๊ทธ๋ฆฌ๊ณ , `save()`๋ฅผ ํตํด Data Base์ Insert Query๊ฐ ๋ ์๊ฐ๋๋ฐ, ํด๋น ๊ฐ์ฒด๋ฅผ ๋ด์ ๋ ์๊ฐ๊ฒ ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
์ฌ๊ธฐ์ `Promise`, `async`, `await`์ ๋ํ ๋ด์ฉ์ `์ด ๊ณณ`์ ์ค๋น ํด ๋ ๊ฒ์ด์์.
๋ฐํ Type์ `Promise`๋ฅผ ๋ฃ๊ณ , ์ด๋ค ๋ฐํ๊ฐ๋ ์์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ `<void>`๋ฅผ ๋ฃ์ด์ค ๊ฒ์ด์์.
Data Base์ ์์ ์ด ์ฒ๋ฆฌ ๋ ๋๊น์ง `await`์ ๋ง๋ `JavaScript`๋ ์์ ์ด ๋๋ ๋๊น์ง ๋๊ธฐ ์ํ๋ก ์๋ค๊ฐ Data Base์์ ์ด ๋๋๋ฉด ๊ทธ ๊ฐ์ ๋ฐํ ํด ์ฃผ๋ ๊ฒ์ด์์.
โบ ๋ณ๊ฒฝ ์ฌํญ
์ฃผ๋ํ๋์ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๊ฐ ๋์๋ค๋ฉด `Response(์๋ต) Data`๋ฅผ ์ป๊ณ ์ถ์ ๊ฒ์ด์์.
๊ทธ๋์ ๋ฐํ Type(220๋ฒ์งธ ์ค - Promise<{...}>)๋ฅผ ํตํด `return(238 ๋ฒ์งธ ์ค)`์ ๊ฐ์ด ๋ฐํ๋๋๋ก ํด์ค ๊ฒ์ด์์.
๐ฆ Service ๊ตฌํ
`Service`๋ ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ๋ฅผ ์ํ Layer์ธ ๊ฒ์ด์์. ์ด๊ณณ์์๋ ๋ฑํ ์ฒ๋ฆฌํ ๊ฒ์ด ์์ด ๋ฐ๋ก `Repository`๋ฅผ ํธ์ถํ๊ณ , ๋ฐํ ๊ฐ์ ๋ฐํํด ์ฃผ๋ ๊ฒ์ด์์.
๐ฆ Contoller ๊ตฌํ
`Spring Boot`์์ API URI๋ฅผ ์ค์ ํ๊ธฐ ์ํด์๋ ์ต์ด Class ์ด๋ฆ ์์ `@RequestMapping`์ ์ด์ฉํด์ `/api/user`์ ๊ฐ์ด ์
๋ ฅ์ ํด์ฃผ์๋๋ฐ, `Nest.js`์์๋ URI ๋งจ ์์ '/'๋ฅผ ์ ๊ฑฐํด์ ์
๋ ฅํด ์ค์ผ ํ๋ค๋ ๊ฒ์ ๊ธฐ์ตํด์ผ ํ๋ ๊ฒ์ด์์.
29 ~ 30๋ฒ์งธ ์ค์ Swagger ์ค์ ๋ถ๋ถ์ด์์.
29๋ฒ์งธ ์ค `@ApiOeration`์ ์์ ๊ฐ์ด 'ํ์ ์ธ์ฆ / ์ธ๊ฐ API'๋ผ๋ ํ์๊ฐ ์๋ ๊ฒ์ฒ๋ผ ํด๋น API ์ค๋ช
์ ์
๋ ฅํด ์ฃผ๋ ๋ถ๋ถ์ธ ๊ฒ์ด์์.
30๋ฒ์งธ ์ค์ `Response`์ HTTP Status Code์ ๋ฐ๋ผ ํด๋น Code๊ฐ ์ด๋ค ๋ด์ฉ์ธ์ง๋ฅผ ์ค๋ช
ํด ์ฃผ๋ ๋ถ๋ถ์ธ ๊ฒ์ด์์.
<์์ ๋ถ๋ถ์์ ์์ ๋์์ต๋๋ค!>
93๋ฒ์งธ ์ค์ `@Post`๋ฅผ ํตํด ํด๋น Method๋ HTTP Method Post๋ก ๋์ํ๊ฒ ๋ง๋ค์๊ณ , URI ๋ง์ง๋ง์ `/signup`์ด ๋ค์ด์์ผ ํธ์ถ ๋๊ฒ ํด ์ค๊ฒ์ด์์. ๊ทธ๋ฐ ๋ค ๋งค๊ฐ ๋ณ์๋ก ํ์ ๊ฐ์ ์ ์์ฑํด์ผ ํ ๋ด์ฉ์ ๋ด์ DTO๋ฅผ ๋ฐ๊ณ , `@Res()`๋ฅผ ๋ฐ๋๋ฐ, ์ด ๋ด์ฉ์ `์ด ๊ณณ`์ ๋ฐ๋ก ์ค๋น ํด ๋ ๊ฒ์ด์.
์ด๋ฅผ ํตํด 101๋ฒ์งธ ์ค์ ๋ฐํ์ ํ ๋ `res.status()`๋ฅผ ํตํด Http Status Code์ `JSON` ํํ๋ก Data๋ฅผ ๋ณด๋ผ ์ ์๋ ๊ฒ์ด์์.
99๋ฒ์งธ ์ค์ ๋ณด๋ฉด ๋จผ์ `DI(Dependency Injection)`ํ `Service`์ signUp()์ ํธ์ถํด์ DTO๋ฅผ ๋ณด๋ด๊ณ , ํด๋น ์์ ์ด ์ฒ๋ฆฌ ๋ ๋๊น์ง ๋๊ธฐ ์ํค๊ธฐ ์ํด `await`์ ์ ์ธํ ๊ฒ์ด์์. ๊ทธ๋ ๊ฒ ๋ฐํ๊ฐ์ด ์ค๊ฒ ๋๋ฉด `UserEntity` Type์ ์์ํ ๊ฐ์ฒด user์ ๊ฐ์ด ๋ด๊ธฐ๊ฒ ๋๋ ๊ฒ์ด์์.
์๋ฃํ Type์ด ๋ค์ด๊ฐ๋ ๊ณณ์ ์ฌ๋ฌ ๋ณ์๊ฐ ์๋๋ฐ, ์ด ๋ถ๋ถ์ Respository์์ HTTP Response์ ๋ง๊ฒ ๋ต๋ณ์ ์ฃผ๊ธฐ ์ํด์ ์์ ์ ํ๊ธฐ ๋๋ฌธ์ธ ๊ฒ์ด์์.
๐ฝ ๊ฒฐ๊ณผ
๐ฆ POSTMAN
์ ๋์ํ๋์ง Postman์ ํตํด Test ํด ๋ณผ ๊ฒ์ด์์.
์์ฃผ ์ ๋๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ ๊ฒ์ด์์.
Data Base๋ฅผ ํ์ธ ํด ๋ณด๋ 9๋ฒ์งธ ์ค์ ์ ์์ ์ผ๋ก Data๊ฐ ์ ๋ ฅ ๋ ๊ฒ์ด์์.
๐ ์ ํจ์ฑ ๊ฒ์ฌ
์์ ๊ฐ์ด DTO๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ด์์.
์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํด ์ธ ๊ฐ์ง๊ฐ ๋ ์ถ๊ฐ๊ฐ ๋์๋๋ฐ, `@MinLength() = ์ต์ ์ ๋ ฅ๋์ด์ผ ํ๋ ๋ฌธ์์ ๊ฐ์`, `@MaxLenth() = ์ต๋ ์ ๋ ฅํ ์ ์๋ ๋ฌธ์์ ๊ฐ์`, `@Mathces() = ์ ๊ท ํํ์์ ์ด์ฉํ ์ ๋ ฅ ๊ฐ๋ฅํ ๋ฌธ์ ๋ฑ๋ก`์ ์ถ๊ฐํ ๊ฒ์ด์์. ๊ทธ๋ฆฌ๊ณ `@Mathces() = ์ ๊ท ํํ์์ ์ด์ฉํ ์ ๋ ฅ ๊ฐ๋ฅํ ๋ฌธ์ ๋ฑ๋ก`์์ `message`๋ฅผ ํตํด ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ์๋ฐ๋๋ฉด ๊ฒฝ๊ณ Message๋ฅผ ๋ฑ๋กํ ์ ์๋ ๊ฒ์ด์์.
์ ๊ท ํํ์๊ณผ ๊ฐ์ ๊ฒ์ ๋ํ ๋ด์ฉ์ `Spring Boot` Project๋ฅผ ํ๋ฉด์ `์ด ๊ณณ`์ ์ค๋น ํด ๋ ๊ฒ์ด์์.
`Controller`์์ `Pipe`๋ฅผ ์ด์ฉํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ 93๋ฒ์งธ ์ค์ ๋งค๊ฐ ๋ณ์ ์ชฝ์ ๋ณด๋ฉด `@Body()`์์ `ValidationPipe`๋ฅผ ๊ผญ ๋ฃ์ด์ค์ผ ํ๋ ๊ฒ์ด์์.
์์ ๊ฐ์ด ํด์ค์ผ `Request(์์ฒญ)`์ด ๋ค์ด์์ ๋, Method๊ฐ ๋์ํ๊ธฐ ์ ์ DTO์ ์ ๋ ฅ๋ Decorator๋ฅผ ์ฐธ๊ณ ํ์ฌ Validation์ ํ๋ ๊ฒ์ด์์.
๐ฝ ๊ฒฐ๊ณผ
๐ฆ POSTMAN
์์ ๊ฐ์ด ์ ์์ ์ผ๋ก ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ์งํ๋๋ ๊ฒ์ ์ ์ ์๋ ๊ฒ์ด์์.
๐ ID Unique Value Config
์ด๋ฒ์๋ ํ์ ๊ฐ์
์ ํ ๋, ๋ณ๋ช
, ID๊ฐ ์ด๋ฏธ ๋ฑ๋ก๋์ด ์๋ค๋ฉด `Error`๋ฅผ ๋ฐํํ ์ ์๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ๊ฒ์ด์์.
์ด ๋ฐฉ๋ฒ์ ๊ตฌํํ๊ธฐ ์ํด์๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์๊ฐ ํด ๋ณผ ์ ์๋ ๊ฒ์ด์์.
โ `Repository`์์ `findOne()`์ ์ด์ฉํด์ ์ด๋ฏธ ๊ฐ์ Data๊ฐ ์๋์ง๋ฅผ ํ์ธํ๊ณ , ์๋ค๋ฉด Data ์ ์ฅ.
- ๋จ์ : Data Base์ ๋๋ฒ ์ ๊ทผ.
โ Data Base Level์์ ๋ง์ฝ ๊ฐ์ ๊ฐ์ ๊ฐ์ง ํ์์ด ์๋ค๋ฉด `Error`๋ฅผ ๋์ ธ์ฃผ๋ ๋ฐฉ๋ฒ.
๋จผ์ ๋๋ฒ์งธ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๋๋ก ํ ๊ฒ์ด์์.
์ต์ด `user.entity.ts`์ Class ์์ `@Unique()`๋ฅผ ์ด์ฉํ์ฌ ์ค๋ณต์ ๋ง๊ณ ์ํ๋ ๊ฐ๋ค์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
`Repository`์์ ๋ฌธ์ ๊ฐ ๋ฐ์ ํ์ ๋, try - catch ๊ตฌ๋ฌธ์ ์ด์ฉํด์ `Error` Mesaage๋ฅผ Handlingํ ์ ์๋ ๊ฒ์ด์์.
์ฃผ๋ํ๋์ `Spring Boot` Project์์ ์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ๊ณผ ๊ฐ์ด ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๊ตฌ์ฑํ ๊ฒ์ด์์.
๋ฌผ๋ก SQL ๋ฌธ์ด ๋๋ฒ ๋ ์๊ฐ๋ ๋ฌธ์ ๊ฐ ์์ง๋ง, ์ผ๋จ `Spring Boot`์ ๋์ผํ๊ฒ ๋ง๋ค์ด ๋ณด๋๋ก ํ ๊ฒ์ด์์.
์ฐธ๊ณ ๋ก ID, ๋ณ๋ช , Email, ํธ๋ํฐ ๋ฒํธ๊น์ง ๋ชจ๋ ์ค๋ณต ์ฒดํฌ๋ฅผ ํ๊ธฐ ๋๋ฌธ์ ID์ ๋ํ ๋ด์ฉ๋ง ์ ๋ฆฌ ํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
/**
* ํ์ ๊ฐ์
์ ๋ฑ๋ก๋ ID ์ ๋ณด ์ธ์ง ํ์ธ
* @param authDuplicateByUserIdDto - ํ์ ๊ฐ์
์ ๋ฑ๋ก ๋์ด ์๋ ID ์ธ์ง ํ์ธ์ ์ํ ์ด์ฉ์ ์
๋ ฅ ์ ๋ณด DTO
* @return User - ํด๋น ํ์์ ์ ๋ณด ๋ฐํ
* @see ""
*/
async duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto): Promise<{ messageKo: string; statusCode: number; data: User; messageEn: string }> {
this.logger.log("UserRepository์ duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto)์ด ํธ์ถ ๋์์ต๋๋ค!");
this.logger.log(`Service๋ก ๋ถํฐ ์ ๋ฌ๋ DTO ๋ด์ฉ : ${authDuplicateByUserIdDto.toString()}`);
this.logger.log(`DB์ Select()๋ฌธ์ ํตํด ํด๋น ID๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฌ ํ๊ฒ ์ต๋๋ค!`);
const { username } = authDuplicateByUserIdDto;
this.logger.log(`username : ${username}`)
const findByUserID = await this.findOne({
where: { username : username}
});
this.logger.log(`DB์์ ์กฐํ๋ ๊ฐ : ${findByUserID}`);
if (findByUserID) {
this.logger.log("์ด๋ฏธ ์กด์ฌํ๋ ID ์
๋๋ค! 409 Code์ ํจ๊ป \"์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ ์
๋๋ค!\" ๋ฐํ ํ๊ฒ ์ต๋๋ค!")
return {
statusCode: 409,
messageKo: "์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ ์
๋๋ค!",
messageEn: "Conflict",
data: findByUserID,
};
} else {
this.logger.log("์ค๋ณต ๋๋ ๊ฐ์ด ์์ต๋๋ค! 200 Code์ ํจ๊ป \"์ฌ์ฉ ๊ฐ๋ฅ!\" ๋ฐํ ํ๊ฒ ์ต๋๋ค!");
return {
statusCode: 200,
messageKo: "์ฌ์ฉ ๊ฐ๋ฅ!",
messageEn: "OK",
data: findByUserID,
};
} // if (findByUserID === null || findByUserID === undefined) ๋
} // duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto) ๋
๋จผ์ `Repository`๋ถํฐ ์ดํด ๋ณผ ๊ฒ์ด์์. ์ต์ด 39๋ฒ์งธ ์ค์ ๋ณด๋ฉด ์ ๋ ฅ์ผ๋ก ๋ค์ด์จ DTO์์ `username` ๋ด์ฉ์ ๋นผ๊ธฐ ์ํด ์์ํ ๋ณ์ username์ ๊ฐ์ ๋ฃ์ด์ค ๊ฒ์ด์์.
๊ทธ๋ฐ ๋ค `findOne() = select * from user where username = ${username]`์ ์ด์ฉํด์ DB์์ Client๊ฐ ID ์ค๋ณต์ฒดํฌ๋ฅผ ์ํด ์ ๋ฌํ ๊ฐ ID ๊ฐ์ด ์๋์ง ์กฐํํ๋๋ก ํ ๊ฒ์ด์์.
`findOne()`์ ๋ณด๋ฉด options๋ก `where`๋ฅผ ํตํด `username`์ ์กฐํํ๋๋ฐ, [where : { username : username}] ๋ถ๋ถ์ ์์ ํ ๋ณ์ username์ด ๊ฐ์ง ๊ฐ์ ๊ฐ์ง๊ณ ์กฐํํ๋ผ๊ณ ํ ๊ฒ์ด์์.
์ด๋ ๊ฒ ์ํ๋ฉด ์๋ฑํ๊ฒ `userId` Column์์ ์ฐพ๊ณ ์๋๋ผ๊ตฌ์.
๐ก ์ฐธ๊ณ ์ฌํญ
TypeORM SQL Show
JPA์์ Console์ ํตํด SQL๋ฌธ์ด ์ด๋ป๊ฒ ๋์ ธ์ง๋์ง๋ฅผ ๋ณด๊ธฐ ์ํด ์ค์ ํ๋ ๋ฐฉ๋ฒ์ด ์๋ ๊ฒ์ด์์.
MyBatis ์ญ์ ๋ง์ฐฌ๊ฐ์ง์ด๊ตฌ์.
TypeORM๋ ์ด ๊ธฐ๋ฅ์ด ์๋๋ฐ,
์์ ๊ทธ๋ฆผ 14๋ฒ์งธ ์ค์ฒ๋ผ logging์ all๋ก ํด์ฃผ๋ฉด Error๋ ๋ฌผ๋ก ์ด๊ณ , SQL ๋ฌธ์ด ์ด๋ป๊ฒ ๋์ ธ์ง๋์ง ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด์์.
/**
* ํ์ ๊ฐ์
์ ๋ฑ๋ก๋ ID ์ ๋ณด ์ธ์ง ํ์ธ
* @param authDuplicateByUserIdDto - ํ์ ๊ฐ์
์ ๋ฑ๋ก ๋์ด ์๋ ID ์ธ์ง ํ์ธ์ ์ํ ์ด์ฉ์ ์
๋ ฅ ์ ๋ณด DTO
* @return User - ํด๋น ํ์์ ์ ๋ณด ๋ฐํ
* @see ""
*/
async duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto): Promise<{ messageKo: string; statusCode: number; data: User; messageEn: string }> {
this.logger.log("UserRepository์ duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto)์ด ํธ์ถ ๋์์ต๋๋ค!");
this.logger.log(`Service๋ก ๋ถํฐ ์ ๋ฌ๋ DTO ๋ด์ฉ : ${authDuplicateByUserIdDto.toString()}`);
this.logger.log(`DB์ Select()๋ฌธ์ ํตํด ํด๋น ID๊ฐ ์กด์ฌํ๋์ง ๊ฒ์ฌ ํ๊ฒ ์ต๋๋ค!`);
const { username } = authDuplicateByUserIdDto;
this.logger.log(`username : ${username}`)
const findByUserID = await this.findOne({
where: { username : username}
});
this.logger.log(`DB์์ ์กฐํ๋ ๊ฐ : ${findByUserID}`);
if (findByUserID) {
this.logger.log("์ด๋ฏธ ์กด์ฌํ๋ ID ์
๋๋ค! 409 Code์ ํจ๊ป \"์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ ์
๋๋ค!\" ๋ฐํ ํ๊ฒ ์ต๋๋ค!")
return {
statusCode: 409,
messageKo: "์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ ์
๋๋ค!",
messageEn: "Conflict",
data: findByUserID,
};
} else {
this.logger.log("์ค๋ณต ๋๋ ๊ฐ์ด ์์ต๋๋ค! 200 Code์ ํจ๊ป \"์ฌ์ฉ ๊ฐ๋ฅ!\" ๋ฐํ ํ๊ฒ ์ต๋๋ค!");
return {
statusCode: 200,
messageKo: "์ฌ์ฉ ๊ฐ๋ฅ!",
messageEn: "OK",
data: findByUserID,
};
} // if (findByUserID === null || findByUserID === undefined) ๋
} // duplicateUserID(authDuplicateByUserIdDto: AuthDuplicateByUserIdDto) ๋
DB์์ ์กฐํ๋ ๊ฐ์ ์์ํ ๋ณ์ findByUserID์ ๋ด๊ธฐ๊ฒ ๋๋ ๊ฒ์ด์์.
๊ทธ๋ฐ ๋ค 49๋ฒ์งธ ์ค์ DB์์ ์กฐํ๋ ๊ฐ์ด ์๋์ง ํ์ธ์ ํ๋๋ฐ, ์๋ค๋ฉด ์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ด๋ผ๊ณ ๋ฐํ์ ์ฃผ๊ณ , ์๋ค๋ฉด ์ฌ์ฉ ๊ฐ๋ฅํ๋ค๊ณ ์๋ ค์ฃผ๋๋ก ๊ตฌํ์ ํ ๊ฒ์ด์์.
์ด๋ ๊ฒ ๊ตฌํํ ์ด์ ๋ `Spring Boot` Project ๊ธฐ๋ฐ์ `Vue.js` Project์์ `Spring Boot`์ ๋์ผํ๊ฒ API๋ฅผ ์ด์ฉํ ์ ์๋๋ก ํ๊ธฐ ์ํจ์ธ ๊ฒ์ด์์.
์ผ๋จ ๋น์ฆ๋์ค ๋ก์ง์ด `Repository`์์ ์ค์ํ๊ณ , `Service`๋ ๋จ์ํ `Repository`์ ํด๋น Method๋ง ํธ์ถํ๊ณ ์๋๋ฐ, ์ด๋ ๊ฒ ํ ์ ๋ฐ์ ์์๋ ์ด์ ๊ฐ DB์์ ์กฐํ๋ ๊ฐ์ ๋ํด Null ๊ฐ ํ์ธ์ ํด์ ์๋์ง ์๋์ง๋ฅผ ํ์ธํด์ผ ํ๋๋ฐ,
`Service`์์ ์ด Logic์ ๊ตฌํํ๋ฉด `Repository`์์ ๋์ด์จ ๊ฐ์ ์๊พธ `Promise Object` ๊ฐ ๋ด๊ฒจ์ ์ ๋ฌ์ด ๋๋ ๊ฒ์ด์์. DB์ ๊ฒฐ๊ณผ๊ฐ์ด ์๋ ์๋์ง์.
๊ทธ๋์ Optional์ฒ๋ผ ๋ญ๊ฐ ๋ณ๊ฒฝํด์ ์ฒ๋ฆฌ๋ฅผ ํด์ผ ํ๋๊ฑด์ง ์๋๋ฉด Method๊ฐ ์กด์ฌํ๋์ง 3์๊ฐ์ด ๋๊ฒ ์ฐพ์๋ดค์ง๋ง! ๐คฌ
์๋์์ ์ผ๋จ ์ด๋ ๊ฒ ๊ตฌํํ ๊ฒ์ด์์.
`Controller`๋ ๋จ์ํ `Service`๋ฅผ ํธ์ถํ๊ณ , `Route` ๊ธฐ๋ฅ์ ์ถฉ์คํ๊ฒ ๊ตฌํํ ๊ฒ์ด์์.
์ฃผ๋ชฉํด์ผ ํ ์ ์ `Repository`์์ result Object์ ๋ฐํํ Data๋ค์ ๋ด์์๋๋ฐ, ์ด ๋๋ฌธ์ ์ด ๊ณณ `Controller`์์๋ ๋ฐํ์ `Promise<any>`๋ก ํด ์ค ๊ฒ์ด์์.
๐ฝ ๊ฒฐ๊ณผ
๋ณด์๋ฉด ์์๊ฒ ์ง๋ง, `TypeORM`, `Nest.js`์ ์ฒ์ ์จ ๋ณด๋ ์ฃผ๋ํ๋์ ์์ฒญ๋๊ฒ ์ฝ์ง์ ํ ๊ฒ์ด์์. ๐ค
๐ฆ POSTMAN
DB์ ์กด์ฌํ๋ Data๋ฅผ ์กฐํํ๋ฉด ์์ ๊ฐ์ด ์ด๋ฏธ ์กด์ฌํ๋ค๊ณ ์๋ ค์ฃผ๋ ๊ฒ์ด์์.
์ด๋ ๊ฒ Validation ์ฒ๋ฆฌ๋ ์๋ฃ ํ์๊ตฌ์.
Data Base์ ํด๋น ๊ฐ์ด ์์ผ๋ฉด ์ฌ์ฉ ๊ฐ๋ฅํ๋ค๊ณ ์๋ ค์ฃผ๋ ๊ฒ์ด์์.
๐ฆ Vue.js
๐ ๋น๋ฐ๋ฒํธ ์ํธํ
์์ Logic์ ๊ตฌํํ๋ฉด์ ์์ง ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํด์ DB์ ์ ์ฅํ๋ Logic์ ๊ตฌํํ์ง ์์ ๊ฒ์ด์์.
`Spring Boot`์์๋ `Spring Security`์ Interface ๊ฐ์ฒด์ธ `PasswordEncoder()`๋ฅผ ์ด์ฉํ๋๋ฐ, `Nest.js`์์๋ `bcryptjs`์ ์ด์ฉํด์ ํด๋ณด๋ ค๊ณ ํ๋ ๊ฒ์ด์์.
๐ฝ ์ค์น
๐ฆ bcryptjs
๋ช ๋ น์ด
npm install bcryptjs --save
์ด์ฉ์์ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ฌธ์ผ๋ก ์ ์ฅ๋๋ ๊ฒ์ ๋ง๋ ์๋๋ ๊ฒ์ด์์.
๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํ ์ด์ (Encryption Key)์ ํจ๊ป ์ํธํ(์๋ฐฉํฅ)์ผ๋ก ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ, ์ด๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ ์๋์์.
์ด๋ ํ ์ํธ๋ฅผ ์ด์ฉํด์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํ ํ๊ณ , ๊ทธ ์ํธ๋ฅผ ์ด์ฉํ์ฌ ๋ณตํธํ๋ ๊ฐ๋ฅํ ๊ฐ๋ ์ด ์๋ฐฉํฅ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ธ ๊ฒ์ด์์. ์ด ์๊ณ ๋ฆฌ์ฆ์ ๋๋ถ๋ถ ์คํ ๋์ด ์๊ธฐ ๋๋ฌธ์ ์ํธํ ์ด์ (Encryption Key)๊ฐ ์ ์์ ์ธ ์ฌ์ฉ์์๊ฒ ํ์ทจ๋์ด ๋ณตํธํ๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ์์ฃผ ๋์ ๊ฒ์ด์์.
๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ `Hash ์๊ณ ๋ฆฌ์ฆ`์ ์ด์ฉํ ๋จ๋ฐฉํฅ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ์ด์ฉํ๋ ๊ฒ์ด์์.
์ด ๊ฐ๋ ์ ํ๋ฒ ์ํธํ๊ฐ ๋๋ฉด ๋ณตํธํ๋ฅผ ํ ์ ์๋๋ก ๋ง๋๋ ๊ฐ๋ ์ธ ๊ฒ์ด์์.
๋ํ์ ์ผ๋ก ์ด์ฉ๋๋ ๊ฒ์ด ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ์ด์์ฒด์ (Linux, Window)์ ๊ณ์ ์ ๋ง๋ค ๋์ Login ํ ๋ ์ด์ฉ๋๋ ๊ฒ์ด์์.
`Hash ์๊ณ ๋ฆฌ์ฆ`์ `Rainbow Table Attack`์ผ๋ก ์ํธํ ๊ฐ์ ๋น๊ตํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ณต๊ฒฉ์ ํ ์ ์๋๋ฐ, ๋๋ถ๋ถ ์ด์ฉ์๋ค์ ๊ฑฐ์ ๋น์ทํ ์ํธ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋ง๋ จ์ด์์. `A ์ด์ฉ์`, `B ์ด์ฉ์`๊ฐ ๋ ๋ค 1234๋ผ๋ ์ํธ๋ฅผ ์ด์ฉํ ๋, `Hash` ์๊ณ ๋ฆฌ์ฆ์ ํตํด ์ํธํ๋ฅผ ํ๊ฒ ๋๋ฉด ๋์ผํ ์ํธ๊ฐ์ด ๋์ค๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด์์.
์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ๊ฐ๋ ์ด ์๊ธ์ ์์ด ๋จ์ด `salt`์ ํ๋ฌธ ๋น๋ฐ๋ฒํธ๋ฅผ ํฉ์น ๋ค์ Hash ์๊ณ ๋ฆฌ์ฆ์ ํจ๊ป ์ํธํ๋ฅผ ํ์ฌ ๋์ผํ ํ๋ฌธ๊ฐ์ ์ ๋ ฅํด๋ ๋ค๋ฅธ ๊ฐ์ด ๋์ฌ ์ ์๋๋ก ํ๋ ๋ฐฉ๋ฒ์ ์ด์ฉํ๋ ๊ฒ์ด์์.
์์์ ์ด์ผ๊ธฐ ํ๋ ์ด์์ฒด์ ๋ ์ด `salt`๋ฅผ ์ด์ฉํด์ ์ํธํ๋ฅผ ํ๊ณ ์๋ต๋๋ค!
`bcryptjs`๋ ์์์ ์ด์ผ๊ธฐํ `Hash` ์๊ณ ๋ฆฌ์ฆ๊ณผ `salt` ๊ฐ๋ ์ ์ด์ฉํ ์ํธํ ์ง์ Module์ธ ๊ฒ์ด์์.
๐ฝ ๊ตฌํํ๊ธฐ
์ต์ด 9๋ฒ์งธ ์ค์ฒ๋ผ `bcryptjs` Module์ `Import` ํด์ค์ผ ํ๋ ๊ฒ์ด์์.
230๋ฒ์งธ ์ค์ ๋ณด๋ฉด `bcrypt Module`์์ `genSalt()`๋ฅผ ํธ์ถํ์ฌ Salt๊ฐ์ ๋ง๋ ๋ค ์ด๋ฅผ ์์ํ ๋ณ์ `salt`์ ๋ฃ์ด์ฃผ๋ ๊ฒ์ด์์.
๊ทธ๋ฐ ๋ค 235๋ฒ์งธ ์ค์ `bcrypt Module`์์ `hash()`๋ฅผ ํธ์ถํ๋ฉด์ ์ด์ฉ์์ ๋น๋ฐ๋ฒํธ์ `salt` ๊ฐ์ ๊ฐ์ด ์ ๋ฌํด์ฃผ์ด `Hash ์ํธํ`๊ฐ ์งํ ๋๋๋ก ํด ์ค ๊ฒ์ด๊ณ , ๊ทธ ๊ฐ์ ์์ํ ๋ณ์ `hashPassword`์ ๋ฃ์ด ์ค ๊ฒ์ด์์.
240๋ฒ์งธ ์ค์ ์์ํ ๋ณ์ `user`์ ์ด์ฉ์๊ฐ ํ์ ๊ฐ์ ์ ์ ๋ ฅํ๋ ๊ฐ๋ค์ ๊ฐ์ง๊ณ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฃผ๋๋ฐ, ์ด๋ ์ด์ฉ์์ ๋น๋ฐ๋ฒํธํ ์ํธํ ๋๋๋ก ์ค์ ํด ์ค ๊ฒ์ด์์.
๐ฝ ๊ฒฐ๊ณผ
๐ฆ POSTMAN
์ ์์ ์ผ๋ก ๊ฐ์ด ๋ค์ด๊ฐ ๊ฒ์ด์์.
๐ฆ Vue.js
Vue.js๋ฅผ ํตํด์๋ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๊ฐ ์๋ฃ ๋๋ ๊ฒ์ด์์!
๐ก ์ฐธ๊ณ ์ฌํญ
๊ทธ๋์ ์์ ํ ๊น?
์์ ๋ฐฉ๋ฒ์ ์์ ํ๊ฒ ์์ ํ์ง ์์ต๋๋ค.
๊ทธ ์ด์ ๋ Client์์ Server์๊ฒ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ฌํ ๋, ํ๋ฌธ๊ฐ์ผ๋ก ๋น๋ฐ๋ฒํธ๊ฐ ์ ๋ฌ๋๊ธฐ ๋๋ฌธ ์ ๋๋ค.
์ ์์ ์ธ ์ด์ฉ์๊ฐ Man in the Middle Attack(์ค๊ฐ์ ๊ณต๊ฒฉ)์ ํตํด ํ์ทจ๋ฅผ ํ๊ฑฐ๋, Sniping์ ํตํด ๋์ฒญ์ ํ๋ฉด ๋น๋ฐ๋ฒํธ๋ฅผ ์ฝ๊ฒ ํ์ทจํ ์ ์๋ ๊ฒ์ด์์.
๊ทธ๋์ ์ด๋ฅผ ๋ง๊ธฐ ์ํด์ SSL/TLS๋ฅผ ์ฐ๋ ๊ฒ์ด๊ณ , ์ง๊ธ ์ฃผ๋ํ๋ Blog ์ฃผ์์ฐฝ์ ๋ณด์๋ฉด https://๋ก ์์๋๋ ๊ฒ์ ๋ณผ ์ ์๋ ๊ฒ ์ ๋๋ค.
๋ค์ ํฌ์คํ ์ Login ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ๊ณต๋ถํด ๋ณด๋๋ก ํ ๊ฒ์ด์์.
๋ค์ ๊ธ : [BackEnd][Node.js][Nest.js] ์ฌ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ์๋น์ค - JWT๋ฅผ ์ด์ฉํ Login