본문 바로가기
게임서버-스파르타코딩NodeJs_7기/분반 수업 Basic-A

12주차 NestJS 정리 - 250211

by GREEN나무 2025. 2. 11.
728x90

 

12주차 NestJS 정리

1. 프레임워크란?

  • 개발을 쉽게 할 수 있도록 제공되는 기본 구조재사용 가능한 코드, 라이브러리의 집합
  • 예시: Express, Next.js, NestJS

2. NestJS를 사용하는 이유

  1. 구조적이고 체계적인 아키텍처
    • 기능을 모듈 단위로 설계 → 이해하기 쉬움
  2. 대규모 프로젝트에서 유지보수 용이
    • 의존성 주입(DI) 방식 → 테스트 용이 & 일관된 틀 유지
  3. 기업에서 활발히 사용 중
    • 최신 기술과 통합이 쉽고 빠른 개발 가능

✅ Express와의 차이점

  • Express: 자유도가 높음 → 프로젝트가 커질수록 유지보수 어려움
  • NestJS: 구조화된 방식 제공 → 유지보수 & 협업 용이

3. NestJS 기본 개념 및 아키텍처

NestJS는 모듈(Module) 단위로 기능을 나누어 구성

① Module (모듈)

  • 하나의 독립적인 기능 블록 (레고처럼 조립 가능)
  • 모듈 안에는 컨트롤러, 서비스, 레포지토리 등이 포함

예시)

  • User 모듈: 사용자 관련 기능
  • Auth 모듈: 인증 관련 기능
  • Order 모듈: 주문 관련 기능

② Controller (컨트롤러) - 요청 처리

  • 사용자 요청을 받아 서비스로 전달하는 역할
  • Express의 req, res 역할을 @Controller()가 담당

✅ 컨트롤러 예제

import { Controller, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  async createUser(@Body() createUserDto: CreateUserDto) {
    try {
      const user = await this.userService.createUser(createUserDto);
      return { message: '회원가입 성공', user }; 
    } catch (error) {
      return { message: error.message };
    }
  }
}
  • @Controller('users'): /users 경로를 처리
  • @Post(): POST 요청을 받음
  • 핵심 로직은 서비스(Service)에서 처리하고, 컨트롤러는 전달 역할

③ Service (서비스) - 비즈니스 로직

  • 실제 비즈니스 로직을 담당
  • @Injectable() 데코레이터 사용
  • 데이터 검증, 중복 체크, 암호화 등의 핵심 동작 처리

✅ 서비스 예제

import { Injectable, ConflictException } from '@nestjs/common';
import { UserRepository } from './user.repository';
import { CreateUserDto } from './dto/create-user.dto';
import * as bcrypt from 'bcrypt';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {}

  async createUser(userDto: CreateUserDto): Promise<User> {
    // 중복 이메일 체크
    const existingUser = await this.userRepository.findByEmail(userDto.email);
    if (existingUser) {
      throw new ConflictException('이미 가입된 이메일입니다.');
    }

    // 비밀번호 해싱
    const hashedPassword = await bcrypt.hash(userDto.password, 10);

    // 사용자 데이터 생성
    const newUser: User = {
      id: Date.now(),
      name: userDto.name,
      email: userDto.email,
      password: hashedPassword,
    };

    // 저장 후 결과 반환
    return this.userRepository.save(newUser);
  }
}
  • @Injectable(): NestJS가 서비스 클래스를 관리
  • 비즈니스 로직을 수행하고, 레포지토리로 데이터를 전달

④ Repository (레포지토리) - 데이터베이스 처리

  • 데이터베이스와 직접적인 CRUD 작업 담당
  • 서비스(Service)에서 호출하여 사용

4. Dependency Injection (의존성 주입, DI)

  • 객체가 직접 필요한 의존성을 생성하지 않고, 외부에서 주입받는 방식
  • 이를 통해 코드 결합도 감소(Loose Coupling) & 유지보수 용이

✅ 의존성 주입 예제

@Injectable()
export class UserService {
  constructor(private readonly userRepository: UserRepository) {} // DI를 통해 자동 주입

  findAll() {
    return this.userRepository.findAll();
  }
}
  • UserService는 UserRepository가 필요하지만 직접 생성하지 않음
  • NestJS의 IoC 컨테이너가 자동으로 주입

IoC (Inversion of Control) 컨테이너

  1. UserService는 UserRepository가 필요하다고 선언
  2. NestJS IoC 컨테이너가 UserRepository를 자동 생성하여 UserService에 주입
  3. UserService에서 UserRepository 사용 가능

5. DTO (Data Transfer Object) - 데이터 검증

  • 데이터 유효성 검사 및 변환을 수행
  • class-validator를 사용하여 데이터 검증

✅ DTO 예제

import { IsString } from 'class-validator';

export class CreateUserDto {
  @IsString()
  name: string;
}
  • @IsString(): name이 문자열인지 검증

✅ 컨트롤러에서 DTO 사용

import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';

@Controller('users')
export class UsersController {
  @Post()
  createUser(@Body() createUserDto: CreateUserDto) {
    return `User ${createUserDto.name} created!`;
  }
}

전역 적용 방법 (main.ts)

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe()); // 전역적으로 DTO 검증 적용
  await app.listen(3000);
}

bootstrap();
  • app.useGlobalPipes(new ValidationPipe()): 모든 요청에 DTO 검증 적용

6. Singleton 패턴

  • 하나의 인스턴스를 생성하여 공유하는 디자인 패턴
  • NestJS의 @Injectable()은 기본적으로 싱글톤으로 동작

✅ 싱글톤 예제

class SingletonLogger {
  private static instance: SingletonLogger;

  private constructor() {}

  static getInstance(): SingletonLogger {
    if (!SingletonLogger.instance) {
      SingletonLogger.instance = new SingletonLogger();
    }
    return SingletonLogger.instance;
  }

  log(message: string) {
    console.log(message);
  }
}

const logger1 = SingletonLogger.getInstance();
const logger2 = SingletonLogger.getInstance();

console.log(logger1 === logger2); // true (같은 객체)
  • NestJS에서는 @Injectable()을 사용하면 자동으로 싱글톤 관리

✅ NestJS에서 싱글톤 사용 예제

@Injectable()
export class LoggerService {
  log(message: string) {
    console.log(`[LOG]: ${message}`);
  }
}
  • 여러 컨트롤러에서 같은 LoggerService 인스턴스를 공유

💡 NestJS 핵심 개념 요약

개념 역할

Module 기능을 독립적인 블록으로 나누어 관리
Controller 사용자 요청을 받아 서비스에 전달
Service 핵심 비즈니스 로직 수행
Repository 데이터베이스 처리 (CRUD)
DI 의존성 자동 주입 (Loose Coupling)
DTO 데이터 검증 및 변환
Singleton 인스턴스를 하나만 생성해 관리