첫 풀스택이다보니 가장 기초중의 기초인 C와 R먼저 잘 작동하는지 해보려 한다.
데이터 모델과 API 엔드포인트가 확립되어 프론트 개발을 이어서 진행해볼 예정
// apps/api/src/posts/post.entity.ts
export class Post {
id: number;
title: string;
createdAt: Date;
}
일단은 todolist를 성공해야하기에 부수적인거 빼고 제목만 넣어보기로!
여기서 entity란
데이터베이스의 테이블을 표현하는 클래스
객체 관계형 매핑(ORM)시스템에서 중요한 개념,
데이터베이스 테이블의 구조를 코드로 정의함
그래서 결론으로 엔티티 클래스는 보통
근데 나는 TypeORM이 아닌 prisma를 사용할 예정이므로 코드내용이 달라짐
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Post {
id Int @id @default(autoincrement())
title String
createdAt DateTime @default(now())
}
이후 package.json쪽 수정이 필요한데
터보레포, 즉 monorepo구조로 프로젝트를 설정하고 있는데,
database쪽 package.json의 수정이 필요하다
이전 코드
{
"name": "@lottodolist/database",
"version": "1.0.0",
"description": "",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"db:generate": "prisma generate",
"db:push": "prisma db push",
"build": "tsup index.ts --format esm,cjs --dts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@prisma/client": "^6.5.0",
"prisma": "^6.5.0"
},
"devDependencies": {
"typescript": "^5.8.2"
}
}
수정 코드
{
"name": "@lottodolist/database",
"version": "0.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"db:generate": "prisma generate",
"db:push": "prisma db push"
},
"dependencies": {
"@prisma/client": "^5.0.0"
},
"devDependencies": {
"prisma": "^5.0.0",
"typescript": "^5"
}
}
차이점을 비교하면
이전 : main, types가 소스 파일(./src/index.ts)를 직접 가리킴
권장: 컴파일된 결과물(dist/index.js, dist/index.d.ts)를 가리
이전: tsup(더 현대적 도구, ESM/CJS양식 모두 지원)
권장:tsc사용(표준 ts 컴파일러)
tsup
TypeScript 프로젝트를 위한 빠르고 간단한 번들링 도구
esbuild를 기반으로 만들어졌고 TS코드를 다양한 형식(ESM,CJS 등)으로 빠르게 컴파일하고 번들링함. 복잡한 설정 없이 사용하기 쉬우며, 특히 라이브러리 만들 때 유용
tsc
TS Compiler의 약자, TS코드를 JS로 변환하는 공식 TS 컴파일러
tsconfig.json 파일 통해 다양한 컴파일 옵션 설정할 수 있으며, 타입 체크, 코드 변환 수행함
ESM
ECMAScript Modules는 JS의 공식 모듈 시스템
ES6에서 도입됨
import, export 키워드 사용하여 모듈 가져오고 내보내는 방식
<script type='module'>
CJS
CommonJS는 노드에서 사용되는 모듈 시스템, 서버사이드 JS 개발 위해 만들어짐
require()과 module.exports를 사용하여 모듈 가져오고 내보냄
이전: prisma가 일반 의존성에 있음
권장:prisma가 개발 의존성에 있음(더 적절한 위치)
일반 의존성
개발 의존성
이전: private:true (npm에 게시 불가)
권장: private 설정 없음(모노레포 내부에서 다른 패키지에 의존성으로 사용 가능)
ts가 개발 언어이지만, node.js는 js만 실행할 수 있음
그래서 개발시엔 .ts파일 작성하고 빌드과정에서 .js로 컴파일됨 그리고 타입 정보는 .d.ts파일로 추출
그리고 필드에서 main은 다른 패키지가 이 패키지 가져올 때 불러올 js 파일
types는 TS가 타입 체크에 사용할 타입 정의 파일
api가 database 패키지를 의존할 경우 중요함
최종 package.json
{
"name": "@lottodolist/database",
"version": "0.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts --outDir dist",
"db:generate": "prisma generate",
"db:push": "prisma db push"
},
"dependencies": {
"@prisma/client": "^6.5.0"
},
"devDependencies": {
"prisma": "^6.5.0",
"typescript": "^5.8.2",
"tsup": "^latest"
}
}
이 설정이 나은 이유는
그 다음에 실제 api의 package.json엔 이 내용 추가해야 데이터베이스쪽 사용 가능
"dependencies": {
"@lottodolist/database": "workspace:*",
// 다른 의존성들...
}
import { PrismaClient } from '@prisma/client';
const prismaClientSingleton = () => {
return new PrismaClient();
};
declare global {
var prisma: undefined | ReturnType<typeof prismaClientSingleton>;
}
export const prisma = globalThis.prisma ?? prismaClientSingleton();
if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;
export * from '@prisma/client';
일단 prisma가 node와 ts를 위한 현대적 데이터베이스 ORM도구
import { PrismaClient } from '@prisma/client';
PrismaClient - Prisma핵심 클래스, DB와의 모든 상호작용 처리
@prisma/client: prisma 스키마 기반 자동 생성된 클라이언트 라이브러리
const prismaClientSingleton = () => {
return new PrismaClient();
};
PrismaClient의 새 인스턴스 생성
싱글톤 패턴 구현 위한 함수
여기서 싱글톤패턴은 데이터베이스 클라이언트 인스턴스를 애플리케이션 전체에서 하나만 생성해서 공유하는걸 의미
Next.js나 서버리스 환경에서 Prisma인스턴스를 계속 새로 만들면 DB 커넥션이 너무 많아져서 에러가 발생되므로 매우 중요
declare global {
var prisma: undefined | ReturnType<typeof prismaClientSingleton>;
}
declare global: TS에게 전역 객체를 확장한다고 알려줌
var prisma: Node.js의 전역 변수에 prisma라는 속성 추가
ReturnType: prismaClientSingleton 함수의 반환 타입을 자동으로 추론
export const prisma = globalThis.prisma ?? prismaClientSingleton();
globalThis.prisma:전역 객체에서 prisma 변수를 찾음
??: Nullish병합 연산자, 왼쪽 값이 null이나 undefined일때만 오른쪽 값 사용
prismaClientSingleton():전역에 prisma없을 시 새 인스턴스 생성
if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;
개발환경에서만 prisma 인스턴스를 전역 객체에 저장
이러면 Hot Reload나 Fast Refresh 중에도 DB 연결 유지됨
Hot Reload
코드가 변경될 때 애플리케이션의 상태 유지하면서 변경된 모듈만 교체하는 기술
Fast Refresh(빠른 새로고침)
React팀이 개발한 개선된 형태의 핫리로드 시스템
만약 개발 중 Hot Reload나 Fast Refresh가 발생하면
그치만 싱글톤 패턴은 이미 존재하는 인스턴스를 재사용해 이 문제를 방지함
이전 코드의
if(process.env.NODE_ENV !== 'production'
부분이 개발 환경에서 이 문제를 해결해줌
즉, 코드가 개발 중 여러번 다시 로드되어도 데이터베이스 연결 하나만 유지되도록 보장함
일단 prisma 기본 세팅은 끝났으니 이제
apps/api/src/prisma/prisma.service.ts 생성
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { prisma } from '@lottodolist/database';
@Injectable()
export class PrismaService implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await prisma.$connect();
}
async onModuleDestroy() {
await prisma.$disconnect();
}
get client() {
return prisma;
}
}
이제 기존 Prisma클라이언트를 database쪽에서 만들어놨으니 NestJS애플리케이션에서 그 클라이언트를 관리하는 서비스를 만드는 코드임
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { prisma } from '@lottodolist/database';
@Injectable()
export class PrismaService implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await prisma.$connect();
}
async onModuleDestroy() {
await prisma.$disconnect();
}
get client() {
return prisma;
}
NestJS에서 Prisma 서비스 만드는 이유
apps/api/src/prisma/prisma.module.ts생성
import { Module, Global } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
이 코드를 이용하면 NestJS의 모듈 시스템을 사용하고 PrismaService를 애플리케이션 전체에서 사용할 수 있게 해줌
import { Module, Global } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
여기서 모듈은 NextJS에선 관련 기능을 모듈로 그룹화 함. 모듈은 서비스,컨트롤러, 미들웨어 등 포함할 수 있음
apps/api/src/posts/dto/create-post.dto.ts 생성
import { IsNotEmpty, IsString } from 'class-validator';
export class CreatePostDto {
@IsNotEmpty()
@IsString()
title: string;
}
게시글 생성 요청의 데이터 구조를 정의하고 검증해주는 DTO(Data Transfer Object)클래스임
DTO란?
계층 간 데이터 전송을 위한 객체
코드 설명
- class-validator 라이브러리에서 검증데코레이터를 가져옴
- CreatePostDto 클래스를 정의함
- title이 비어있지 않은 문자열이여야함
여기서 게시글 모듈이란?
Next.JS에서 모듈은 관련 기능을 그룹화하는 방법.
즉 게시글 관련된 모든 기능을 모아둔다 생각하면 됨
| class-validator와 class-transformer 설치해야하는 이유
NestJS애플리케이션에서 데이터 검증과 변환을 위한 라이브러리임
여기서 ValidationPipe는 main.ts에 적용하는데
// apps/api/src/main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 전역 파이프로 ValidationPipe 추가
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 속성은 자동으로 제거
transform: true, // 자동으로 타입 변환 활성화
}));
await app.listen(3000);
}
bootstrap();
이런식으로 사용한다
apps/api/src/posts/posts.service.ts 생성
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreatePostDto } from './dto/create-post.dto';
@Injectable()
export class PostsService {
constructor(private prisma: PrismaService) {}
create(createPostDto: CreatePostDto) {
return this.prisma.client.post.create({
data: createPostDto,
});
}
findAll() {
return this.prisma.client.post.findMany({
orderBy: {
createdAt: 'desc',
},
});
}
findOne(id: number) {
return this.prisma.client.post.findUnique({
where: { id },
});
}
}
NestJS의 PostsService 정의
게시글과 관련된 데이터베이스 작업을 처리하는 서비스 클래스
일단 당장은 생성만 하기로 했으니 나머지는 뺄 예정이고(추가로 공부하면서 그때 다시 추가하면서 리마인드)
@Injectable()
export class PostsService {
constructor(private prisma: PrismaService) {}
create(createPostDto: CreatePostDto) {
return this.prisma.client.post.create({
data: createPostDto,
});
}
결국 이 서비스 클래스는 컨트롤러에서 사용되어 HTTP요청을 처리하게 됨
컨트롤러는 클라이언트 요청을 받아 이 서비스의 메서드를 호출하고, 서비스는 실제 데이터베이스 작업을 수행함
apps/api/src/posts/posts.controller.ts
import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common';
import { PostsService } from './posts.service';
import { CreatePostDto } from './dto/create-post.dto';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Post()
create(@Body() createPostDto: CreatePostDto) {
return this.postsService.create(createPostDto);
}
@Get()
findAll() {
return this.postsService.findAll();
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.postsService.findOne(id);
}
}
이 코드는 NestJS의 컨트롤러.
클라이언트의 HTTP 요청을 받아서 처리하는 역할임
물론 Post 제외하면 아직 안씀
import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common';
import { PostsService } from './posts.service';
import { CreatePostDto } from './dto/create-post.dto';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Post()
create(@Body() createPostDto: CreatePostDto) {
return this.postsService.create(createPostDto);
}
결국 계속 리마인드 하자면
apps/api/src/posts/posts.module.ts 생성
import { Module } from '@nestjs/common';
import { PostsService } from './posts.service';
import { PostsController } from './posts.controller';
@Module({
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
post관련 모듈 정의한 것
import { Module } from '@nestjs/common';
import { PostsService } from './posts.service';
import { PostsController } from './posts.controller';
@Module({
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
모듈의 역할과 중요성
- 조직화: 관련 기능을 함께 그룹화하여 코드를 더 체계적으로 관리
- 캡슐화: 모듈 내부 구현을 숨기고 필요한 것만 내보냄
- 의존성 관리: 다른 모듈과의 의존성을 명확하게 정의
즉 이 PostsModule은 게시글 관련 모든 기능(컨트롤러와 서비스)을 하나로 묶어 놓은 것
이 모듈은 루트 모듈(AppModule)에 가져와져서 애플리케이션의 일부로 등록
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [PostsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
이런식으로 imports에 넣어서 사용
여기서
import { prisma } from '@lottodolist/database';
임포트 에러가 ㅅ발생하고 있는데 이유는 @lottodolist/database 패키지에서 prisma를 제대로 내조내지 않거나, 모노레포 설정때문일 수 있음
database 패키지의 index.ts를 살펴보니
여기서부터 @prisma/client와 process 객체를 찾고 있지 못하고 있었음 그래서 추가로 설치 시도
pnpm add @prisma/client
이걸 추가 설치하니 해결되었는데,
@prisma/client란?
Prisma ORM의 핵심 구성 요소 중 하나
데이터베이스와 상호작용 하는데 필요한 클라이언트 라이브러리
일반적으로 Prisma사용할 때 2개 패키지가 필요
이것은 개발 도구로, Prisma CLI를 포함함. 데이터베이스 스키마를 정의하고, 마이그레이션을 관리하고, 데이터베이스를 시각화하는 등의 작업에 사용됨
애플리케이션 코드에서 데이터베이스와 통신하는데 사용하는 실제 클라이언트. Prisma 스키마를 기반으로 자동 생성된 타입-세이프한 쿼리빌더
일반적 워크플로우는
다음은 process 객체인데
process는 Node.js에서 제공하는 전역 객체
이 객체는 현재 실행 중인 Node.js 프로세스에 대한 정보와 제어 기능을 제공
process.env는 환경 변수에 접근할 수 있게 도와줌
process를 찾지 못하는 원인 일 수 있는 문제들
- 브라우저 환경에서 실행중
브라우저엔 process객체가 기본적으로 전재하지 않음. Node.js환경에서만 사용할 수 있음
TypeScript에서 process를 인식하지 못하는 경우
Webpack같은 번들러가 process 객체를 자동으로 제공안할 수도있음
일단 패키지가 다양한 디렉토리에 존재하는데
packages/database쪽 루트로 이동해서 @types/node 패키지를 개발 의존성으로 설치 시도
pnpm add -D @types/node
여기서 개발 의존성으로 설치하는 이유는, @types/node는 TS에서 Node.js의 타입 정의를 제공하는 패키지로써 개발 시에만 필요하고 실제 프로덕션 환경에서는 필요하지 않아서 개발 의존성으로 설치하는 것임
이후 packages/database에서 tsconfig.json 파일을 추가하고
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node"],
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
해당 내용을 넣으니 process의 빨간줄이 사라졌는데
tsconfig.json파일은 TS 프로젝트의 컴파일 옵션과 설정을 정의하는 파일임
보통 프로젝트 루트 디렉토리나 각 패키지 디렉토리에 위치함
나같은 경우는 모노레포여서 각 패키지에 넣어주는게 좋을 듯 함
@types/node 패키지를 설치했기 때문에, Node.js의 내장 객체와 API에 대한 TS 타입 정의를 제공받음
tsconfig.json파일 내용에
{
"compilerOptions": {
"types": ["node"]
}
}
이 설정을 넣었으니 Node.js의 타입 정의를 사용하라고 명시적으로 알려준것임
이 부분을 해결했어도 api쪽의 prisma.service.ts에서 여전히
@lottodolist/database
패키지에서 prisma
를 제대로 내보내고 있지 않음
api쪽의 package.json에 의존성을 넣어주려고
"dependencies": {
"@lottodolist/database": "workspace:*",
// 다른 의존성들...
}
이 내용을 담아줬어도 소용이 없엇다.
이후 해결을 위해
cd packages/database
pnpm db:generate
database쪽에서 prisma generate실행해서 Prisma 클라이언트를 생성함
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
기존 코드는
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node"],
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
이렇게 넣어서 index.ts의 process 에러는 해결은 했지만 왜 이렇게 차이가 나는지 비교해보자
주요 차이점
- extends
기존: 루트 디렉토리의 tsconfig.json 파일 상속
이후: 독립적인 설정으로, 상속 없음
기존:루트에서 상속받고, 패키지별 특수 설정만 추가
이후:모든 필요한 설정 직접 명시(타겟,모듈 시스템,엄격모드 등)
기존도 충분히 근거있는 방식이지만, 기존 방식으로 하였을 때 tsconfig.json을 찾지 못한다고 하였기 때문에, 이후 방식을 채택해보자(그리고 심지어 루트에 tsconfig.json은 없엇네 ;;)
이렇게 하니 tsconfig.json파일도 정상적으로 작동!!
이제 prisma 클라이언트 생성을 위해
pnpm db:generate 이후
pnpm build 까지 시도하였으나 여기서 또 에러가 발생했다
Prisma 클라이언트 생성까진 잘 되었으나, tsup 빌드 중에 오류가 발생한 것인데,
src/index.ts(1,10): error TS2305: Module '"@prisma/client"' has no exported member 'PrismaClient'.
@prisma/client 에서 PrismaClient 가져오는 방식이 문제가 있다는 것이다.
그치만 코드 자체가 문제가 없으므로, packages에서 database 폴더 안에서 관리하던 prisma 관련된 코드를 api에서 직접 prisma를 설정하는걸로 바꿔보려 한다.
일단 apps/api쪽으로 넘어와서
pnpm add -D prisma
pnpm add @prisma/client
설치 진행
그리고 src/prisma쪽에서 module, service 생성한 것과 다르게 prisma 디렉토리를 api바로밑에 생성
schema.prisma 파일도 생성(내용 그대로)
이후 Prisma 클라이언트 생성
cd apps/api
pnpm prisma generate
생성은 이전에도 잘되었으니 이번에도 잘 되었고
const prisma = new PrismaClient();
직접 인스턴스 생성해서 해결됨 후..
당장은 기존 database에서 가져다 쓰려고 많은 부분을 코드입력했고, 그거때문에 다시 재수정해야하긴하지만.. 일단은 넘어가자 나중에 중앙화 시킬수도있기에..database쪽에
그리고 prisma generate 명령어는 항상 실행해야 하는 것은 아님
이후 스키마 변경 사항 실제 데이터 베이스에 적용하려면 prisma db push 이용하면 됨
이제 docker-compose up 하면 정상 실행되지만,
설정 수정 중 npm으로 설치하고 prisma관련 내용을 넣어줘야해서 수정작업을 들어갔다
FROM node:18-alpine
# NestJS CLI 전역 설치
RUN npm install -g @nestjs/cli
WORKDIR /app
# 패키지 파일만 복사
COPY package.json .
COPY tsconfig.json .
COPY nest-cli.json .
# 의존성 설치
RUN npm install
# 소스 코드 복사
COPY src/ ./src/
# 개발 서버 실행
CMD ["npm", "run", "dev"]
기존 내용에서,
FROM node:18-alpine
# pnpm 전역 설치
RUN npm install -g pnpm
# NestJS CLI 전역 설치 (필요한 경우)
RUN pnpm add -g @nestjs/cli
WORKDIR /app
# 패키지 파일 복사
COPY package.json .
COPY pnpm-lock.yaml . # pnpm 락 파일도 복사
COPY tsconfig.json .
COPY nest-cli.json .
# pnpm으로 의존성 설치
RUN pnpm install
# 소스 코드 복사
COPY src/ ./src/
COPY prisma/ ./prisma/ # prisma 폴더도 복사
# prisma 클라이언트 생성
RUN pnpm prisma generate
# 개발 서버 실행
CMD ["pnpm", "run", "dev"]
수정하고 up했을 때 정상실행 완료 후후
아직 클라이언트 쪽에서 데이터를 전송안했을뿐이지。。。 prisma와 nestJS 설정 완료햇다 ㅠㅠㅠㅠㅠ 오늘은 여기까지
이전 캡쳐 화면을 보면 단순히 클라이언트쪽을 만들지 않아서 응답을 반환하고 있지 않다고 생각했으나,
ERR_EMPTY_RESONSE 상태의 원인으로는
나는 무조건 1번이라 생각했는데
docker-compose logs api
로 로그를 보더라도 에러가 없었다.
이제 찐 마지막으로 CORS 설정을 위해
apps/api/src/main.ts 코드를 보자면
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
});
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
이 코드로 할 시
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
transform: true,
}));
await app.listen(process.env.PORT ?? 3001);
}
bootstrap();
이 코드는
이 두 코드에서 실용적으로 종합해서 사용하면
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// CORS 설정 - Docker 환경에서 중요
app.enableCors({
origin: 'http://localhost:3000', // 프론트엔드 주소
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
});
// 유효성 검사 파이프
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 속성 제거
transform: true, // 자동 타입 변환
}));
// 모든 인터페이스에서 리스닝
await app.listen(3000, '0.0.0.0');
}
bootstrap();
whilelist는 DTO에 정의되지 않은 속성 제거시키고,
transform으로 JSON 데이터 타입 변환 시켜주기
그리고 main.ts는 NestJS애플리케이션의 시작점이라 할 수 있는데
즉 애플리케이션을 시작하는 전원 버튼과 같다
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
비동기로 서버시작을 처리한다는 느낌으로 bootstrap은 애플리케이션을 부팅한다는 의미로 쓰임
const app = await NestFactory.create(AppModule);
NestFactory.create - NestJS 애플리케이션 인스턴스 생성
AppModule - 애플리케이션의 구조를 정의한 모듈 전달
app.enableCors({
origin: '[http://localhost:3000](http://localhost:3000/)',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
});
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
컨테이너 내부에서 3000포트에서 시행된다는 의미
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 속성은 자동으로 제거
transform: true, // 자동으로 타입 변환 활성화
}));
앞에 말했던 부분 한 번 더 리마인드 하자면
DTO에 없는 속성이 요청에 포함되면 자동 제거
ex- DTO에 name과 age만 있는데, 요청에 secret도 있으면 secret 제거시킴
문자열 등을 적절한 타입으로 자동 변환
await app.listen(process.env.PORT ?? 3001);
await app.listen(3000, '0.0.0.0');
모든 네트워크 인터페이스에서 연결을 받겠다는 의미
Docker에선 이 설정이 중요. 이게 없으면 컨테이너 외부에서 접근 불가
즉 main.ts는 NestJS 애플리케이션 설정하고 시작하는 파일