심심한 개발자의 취미생활

Controller

  • 컨트롤러는 NestJS 애플리케이션의 핵심 구성 요소 중 하나로, 클라이언트의 HTTP 요청을 받고 응답을 반환하는 역할을 담당한다. 즉, 특정 URL 경로(Endpoint)에 대한 로직을 정의하는 곳이다.

컨트롤러 생성과 라우팅 (Routing)

  • @Controller() 데코레이터 : 클래스 선언 위에 @Controller() 데코레이터를 붙여 해당 클래스가 컨트롤러임을 나타낸다.
import { Controller, Get } from '@nestjs/common';

@Controller('cats') // '/cats' 경로로 들어 오는 요청을 본 컨트롤러가 처리
export class CatsController {
    @Get() // GET /cats 요청을 본 메서드가 처리
    findAll(): string {
        return 'This action returns all cats';
    }

    @Get('special') // GET /cats/special 요청을 본 메서드가 처리
    findSpecial(): string {
        return 'This is a special Cat';
    }
}
  • 경로 접두사 (Prefix) : @Controller('cats')와 같이 인자를 주면 해당 컨트롤러 내부의 모든 라우팅 경로 앞에 /cats가 공통으로 붙어, 이를 통해 관련 API 를 그룹화 할 수 있다.
  • HTTP 요청 메서드 데코레이터 : @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), @Head() 등 다양한 HTTP 메서드에 맞는 데코레이터를 사용하여 요청을 처리할 메서드(핸들러)를 지정한다.

서브 도메인 라우팅

  • NestJS에서 서브 도메인 기반으로 라우팅을 처리하는 방법으로, HTTP 요청의 도메인에 따라 다른 컨트롤러가 요청을 처리하도록 설계하는 기능이다.
  • @Controller() 데코레이터에서 host 옵션을 사용하여, 특정 HTTP 호스트(도메인)에서 들어오는 요청만 해당 컨트롤러가 처리하도록 제안할 수 있다.
  • 관리자 페이지와 같은 특정 서브 도메인에서만 접근해야 하는 기능을 구현할 때 유용하다.
@Controller({ host: 'admin.example.com' }) // 본 컨트롤러는 'admin.example.com'으로 들어오는 요청만 처리한다.
export class AdminController {
    @Get() // GET admin.example.com/ 오청을 처리한다.
    index(): string {
        return 'Admin Page'
    }
}
  • NestJS의 기본 설정인 Express 어댑터 이외의 Fastify는 서브도메인 라우팅과 같은 중첩된 라우팅을 지원하지 않기 때문에, 복잡한 라우팅 구조를 사용할 때는 Express를 사용해야 한다.

@HostParam()

@Controller({ host: ':account.example.com' }) // 본 컨트롤러는 'admin.example.com'으로 들어오는 요청만 처리한다.
export class AccountController {
    @Get() // GET admin.example.com/ 오청을 처리한다.
    index(@HostParam('account') account: string): string {
        return account
    }
}

Request 처리

  • 클라이언트가 보낸 데이터를 가져오기 위해 다양한 데코레이터를 사용한다.

  • @Param(key) : 경로의 일부를 동적인 값으로 받을때 사용

    • 경로 : @Get(':id')
    • URL 예시 : /cats/123
    • findOne(@Param('id') id: string) : id 변수에 '123'이 담긴다.
  • @Query(key) : URL의 쿼리 스트링 값을 가져올때 사용

    • URL 예시 : /cats?name=goyangee&age=2
    • find(@Query('name') name: string) : name 변수에 'goyangee'가 담긴다.
  • @Body() : POSTPUT요청 시 본문(body)에 담겨 오는 데이터를 객체로 받아 올때 사용

    • 요청 Body (JSON) : { "name": "goyangee", "age": 2, "species": "horangee" }
    • create(@Body() createCatDto: CreateCatDto) : createCatDto 객체에 JSON 데이터가 매핑된다.
  • DTO (Data Transfer Object) : @Body()로 데이터를 받을 때, Class를 사용하여 데이터의 형태를 정의하는 것이 좋다. 이를 DTO라고 부르며, 타입 안정성을 높이고 코드의 가독성을 향상 시킨다.

  • @Req(), @Request() : ExpressFastify의 원시 요청(request) 객체에 직접 접근해야 할 때 사용하지만 일반적으로 @Param, @Body등의 데코레이터를 사용하는 것이 권장된다.

Reponse 처리

  • NestJS는 응답을 처리 하기 위해 표준 방식(Standard), 라이브러리 지정 방식(Library-specific) 두 가지 방식을 제공한다.

  • 표준 방식 (Standard)

    • 권장되는 방식으로, 핸들러 메서드에서 JS 객체나 배열, 원시 타입 값을 return 하면 NestJS가 자동으로 JSON으로 변환하고 적절한 HTTP 상태 코드와 함께 응답한다.
    • @HttpCode(code): 기본 상태 코드를 다른 것으로 변경하고 싶을 때 사용한다.
    • @Header(key, value): 응답 헤더를 설정할 때 사용한다.
  • 라이브러리 지정 방식 (Library-specific)

    • @Res() 데코레이터를 사용하여 응답 객체에 직접 접근한다.
    • res.status(200).json(...) 과 같이 Express의 네이티브 응답 메서드를 호출하여 수동으로 응답을 제어한다.
    • 해당 방식을 사용하면 NestJS의 표준 응답 처리 기능이 비활성화 된다.

Running

  • NestJS는 모듈 기반의 아키텍처를 가지고 있어, 모든 애플리케이션 구성 요소(Controller, Service, Provider 등)는 반드시 특정 모듈(Module)에 속해야 한다.
  • NestJS는 이 모듈들을 통해 애플리케이션의 구조를 파악하고, 필요한 인스턴스를 생성하며, 의존성 주입을 관리한다.
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';

@Module({
  controllers: [CatsController],
})
export class AppModule {}
  • @Module 데코레이터는 모듈 클래스에 메타데이터를 첨부하는 역할로, NestJS는 이 메타데이터를 읽어서 어떤 Controller들을 마운트 해야 하는지 파악한다.