NestJS 공식문서 Model-View-Controller

GGAE99·2023년 9월 5일
0

NestJS 공식 문서

목록 보기
27/33
post-thumbnail

Model-View-Controller

Nest는 내부적으로 Express 라이브러리를 사용하기 때문에, MVC 패턴을 사용하는 모든 기술을 Nest에서도 적용됩니다. CLI를 사용해 프로젝트를 생성할 수 있습니다.

$ npm i -g @nestjs/cli
$ nest new project

MVC 앱을 생성하기 위해 HTML 뷰를 렌더링할 템플릿 엔진이 필요합니다.

$ npm install --save hbs

hbs ( Handlebars ) 엔진을 사용했지만 요구 사항에 따라 다른 엔진을 선택할 수 있습니다.
설치가 완료되면 Express 인스턴스를 구성해야 합니다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(
    AppModule,
  );

  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('hbs');

  await app.listen(3000);
}
bootstrap();

위 코드에서는 Express에게 정적인 assets를 저장할 public 디렉토리, 뷰를 포함한 템플릿을 가질 views 디렉토리, 그리고 hbs 템플릿 엔진을 사용하여 HTML 출력을 렌더링하도록 알려줍니다.

Template rendering

이제 views 디렉토리를 생성하고 그 안에 index.hbs 템플릿을 만듭니다.
템플릿에서 컨트롤러에서 전달된 메시지를 출력합니다.

<!--views/index.hbs-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>App</title>
  </head>
  <body>
    {{ message }}
  </body>
</html>

그 다음, app.controller 파일을 열고 root() 메서드를 다음 코드로 대체합니다.

// app.controller.ts
import { Get, Controller, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Render('index')
  root() {
    return { message: 'Hello world!' };
  }
}

이 코드에서는 @Render() 데코레이터에서 사용할 템플릿을 지정하고, 라우트 핸들러 메서드의 반환 값이 템플릿에 전달되도록 합니다. 반환 값은 템플릿에서 생성한 message 플레이스홀더와 일치하는 message 속성을 가진 객체입니다.

애플리케이션이 실행 중일 때 웹 브라우저를 열고 http://localhost:3000로 이동하면 "Hello world!"메시지가 표시됩니다.

Dynamic template rendering

만약 애플리케이션 로직이 동적으로 어떤 템플릿을 렌더링해야 하는 경우, @Res() 데코레이터를 사용하고 라우트 핸들러에서 뷰 이름을 제공해야 합니다. 이때 @Render() 데코레이터를 사용하는 대신에 라우트 핸들러에서 뷰 이름을 지정합니다.

// app.controller.ts
import { Get, Controller, Res, Render } from '@nestjs/common';
import { Response } from 'express';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private appService: AppService) {}

  @Get()
  root(@Res() res: Response) {
    return res.render(
      this.appService.getViewName(), // 동적으로 결정되는 뷰 이름
      { message: 'Hello world!' },
    );
  }
}

이렇게 하면 렌더링할 템플릿 이름이 동적으로 결정되며 메시지가 포함된 템플릿이 렌더링됩니다. Nest는 @Res() 데코레이터를 감지하고 라이브러리별 응답 객체를 주입하며, 이 객체를 사용하여 템플릿을 동적으로 렌더링할 수 있습니다.

질문 및 생각

  • MVC 패턴이란 : Model-View-Controller의 약자로 애플리케이션을 세 가지 역할로 구분한 개발 방법론. Controller를 조작하면 Controller는 Model을 통해 데이터를 가져오고 그 데이터를 바탕으로 View를 통해 시각적 표현을 제어하여 사용자에게 전달한다.

  • Model : 모델은 어플리케이션의 정보, 데이터를 나타낸다. 데이타베이스, 초기화 된 상수나 값, 변수 등을 뜻한다. 비즈니스 로직을 처리한 후 모델의 변경 사항을 컨트롤러와 뷰에 전달한다.

    모델 규칙
    사용자가 편집하길 원하는 모든 데이터를 가지고 있어야 합니다.
    뷰나 컨트롤러에 대해서 어떤 정보도 알지 말아야 합니다.
    변경이 일어나면, 변경 통지에 대한 처리 방법을 구현해야만 합니다.

  • View : 여러 개의 뷰가 존재할 수 있으며, 모델에게 질의하여 데이터를 전달받는다. 뷰는 받은 데이터를 화면에 표시해주는 역할을 가지고 있다. 모델에게 전달받은 데이터를 별도로 저장하지 않아야 한다. 사용자가 화면에 표시된 내용을 변경하게 되면 모델에게 전달하여 모델을 변경해야 한다.

    뷰 규칙
    모델이 가지고 있는 정보를 따로 저장해서는 안됩니다.
    모델이나 컨트롤러와 같이 다른 구성 요소들을 몰라야 됩니다.
    변경이 일어나면 변경통지에 대한 처리방법을 구현해야만 합니다.

  • Controller : 모델이나 뷰는 서로의 존재를 모르고 있다. 변경 사항을 외부로 알리고 수신하는 방법만 존재한다. 컨트롤러는 이를 중재하기 위한 컴포넌트다. 모델과 뷰에 대해 알고 있으며 모델이나 뷰로부터 변경 내용을 통지 받으면 이를 각 구성 요소에게 통지한다.

    컨트롤러 규칙
    모델이나 뷰에 대해서 알고 있어야 합니다.
    모델이나 뷰의 변경을 모니터링 해야 합니다.

  • hbs : Handlebars.js 템플릿 엔진을 사용하여 생성되는 템플릿 파일. (Mustache, Pug, Nunjucks 등 다양한 템플릿 파일이 존재)

내 정리

MVC 패턴을 사용하는 이유는 유지보수의 편리성에 있다.
최초 설계를 꼼꼼하게 진행한 시스템이라도 유지 보수가 발생하기 시작하면 각 기능간의 결합도(coupling)가 높아진다. 결합도가 높아진 시스템은 유지보수 작업 시 다른 비즈니스 로직에 영향을 미치게 되므로 사소한 코드의 변경이 의도치 않은 버그를 유발할 수 있다.
그렇기 때문에 MVC 패턴이라는 디자인 패턴을 사용하여 3개의 핵심 컴포넌트 모델, 뷰, 컨트롤러라는 책임을 나누어서 각 컨포넌트가 자신의 수행 결과를 다른 컴포넌트에게 전달하는 프로그래밍 방식으로 결합도를 낮춘다.

  • 화면의 변경은 뷰를 수정하여 반영
  • 데이터나 비즈니스 요건이 변경은 모델을 수정하여 반영
  • 뷰와 모델 변경에 따른 컨트롤러를 수정

0개의 댓글