๐Ÿณ Docker๋กœ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ ์‹คํ–‰ํ•˜๊ธฐ: ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๋ฐœํ–‰: (2026๋…„ 5์›” 24์ผ AM 02:28 GMT+9)
8 ๋ถ„ ์†Œ์š”
์›๋ฌธ: Dev.to

Source: Dev.to

์ œ๋กœ๋ถ€ํ„ฐ ๋ช‡ ๋ถ„ ์•ˆ์— ์ปจํ…Œ์ด๋„ˆํ™” โ€” โ€œ๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋™์ž‘ํ•ดโ€๋Š” ์ด์ œ ๊ทธ๋งŒ

์•„๋งˆ ํ•œ ๋ฒˆ์ฏค ๋“ค์–ด๋ณด์…จ์„ ๊ฒ๋‹ˆ๋‹ค: โ€œ๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋™์ž‘ํ•ด.โ€ Docker๋Š” ์ด ๋ง์„ ์‚ฌ๋ผ์ง€๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
Docker๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๊ทธ ๋ชจ๋“  ์˜์กด์„ฑ(๋Ÿฐํƒ€์ž„, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์„ค์ • ํŒŒ์ผ ๋“ฑ)์„ ์ปจํ…Œ์ด๋„ˆ๋ผ๋Š” ํ•˜๋‚˜์˜ ์ด์‹ ๊ฐ€๋Šฅํ•œ ๋‹จ์œ„์— ๋ฌถ์–ด์ค๋‹ˆ๋‹ค. ๊ทธ ์ปจํ…Œ์ด๋„ˆ๋Š” ๋…ธํŠธ๋ถ, ํŒ€์›์˜ Windows PC, CI ์„œ๋ฒ„, ํ˜น์€ ํด๋ผ์šฐ๋“œ VM ์–ด๋””์„œ๋“  ๋™์ผํ•˜๊ฒŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๊ธฐ์–ตํ•ด ๋‘๋ฉด ์ข‹์€ ๊ฐœ๋…

  • Image โ†’ ์„ค๊ณ„๋„(๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ํด๋ž˜์Šค์™€ ๊ฐ™์€ ๊ฐœ๋…)
  • Container โ†’ ์ด๋ฏธ์ง€์˜ ์‹คํ–‰ ์ธ์Šคํ„ด์Šค(๊ฐ์ฒด์™€ ๊ฐ™์€ ๊ฐœ๋…)
  • Dockerfile โ†’ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋ ˆ์‹œํ”ผ
  • Docker Compose โ†’ ์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ•จ๊ป˜ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋„๊ตฌ

ํ•„์ˆ˜ ์‚ฌ์ „ ์กฐ๊ฑด

  • Docker Desktop ์„ค์น˜(Compose ํฌํ•จ)
  • ๊ธฐ๋ณธ ํ„ฐ๋ฏธ๋„ ์‚ฌ์šฉ๋ฒ• ์ต์ˆ™ํ•จ
  • ์ปจํ…Œ์ด๋„ˆํ™”ํ•  ํ”„๋กœ์ ํŠธ(์˜ˆ์‹œ๋กœ Node.js, Python, ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜์ ์ธ ์ ‘๊ทผ๋ฒ•์„ ์‚ฌ์šฉํ•  ์˜ˆ์ •)

์„ค์น˜ ํ™•์ธ

docker --version
# Docker version 26.x.x

docker compose version
# Docker Compose version v2.x.x

Dockerfile์ด๋ž€?

Dockerfile์€ Docker๊ฐ€ ์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝ์–ด ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋Š” ํ”Œ๋ ˆ์ธ ํ…์ŠคํŠธ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

# 1. Base image โ€” what you're building ON TOP OF
FROM node:20-alpine

# 2. Set the working directory inside the container
WORKDIR /app

# 3. Copy dependency files first (for layer caching)
COPY package*.json ./

# 4. Install dependencies
RUN npm install

# 5. Copy the rest of your source code
COPY . .

# 6. Expose the port your app listens on
EXPOSE 3000

# 7. The command to run when the container starts
CMD ["node", "server.js"]
๋ช…๋ น์–ด๋ชฉ์ 
FROM๋ฒ ์ด์Šค ์ด๋ฏธ์ง€๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ์ฒซ ๋ฒˆ์งธ ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค.
WORKDIR์ดํ›„ ๋ช…๋ น์–ด๋“ค์˜ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
COPYํ˜ธ์ŠคํŠธ์˜ ํŒŒ์ผ์„ ์ด๋ฏธ์ง€ ์•ˆ์œผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.
RUN๋นŒ๋“œ ๋‹จ๊ณ„์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค(ํŒจํ‚ค์ง€ ์„ค์น˜, ์ฝ”๋“œ ์ปดํŒŒ์ผ ๋“ฑ).
ENV๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
EXPOSE์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‚ฌ์šฉํ•˜๋Š” ํฌํŠธ๋ฅผ ๋ฌธ์„œํ™”ํ•ฉ๋‹ˆ๋‹ค(์‹ค์ œ ํฌํŠธ ๊ฐœ๋ฐฉ์€ ์•„๋‹˜).
CMD์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ ์‹œ ๊ธฐ๋ณธ์œผ๋กœ ์‹คํ–‰๋  ๋ช…๋ น์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. Dockerfile๋‹น ํ•˜๋‚˜๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
ENTRYPOINTCMD์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋ฎ์–ด์“ฐ๊ธฐ ์–ด๋ ค์›Œ โ€œํ•ญ์ƒ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š”โ€ ๋ช…๋ น์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํŒ: Dockerfile์„ ๋ณ€๊ฒฝ ๋นˆ๋„๊ฐ€ ๋‚ฎ์€ ์ˆœ์œผ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”. Docker๋Š” ๊ฐ ๋ ˆ์ด์–ด๋ฅผ ์บ์‹œํ•˜๋ฏ€๋กœ, ์˜์กด์„ฑ ์„ค์น˜์™€ ๊ฐ™์€ ์•ˆ์ •์ ์ธ ๋ ˆ์ด์–ด๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ ๋•Œ ์žฌ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

my-app/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ index.js
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ package-lock.json
โ””โ”€โ”€ Dockerfile
FROM node:20-alpine

WORKDIR /app

# ์บ์‹œ ํšจ์œจ์„ ์œ„ํ•ด lockfile๊ณผ package.json์„ ๋จผ์ € ๋ณต์‚ฌ
COPY package*.json ./
RUN npm ci --only=production

COPY src/ ./src/

EXPOSE 3000
CMD ["node", "src/index.js"]
# ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํƒœ๊ทธ ์ง€์ •
docker build -t my-node-app .

# ํ˜ธ์ŠคํŠธ ํฌํŠธ 8080์„ ์ปจํ…Œ์ด๋„ˆ ํฌํŠธ 3000์— ๋งคํ•‘ํ•˜์—ฌ ์‹คํ–‰
docker run -p 8080:3000 my-node-app

http://localhost:8080 ์— ์ ‘์†ํ•˜๋ฉด Docker ์•ˆ์—์„œ ์‹คํ–‰ ์ค‘์ธ ์•ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Python ์˜ˆ์‹œ

FROM python:3.12-slim

WORKDIR /app

# ์˜์กด์„ฑ ์„ค์น˜
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

์ฃผ์˜: --host 0.0.0.0 ์˜ต์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŽ์€ ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ 127.0.0.1(์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€)๋งŒ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์™ธ๋ถ€ ์—ฐ๊ฒฐ์„ ํ—ˆ์šฉํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ 0.0.0.0์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


Docker Compose๋กœ ๋‹ค์ค‘ ์„œ๋น„์Šค ๊ตฌ์„ฑํ•˜๊ธฐ

์‹ค์ œ ํ”„๋กœ์ ํŠธ๋Š” ๋ณดํ†ต ํ•˜๋‚˜์˜ ์„œ๋น„์Šค๋งŒ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์บ์‹œ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์›Œ์ปค ๋“ฑ์ด ํ•„์š”ํ•˜์ฃ . Docker Compose๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ๋ชจ๋“  ์„œ๋น„์Šค๋ฅผ ํ•˜๋‚˜์˜ ํŒŒ์ผ์— ์ •์˜ํ•˜๊ณ  ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydb
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    volumes:
      - .:/app              # ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ๋ฆฌ๋กœ๋“œ๋ฅผ ์œ„ํ•ด ๋งˆ์šดํŠธ
      - /app/node_modules   # ํ˜ธ์ŠคํŠธ์˜ node_modules๊ฐ€ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 5s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:
# ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ชจ๋“  ์„œ๋น„์Šค ์‹œ์ž‘
docker compose up -d

# ์•ฑ ๋กœ๊ทธ ์‹ค์‹œ๊ฐ„ ๋ณด๊ธฐ
docker compose logs -f app

# ์ „์ฒด ์„œ๋น„์Šค ์ค‘์ง€
docker compose down

# ๋ณผ๋ฅจ๊นŒ์ง€ ์‚ญ์ œ (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”)
docker compose down -v

๋น„๋ฐ€ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๊ธฐ

.env ํŒŒ์ผ์„ ํ™œ์šฉํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

# .env  (git์— ์ปค๋ฐ‹๋˜์ง€ ์•Š๋„๋ก .gitignore์— ์ถ”๊ฐ€!)
POSTGRES_PASSWORD=supersecret
API_KEY=abc123

Docker Compose๋Š” ๊ฐ™์€ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ .env ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์ฝ์–ด๋“ค์ž…๋‹ˆ๋‹ค.

services:
  app:
    environment:
      - API_KEY=${API_KEY}
  db:
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” Docker Secrets, Vault, AWS Secrets Manager ๋“ฑ ์ „์šฉ ๋น„๋ฐ€ ๊ด€๋ฆฌ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ๋ณ„ Compose ํŒŒ์ผ ๋ถ„๋ฆฌ

ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ ์˜ˆ์‹œ:

my-app/
โ”œโ”€โ”€ docker-compose.yml          # ๊ธฐ๋ณธ ์„ค์ •
โ”œโ”€โ”€ docker-compose.dev.yml     # ๊ฐœ๋ฐœ์šฉ ์˜ค๋ฒ„๋ผ์ด๋“œ(ํ•ซ ๋ฆฌ๋กœ๋“œ, ๋””๋ฒ„๊ทธ ํฌํŠธ)
โ””โ”€โ”€ docker-compose.prod.yml    # ํ”„๋กœ๋•์…˜์šฉ ์˜ค๋ฒ„๋ผ์ด๋“œ(๋ ˆํ”Œ๋ฆฌ์นด, ๋กœ๊น…)

๊ฐœ๋ฐœ์šฉ ์˜ค๋ฒ„๋ผ์ด๋“œ (docker-compose.dev.yml)

services:
  app:
    volumes:
      - .:/app
    command: npm run dev
    environment:
      - NODE_ENV=development
0 ์กฐํšŒ
Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

๋‚ด ์Šคํ‚ฌ

ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ AI ์ง€์‹œ๋ฌธ์„ ๋งŒ๋“ค๊ณ , ์„ค์น˜ํ•˜๊ณ , ๊ด€๋ฆฌํ•˜์„ธ์š” โ€” ์ฝ”๋”ฉ์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. CREATE ์ด๋ฆ„์„ ์ •ํ•˜๊ณ , ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์„ ํƒํ•˜๊ณ , ์›ํ•˜๋Š” ๊ฒƒ์„ ์„ค๋ช…ํ•˜์„ธ์š” โ€” ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

PREDICTION-20260525-0007: ๋น„๋Œ€์นญ ๋ ˆ๋ฒ„๋ฆฌ์ง€๋ฅผ ์ด์šฉํ•œ ์ง€๋ฃจํ•จ [2026-Q3 through 2027-Q3]

Motivationโ€‘Patternโ€‘Log Public์€ ๋™๊ธฐ ๋ถ„์„์— ๊ธฐ๋ฐ˜ํ•œ AI ์‹œ๋Œ€ ์‚ฌ์ด๋ฒ„ ๋ณด์•ˆ ๊ณต๊ฒฉ ํŒจํ„ด์— ๋Œ€ํ•œ ๋‚ ์งœ๊ฐ€ ๊ธฐ์žฌ๋œ, ๊ฒ€์ฆ ๊ฐ€๋Šฅํ•œ ์˜ˆ์ธก ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ธก์€ โ€ฆ

์„œ๋ฒ„ ์—†์ด 100๊ฐœ์˜ ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€ ๋„๊ตฌ๋ฅผ ๋งŒ๋“  ๋ฐฉ๋ฒ• (FFmpeg WASM, PDF-lib, AI Background Removal)

์ œ๊ฐ€ ImgToolkit์„ ๋งŒ๋“ค๊ธฐ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ ๋ชฉํ‘œ๋Š” ๊ฐ„๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค: ์ œ๊ฐ€ ์‚ฌ์šฉํ•˜๋˜ ๋ชจ๋“  ์ด๋ฏธ์ง€โ€‘ํˆด ์‚ฌ์ดํŠธ๋Š” ํŒŒ์ผ์„ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์„œ๋ฒ„์— ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜, ์ถœ๋ ฅ๋ฌผ์— ์›Œํ„ฐ๋งˆํฌ๋ฅผ ์‚ฝ์ž…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.