본문 바로가기
NodeJS/NestJS

[NestJS] 프로젝트 구조 및 Controllers 파악하기

by 검은냥냥이 2023. 6. 9.

프로젝트 구조

NestJS Project File

구조 설명
app.controller.ts 단일 라우트를 가진 기본 컨트롤러
app.controller.spec.ts 컨트롤러에 대한 유닛 테스트
app.module.ts 애플리케이션의 루트 모듈
app.service.ts 단일 메서드를 가진 기본 서비스
main.ts NestFactory라는 코어 함수를 사용하여 Nest 애플리케이션 인스턴스를 생성하는 애플리케이션의 진입 파일

 

컨트롤러

컨트롤러는 들어오는 요청을 처리하고 클라이언트에 응답을 반환하는 역할을 합니다.

NestJS Controller

컨트롤러의 목적은 응용 프로그램에 대한 특정 요청을 받는 것입니다. 라우팅 메커니즘은 어떤 컨트롤러가 어떤 요청을 받아야 하는지를 제어합니다. 일반적으로 각 컨트롤러에는 하나 이상의 라우트가 있으며, 각각의 라우트는 다른 동작을 수행할 수 있습니다.

기본 컨트롤러를 만들기 위해서는 클래스와 데코레이터를 사용합니다. 데코레이터는 클래스와 필요한 메타데이터를 연결하고, Nest가 라우팅 맵을 생성 하여 요청을 해당 컨트롤러에 연결할 수 있게 합니다.

 

Routing

다음 예시에서는 @Controller()cats를 사용합니다. @Controller() 데코레이터에서 경로 접두사를 사용하면 관련된 라우트 집합을 쉽게 그룹화하고 중복되는 코드를 최소화할 수 있습니다. 예를 들어, 고양이 엔티티와 상호작용을 관리하는 일련의 라우트를 /cats 경로 아래에 그룹화할 수 있습니다. 이 경우, 파일의 각 라우트마다 경로의 해당 부분을 반복하지 않도록 @Controller() 데코레이터에서 경로 접두사 cats를 지정할 수 있습니다.

import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  @HttpCode(200)
  findAll(): string {
    return 'This action returns all cats';
  }
  
  @Get('white')
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
  
  @Get('food')
  findAll(@Res() response): string {
    return response.status(200).send('This action returns cat food');
  }
}

 

Request object

import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
}

아래는 @Body() or @Query()와 같은 데코레이터 목록입니다.

 

데코레이터 매개변수 객체
@Request(), @Req() req 요청(request) 객체
@Response(), @Res()* res 응답(response) 객체
@Next() next 다음 미들웨어 함수
@Session() req.session 세션(session) 객체
@Param(key?: string) req.params / req.params[key] 라우트 매개변수(params)
@Body(key?: string) req.body / req.body[key] 요청 본문(body)
@Query(key?: string) req.query / req.query[key] 쿼리 매개변수(query)
@Headers(name?: string) req.headers / req.headers[name] 요청 헤더(headers)
@Ip() req.ip 클라이언트 IP 주소
@HostParam() req.hosts 호스트(host) 매개변수

 

Resources

import { Controller, Get, Post } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Post()
  create(): string {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}
데코레이터 설명
@Get() GET 요청을 처리하는 엔드포인트를 정의합니다.
@Post() POST 요청을 처리하는 엔드포인트를 정의합니다.
@Put() PUT 요청을 처리하는 엔드포인트를 정의합니다.
@Delete() DELETE 요청을 처리하는 엔드포인트를 정의합니다.
@Patch() PATCH 요청을 처리하는 엔드포인트를 정의합니다.
@Options() OPTIONS 요청을 처리하는 엔드포인트를 정의합니다.
@Head() HEAD 요청을 처리하는 엔드포인트를 정의합니다.
@All() 모든 HTTP 메서드를 처리하는 엔드포인트를 정의합니다.

 

Route wildcards

패턴 기반 경로도 지원됩니다. 예를 들어 별표는 와일드카드로 사용되며 모든 문자 조합과 일치합니다.

@Get('ab*cd')
findAll() {
  return 'This route uses a wildcard';
}

'ab*cd' 경로 경로는 abcd, ab_cd, abecd 등과 일치합니다. ?, +, * 및 () 문자는 경로 경로에서 사용할 수 있으며 해당 정규식의 하위 집합입니다. 하이픈(-)과 점(.)은 문자 그대로 문자열 기반 경로로 해석됩니다.

 

Headers

사용자 정의 응답 헤더를 지정하려면 @Header() 데코레이터 또는 라이브러리별 응답 객체를 사용하고 res.header()를 직접 호출할 수 있습니다.

@Post()
@Header('Cache-Control', 'none')
create() {
  return 'This action adds a new cat';
}

 

Redirection

응답을 특정 URL로 리디렉션 하려면 @Redirect() 데코레이터 또는 라이브러리별 응답 객체를 사용하고 res.redirect()를 직접 호출할 수 있습니다.

@Redirect()는 url과 statusCode라는 두 개의 인수를 사용하며 둘 다 선택 사항입니다. 생략된 경우 statusCode의 기본값은 302(Found)입니다.

@Get()
@Redirect('https://nestjs.com', 301)

 

경우에 따라 HTTP 상태 코드 또는 리디렉션 URL을 동적으로 결정해야 할 수 있습니다. 아래는 기본 Response입니다.

{
  "url": string,
  "statusCode": number
}

 

반환된 값은 @Redirect() 데코레이터에 전달된 모든 인수를 재정의합니다. 예를 들어:

@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
  if (version && version === '5') {
    return { url: 'https://docs.nestjs.com/v5/' };
  }
}

 

Route parameters

GET /cats/1은 id가 1@Get()인 고양이를 가져옵니다. 아래의 데코레이터 예제는 이 사용법을 보여줍니다. 이 방법으로 선언된 경로 매개변수는 메소드 서명에 추가되어야 하는 @Param() 데코레이터를 사용하여 액세스 할 수 있습니다.

주의할 점은 매개변수가 있는 경로는 정적 경로 다음에 선언해야 합니다. 이렇게 하면 매개 변수가 있는 경로가 정적 경로로 향하는 트래픽을 가로채는 것을 방지할 수 있습니다.

 

@Get(':id')
findOne(@Param() params: any): string {
  console.log(params.id);
  return `This action returns a #${params.id} cat`;
}

@Param()은 메서드 매개변수(params.id를 참조하여 paramsid 매개변수)를 장식하는 데 사용됩니다. 특정 매개변수 토큰을 데코레이터에 전달한 다음 메서드 본문에서 이름으로 경로 매개변수를 직접 참조할 수도 있습니다.

 

Sub-Domain Routing

@Controller 데코레이터는 들어오는 요청의 HTTP 호스트가 특정 값과 일치하도록 요구하는 호스트 옵션을 사용할 수 있습니다.

주의할점은 FASTIFY은 사용할 수 없으며, 중첩 라우터에 대한 지원이 부족하므로 하위 도메인 라우팅을 사용할 때 대신 (기본) Express 어댑터를 사용해야 합니다.

@Controller({ host: 'admin.example.com' })
export class AdminController {
  @Get()
  index(): string {
    return 'Admin page';
  }
}

 

Asynchronicity

모든 비동기 함수는 Promise를 반환해야 합니다. 이는 Nest가 자체적으로 해결할 수 있는 지연된 값을 반환할 수 있음을 의미합니다. 이에 대한 예를 살펴보겠습니다.

@Get()
async findAll(): Promise<any[]> {
  return [];
}

위의 코드는 완전히 유효합니다. 게다가, Nest 라우트 핸들러는 RxJS 옵저버블 스트림을 반환함으로써 더욱 강력해집니다. Nest는 내부에서 소스에 자동으로 구독하고, 스트림이 완료될 때 마지막으로 발행된 값을 가져옵니다.

 

controller.ts

import { Controller, Get } from '@nestjs/common';
import { Observable } from 'rxjs';
import { UserService } from './user.service';

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

  @Get()
  getUsers(): Observable<string[]> {
    return this.userService.getUsers();
  }
}

 

service.ts

import { Injectable } from '@nestjs/common';
import { Observable, of } from 'rxjs';

@Injectable()
export class UserService {
  getUsers(): Observable<string[]> {
    // 비동기적으로 사용자 데이터를 가져오는 작업을 시뮬레이션합니다.
    const users: string[] = ['John', 'Jane', 'Alice'];

    return of(users); // 옵저버블로 사용자 데이터를 반환합니다.
  }
}

 

Request payloads

이전에 작성한 POST 라우트 핸들러 예제에서는 클라이언트 매개변수를 받지 않았습니다. @Body() 데코레이터를 추가하여 이를 수정해 보겠습니다.

그러기에 앞서 (TypeScript를 사용하는 경우) DTO (Data Transfer Object) 스키마를 정의해야 합니다. DTO는 데이터가 네트워크를 통해 전송되는 방식을 정의하는 객체입니다. 우리는 TypeScript 인터페이스를 사용하거나 간단한 클래스를 사용하여 DTO 스키마를 정의할 수 있습니다. 흥미롭게도, 여기서는 클래스를 사용하는 것을 권장합니다. 그 이유는 클래스가 JavaScript ES6 표준의 일부이며, 따라서 컴파일된 JavaScript에서 실제 엔티티로 유지되기 때문입니다. 반면에 TypeScript 인터페이스는 트랜스파일 중에 제거되기 때문에 Nest는 런타임에서 해당 인터페이스를 참조할 수 없습니다. 이는 Pipes와 같은 기능들이 런타임에서 변수의 메타타입에 액세스 할 수 있는 경우 추가적인 가능성을 제공하는 데 중요합니다.

이제 CreateCatDto 클래스를 만들어봅시다.

 

create-cat.dto.ts

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

 

cats.controller.ts

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  return 'This action adds a new cat';
}

 

Full resource sample

import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
728x90
사업자 정보 표시
레플라 | 홍대기 | 경기도 부천시 부일로 519 화신오피스텔 1404호 | 사업자 등록번호 : 726-04-01977 | TEL : 070-8800-6071 | Mail : support@reafla.co.kr | 통신판매신고번호 : 호 | 사이버몰의 이용약관 바로가기