(3D)Dev Deep Dive/Backend origin

[Nest.js, Swagger]간단한 게시판 서버 만들기 - 1편

  • -
728x90

우선, 이 파트는 메인 분야가 아니므로 천천히 업데이트될 예정임을 알립니다.

갑자기 이러는 데 이유가 있을 꺼 아니에요..

그동안 프론트엔드의 길을 잘 걷고 있다가 문득 든 생각이 있습니다.

"백엔드에 대해 어느 정도 알고 있으면 협업이 더 잘 되지 않을까?"

맞습니다. 요즘에는 프론트엔드에서도 백엔드의 구조를 어느 정도 알고 있어야 하는 시대입니다.

그리고 백엔드도 할 줄 알면 더 좋다는 건 다른 사람들도 알고 있는 사실이죠.

물론 가장 근본적인 이유는 이 타입스크립트를 이용해 백엔드 코드까지 직접 작성하다 보면 코딩테스트 준비에도 많은 도움이 될 것이라고 생각해서이기도 합니다.

 

그럼, Nest.js의 세계에서 백엔드와 TypeScript라는 두마리의 토끼를 함께 잡으려고 하는 글을 이제 시작하겠습니다.

 

2023 Series

 

[ 2023 ] CheckPoint, 2023년

2023년 동안 작성했던 의미있다고 생각하는 포스팅들을 모아 둔 게시글입니다 이 글은 2023년이 끝날 때까지 계속해서 업데이트 해 나갈 예정입니다 [ January ] - 변화의 시작, 1월 더보기 [2022WinterBoo

time-map-installer.tistory.com

 


 

왜 Nest.JS를 선택했나요?

사실 의문이 들 법한 상황입니다.
한국에서 백엔드로 유명한 Spring boot를 놔두고 왜 하필 Nest.JS를 선택하였을까요?
그래서 작성자에게 물어보았습니다. "왜 그런 선택을 하셨죠?"
머지 않아 답변이 돌아왔고, 간단하게 정리하자면 다음과 같았습니다.

 

 

[Nest.JS] TypeScript 기반 프레임워크, Nest.JS에 대해 알아보자

오늘 저는 여러분에게 백엔드 개발에 있어 효율적이고 강력한 도구인 Nest.js에 대해 소개하고자 합니다. 이 글을 통해 Nest.js가 무엇인지 알아보고, 지금 프론트엔드 개발자의 길을 향해가고 있는

time-map-installer.tistory.com

 


 

Nest Project 생성하기

Nest.JS의 개발을 위해 Node.js가 필요했고, node -v 통해 깔려있는 지 확인했습니다.

정상적으로 설치가 되어있었고, 다음 단계로 넘어갔습니다.

 

npm i -g @nestjs/cli

위명령어를 입력하여 전역적으로 NestJS cli를 설치합니다.

이 두 과정을 거치고 나면 이제 적용된 CLI를 통해 프로젝트를 만들 수 있습니다.

 

nest new project-name

저는 프로젝트명을 first-nest로 하였습니다.

최근 프로젝트에서 사용했던 yarn을 선택했습니다.
잘 만들어졌네요!

이제 잘 만들어진 nest 프로젝트를 저는 깃 저장소에 올리고 진행하겠습니다.

 

GitHub - TMInstaller/first-nest

Contribute to TMInstaller/first-nest development by creating an account on GitHub.

github.com

 


 

Nest Project 살펴보기

이제 다음과 같은 명령어를 통해 프로젝트를 실행시켜 봅니다.

yarn run start

실행 이후 localhost:3000으로 접속하면 아래와 같은 화면이 뜰 것입니다.

폴더 구조 살펴보기

가장 먼저, nest를 생성했을 때의 구조입니다. 그리고 각각의 파일/디렉터리를 설명하자면 다음과 같습니다.

 

디렉터리

1. (folder) dist : TypeScript를 JavaScript로 컴파일한 결과물이 저장되는 곳입니다.

2. (folder) node_modules : Node.js에서 사용하는 모든 외부 패키지들이 디렉터리에 설치되는 곳입니다.

3. (folder) src : 프로젝트의 소스코드가 저장되는 곳입니다. 내부의 main.ts에서 애플리케이션이 시작됩니다.

4. (folder) test : 프로젝트의 테스트코드를 포함합니다. Jest와 같은 테스트 러너를 통해 테스트를 진행합니다.

5. (file) .eslintrc.js : ESLint 구성 파일입니다. 이 파일을 수정하여 규칙을 설정하고 프로젝트에 맞게 구성할 수 있습니다.

 

[Template] ESLint Template for FrontEnd Developers

ESLint는 개발할 때 있어 매우 유용하게 쓰이는 코드 품질을 책임져주는 도구들 중 하나입니다. 그리고 여기에는 개발을 하고 ESLint를 사용할 때 미리 커스텀을 해둘 수 있는 파일인 .eslintrc.js의 템

time-map-installer.tistory.com

 

6. (file) .gitignore : Git 버전 관리에서 제외할 파일이나 디렉터리를 지정하는 파일입니다. dist, node_modules 등이 여기에 올라가 있습니다.

7. (file) .prettierrc : Prettier 구성 파일입니다. Prettier는 코드를 자동으로 정렬해 주는 도구로, .prettierrc 파일에서 Prettier의 설정을 할 수 있습니다.

 

[Template] Prettier Template for FrontEnd Developers

Prettier은 개발할 때 있어 매우 유용하게 쓰이는 코드 품질을 책임져주는 도구들 중 하나입니다. 그리고 여기에는 프론트엔드 개발을 하고 prettier을 사용할 때 미리 커스텀을 해둘 수 있는 파일인

time-map-installer.tistory.com

8. (file) nest-cli.json : Nest CLI 설정 파일입니다. 프로젝트의 구조나 특정 경로, TypeScript 컴파일러 옵션 등을 설정할 수 있습니다.

9. (file) package.json : Node.js 프로젝트의 메타데이터를 저장합니다. 프로젝트 이름, 버전, 설명, 저자 정보, 라이선스 정보와 함께 프로젝트의 의존성 목록과 실행 가능한 스크립트 등이 이 파일에 포함됩니다.

10. (file) README.md : 프로젝트에 대한 정보를 제공하는 파일입니다. 보통 프로젝트의 설명, 설치 및 실행 방법, API 문서, 기여 방법 등이 이 파일에 작성됩니다.

이렇게 멋진 리드미를 Nest에서 생성해 줍니다

11. (file) tsconfig.build.json : TypeScript 컴파일러 설정 파일입니다. 이 파일은 TypeScript 코드를 JavaScript 코드로 변환하는 데 필요한 설정을 포함하고 있습니다.

여기서 es2017(ES8)까지 지원하고 있는 것을 볼 수 있습니다.

12. (file) yarn.lock : Yarn 패키지 매니저를 사용하는 경우, 프로젝트의 의존성 정보를 정확하게 저장하는 파일입니다. 이 파일은 yarn install 명령을 실행할 때 자동으로 생성되며, 모든 개발자가 동일한 버전의 의존성을 사용하도록 보장합니다.

 


 

이제 이걸로 무엇을 해볼 수 있을까?

프로젝트를 생성했으니 무언가를 만들어 보아야 하지 않을까요?

그래서 저는 이걸로 간단한 CRUD(Create, Read, Update, Delete)를 수행하도록 만들어보고자 합니다.

 

먼저, 데이터베이스와 연결하는 부분까지는 오늘의 목표가 아니기에

우선은 Mock Data를 로컬에 담아서 데이터를 처리하는 방식으로 가보도록 하겠습니다.

 

오늘의 목표 : 간단한 CRUD API 만들기

오늘 해 볼 것들은 무엇이 있을까요?

1. CLI를 통해 초기 파일 생성하기
2. CRUD 구현하기
3. Swagger 적용하기
4. Swagger에서 제대로 작동하는지 확인해 보기

1. CLI를 통해 초기 파일 생성하기

우선, 게시판 기능을 담당할 모듈을 생성합니다.

nest generate module board

 

그리고 게시판에 관련된 로직을 담당할 서비스를 생성합니다.

nest generate service board

이렇게 생성된 서비스는 `board.module.ts`에 자동으로 등록됩니다.

 

요청을 처리할 컨트롤러도 생성합니다.

nest generate controller board

이 컨트롤러 또한 `board.module.ts`에 자동으로 등록됩니다.

 

궁금한 게 있어요, 왜 `nest generate`를 이용해서 생성하는 거죠?

 

위에서 커맨드 라인들을 통해 생성한 파일들은 모두 Nest.js CLI를 사용하여 생성한 파일입니다.

그리고 CLI를 사용하면 몇 가지 이점이 있습니다.

  1. 일관성
    • Nest.js CLI를 사용하면 프로젝트의 구조를 일관성 있게 유지하는 데 도움을 줍니다.
    • CLI를 통해 생성된 파일은 모두 Nest.js에서 권장하는 프로젝트의 구조를 따르게 됩니다.
  2. 보일러플레이트 코드 생성
    • CLI를 사용하여 생성하는 파일에는 기본 코드가 포함되어 있는데, 이를 보일러플레이트라고 부릅니다.
    • 이 코드는 해당 클래스가 Nest.js에서 제대로 작동하기 위해 필요한 기본 설정을 미리 포함합니다.
  3. 자동 등록
    • CLI를 통해 생성한 컨트롤러나 서비스는 자동으로 모듈에 등록됩니다.
    • 반대로 이 작업을 수동으로 한다면 다른 것들도 수동으로 작업해야겠죠?

 

2. CRUD 구현하기

게시판 모델을 생성합니다.

먼저, DTO(Data Transfer Object)를 생성해 줍니다.

 // src/board/create-board.dto.ts

export class CreateBoardDto {
  title: string;
  content: string;
}

 

서비스 로직을 추가합니다.

CRUD 로직이 이 파일에 작성될 것이며, 이 프로젝트에서 Mock Data를 사용할 것이기에

아래 코드처럼 초기 데이터와 각 CRUD 메서드를 작성합니다.

// src/board/board.service.ts

import { Injectable } from '@nestjs/common';
import { CreateBoardDto } from './create-board.dto';

@Injectable()
export class BoardService {
  private boards: CreateBoardDto[] = [
    {
      title: 'Test Board 1',
      content: 'Hello, this is test board 1',
    },
    {
      title: 'Test Board 2',
      content: 'Hello, this is test board 2',
    },
  ];

  getAllBoards(): CreateBoardDto[] {
    return this.boards;
  }

  createBoard(board: CreateBoardDto) {
    this.boards.push(board);
  }

  updateBoard(index: number, board: CreateBoardDto) {
    this.boards[index] = board;
  }

  deleteBoard(index: number) {
    this.boards.splice(index, 1);
  }
}

 

그다음, 컨트롤러 로직을 추가합니다.

컨트롤러에서는 요청을 처리하는 로직을 추가할 수 있습니다.

BoardService의 각각의 메서드를 호출하는 API 엔드포인트를 만듭니다.

// src/board/board.controller.ts

import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import { CreateBoardDto } from './create-board.dto';
import { BoardService } from './board.service';

@Controller('board')
export class BoardController {
  constructor(private readonly boardService: BoardService) {}

  @Get()
  getAllBoards(): CreateBoardDto[] {
    return this.boardService.getAllBoards();
  }

  @Post()
  createBoard(@Body() board: CreateBoardDto) {
    this.boardService.createBoard(board);
  }

  @Put(':index')
  updateBoard(@Param('index') index: number, @Body() board: CreateBoardDto) {
    this.boardService.updateBoard(index, board);
  }

  @Delete(':index')
  deleteBoard(@Param('index') index: number) {
    this.boardService.deleteBoard(index);
  }
}

 

3. Swagger 적용하기

이렇게 모든 작업이 끝나고 간단한 Nest.js 기반의 게시반 서버가 만들어졌습니다.

yarn run start:dev
// npm 선택시엔 npm run start:dev

위 명령어를 통해 개발모드로 애플리케이션을 실행할 수 있습니다.

하지만 저는 Swagger을 사용하여 api들이 작동하는 모습을 보고 싶었기 때문에

이를 Nest.js 프로젝트에 적용시켜보고자 하였습니다.

 

방법은 세 단계로 이루어져 있습니다.

 

1. Swagger 패키지 설치

yarn add @nestjs/swagger swagger-ui-express
// npm일 경우 npm install @nestjs/swagger swagger-ui-express

 

2. main.ts에 SwaggerModule 설정하기

// src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const options = new DocumentBuilder()
    .setTitle('Board API')
    .setDescription('The Board API description')
    .setVersion('1.0')
    .addTag('board')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api', app, document);

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

 

3. BoardController에 Swagger 데코레이터 추가하기

아래와 같이 Swagger을 커스텀할 수 있는 패키지를 가져온 후

아래와 같이 꾸며 결과를 더 잘 확인할 수 있도록 해줍니다.

// src/board/board.controller.ts

// prettier-ignore
import {Body, Controller, Delete, Get, Param, Post, Put} from '@nestjs/common';
import { BoardService } from './board.service';
import { CreateBoardDto } from './create-board.dto';
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';

@ApiTags('board')
@Controller('board')
export class BoardController {
  constructor(private readonly boardService: BoardService) {}

  @Get()
  @ApiOperation({ summary: 'Get all boards' })
  @ApiResponse({ status: 200, description: 'Success' })
  getAllBoards(): CreateBoardDto[] {
    return this.boardService.getAllBoards();
  }

  @Post()
  @ApiOperation({ summary: 'Create a board' })
  @ApiResponse({ status: 201, description: 'Created' })
  createBoard(@Body() board: CreateBoardDto) {
    this.boardService.createBoard(board);
  }

  @Put(':index')
  @ApiOperation({ summary: 'Update a board' })
  @ApiParam({
    name: 'index',
    required: true,
    description: 'Index of a board',
  })
  @ApiResponse({ status: 200, description: 'Updated' })
  updateBoard(@Param('index') index: number, @Body() board: CreateBoardDto) {
    this.boardService.updateBoard(index, board);
  }

  @Delete(':index')
  @ApiOperation({ summary: 'Delete a board' })
  @ApiParam({ name: 'index', required: true, description: 'Index of a board' })
  @ApiResponse({ status: 200, description: 'Deleted' })
  deleteBoard(@Param('index') index: number) {
    this.boardService.deleteBoard(index);
  }
}

 

이렇게 데코레이팅을 진행하면 다음과 같은 결과를 볼 수 있습니다.

미리 설정해 둔다면 후에 도움이 될 것 같네요!

 

4. Swagger에서 제대로 작동하는지 확인하기

이제 제대로 작동하는 지 확인해 볼 시간이에요!

그전에 잠시 짚고 넘어가는 TroubleShooting Time!

여기서 여러분들이 조심하셔야 할 부분이 있습니다.
Swagger에 보통 JSON 형식으로 데이터를 넘겨주는데, 이 JSON에서는 문자열을 큰따옴표로 묶어 넘기지 않는다면 오류가 발생하기 조심해 주세요! (Single Quotation Marks는 안됩니다!)

그리고 숫자 형식의 인덱스가 없어도 0은 가장 첫 번째 데이터를 의미하니 이 점 참고해 주세요!

 

`localhost:3000/api` 로 들어가면 Swagger 페이지가 나올꺼에요

Read 부분부터 확인해 보도록 하겠습니다.

좌측의 부분에서 Create가 진행되었고 우측에서 볼 수 있듯이 정상적으로 결과를 읽어와서 출력이 된 것을 볼 수 있습니다.

 

다음은 Create입니다.

기존의 데이터에 더해 새로운 데이터를 추가하는 부분이며, 정상적으로 추가가 된 모습입니다.

 

그리고 Update와 Delete입니다. 이 또한 정상적으로 잘 돌아가네요!

 


후기

이렇게 오늘 간단하게 Board 서버를 만들어 보았습니다.

스프링부트와 비슷한 MVC 패턴이기에 후에 Java로 넘어간다 해도 적응이 조금 덜 걸릴 것 같네요

백엔드와 관련된 첫 번째 DeepDive라 그런지 아직은 모르는 것도 많고, 부족한 부분도 많은 것 같습니다.

그나마 다행인 점은 언어가 많이 친숙해서 언어의 특성을 더 공부해야한다던가 하는 부담이 적었습니다.

 

프론트엔드와의 차이점을 느껴보자면 백엔드는 대부분이 문자와 문장으로써 나타나는 결과물이 많아

레거시 코드의 경우 프론트엔드보다 더 어렵다고 느껴질 수 있겠다 싶었습니다.

 

그러고보니 프론트엔드엔 Next.js가, 백엔드에는 Nest.js가 있네요. 역시 프레임워크는 Ne{ }t.js 인 것 같습니다.

 

이 다음엔 어떤 주제로 진행이 될까요?

아니면 새로운 주제로 다른 프로젝트가 시작될까요?

다음 글을 기대해주세요!

이 포스팅은 여기서 마칩니다


To be Continue...

728x90
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.