Node.js (Express) 와 Mongoose를 사용하여 MongoDB를 Docker로 띄우고 CRUD API를 구축

프로젝트 구성을 위해 Docker Compose를 사용하여 DB와 API 서버를 한 번에 관리하는 방식으로 구성하겠습니다.


📂 프로젝트 폴더 구조

먼저 폴더를 하나 만들고 아래와 같은 구조로 파일들을 생성합니다.

Text

my-mongo-api/
├── Dockerfile          # Node.js 앱 이미지 빌드 설정
├── docker-compose.yml  # MongoDB와 Node 앱 오케스트레이션
├── package.json        # Node.js 의존성 파일
└── server.js           # API 서버 코드 (CRUD 로직)

1. package.json 작성

Node.js 프로젝트의 의존성을 정의합니다.

JSON

{
  "name": "mongo-docker-crud",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.0.3"
  }
}

2. Dockerfile 작성

Node.js 애플리케이션을 도커 컨테이너로 만들기 위한 설정입니다.

Dockerfile

# Node.js 최신 LTS 버전 사용
FROM node:18-alpine

# 작업 디렉토리 설정
WORKDIR /usr/src/app

# 의존성 파일 복사 및 설치
COPY package*.json ./
RUN npm install

# 소스 코드 복사
COPY . .

# 포트 노출
EXPOSE 3000

# 앱 실행
CMD ["npm", "start"]

3. docker-compose.yml 작성

MongoDB와 Node.js 앱을 함께 실행하기 위한 설정입니다.

Yaml

version: '3.8'

services:
  # 1. MongoDB 서비스
  mongo:
    image: mongo:latest
    container_name: my-mongo-container
    restart: always
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: examplepassword
    volumes:
      - mongo-data:/data/db # 데이터 영구 저장을 위한 볼륨

  # 2. Node.js API 앱 서비스
  app:
    build: .
    container_name: my-node-api
    ports:
      - "3000:3000"
    depends_on:
      - mongo # DB가 먼저 실행되도록 설정
    environment:
      # 'mongo'는 위에서 정의한 서비스 이름입니다.
      MONGO_URI: mongodb://root:examplepassword@mongo:27017/admin

volumes:
  mongo-data:

4. server.js (CRUD API 구현)

Express와 Mongoose를 사용하여 실제 로직을 작성합니다.

JavaScript

const express = require('express');
const mongoose = require('mongoose');

const app = express();
const PORT = 3000;

// JSON 요청 본문 파싱 미들웨어
app.use(express.json());

// MongoDB 연결 설정
// docker-compose에서 설정한 환경변수 MONGO_URI 사용
const mongoUri = process.env.MONGO_URI || 'mongodb://localhost:27017/test';

mongoose.connect(mongoUri)
  .then(() => console.log('✅ MongoDB Connected successfully'))
  .catch(err => console.error('❌ MongoDB Connection Error:', err));

// ---------------------------------------------------
// 1. 스키마 및 모델 정의 (예: User)
// ---------------------------------------------------
const UserSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: { type: Number }
});

const User = mongoose.model('User', UserSchema);

// ---------------------------------------------------
// 2. CRUD API 라우트 작성
// ---------------------------------------------------

// [C] Create: 유저 생성
app.post('/users', async (req, res) => {
  try {
    const newUser = new User(req.body);
    const savedUser = await newUser.save();
    res.status(201).json(savedUser);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// [R] Read: 모든 유저 조회
app.get('/users', async (req, res) => {
  try {
    const users = await User.find();
    res.status(200).json(users);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// [R] Read: 특정 유저 조회
app.get('/users/:id', async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ message: 'User not found' });
    res.status(200).json(user);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// [U] Update: 유저 정보 수정
app.put('/users/:id', async (req, res) => {
  try {
    const updatedUser = await User.findByIdAndUpdate(
      req.params.id, 
      req.body, 
      { new: true } // 업데이트된 문서를 반환하도록 설정
    );
    if (!updatedUser) return res.status(404).json({ message: 'User not found' });
    res.status(200).json(updatedUser);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// [D] Delete: 유저 삭제
app.delete('/users/:id', async (req, res) => {
  try {
    const deletedUser = await User.findByIdAndDelete(req.params.id);
    if (!deletedUser) return res.status(404).json({ message: 'User not found' });
    res.status(200).json({ message: 'User deleted successfully' });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// 서버 실행
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
});

5. 실행 및 테스트

Node.js (Express) 와 Mongoose를 사용하여 MongoDB를 Docker로 띄우고 CRUD API를 구축

1) Docker Compose 실행

터미널에서 프로젝트 폴더로 이동한 뒤 아래 명령어를 입력합니다.

Bash

docker-compose up --build

참고: 처음 실행 시 이미지를 다운로드하느라 시간이 조금 걸릴 수 있습니다. 실행이 완료되면 콘솔에 ✅ MongoDB Connected successfully가 표시됩니다.

2) API 테스트 (cURL 또는 Postman)

① 유저 생성 (Create)

Bash

curl -X POST http://localhost:3000/users \
     -H "Content-Type: application/json" \
     -d '{"name": "Hong Gil Dong", "email": "hong@example.com", "age": 30}'

② 유저 목록 조회 (Read)

Bash

curl -X GET http://localhost:3000/users

③ 유저 수정 (Update)
(위에서 조회한 _id 값을 URL 뒤에 넣어야 합니다. 예: 65a…)

Bash

curl -X PUT http://localhost:3000/users/{여기에_ID_입력} \
     -H "Content-Type: application/json" \
     -d '{"age": 35}'

④ 유저 삭제 (Delete)

Bash

curl -X DELETE http://localhost:3000/users/{여기에_ID_입력}

💡 요약

  1. Docker Compose를 사용해 MongoDB와 Node.js 앱을 하나의 네트워크로 묶었습니다.

  2. Node.js 내에서 MongoDB 접속 URL은 localhost가 아닌 서비스 명인 **mongo**를 사용합니다. (Docker 내부 네트워킹)

  3. Mongoose를 사용하여 간단하게 Schema를 정의하고 CRUD 기능을 구현했습니다.