NestJS에서 Prisma 7와 Docker 및 Docker Compose 사용 완전 가이드
Source: Dev.to
만약 Docker 안에서 Prisma를 실행해 보았고, 전혀 이해할 수 없는 연결 오류에 갇힌 적이 있다면, 이 글이 도움이 될 것입니다.
현대 백엔드 애플리케이션은 데이터베이스 접근을 위해 Prisma, TypeORM, Mongoose와 같은 ORM에 의존하는 경우가 많으며, 일관된 개발 환경을 위해 Docker를 함께 사용합니다. 각각의 도구는 독립적으로 잘 동작하지만, Prisma를 Docker와 결합—특히 컨테이너화된 데이터베이스와 함께 사용할 때—는 생각보다 까다로울 수 있습니다.
TL;DR – 나는 Prisma가 Docker에서 실행 중인 PostgreSQL 데이터베이스에 연결되지 않는 원인을 디버깅하는 데 거의 이틀을 보냈다. 이 가이드는 단계별 설정, 흔히 발생하는 함정, 그리고 컨테이너화된 환경에서 마이그레이션을 실행하기 위한 모범 사례를 안내한다.
📋 필수 조건
- NestJS에 대한 기본 지식
- Docker 및 Docker‑Compose에 대한 기본 이해
- Docker Desktop이 설치되고 실행 중
- 코드 편집기 (예: VS Code)
Prisma란?
Prisma는 테이블을 JavaScript/TypeScript 객체에 매핑하여 데이터베이스 작업을 단순화하는 오픈‑소스 ORM(Object‑Relational Mapper)입니다. 원시 SQL을 직접 작성하는 대신, 타입‑안전 API를 사용해 데이터베이스와 상호작용합니다.
// Raw SQL
SELECT * FROM users WHERE email = 'john@example.com';
// Prisma
const user = await prisma.user.findUnique({
where: { email: 'john@example.com' },
});
장점
- 가독성 향상
- 런타임 오류 감소
- 뛰어난 TypeScript 지원 제공
Prisma 7 하이라이트 (Docker와 관련)
| 기능 | Docker와 관련된 이유 |
|---|---|
| Rust‑free 클라이언트 엔진 | 특히 Alpine 이미지에서 바이너리 호환성 문제가 적음 |
| 타입 정의 감소 | TypeScript 컴파일 속도 향상, 에디터 성능 개선 |
| 현대 JavaScript 지원 | 새로운 Node.js 런타임과 일치 |
| 보다 깔끔한 개발자 경험 | 구성이 간소화되고, 엣지 케이스 오류가 감소 |
최소 요구 사항
- Node.js 20.19.0+
- TypeScript 5.4.0+
왜 Docker인가?
Docker는 애플리케이션과 그 종속성을 컨테이너에 패키징하여 어디서나 동일하게 실행되도록 보장합니다.
장점
- 팀 간 일관된 환경
- 빠른 온보딩
- 더 쉬운 배포 및 확장
- “내 컴퓨터에서는 작동해요” 문제 감소
NestJS 개요
NestJS는 효율적이고 확장 가능한 Node.js 서버‑사이드 애플리케이션을 구축하기 위한 프레임워크입니다. TypeScript로 구축되었으며 다음을 지원합니다:
- 객체 지향 프로그래밍 (OOP)
- 함수형 프로그래밍 (FP)
- 함수형 반응형 프로그래밍 (FRP)
내부적으로 기본적으로 Express를 사용합니다 (원한다면 Fastify도 가능합니다).
NestJS CLI를 전역으로 설치하기
npm install -g @nestjs/cli
새 프로젝트 만들기
nest new backend
스캐폴드:
- TypeScript 설정
- 프로젝트 구조
- 핵심 의존성
필요에 따라 컨트롤러/서비스를 생성합니다:
nest g controller users
nest g service users
NestJS 프로젝트에 Prisma 추가
1️⃣ Prisma 패키지 설치
# Development dependencies
npm install prisma @types/pg --save-dev
# Runtime dependencies
npm install @prisma/client @prisma/adapter-pg pg dotenv
2️⃣ Prisma 초기화
npx prisma init
prisma/ 디렉터리가 생성되고 schema.prisma와 .env 파일이 만들어집니다.
3️⃣ Prisma 클라이언트 제너레이터 설정 (Prisma 7)
prisma/schema.prisma(또는 별도 prisma.config.ts)에 다음을 추가합니다:
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "cjs"
}
4️⃣ NestJS Prisma 모듈 및 서비스 생성
nest g module prisma
nest g service prisma
src/prisma/prisma.service.ts
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from 'src/generated/prisma/client';
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
constructor() {
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
});
super({ adapter, log: ['query', 'info', 'warn', 'error'] });
}
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
이 서비스:
prisma/디렉터리를 생성하고schema.prisma를 생성합니다.env파일을 추가합니다(없을 경우)prisma.config.ts를 생성합니다(Prisma 7에서 새로 추가)
Dockerising PostgreSQL (stand‑alone container)
Tip: 진행하기 전에 Docker Desktop이 실행 중인지 확인하세요.
1️⃣ 프로젝트 루트에 docker-compose.yml 만들기
services:
postgres:
image: postgres:15
restart: always
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: backend
ports:
- "5432:5432"
networks:
- prisma-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 5s
timeout: 2s
retries: 20
volumes:
- postgres_data:/var/lib/postgresql/data
logging:
options:
max-size: "10m"
max-file: "3"
networks:
prisma-network:
volumes:
postgres_data:
2️⃣ DB 실행하기
docker compose up -d
3️⃣ 연결 문자열 설정하기
.env 파일에 추가하거나 기존 DATABASE_URL을 교체합니다:
DATABASE_URL="postgresql://postgres:backend@localhost:5432/postgres?schema=public"
Note: 머신에서
localhost가 동작하지 않으면127.0.0.1을 사용하세요.
4️⃣ 첫 번째 마이그레이션 실행 및 클라이언트 생성
npx prisma migrate dev --name initial-migration
npx prisma generate
5️⃣ NestJS 앱 시작 및 Prisma Studio 열기
npm run start # or npm run dev
npx prisma studio
이제 Prisma와 Dockerized PostgreSQL 인스턴스를 백엔드로 사용하는 정상적인 NestJS API를 확인할 수 있습니다.
NestJS 서버용 Docker 이미지 빌드
프로젝트 루트에 Dockerfile을 생성합니다. Prisma 7에서는 Alpine 기반 이미지와 Slim 기반 이미지 모두를 지원합니다.
예시 Dockerfile (Alpine)
# Use the LTS Alpine image (lightweight)
FROM node:lts-alpine
# Set working directory
WORKDIR /usr/src/app
# Install production dependencies
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# Copy source code
COPY . .
# Run DB migrations then start the server
CMD ["sh", "-c", "npm run db:deploy && npm run dev"]
대체 베이스 이미지
node:lts-slim– 약간 더 크지만 안정적임node:lts– 전체 Debian 기반 이미지
Source: …
docker‑compose.yml에 서버 추가하기
기존 파일에 server 서비스를 확장하여 Dockerfile을 빌드하도록 합니다.
services:
postgres:
# ... (same as before)
server:
build:
context: .
dockerfile: Dockerfile
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: "postgresql://postgres:backend@postgres:5432/postgres?schema=public"
ports:
- "3000:3000"
networks:
- prisma-network
command: ["sh", "-c", "npm run db:deploy && npm run start:prod"]
핵심 포인트
depends_on은 DB가 정상 상태가 될 때까지 NestJS 애플리케이션이 시작되지 않도록 보장합니다.DATABASE_URL은 Docker 네트워크 내부의 서비스 이름(postgres)을 가리키며,localhost가 아닙니다.
전체 실행하기
docker compose up -d
NestJS API는 http://localhost:3000 에서, Prisma Studio는 (노출된 경우) http://localhost:5555 에서 접근할 수 있습니다.
일반적인 함정 및 회피 방법
| 증상 | 가능한 원인 | 해결 방법 |
|---|---|---|
PrismaClientInitializationError: Unable to connect to database | 잘못된 호스트 (localhost 대신 서비스 이름) | Docker 내부에서 실행할 때 DATABASE_URL에 postgres(서비스 이름)를 사용하세요 |
binary not found or unsupported platform | 오래된 Rust‑based 엔진을 사용하는 Alpine 이미지 사용 | Prisma 7(Rust‑free)으로 업그레이드하거나 Alpine이 아닌 베이스로 전환하세요 |
| Migrations keep failing | 마이그레이션 실행 시 DB가 준비되지 않음 | Docker healthcheck와 depends_on에 condition: service_healthy를 사용하세요 |
| TypeScript compilation slows down | 타입 정의가 너무 많음 (pre‑Prisma 7) | Prisma 7으로 업그레이드 – 정의가 줄어들고 빌드가 빨라집니다 |
요약
- NestJS 설정 및 Prisma 추가.
- Dockerized PostgreSQL 서비스를 healthcheck와 함께 생성.
- Prisma 클라이언트를 Docker 네트워크 호스트를 사용하도록 구성.
- 서버 시작 전에 마이그레이션 실행 (
npx prisma migrate dev). - 가벼운 Node 이미지로 NestJS 앱을 Dockerize.
docker‑compose.yml로 모든 것을 Compose하고 실행.
이제 Prisma 7, Docker, NestJS가 원활하게 함께 작동하는 재현 가능한 컨테이너‑우선 개발 환경을 갖추었습니다. 즐거운 코딩 되세요! 🚀
Docker Compose 설정
services:
app:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
stdin_open: true
tty: true
depends_on:
postgres_db:
condition: service_healthy
env_file:
- .env.prod
networks:
- prisma-network
networks:
prisma-network:
name: prisma-network
환경 파일
각 환경에 대해 두 개의 별도 파일을 생성합니다:
.env.dev.env.prod
두 파일 모두 데이터베이스 연결 문자열을 포함해야 합니다:
DATABASE_URL="postgresql://postgres:prisma@postgres_db:5432/postgres?schema=public"
컨테이너 실행
-
분리 모드 (로그 감소)
docker compose up --build -d -
전체 로그
docker compose up --build
Tip:
-d플래그를 추가하면 컨테이너가 분리 모드로 실행되어 대부분의 로그 출력이 억제됩니다.
일반적인 함정 및 해결책
| 문제 | 해결책 |
|---|---|
Docker 내부에서는 localhost가 컨테이너 자체를 가리킵니다. | 대신 Docker 서비스 이름을 사용하세요: postgres_db:5432. |
| 컨테이너가 PostgreSQL이 준비되기 전에 시작될 수 있습니다. | 마이그레이션을 실행하기 전에 대기 스크립트를 추가하거나 Docker 헬스 체크를 사용하세요. |
| 컨테이너 내부에서 Prisma Client가 생성되지 않아 충돌이 발생합니다. | Docker 빌드 단계에서 npx prisma generate를 실행하세요. |
| 구버전 Prisma는 Alpine 이미지와 호환성 문제가 있습니다. | Prisma 7로 업그레이드하세요 – Rust‑없는 클라이언트 엔진으로 이 문제를 크게 줄일 수 있습니다. |
왜 Prisma 7을 Docker 및 Docker‑Compose와 함께 사용할까?
- 신뢰할 수 있는 마이그레이션 – 헬스 체크를 통해 DB가 준비된 후에 적용됩니다.
- 환경에 구애받지 않음 – 별도의
.env파일을 사용하면 특정 환경에서만 나타나는 버그를 방지할 수 있습니다. - 확장 가능한 워크플로우 – 동일한 설정이 로컬 개발부터 프로덕션까지 모두 작동합니다.
이 가이드가 도움이 되었다면, 좋아요, 댓글, 공유를 자유롭게 해 주세요.
전체 예제 저장소를 확인해 보세요: