๐Ÿฅ BLOCKTRIP ํ”„๋กœ์ ํŠธ -4 | DB ์—ฐ๋™

blocktrip 4๋ฒˆ์งธ ์‚ฝ์งˆ

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

  • nestJS init ๊ณผ์ • ์ƒ๋žต

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์„ฑ

์šฐ์„  ๋‚ด ๋‚˜๋ฆ„๋Œ€๋กœ์˜ ์„ค๊ณ„์— ๋”ฐ๋ฅด๋ฉด ์‹œ์ž‘์€ ์œ ์ €๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š”๊ฒƒ๋ถ€ํ„ฐ์˜€๋‹ค.
๊ทธ๋ž˜์„œ ๋กœ์ปฌ์— ์žˆ๋Š” mysql์„ ์—ฐ๊ฒฐํ•˜๋ ค๋‹ค๊ฐ€ ์ด๋ฒˆ๊ธฐํšŒ์— mysql์„ ๋„์ปค์— ๋‹ด์•„์„œ ๊ด€๋ฆฌํ•˜๋Š”๊ฒŒ
์ข€๋” ๊ฐ€๋ณ๊ณ  ํŽธ๋ฆฌํ•˜์ง€ ์•Š์„๊นŒ ? ๋ผ๋Š” ์ƒ๊ฐ์— ๋„์ปค์— mysql ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์˜ฌ๋ ธ๋‹ค.

๋„์ปค๋ฅผ ์„ค์น˜ํ–ˆ๋‹ค๋Š” ๊ณผ์ •ํ•˜์— ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค.
๋‚ด ๊ฒฝ์šฐ๋Š” ์œˆ๋„์šฐ๋ผ docker desktop ์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„ ์‚ฌ์šฉํ–ˆ๋‹ค.

WSL2 ํ•„์š”

| mysql ์ด๋ฏธ์ง€๋ฅผ ๋‚ด๋ ค๋ฐ›๋Š”๋‹ค.

docker pull mysql

| ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•œ๋‹ค.

docker run --name mysql -e MYSQL_ROOT_PASSWORD=<password> -d -p 3306:3306 mysql:latest

| mysql docker ์ ‘์†

docker exec -it mysql-container bash

์•„ ์ ‘์†์ „์— docker desktop ์—์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰์‹œ์ผœ์ค˜์•ผ bash์— ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ํ›„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ ‘์†ํ•˜๋ฉด ์•„๋ž˜์™€๊ฐ™์ด SHOW DATABASES๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ ๊นŒ์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ docker mysql ๊ตฌ์„ฑ์„ ์‹ฌํ”Œํ•˜๊ฒŒ ํ–ˆ๋‹ค.
๋‹ค์Œ์€ ์‹ค์ œ ์—ฐ๊ฒฐ์ด ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž

nestJS ์™€ mysql ์—ฐ๋™

nestJS - Database
๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์‚ฌ์‹ค ๊ธˆ๋ฐฉ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‚˜๋Š” DB ์ •๋ณด๋ฅผ ์ˆจ๊ธธ ํ•„์š”๊ฐ€ ์žˆ์—ˆ๊ณ  ,
Database ๋Š” ๋”ฐ๋กœ ๋ถ„๋ฆฌ์‹œ์ผœ์„œ nestJS์˜ ํŒจํ„ด์— ๋งž๊ฒŒ ์ž‘์—…ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.

npm install --save @nestjs/typeorm typeorm mysql2

์šฐ์„  typeorm์„ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ ์ž‘์—…์„ ํ•˜๊ธฐ์œ„ํ•ด ์œ„์™€ ๊ฐ™์€ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ–ˆ๋‹ค.

npm install --save @nestjs/config joi

๊ทธ๋ฆฌ๊ณ  ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ธํŒ…์„ ์œ„ํ•œ config ์™€ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ joi ํŒจํ‚ค์ง€๋ฅผ ์„ ํƒํ•ด์„œ ์„ค์น˜ํ–ˆ๋‹ค.

// app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
import { UserModule } from './user/user.module';
import { DataBaseService } from './database/database.service';
import { DatabaseModule } from './database/database.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        DATABASE_PORT: Joi.number().required(),
        DATABASE_USER: Joi.string().required(),
        DATABASE_PASSWORD: Joi.string().required(),
        DATABASE_NAME: Joi.string().required(),
        JWT_SECRET_KEY: Joi.string().required(),
      }),
    }),
    TypeOrmModule.forRoot(
      new DataBaseService(new ConfigService()).getTypeOrmConfig(),
    ),
    UserModule,
    DatabaseModule,
    AuthModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {
  constructor(private connection: Connection) { }
}

๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด nodejs ์—์„œ ์‚ฌ์šฉํ•˜๋Š” dotenv ๋Œ€์‹  nestjs์—์„œ ์ œ๊ณตํ•˜๋Š” ConfigModule์„ ์ด์šฉํ•ด
env๋ฅผ ์„ธํŒ…ํ–ˆ๋‹ค.

    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        DATABASE_PORT: Joi.number().required(),
        DATABASE_USER: Joi.string().required(),
        DATABASE_PASSWORD: Joi.string().required(),
        DATABASE_NAME: Joi.string().required(),
        JWT_SECRET_KEY: Joi.string().required(),
      }),

ConfigModule.forRoot๋ฅผ ์ด์šฉํ•˜๋ฉด ์–ด๋””์„œ๋“  ํ˜ธ์ถœํ•ด์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
isGlobal ์˜ต์…˜์„ true ๋กœ ์ค˜์„œ ์ „์—ญ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ๋” ํ•œ๋‹ค.
validationSchema ๋ฅผ ์‚ฌ์šฉํ•ด ์•„๊นŒ ์„ค์น˜ํ•œ Joi๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค.

Database ์—ฐ๊ฒฐ์„ ์œ„ํ•ด์„  TypeOrmModule์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

   TypeOrmModule.forRoot(
      new DataBaseService(new ConfigService()).getTypeOrmConfig(),
    )

new DataBaseService ๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“  DataBase ์—ฐ๊ฒฐ์šฉ ์„œ๋น„์Šค ์ธ๋ฐ ์ฝ”๋“œ๋Š” ์ด๋ ‡๋‹ค.

// database/database.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';

@Injectable()
export class DataBaseService {
    constructor(private readonly config: ConfigService) { }

    public getTypeOrmConfig(): TypeOrmModuleOptions {
        return {
            type: 'mysql',
            host: 'localhost',
            port: +this.config.get('DATABASE_PORT'),
            username: this.config.get('DATABASE_USER'),
            password: this.config.get('DATABASE_PASSWORD'),
            database: this.config.get('DATABASE_NAME'),
            entities: ["dist/**/*.entity{.ts,.js}"],
            synchronize: true,
            autoLoadEntities: true,
        };
    }
}

๋‚ด ๊ฒฝ์šฐ ์•„์ง local ์—์„œ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— host๋Š” localhost์ด๋‹ค.
synchronize ์˜ต์…˜์€ prod ์—์„  ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š”๋‹ค.
ํ…Œ์ด๋ธ”์„ ๋ฐ”๋กœ ๋งŒ๋“ค์–ด์ค˜์„œ ๋งค์šฐ ํŽธ๋ฆฌํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ณต์‹๋ฌธ์„œ์—์„œ๋„ prod ์—์„ 
์ง€์–‘ํ•˜๋ผ๊ณ  ํ•œ๋‹ค.

autoLoadEntities ๋ฅผ ์‚ฌ์šฉํ•ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋กœ๋“œํ•œ๋‹ค.
๋‚ด ์—”ํ‹ฐํ‹ฐ๋Š” ์ปดํŒŒ์ผ์ด ๋๋‚œ dist ํด๋”์— ๋“ค์–ด์žˆ์–ด์„œ ๊ฒฝ๋กœ๋ฅผ ์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ–ˆ๋‹ค.
๋‚˜๋จธ์ง€ ์ •๋ณด๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ์„œ ConfigService.get() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

env ์„ธํŒ… ( ๋ณดํ†ต์€ dev , prod ๋ฅผ ๋‚˜๋ˆˆ๋‹ค. )

// .env
DATABASE_PORT=3306
DATABASE_USER=USER
DATABASE_PASSWORD=PASSWORD
DATABASE_NAME=NAME

database module ์—์„œ providers ์— ์„œ๋น„์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š”๊ฑธ ๋นผ๋จน์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

// database/database.module.ts
import { Module } from '@nestjs/common';
import { DataBaseService } from './database.service';

@Module({
    providers: [DataBaseService],
})
export class DatabaseModule { }

docker mysql์€ ์ผœ์ ธ์žˆ๊ณ  ์„ธํŒ…๋„ ๋๋‚ฌ์œผ๋‹ˆ npm run start๋ฅผ ํ•ด๋ณด๋ฉด...

์ •์ƒ์ ์œผ๋กœ ๋กœ๋“œ๊ฐ€ ๋๋‚ฌ๋‹ค.
์• ์ดˆ์— ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฑ‰์œผ๋‚˜
์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ ํ™•์ธ์ด๋‚˜ ํ…Œ์ด๋ธ”์„ ํ™•์ธํ•ด๋ณด๋ ค๋ฉด db ํˆด์„ ์ด์šฉํ•ด ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

ํˆด์—์„œ ์—ฐ๊ฒฐ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด

์ปค๋„ฅ์…˜ ํ…Œ์ŠคํŠธ ํ–ˆ์„๋•Œ ์„ฑ๊ณต์„ ํ–ˆ๋‹ค.
์ด๋Œ€๋กœ ์™„๋ฃŒ๋ฅผ ํ•˜๋ฉด ์ปค๋„ฅํŠธ ์„ฑ๊ณต !

์—ฌ๊ธฐ๊นŒ์ง€ ๋‚ด ์„œ๋ฒ„์™€ docker mysql์„ ํ†ตํ•ด ์—ฐ๋™์„ ์‹œ์ผฐ๋‹ค. !
์šด์ข‹๊ฒŒ ํšŒ์‚ฌ์—์„œ ๋ฐฑ์—”๋“œ๋กœ์„œ ํฌ์ง€์…˜ ์ „ํ™˜์ด ๋˜๋ฉด์„œ php ๋‚˜ express ๊ฐ€ ์•„๋‹Œ
๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์ž‘์—…ํ•˜๋Š”๊ฑด ์ด๋ฒˆ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ฒ˜์Œ์ธ๋ฐ,

NestJS๋Š” express ์™€ fastify ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ•ฉ์ณค๊ณ  , ์ž๋ฐ” ์Šคํ”„๋ง์˜ ํŒจํ„ด์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผ๊ฐ€
ํ•„์š”ํ•œ๊ฒƒ๋“ค์ด ์ค€๋น„๋˜์–ด์žˆ๊ณ  ๋น ๋ฅด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ๋‚˜๋ˆ ์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์€๊ฒƒ ๊ฐ™๋‹ค.

๋‹ค์Œ์€ ์œ ์ € ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด ์ƒ์„ฑํ•˜๊ณ  ๋กœ๊ทธ์ธํ•˜๋Š” ๊ณผ์ •๊นŒ์ง€ ํ•ด๋ณด๊ณ ์žํ•œ๋‹ค.
ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ›„ ์ถ”ํ›„์— ์‚ญ์ œ๋‚˜ ์ˆ˜์ •์„ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ ํ•œ๋‹ค.

์‚ฌ์‹ค ๋๋‚œ๊ฑด ์•„๋‹ˆ๊ณ  ์•ž์œผ๋กœ ์ถ”๊ฐ€ํ•ด์•ผํ•  ์ŠคํŽ™๋“ค์ด ๋” ๋งŽ๋‹ค. ๊ฐˆ๊ธธ์ด ๋ฉ€๋‹ค๋Š” ๋œป..
์–ด์จŒ๋“  ์„ค๊ณ„๋Œ€๋กœ ํ•œ๋ฒˆ ์ง„ํ–‰์„ ๊ณ„์† ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ