nestjs

2.-1 NestJS ๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ (Postgresql)๊ณผ User ๋ชจ๋“ˆ

goldjun 2022. 8. 10. 16:53
 

๐Ÿ‘Œ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๊ฒฐํ•˜๊ธฐ !!

PostgreSQL ์„ค์น˜!!

์—ฌ๋Ÿฌ๋ถ„ ๋“œ๋””์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐํ•  ์‹œ๊ฐ„์ด ์™”์Šต๋‹ˆ๋‹ค!!.
์‹ค์ œ ์—ฐ์Šตํ•  ๋•Œ๋Š” ๋ฐฐ์—ด ๊ฐ™์€ ์ž„์˜ ์ ์ธ ๋†ˆ์„ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ ํ•˜๊ธดํ•˜์ง€๋งŒ... ๋ฐ”๋กœ ์—ฐ๊ฒฐํ•ด๋ณด์ฃ !

์ €ํฌ๋Š” Postgresql ์„ ์‚ฌ์šฉํ•  ๊ฒ๋‹ˆ๋‹ค!! ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค!!

Postgresql ์€ ์˜คํ”ˆ์†Œ์Šค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ SQL ํ‘œ์ค€์„ ์ž˜ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋‹จ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค!!
์„ค์น˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ •์‹ ๋ฌธ์„œ๋‚˜ ํƒ€ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋ฅผ ์ฐธ์กฐํ•ด์ฃผ์„ธ์š”. 
postgresql ์„ค์น˜๋ฐฉ๋ฒ•

ํŽธ์˜๋ฅผ ์œ„ํ•ด pgAdmin ํˆด์„ ์ด์šฉํ•˜์—ฌ postgresql ์„ ์ œ์–ดํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.!!

๊ธฐ์กด์˜ ์„œ๋ฒ„ (PostgreSQL 13 ์ด๊ตฐ์š” ์ €๋Š”) ๋ฅผ ์šฐํด๋ฆญํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“ญ์‹œ๋‹ค!!
์ €๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„์„ nest_blog ๋ผ๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค!!

์›ํ•˜์‹œ๋Š” ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์…”๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์Šคํ‚ค๋งˆ ๊ตฌ์กฐ์— ๋Œ€ํ•ด์„œ๋Š” ์•„์ง ์ •์˜ํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. NestJS๊ฐ€ ํ•ด์ค„๊ฑฐ๊ฑฐ๋“ ์š”!!

TypeORM !!

TypeORM ์€ NodeJS ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ORM (Object Relational Mapping) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค!!

ORM ์€ ๊ฐ„๋‹จํžˆ ๋งํ•ด Object ๋ฅผ Database ์Šคํ‚ค๋งˆ์— ์ ์ ˆํ•˜๊ฒŒ ์ ์šฉ์‹œํ‚ค๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋ผ ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•ฉ๋‹ˆ๋‹ค.!!

์„ค์น˜ ์„ค์น˜~ nestjs ๋ฃจํŠธ ํด๋”์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ์„ค์น˜ํ•ด์ฃผ์„ธ์š” !!

   $ npm install typeorm @nestjs/typeorm --save

์„ค์น˜๊ฐ€ ๋ฌ์œผ๋ฉด ormconfig.json ์ด๋ผ๋Š” ํŒŒ์ผ์„ ๋ฃจํŠธ ํด๋”์— ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.
๋‚˜์ค‘์— TypeORM ๋ชจ๋“ˆ์ด ์ด ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค!!

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”!!

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "postgres",
  "password": ์—ฌ๋Ÿฌ๋ถ„์ด ์„ค์ •ํ–ˆ๋˜ ๋น„๋ฐ€๋ฒˆํ˜ธ!,
  "database": "nest_blog",
  "entities": ["dist/**/**.entity{.ts,.js}"],
  "synchronize": true
}
  • type : ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • host : db ์—ฐ๊ฒฐ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ localhost ๋ฅผ ์ด์šฉํ•˜๊ณ  db ๋ฅผ ๋‹ค๋ฅธ ๋จธ์‹ ์—์„œ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ๊ทธ ๋จธ์‹ ์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•œ ์ฃผ์†Œ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • port : db ์—ฐ๊ฒฐ ํฌํŠธ ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์€ 5432 ์ž…๋‹ˆ๋‹ค.
  • username : ๋ณ„ ๋‹ค๋ฅธ ์„ค์ •์„ ์•ˆํ–ˆ๋‹ค๋ฉด postgres ์ž…๋‹ˆ๋‹ค
  • password : ์„ค์น˜ ๊ณผ์ •์—์„œ ์„ค์ •ํ–ˆ๋˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.
  • database : ์œ„์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ €๋Š” nest_blog ๋ผ๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • entities : ๋ฐ˜๋“œ์‹œ dist ์•„๋ž˜์˜ ํด๋”๋กœ ์ง€์ •ํ•ด์ฃผ์„ธ์š”. ์•ˆ๊ทธ๋Ÿฌ๋ฉด ์˜ค๋ฅ˜๋‚  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„ค์ •์˜ ๊ฒฝ๋กœ์— ์žˆ๋Š” ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•˜์—ฌ DB ์—์„œ ์Šคํ‚ค๋งˆ๋ฅผ ์„ค์ •ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • synchronize : ์ด ์„ค์ •์„ true ๋กœ ํ•˜์‹œ๋ฉด ์•ฑ ์ž‘๋™์‹œ DB ์Šคํ‚ค๋งˆ๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ormconfig ํƒ๋ฐฉํ•˜๊ธฐ!! ์„ ์ฐธ์กฐํ•ด์ฃผ์„ธ์š”~

app ๋ชจ๋“ˆ์—์„œ import !! ๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰!!

์ž ์ด์ œ app.moudle.ts ํŒŒ์ผ๋กœ ๊ฐ€์„œ TypeOrmMoudle ์„ import ํ•ด์ค์‹œ๋‹ค!!

@Module({
  imports: [UsersModule, TypeOrmModule.forRoot()],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

ormconfig.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด, forRoot ํ•จ์ˆ˜ ์•ˆ์— ์„ค์ • ๋‚ด์šฉ์„ ์ ์œผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.!!
ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•˜๋Š”๊ฒŒ ๋” ํŽธํ•˜๊ฒ ์ฃ ~?

์ž ์ด์ œ ๋ฃจํŠธ ํด๋”์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น์–ด๋ฅผ ์ˆ˜ํ–‰ํ•ด๋ด…์‹œ๋‹ค!

$ npm run start

๋ณ„ ๋‹ค๋ฅธ ์˜ค๋ฅ˜๊ฐ€ ์—†๋‹ค๋ฉด ์ •์ƒ์ ์œผ๋กœ ์—ฐ๊ฒฐ ๋œ๊ฒ๋‹ˆ๋‹ค!! ์šฐ๋ผํ•˜!!!
์ €ํฌ๋Š” ์•„์ง Entity์— ๋Œ€ํ•ด์„œ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ด๋ธ”์— ๋ณ„ ๋‚ด์šฉ์€ ์—†์Šต๋‹ˆ๋‹ค.

์ดํ›„์— Entity ๋ฅผ ๋งŒ๋“  ํ›„์—๋Š” DB ์—์„œ ํ…Œ์ด๋ธ”์„ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ์—ฐ๊ฒฐํ•ด์ฃผ์‹œ๋ฉด ๋งŒ๋“  ๋‚ด์šฉ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค!

๐Ÿ›ด User Entity ๋งŒ๋“ค๊ธฐ.

ํ†ก.

DB ๋„ ์—ฐ๊ฒฐํ–ˆ๊ฒ ๋‹ค.

ํ†กํ†ก.

User Entity Template ๋„ ์ƒ์„ฑํ–ˆ๊ฒ ๋‹ค.

ํ†กํ†ก.

๋‚ด์šฉ๋งŒ ๋„ฃ์œผ๋ฉด ๋˜๋„ค?

์ง!

src/users ์•ˆ์˜ ๊ตฌ์กฐ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด์ฃผ์„ธ์š”!!
spec ํŒŒ์ผ๋“ค์€ ์ง€๊ธˆ ์—†์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.

users
โ”‚  users.repository.ts
โ”‚  users.controller.spec.ts
โ”‚  users.controller.ts
โ”‚  users.entity.ts
โ”‚  users.module.ts
โ”‚  users.service.spec.ts
โ”‚  users.service.ts
โ”‚
โ””โ”€dto
        create-user.dto.ts
        index.ts
        update-user.dto.ts

์šฐ๋ฆฌ๋Š” User ์˜ ์š”๊ตฌ์‚ฌํ•ญ ๋งž๊ฒŒ ์ ์ ˆํžˆ Entity ๋ฅผ ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒ๋ฉด ์ด ์•ˆ์— ์žˆ๋Š” ๋‚ด์šฉ์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์Šคํ‚ค๋งˆ๋กœ ์ ์šฉ๋  ๋…€์„๋“ค์ด๊ฑฐ๋“ ์š”!!

์š”๊ตฌ์‚ฌํ•ญ๋ณด๊ธฐ

๋ฐ”๋กœ ๋ชจ๋“  ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑ์‹œํ‚ค๊ธฐ๋Š” ๋ณต์žกํ•˜๋‹ˆ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฑฐ ๋จผ์ € ์ฑ„์›Œ ๋‚˜๊ฐ‘์‹œ๋‹ค!!

users.entity.ts ์•ˆ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฑ„์›Œ์ฃผ์„ธ์š”!!

import { BeforeInsert, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import * as argon2 from "argon2";

@Entity("user")
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 32 })
  username: string;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;

  @BeforeInsert()
  async hashPassword() {
    this.password = await argon2.hash(this.password);
  }

  @Column({ default: "" })
  imageLink: string;
}
  • @Entity : ์ด annotation ์ด ์žˆ์œผ๋ฉด nestjs๊ฐ€ ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€ DB ์Šคํ‚ค๋งˆ ์ ์šฉ์„ ์œ„ํ•ด ์ฐธ๊ณ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!! ์ดํ›„ repository ํด๋ž˜์Šค์—์„œ๋„ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ด„ํ˜ธ์•ˆ์— ๋„ฃ์€ ๋‚ด์šฉ์ด ํ…Œ์ด๋ธ”์˜ ์ด๋ฆ„์ด ๋ฉ๋‹ˆ๋‹ค.
  • PrimaryGeneratedColumn : ์ด annotion์ด ์žˆ๋‹ค๋ฉด ์ด ํ…Œ์ด๋ธ”์€ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ primary key ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ด„ํ˜ธ ์•ˆ์— ์•„๋ฌด๋Ÿฐ ๋‚ด์šฉ์„ ๋„ฃ์ง€ ์•Š์œผ๋ฉด id ๋Š” auto increment ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.uuid vs auto increment
  • Column : ์ด annotion ์ด ์žˆ์œผ๋ฉด ํ…Œ์ด๋ธ”์˜ ํ•„๋“œ๋กœ ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ด„ํ˜ธ ์•ˆ์— ์—ฌ๋Ÿฌ ์˜ต์…˜์„ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ด„ํ˜ธ ์•ˆ์˜ ์˜ต์…˜ ๋ณด๊ธฐ
  • BeforeInsert : ์š” ๋…€์„์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— insert ํ•˜๊ธฐ ์ „์— ์ˆ˜ํ–‰๋˜๋Š” ๋…€์„์ž…๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณตํ˜ธํ™” ํ•  ์ˆ˜ ์—†๊ฒŒ hash ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ €์žฅํ•ฉ์‹œ๋‹ค.

์•„์ฐธ ํ•ด์‰ฌ ํ•จ์ˆ˜๋ฅผ ์“ฐ๊ธฐ ์œ„ํ•ด argon2 ๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

$ npm install argon2 --save

๐Ÿ”จ Repository ์™€ Dto ๋ณ€๊ฒฝํ•˜๊ธฐ!

Repository

Repository ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  Entity ๋ฅผ ๊ด€๋ฆฌ(insert, update, delete, load, etc.)ํ•ด์ค„ ๋…€์„์ด๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

users.repository.ts ์•ˆ์˜ ๋‚ด์šฉ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฑ„์›Œ์ฃผ์„ธ์š”!!

import { EntityRepository, Repository } from "typeorm";
import { UserEntity } from "./users.entity";

@EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {}

์—ฅ? ์ด๊ฒƒ๋งŒ ํ•ด๋„ ๋˜๋Š”๊ฑด๊ฐ€??

  • ๋„ค!!

์ด๋ ‡๊ฒŒ Repository ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์‹œ๋ฉด UserEntity์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ๊ทธ๋ƒฅ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋Š” ์—ฌ๊ธฐ์„œ ๋งŒ๋“ค์–ด์ฃผ์…”๋„ ๋˜๊ณ  ์ดํ›„ ๋‚˜์˜ฌ query builder ๋กœ ํ•ด๋‹น ํด๋ž˜์Šค์—์„œ ์“ฐ์…”๋„ ๋ฉ๋‹ˆ๋‹ค!

DTO

DTO(Data Transfer Object) ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋–ค ๊ตฌ์กฐ๋กœ ์ „์†ก๋˜๋Š”์ง€์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค ์ž…๋‹ˆ๋‹ค.

dto ํด๋” ์•ˆ์—
create-user.dto.ts ํด๋”์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”

import { IsNotEmpty } from "class-validator";

export class CreateUserDto {
  @IsNotEmpty()
  readonly username: string;

  @IsNotEmpty()
  readonly email: string;

  @IsNotEmpty()
  readonly password: string;
}

class-validator ๋Š” data ๊ฐ€ ์ „์†ก๋  ๋•Œ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ์ ์ ˆํ•œ ์œ ํšจ์„ฑ์„ ๊ฐ€์ง€๋Š”์ง€ ๊ฒ€์‚ฌํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

์ž ์ด์ œ Service ๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฉ๋‹ˆ๋‹ค!!

๐Ÿค— User Service ๋ณ€๊ฒฝํ•˜๊ธฐ!!

์ž ๊ตฌ์กฐ๋Š” ์ค€๋น„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋กœ์ง๋งŒ ์ž˜ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค ํ™”์ดํŒ…!!

๋จผ์ € ์˜์กด์„ฑ ์ฃผ์ž…์„ ์œ„ํ•ด UserService ์ƒ์„ฑ์ž์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ์–ด์ค์‹œ๋‹ค!

constructor(private readonly userRepository: UserRepository) {}

์ž ์ด๊ฒƒ ๋งŒ์œผ๋กœ ๊ฐ์ฒด๊ฐ€ ์ฃผ์ž…๋ฌ์Šต๋‹ˆ๋“œ์–ด์–ด์–ด!! ์–ผ๋งˆ๋‚˜ ์ข‹์•„~!

User create

users.service.ts ํŒŒ์ผ ๋‚ด์— create ํ•จ์ˆ˜ ์•ˆ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ”์ฃผ์„ธ์š”!

  async create(createUserDto: CreateUserDto) {
    const { username, email, password } = createUserDto;
    const getByUserName = getRepository(UserEntity)
      .createQueryBuilder('user')
      .where('user.username = :username', { username });

    const byUserName = await getByUserName.getOne();
    if (byUserName) {
      const error = { username: 'UserName is already exists' };
      throw new HttpException(
        { message: 'Input data validation falied', error },
        HttpStatus.BAD_REQUEST,
      );
    }
    const getByEmail = getRepository(UserEntity)
      .createQueryBuilder('user')
      .where('user.email = :email', { email });

    const byEmail = await getByEmail.getOne();
    if (byEmail) {
      const error = { email: 'email is already exists' };
      throw new HttpException(
        { message: 'Input data validation falied', error },
        HttpStatus.BAD_REQUEST,
      );
    }
    // const thisUser = this.userRepository.findOne({ username: username });
    // const thisEmail = this.userRepository.findOne({ email: email });

    // create new user
    let newUser = new UserEntity();
    newUser.email = email;
    newUser.password = password;
    newUser.username = username;
    const validate_error = await validate(newUser);
    if (validate_error.length > 0) {
      const _error = { username: 'UserInput is not valid check type' };
      throw new HttpException(
        { message: 'Input data validation failed', _error },
        HttpStatus.BAD_REQUEST,
      );
    } else {
      return await this.userRepository.save(newUser);
    }
  }

๋จผ์ € username ๊ณผ email ์€ ์œ ์ผํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๊ฒ€์‚ฌํ•ด๋ณด์ฃ !!

๋จผ์ € ์‚ดํŽด๋ณผ ๊ฒƒ์€ createQueryBuilder ์ž…๋‹ˆ๋‹ค.
typeorm ์€ createQueryBuilder ๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ์•ˆ์—์„œ SQL ๋ฌธ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
์ด ์ฝ”๋“œ๋กœ ์ธํ•ด ์ž‘์„ฑ๋œ sql ๋ฌธ์€ ๋ฐ”๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์‹ค์งˆ์ ์œผ๋กœ DB ์™€ ๋ฐ์ดํ„ฐ ๊ตํ™˜์ด ์ผ์–ด๋‚˜๋ฉด ์‹คํ–‰ ๋ฉ๋‹ˆ๋‹ค.

๋งจ ์ฒ˜์Œ ๋งŒ๋“  SQL ๋ฌธ์€

const byEmail = await getByEmail.getOne();

์—ฌ๊ธฐ์„œ ์ˆ˜ํ–‰ ๋ฉ๋‹ˆ๋‹ค. DB ์˜ ์ฟผ๋ฆฌ ์†๋„์™€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์†๋„ ์ฐจ์ด๊ฐ€ ๋งŽ์ด ๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„๋™๊ธฐ ์‹์œผ๋กœ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ username ๋„ ๊ฒ€์‚ฌํ•ด์ค์‹œ๋‹ค!!

์‚ฌ์‹ค ์ด ๊ธฐ๋Šฅ์€ createQueryBuilder ๋ฅผ ์•ˆ์จ๋„ ๋ฉ๋‹ˆ๋‹ค. Repository ๋ฅผ ์ž‘์„ฑํ–ˆ์œผ๋ฏ€๋กœ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋Šฅ์€ ์ด๋ฏธ ์ง€์›์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
์˜ˆ๋“ค ๋“ค์–ด username ์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

const user = this.userRepository.findOne({ username: username });

์ดํ•ด๋ฅผ ๋•๊ณ ์ž createQueryBuilder ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ findByEmail ํ•จ์ˆ˜๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์‹œ๋ฉด

  async findByEmail(email: string) {
    return await this.userRepository.findOne({ email });
  }

email ๊ธฐ์ค€์œผ๋กœ ์œ ์ €๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!!

๐Ÿ”จ Module ์— import ํ•ด์ฃผ๊ธฐ!!

์ž ๊ฑฐ์˜ ๋‹ค ์™”์Šต๋‹ˆ๋‹ค!!

users.module.ts ํŒŒ์ผ ์•ˆ์— imports ๋ถ€๋ถ„์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ชจ๋“ˆ์„ import ํ•ด์ค์‹œ๋‹ค!!

@Module({
  imports: [TypeOrmModule.forFeature([UserRepository])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

๐Ÿ˜ƒ ์ด์ œ ์‹คํ–‰๋œ๋‹ค!!

$ npm run start

์œ„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰์„ ํ•ฉ์‹œ๋‹ค.
์˜ค๋ฅ˜๊ฐ€ ์—†๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋กœ๊ทธ๊ฐ€ ๋œน๋‹ˆ๋‹ค!!

[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [NestFactory] Starting Nest application...
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [InstanceLoader] TypeOrmModule dependencies initialized +97ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [InstanceLoader] AppModule dependencies initialized +1ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [InstanceLoader] TypeOrmCoreModule dependencies initialized +213ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [InstanceLoader] UsersModule dependencies initialized +1ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RoutesResolver] AppController {}: +14ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {, GET} route +18ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RoutesResolver] UsersController {/users}: +10ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {/users, POST} route +7ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {/users, GET} route +3ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {/users/:email, GET} route +1ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {/users/:id, PUT} route +11ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [RouterExplorer] Mapped {/users/:id, DELETE} route +1ms
[Nest] 33080   - 2021-02-01 2:36:58 โ”œF10: PMโ”ค   [NestApplication] Nest application successfully started +2ms

์šฐํ›„ ์šฐํ›„~ ์šฐํ›„~ ์šฐํ›„~ ์„ฑ๊ณต!!
๊ณ ์ƒํ•˜์…จ์Šต๋‹ˆ๋‹ค.

๐Ÿคฃ ์•„์ง ํ…Œ์ŠคํŠธ๊ฐ€ ๋‚จ์•˜๋‹ค!!!

์ €๋Š” Rest Client ๋ผ๋Š” vscode extension ์„ ์ด์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. postman, curl ์—ฌ๋Ÿฌ๋ถ„์ด ํŽธํ•˜์‹  ํˆด์„ ์ด์šฉํ•ด์ฃผ์„ธ์š”!!

์ž ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์š”์ฒญ์„ ํ•˜๋ฉด!

    POST http://localhost:3000/users HTTP/1.1
    Content-Type: application/json

    {
        "username": "manynang",
        "email" : "nono@cat.co.kr",
        "password" : "manyang"
    }

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ถœ๋ ฅ์ด ๋ฉ๋‹ˆ๋‹ค!!

    HTTP/1.1 201 Created
    X-Powered-By: Express
    Content-Type: application/json; charset=utf-8
    Content-Length: 179
    ETag: W/"b3-G/i5vHuN9KZDYAV9wTSwyMToetE"
    Date: Mon, 01 Feb 2021 05:41:10 GMT
    Connection: close

    {
    "email": "nono@cat.co.kr",
    "password": "$argon2i$v=19$m=4096,t=3,p=1$LthoEnjA4KlaeaE/8YhMPg$2bNUqzjlCqbomBtDTkcYzTHjibaSrjefgJDzJWTvGFQ",
    "username": "manynang",
    "id": 1,
    "imageLink": ""
    }

์›์Šค .. ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋‚˜์˜ค๋Š”๊ตฐ์š”... ์ด๋ž˜์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘์š”ํ•œ๊ฒ๋‹ˆ๋‹ค.

์ €ํฌ๋Š” ์œ ์ € id๋งŒ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
UserCreate save ํ•˜๋Š” ๋ถ€๋ถ„์„

return await this.userRepository.save(newUser).then((v) => v.id);

์ด์™€ ๊ฐ™์ด ๋ฐ”๊ฟ”์ค์‹œ๋‹ค.

    HTTP/1.1 201 Created
    X-Powered-By: Express
    Content-Type: text/html; charset=utf-8
    Content-Length: 1
    ETag: W/"1-NWoZK3kTsExUV00Ywo1G5jlUKKs"
    Date: Mon, 01 Feb 2021 05:50:44 GMT
    Connection: close

    1

์ž ์„ฑ๊ณต!

์ž ๋‹ค์‹œ ์œ„์— POST ๊ด€๋ จ ๋ฌธ์„ ๋‹ค์‹œ ๋ณด๋‚ด๋ฉด?

    HTTP/1.1 400 Bad Request
    X-Powered-By: Express
    Content-Type: application/json; charset=utf-8
    Content-Length: 92
    ETag: W/"5c-j/u2GjtMeXEpPJw+5WWgYELKwpM"
    Date: Mon, 01 Feb 2021 05:54:56 GMT
    Connection: close

    {
    "message": "Input data validation falied",
    "error": {
        "username": "UserName is already exists"
    }
    }

์„ฑ๊ณต~~!!!!
์ค‘๋ณต๋œ username ์„ ๊ฑธ๋ €์Šต๋‹ˆ๋‹ค ใ…œใ…œ!!


๊ณ ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค ใ…œใ…œ!!!!

๋‹ค์Œ์—๋Š” ์œ„ ์ฝ”๋“œ๋ฅผ Refactor ํ•˜๋Š” ๊ณผ์ •์œผ๋กœ ์ฐพ์•„๋ต™๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!
์˜ค๋Š˜๋„ ์ฆ๊ฑฐ์šด ํ•˜๋ฃจ ๋ณด๋‚ด์„ธ์š”!!

ํ”ผ๋“œ๋ฐฑ์€ ํ•ญ์ƒ ํ™˜์˜์ž…๋‹ˆ๋‹ค.

Park Ji You