본문 바로가기

프로젝트/복지맵(중단)

[복지맵 프로젝트] 백엔드 환경설정 - NestJS (+Docker + Postgres + TypeORM + 유효성 검사 패키지)

반응형

이전 환경 셋팅 - Vite + React + PWA

 

[복지맵 프로젝트] 환경설정 - Vite 기반 리액트 웹 PWA 설정

PWA 설정 이유모바일 환경에서도 웹을 앱과 유사한 환경에서 구동할 수 있다는 것은 많은 이점을 준다. 현재 내가 개발하고자 하는 웹 사이트는 모바일에서도 오프라인 환경에서도 많은 사용자

duklook.tistory.com

 

복지맵에서 사용할 백엔드 언어

이번 프로젝트에서 사용되는 언어는 NestJS 이다. 이는 NodeJS Express 를 기반으로 구축된 백엔드 프레임워크로서 DI, IoC 등을 적극적으로 도입하여 객체지향 프로그래밍의 강점을 최대한 살린 방식으로 보다 안정적인고 확장 가능한 백엔드 환경을 구축할 수 있도록 도와준다.

 

이번 프로젝트에서는 새롭게 Docker 를 적용하여 어느 환경에서도 동일한 환경셋팅이 가능하게 하여, 보다 빠르고 안정적인 애플리케이션 배포가 가능하도록 설정할 것이다. 데이터베이스의 경우에는 PostgreSQL 를 사용하는데, 여기서 NestJS 진영에서 자주 사용되는 TypeORM 를 사용할 것이다. 

 

다만 도커를 사용할 때에는 보안상의 문제를 염두에 두어야 할 것 같다. 이와 관련한 사항은 아래 기술 포스트를 읽어보면 좋을 듯하다.

 

 

도커 컨테이너의 심각한 보안 이슈

업계 표준처럼 사용되는 도커는 보안 취약점 발생 시 큰 피해가 발생할 수 있다. 이번 글에서는 공격 표면 노출로 인해 발생할 수 있는 도커 컨테이너의 심각한 보안 이슈와 도커 컨테이너 공격

blog.criminalip.io

 

NestJS 설치

C L I 설치

NestJS 를 설치하기 위해서는 우선적으로 cli 를 설치해야 한다. 해당 도구는 NestJS 설치뿐만 아니라 컨트롤러, 엔티티, 모델 등을 쉽고 빠르게 생성할 수 있도록 돕는 다양한 명령어를 지원한다.

 npm i -g @nestjs/cli

 

NestJS 설치

그 다음에는 CLI 를 사용하여 NestJS 를 설치한다.

 nest new project-name

 

정상적으로 설치되었다면 다음과 같이 기본적으로 셋팅된 보일러 플레이트를 확인할 수 있다.

 

 

 

프리티어와 린트 설정

NestJS 를 설치하면 기본적으로 eslint 와 prettier 설정이 되어 있다. 즉, 별도로 설치할 필요 없이 기본 셋팅을 자신의 환경에 맞게 바꿔주면 된다.

 

ESLint

ESLint 는 이대로 두고 넘어간다.

 

Prettier 설정

.prettierrc 파일에 들어가서 아래와 같이 기본셋팅을 한다.

{
  "singleQuote": true,
  "semi": true,
  "useTabs": false,
  "tabWidth": 2,
  "trailingComma": "all"
}

 

이대로 설정한다면,  " 이 아니라 ' 으로 감싸는 것만 허용한다. semi 이는 ; 를 사용한다는 의미이다.

useTabs 는 말그대로 [Tab] 사용을 거절한다는 의미이다. tabWidth 는 스페이스바로 공백을 2번 띄우면 그것을 Tab 과 동일한 것으로 간주한다는 의미이다.

 

trailingComma 는 줄바꿈으로 구분되는 마지막 요소의 끝에 콤마를 어떤 경우에만 찍을 것인지 설정하는 것으로 all 로 지정하면 모든 줄바꿈 이후에 콤마를 추가한다.

 

애플리케이션 도커 컨테이너 설정

향후 NestJS 를 배포 자동화하기 위한 기초 셋팅으로 도커 환경을 셋팅한다. 사실 이부분은 도커 컴포즈를 사용하면 같이 실행시킬 것이므로 도커 파일 부분만 작성하고, 다음 목차로 넘어가도 된다. 이해를 위해서 도커 파일만 빌드하여 컨테이너를 실행하는 과정을 정리해본다.

Dockerfile 생성

우선 프로젝트의 루트 경로에 Dockerfile 을 생성한다.

project-name
ㄴsrc
ㄴdist
ㄴDockerfile -> 이 친구 생성

 

Dockerfile 작성

명령어를 다음과 같이 입력해준다.

# FROM: 사용할 도커 이미지 설정
# NodeJS 버전 중 20 번대를 선택하여 이미지를 가져온다. 
FROM node:20

# RUN:  지정한 명령어 실행
# mkdir -p /var/app : 컨테이너 내부에 경로 생성
# -p : 부모 디렉토리가 없으면 경고 없이 경로를 생성한다.
# 즉, /var/app 이라는 경로를 생성 시 /var 가 없으면 경고 없이 생성해준다.
RUN mkdir -p /var/app 

# WORKDIR: 코드 실행 경로(작업 경로)
WORKDIR /var/app

# COPY source(카피할 파일 경로 -) dest(카피할 파일을 넣을 경로 = WORKDIR)
# . : 호스트의 현재 디렉토리, . : 컨테이너 내부의 작업 디렉토리
# 즉, 현재 디렉토리에 있는 모든 파일을 컨테이너 내부의 /var/app 으로 복사
COPY . .

# 모듈 설치 및 빌드
RUN npm install
RUN npm run build

# 3001 포트 열기(main.ts 에 지정된 포트 번호를 입력한다. 원래 기본은 3000)
EXPOSE 3001

# node 는 Node 환경에서 파일을 실행 시 사용되는 키워드이다.
# dist/main.js 는 node 키워드로 실행하고자 하는 파일의 경로이다.
# 즉, 도커 컨테이너가 실행될 때 dist/mian.js 를 같이 실행(node) 한다.
CMD [ "node","dist/main.js" ]

 

참고로 CMD 는 npm run build 이후에 프로젝트를 실행하는 명령어를 띄어쓰기를 요소 단위로 나눠서 배열에 담은 것인데, 현재 위 예시의 경우에는 프로덕션에서 실행하는 경우를 가정한 것이다.

 

만일 현재 도커의 실행을 개발 서버로 구동하고자 한다면, 다음과 같이 바꿔주면된다. 

CMD ["npm", "run", "start:dev"]

 

이렇게 되면, package.json 에 있는 script 속성 중 start:dev 에 부여된 코드를 실행하여 프로젝트를 개발 환경에서 실행하게 된다.

 

.dockerignore 파일 생성

도커 이미지 빌드 시 도커 데몬이 이미지 생성 시 참고하는 파일의 목록에서 제외시킬 때 사용하는 파일이다. 즉, .gitignore 와 같은 기능을 수행한다고 보면된다.

 

도커 이미지 빌드 시 node_modules, dist 등과 같은 불필요한 파일을 제외시키고자 한다면 다음과 같이 작성해준다.

# 불필요한 파일이 도커 이미지에 빌드 되는 것을 방지
.git
Dockerfile
node_modules
dist

 

도커 이미지 빌드 실행하기

다음 명령어를 입력하여 이미지를 빌드한다.

docker build . -t <이미지명>

 

 

도커.exe 실행 후 [images ] 에서 생성된 이미지가 보이면 성공이다.

 

 

 

참고로 도커 cli 를 사용해서 쉽게 이미지 목록을 확인할 수 있는데, docker images 를 입력하면 된다.

 

 

도커 컨테이너 실행 해보기

앞서 도커 설정이 끝났다면, 프로젝트 이미지가 생성된 직후가 된다. 그럼 해당 이미지를 기반으로 컨테이너를 실행할 수 있는데, 다음 명령어를 따른다.

docker container run -d -p 3001:3001 <이미지명>

 

용어 설명

-p 는 포트포워딩의 약자이다. 로컬에서의 3001 포트와 컨테이너 내부에서의 3001 포트는 서로 다른 환경에서 열려 있기 때문에,  클론(:) 을 기준으로 좌측은 로컬, 우측은 컨테이너로 로컬의 3001 포트에 요청이 오면 컨테이너의 3001 포트로 포워딩 해주기 위해 사용한다.

 

docker container 는 말그대로 도커 컨테이너를 의미하며,

run 은 실행한다는 의미이다.

-d 는 백그라운드 에서 컨테이너를 실행한다는 의미로 도커 컨테이너를 실행한 터미널이 닫혀도 컨테이너를 백그라운드에서 실행하기 때문에 종료되지 않도록 해준다.

 

즉, 위 명령어를 종합하여 설명하면,

 

" container 를 터미널이 종료된 상태에서도 실행을 유지하는데, 이 때 컨테이너에 접근하기 위한 포트는 3001 으로 로컬의 3001 포트로 접근 시 해당 컨테이너 포트로 연결해준다 "

 

라고 이해하면된다.

 

실행 후 상태 확인

도커 컨테이너가 성공적으로 실행되었는지 확인하려면, docker ps 를 입력하면된다.

docker ps # 현재 실행중인 컨테이너 목록을 나열한다.

 

실행 해보면 아래와 같이 컨테이너가 표시된다.

 

 

Docker + PostgreSQL + Docker compose 셋팅

앞서 Nestjs 자체를 이미지로 빌드하고, 컨테이너로 실행하는 것 까지 성공적으로 완료 하였다. 지금 부터는 도커 컴포즈를 설정하여 PostgreSQL 와 앞서 생성한 DockerFile 을 동시에 빌드하여 컨테이너를 관리할 수 있도록 해볼 것이다.

 

docker-compose.yaml 파일 생성 및 설정

우선 Dockerfile 과 같은 동일 경로에 docker-compose.yaml 파일을 생성하고, 다음과 같이 설정해준다.

version: '3.5' # 실행시 참고할 도커 컴포즈 문서의 버전

 # services: 실행할 서비스들을 정의
services:
 # db: 데이터베이스(사용자 작명)
  db:
    image: postgres:15 # 가져올 이미지 이름 및 버전 지정
    restart: always  # 빌드 실행 시 자동 재시작
    environment: # 환경변수 설정
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres
    container_name: postgres # 컨테이너 이름(유저 작명)
    # volumes : 도커 이미지 내 폴더와 로컬 내 폴더를 동기화(링크 연결)
    # 즉, 이미지 내 폴더 내 파일 변경 시 local 의 postgresql-data 내 파일도 같이 변경
    volumes: 
      - ./postgresql-data:/var/lib/postgresql/data
    # 로컬 5432 로 들어오는 요청을 이미지 내 5432 포트로 포워딩
    ports:
      - '5432:5432' # 만일 로컬에서 5432 를 이미 쓰고 있다면 5431:5432 와 같이 로컬 포트 자체를 바꿔서 시도

  # nest 프로젝트 자체
  app:
    build:
      context: . # 현재 디렉토리의
      dockerfile: Dockerfile # 도커파일 빌드
    container_name: app
    environment:
      - PORT=${PORT}
    ports:
      - '3001:3001'
    depends_on:
      - db
    volumes:
      - ./src:/app/src

 

컨테이너 빌드(도커 실행)

다음 명령어를 입력하여 도커 컴포즈를 실행하고, 이미지 빌드 후 각 컨테이너를 실행한다.

docker compose up

 

실행중인 도커 컴포즈

 

이후 로컬 사이트에 접속하면 성공적으로 접속이 이루어진다.

 

TypeORM 설정

이번에는 PostgreSQL 을 맵핑하는 TypeORM 을 설정해볼 것이다.

 

패키지 설치

우선 TypeORM 과 PostgreSQL 에 접속할 수 있도록 해주는 pg 를 설치해준다.

또한 타입 유효성 검사를 위한 패키지인 class-validator 와 class-transformer 를 같이 설치해준다.

마지막으로 .env 환경변수를 시의적절하게 바꿔가며 사용할 수 있도록 돕는 @nestjs/config  도 같이 설치해준다.

npm install @nestjs/typeorm typeorm pg
npm install class-validator class-transformer
npm i @nestjs/config

 

환경변수 설정 파일 생성

DB 정보 등을 담아둘 환경변수 파일을 생성하고, DB_PORT, DB_NAME, DB_HOST, DB_USERNAME, DB_PASSWORD 등을 입력해둔다.

 

 

app.module 에서 TypeORM 및 nestjs/config 모듈 설정

그 다음에는 app.module.ts 파일로 가서 아래와 같이 설정한다.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // true 지정 시 다른 모듈에서 import 하지 않고 바로 사용 가능
      envFilePath: ['.env.development', '.env.development.local'], // 접근 가능한 환경변수 목록
    }),
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: process.env.PG_HOST,
      port: Number(process.env.PG_PORT || 3001),
      username: process.env.PG_USERNAME,
      database: process.env.PG_DBNAME,
      entities: [], // 앞으로 생성할 테이블을 넣는 배열
      synchronize: true, // ORM 과 DB 간 자동 연동 유무 설정:  TypeORM이 엔터티를 기반으로 데이터베이스 테이블을 자동으로 생성한다
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

 

환경변수의 경우 향후 Service.ts 파일에서 사용하는 경우에는 아래 예시와 같이 사용하면 된다. 

constructor(private configService: ConfigService) {}
// get an environment variable
const dbUser = this.configService.get<string>('DATABASE_USER');

// get a custom configuration value
const dbHost = this.configService.get<string>('database.host');

 

 

[나가는 말] 일단은 1차 셋팅 마무리

지금 까지 NestJS + Docker + PostgreSQL + TypeORM 을 설정하여 welfare-map 프로젝트의 백엔드 환경을 구축해 보았다. 여기서 CI/CD 까지 포함하려면 몇 가지 더 작업이 필요하지만, 현재는 기본적인 환경 셋팅만 해두고, 개발이 우선되어야 할 듯하다.

 

 

참고자료

- nest 공식문서

https://docs.nestjs.com/techniques/configuration#custom-env-file-path

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

 

- 유효성 라이브러리(깃허브)

https://github.com/typestack/class-validator

 

GitHub - typestack/class-validator: Decorator-based property validation for classes.

Decorator-based property validation for classes. Contribute to typestack/class-validator development by creating an account on GitHub.

github.com

 

- 블로그

https://dev.to/chukwutosin_/step-by-step-guide-setting-up-a-nestjs-application-with-docker-and-postgresql-5hei

작성자 프로필

 

- 유튜브

https://www.youtube.com/watch?v=Dr4OXDLGsmI

 

- 유튜브

https://www.youtube.com/watch?v=gqFauCpPSlw

 

반응형