[What To Eat] MySQL + Prisma 데이터베이스 연결하기

My_Code·2025년 5월 29일
0

What To Eat

목록 보기
2/6

이번에는 백엔드 API 구현에 앞서 데이터베이스를 연결하려고 합니다.

모던 웹 개발에서 백엔드 API 서버를 구축할 때 데이터베이스 연결과 에러 처리는 필수적인 요소입니다. 이번 글은 Express.js와 TypeScript를 기반으로 Prisma ORM을 사용하여 MySQL 데이터베이스에 연결하고, 에러 처리 미들웨어를 구현하는 과정을 설명하겠습니다.


MySQL + Prisma 데이터베이스 연결하기

프로젝트 기본 설정

필요한 패키지 설치

@prisma/client: 런타임에서 데이터베이스와 상호작용하는 클라이언트
prisma: 개발 도구 (마이그레이션, 스키마 관리 등)

# 백엔드 디렉토리로 이동
cd apps/backend

# Prisma 관련 패키지 설치
npm install @prisma/client
npm install -D prisma

# 기타 필요한 패키지
npm install express cors dotenv cookie-parser
npm install -D @types/express @types/cors @types/cookie-parser

Prisma 초기화

아래 명령어를 통해 prisma 디렉토리와 .env 파일을 생성합니다. prisma/schema.prisma 파일에 데이터베이스 스키마를 정의하고, .env 파일에 데이터베이스 연결 정보를 저장합니다.

npx prisma init

환경 변수 설정

.env 파일 구성

  • DATABASE_URL: MySQL 데이터베이스 연결 문자열
  • SERVER_PORT: 서버 포트 번호
# .env 파일
DATABASE_URL="mysql://username:password@localhost:3306/whattoeat"
SERVER_PORT=3000

Prisma 스키마 정의

prisma/schema.prisma 설정

  • generator: Prisma 클라이언트 생성 설정
  • datasource: 데이터베이스 연결 설정
  • model: 데이터베이스 테이블 구조 정의 (Food 테이블 예시)
// backend/prisma/schema.prisma

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["omitApi"]
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  password  String
  createdAt DateTime @default(now())

  @@map("users")
}

데이터베이스에 적용

아래 명령어를 통해 스키마를 기반으로 실제 데이터베이스 테이블을 생성합니다.

npx prisma db push

Prisma 클라이언트 설정

// src/utils/prisma.util.ts

import { PrismaClient } from '@prisma/client';

export const prisma = new PrismaClient({
  log: ['query', 'info', 'warn', 'error'],

  // 에러 메시지를 평문이 아닌, 개발자가 읽기 쉬운 형태로 출력
  errorFormat: 'pretty',
}); // PrismaClient 인스턴스를 생성합니다.

const connectDB = async () => {
  try {
    await prisma.$connect();
    console.log('DB 연결에 성공했습니다.');
  } catch (error) {
    console.error('DB 연결에 실패했습니다.', error);
  }
};

connectDB();

에러 처리 미들웨어 구현

커스텀 에러 클래스 생성

HTTP 상태 코드와 메시지를 포함하는 커스텀 에러 클래스입니다. 표준 Error 클래스를 확장하여 API 에러 처리에 특화된 기능을 제공합니다.

// src/utils/error-exception.util.ts

class HttpException extends Error {
  status: number;
  message: string;

  constructor(status: number, message: string) {
    super(message);
    this.status = status;
    this.message = message;
  }
}

export default HttpException;

에러 처리 미들웨어

  • Express의 에러 처리 미들웨어는 4개의 매개변수 (err, req, res, next)를 가져야 함
  • 개발 환경에서만 스택 트레이스를 응답에 포함
  • 모든 에러를 일관된 JSON 형태로 응답
// src/middlewares/error-handler.middleware.ts

import { Request, Response, NextFunction } from 'express';
import HttpException from '../utils/error-exception.util';

export const errorHandlerMiddleware = (
  err: HttpException,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const status = err.status || 500;
  const message = err.message || 'Internal Server Error';

  res.status(status).json({
    status,
    message,
  });
};

Express 서버 설정

  • dotenv.config()를 최상단에 배치하여 환경변수를 먼저 로드
  • 미들웨어 등록 순서가 중요: 에러 처리 미들웨어는 반드시 마지막에 등록
  • DB 연결 상태를 확인을 위해 prisma.$queryRaw 사용 (추후 삭제)
// src/index.ts

import express, { NextFunction, Request, Response } from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';

// 환경변수를 다른 모듈보다 먼저 로드
dotenv.config();

import { errorHandlerMiddleware } from './middlewares/error-handler.middleware';
import { prisma } from './utils/prisma.util';

const app = express();
const SERVER_PORT = process.env.SERVER_PORT || 3000;

// 미들웨어
app.use(cors());
app.use(express.json());
app.use(cookieParser());

// 라우트 정의
app.get('/', (req: Request, res: Response) => {
  res.json({ message: 'API 서버가 실행 중입니다.' });
});

console.log('DB 연결 테스트 시작...');
prisma.$queryRaw`SELECT 1`;

// 에러 처리 미들웨어 등록
app.use(errorHandlerMiddleware);

app.listen(SERVER_PORT, () => {
  console.log(`서버가 포트 ${SERVER_PORT}에서 실행 중입니다`);
});

프로젝트 실행

아래 명령을 통해 프로젝트 실행합니다.

npm run dev

아래와 같은 출력이 나오면 정상적으로 실행 및 DB 연결이 된겁니다.

[nodemon] restarting due to changes...
[nodemon] starting `ts-node src/index.ts`
DB 연결 테스트 시작...
서버가 포트 3000에서 실행 중입니다
prisma:info Starting a mysql pool with 13 connections.
DB 연결에 성공했습니다.

트러블 슈팅

문제:

Could not find a declaration file for module 'cookie-parser'

원인:
cookie-parser 패키지의 TypeScript 타입 정의가 없어서 발생하는 에러

해결:

npm install --save-dev @types/cookie-parser

에러 핸들러 미들웨어 타입 문제

문제:

No overload matches this call. Argument of type '(err: any, req: Request, res: Response, next: NextFunction) => Response' is not assignable to parameter of type 'PathParams'

원인:
Express는 에러 핸들러를 특별한 방식으로 인식하는데, 4개의 매개변수를 가진 함수가 에러 핸들러로 인식되지 않았습니다.

해결:
커스텀 에러 인터페이스를 정의하여 타입 안전성 확보

// backend/src/middlewares/error-handler.middleware.ts

import { Request, Response, NextFunction } from 'express';
import HttpException from '../utils/error-exception.util';

export const errorHandlerMiddleware = (
  err: HttpException,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // 에러 처리 로직
};

Top-level await 사용 문제

문제:

Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext'...

원인:
비동기 함수로 래핑하여 사용 (TypeScript 설정 변경 없이 해결)

해결:

const connectDB = async () => {
  try {
    await prisma.$connect();
    console.log('DB 연결에 성공했습니다.');
  } catch (error) {
    console.error('DB 연결에 실패했습니다.', error);
  }
};

connectDB();
profile
조금씩 정리하자!!!

0개의 댓글