초보자를 위한 Docker: 실용적인 시작 가이드
· 12분 읽기
📑 목차
Docker는 악명 높은 "내 컴퓨터에서는 작동하는데" 문제를 해결하여 소프트웨어 개발에 혁명을 일으켰습니다. 시스템에 직접 종속성을 설치하고 버전 충돌을 처리하는 대신, Docker는 애플리케이션에 필요한 모든 것을 격리된 컨테이너로 패키징하여 노트북에서 프로덕션 서버까지 어디서나 동일하게 실행됩니다.
2026년에 아직 Docker를 배우지 않았다면, 지금이 바로 그때입니다. 전 세계 1,300만 명 이상의 개발자가 사용하고 거의 모든 현대 개발 워크플로우에 통합된 사실상의 표준이 되었습니다.
Docker란 무엇이며 왜 중요한가?
Docker는 애플리케이션과 모든 종속성을 컨테이너라는 표준화된 단위로 래핑하는 컨테이너화 플랫폼입니다. 소프트웨어를 실행하는 데 필요한 모든 것(코드, 런타임, 시스템 도구, 라이브러리 및 설정)을 포함하는 가볍고 이식 가능한 패키지라고 생각하면 됩니다.
Docker 이전에는 개발자들이 끊임없는 환경 불일치에 직면했습니다. Node 18이 설치된 MacBook에서는 Node.js 앱이 완벽하게 작동하지만, Node 16을 실행하는 동료의 Windows 컴퓨터에서는 충돌할 수 있습니다. 더 나쁜 경우, 개발 환경에서는 작동하지만 시스템 라이브러리의 미묘한 차이로 인해 프로덕션에서 신비롭게 실패할 수 있습니다.
Docker는 일관되고 재현 가능한 환경을 만들어 이러한 골칫거리를 제거합니다. 애플리케이션을 컨테이너화하면 실행 위치에 관계없이 동일한 방식으로 작동할 것을 보장합니다.
Docker 사용의 주요 이점
- 환경 간 일관성: 개발, 스테이징 및 프로덕션 모두 동일한 컨테이너를 실행합니다
- 빠른 온보딩: 새로운 팀원이 환경 설정에 며칠을 소비하는 대신 몇 분 안에 기여를 시작할 수 있습니다
- 격리: 간섭 없이 동일한 컴퓨터에서 충돌하는 종속성을 가진 여러 프로젝트를 실행합니다
- 효율적인 리소스 사용: 컨테이너는 호스트 OS 커널을 공유하므로 가상 머신보다 훨씬 가볍습니다
- 간소화된 배포: 전체 애플리케이션 스택을 단일 아티팩트로 제공합니다
- 마이크로서비스 아키텍처: 모놀리식 애플리케이션을 관리 가능하고 독립적으로 배포 가능한 서비스로 분해합니다
프로 팁: Docker는 프로덕션 배포만을 위한 것이 아닙니다. 많은 개발자가 시스템을 설치로 어지럽히지 않고 로컬에서 데이터베이스, 캐싱 레이어 및 기타 서비스를 실행하는 데 사용합니다. 한 프로젝트에는 PostgreSQL이 필요하고 다른 프로젝트에는 MySQL이 필요한가요? 충돌 없이 둘 다 컨테이너에서 실행하세요.
핵심 개념: 이미지, 컨테이너, Dockerfile
Docker 명령어와 워크플로우를 시작하기 전에 세 가지 핵심 개념을 이해하는 것이 필수적입니다.
Docker 이미지
이미지는 애플리케이션을 실행하는 데 필요한 모든 것을 포함하는 읽기 전용 템플릿입니다. 객체 지향 프로그래밍의 클래스처럼 생각하면 됩니다. 실행 중인 인스턴스가 아니라 청사진입니다.
이미지는 레이어로 구축되며, 각 레이어는 변경 사항이나 명령을 나타냅니다. 이 레이어 아키텍처는 Docker가 변경된 레이어만 다운로드하거나 저장하면 되므로 효율적인 저장 및 전송을 가능하게 합니다.
Docker Hub(공식 레지스트리)에서 사전 빌드된 이미지를 가져오거나 자체 사용자 정의 이미지를 빌드할 수 있습니다. 인기 있는 베이스 이미지에는 node, python, nginx, postgres, redis가 있습니다.
Docker 컨테이너
컨테이너는 이미지의 실행 중인 인스턴스입니다. 클래스에서 인스턴스화된 객체와 같습니다. 하나의 이미지는 여러 컨테이너를 생성할 수 있으며, 각각은 자체 격리된 파일 시스템, 네트워크 및 프로세스 공간으로 독립적으로 실행됩니다.
컨테이너는 설계상 임시적입니다. 컨테이너를 중지하고 제거하면 볼륨을 사용하여 영구 저장소를 명시적으로 구성하지 않는 한 내부에 작성된 모든 데이터가 사라집니다(나중에 자세히 설명).
Dockerfile
Dockerfile은 Docker 이미지를 빌드하기 위한 명령이 포함된 텍스트 파일입니다. 기본적으로 Docker에게 애플리케이션 환경을 단계별로 조립하는 방법을 알려주는 레시피입니다.
Dockerfile의 각 명령은 이미지에 새 레이어를 생성합니다. Docker는 이러한 레이어를 지능적으로 캐시하므로 이미지를 다시 빌드할 때 변경된 레이어만 처리하여 후속 빌드가 매우 빠릅니다.
다음은 Node.js 애플리케이션을 위한 최소한의 Dockerfile입니다:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
각 명령이 수행하는 작업을 분석해 보겠습니다:
FROM은 베이스 이미지를 지정합니다(Alpine Linux의 Node.js 20, 최소 배포판)WORKDIR은 컨테이너 내부의 작업 디렉토리를 설정합니다COPY는 호스트 컴퓨터에서 컨테이너로 파일을 전송합니다RUN은 빌드 프로세스 중에 명령을 실행합니다(종속성 설치)EXPOSE는 애플리케이션이 수신하는 포트를 문서화합니다CMD는 컨테이너를 시작할 때 실행할 기본 명령을 정의합니다
시스템에 Docker 설치하기
Docker 설치는 운영 체제에 따라 약간 다르지만 모든 주요 플랫폼에서 프로세스는 간단합니다.
macOS 및 Windows
공식 웹사이트에서 Docker Desktop을 다운로드하여 설치하세요. Docker Desktop에는 필요한 모든 것이 포함되어 있습니다: Docker Engine, Docker CLI, Docker Compose 및 컨테이너 관리를 위한 사용자 친화적인 GUI.
설치 후 Docker Desktop은 백그라운드에서 실행되며 설정 및 실행 중인 컨테이너에 빠르게 액세스할 수 있도록 메뉴 바 아이콘(macOS) 또는 시스템 트레이 아이콘(Windows)을 추가합니다.
Linux
Linux에서는 배포판의 패키지 관리자를 사용하여 Docker Engine을 직접 설치합니다. Ubuntu/Debian의 경우:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
그룹 멤버십이 적용되도록 로그아웃했다가 다시 로그인하면 sudo 없이 Docker 명령을 실행할 수 있습니다.
설치 확인
다음을 실행하여 Docker가 올바르게 작동하는지 확인하세요:
docker --version
docker run hello-world
두 번째 명령은 작은 테스트 이미지를 가져와 컨테이너에서 실행합니다. 환영 메시지가 표시되면 Docker가 설치되어 제대로 작동하는 것입니다.
모든 개발자가 알아야 할 필수 Docker 명령어
몇 가지 핵심 명령을 마스터하면 일상적인 Docker 사용의 90%를 커버할 수 있습니다. 다음은 포괄적인 참조 표입니다:
| 명령어 | 수행 작업 | 일반적인 옵션 |
|---|---|---|
docker build -t myapp . |
현재 디렉토리의 Dockerfile에서 이미지 빌드 | -t는 이미지에 이름을 태그합니다 |
docker run myapp |
이미지에서 컨테이너 생성 및 시작 | -d (분리), -p (포트 매핑), --name |
docker ps |
실행 중인 컨테이너 나열 | -a는 모든 컨테이너 표시(중지된 것 포함) |
docker stop [id] |
실행 중인 컨테이너를 정상적으로 중지 | 컨테이너 ID 또는 이름 사용 |
docker rm [id] |
중지된 컨테이너 제거 | -f는 실행 중인 컨테이너를 강제로 제거 |
docker images |
시스템의 모든 이미지 나열 | -a는 중간 이미지 표시 |
docker rmi [image] |
이미지 제거 | -f는 강제 제거 |
docker logs [id] |
컨테이너 출력 및 로그 보기 | -f는 실시간으로 로그 출력을 따릅니다 |
docker exec -it [id] sh |
실행 중인 컨테이너 내부에서 대화형 셸 열기 | -it는 대화형 터미널을 활성화합니다 |
docker pull [image] |
레지스트리에서 이미지 다운로드 | nginx:1.25와 같이 태그 지정 |
docker compose up |
docker-compose.yml에 정의된 모든 서비스 시작 | -d는 백그라운드에서 실행 |
docker compose down |
compose 파일의 모든 컨테이너 중지 및 제거 | -v는 볼륨도 제거 |
실용적인 명령어 예제
포트 매핑 및 환경 변수로 컨테이너 실행:
docker run -d \
--name my-postgres \
-p 5432:5432 \
-e POSTGRES_PASSWORD=secret \
postgres:16
이것은 백그라운드에서 PostgreSQL을 시작하고, 포트 5432를 호스트 컴퓨터에 매핑하며, 데이터베이스 비밀번호를 설정합니다.
컨테이너에서 실시간 로그 보기:
docker logs -f my-postgres
실행 중인 컨테이너 내부에서 명령 실행:
docker exec -it my-postgres psql -U postgres
이것은 컨테이너 내부에서 대화형 PostgreSQL 셸을 엽니다.
빠른 팁: 전체 컨테이너 ID를 입력할 필요가 없습니다. Docker는 고유한 접두사를 허용하므로 컨테이너 ID가 a3f8b2c1d4e5인 경우 다른 컨테이너 ID가 해당 문자로 시작하지 않는 한 docker stop a3f를 사용할 수 있습니다.
첫 번째 Dockerfile 작성하기
실제 Node.js 애플리케이션을 위한 완전한 Dockerfile을 빌드하고 각 결정을 설명하겠습니다.
# Alpine Linux에서 특정 버전의 Node.js 사용(더 작은 이미지 크기)
FROM node:20-alpine
# 필요한 경우 시스템 종속성 설치
RUN apk add --no-cache python3 make g++
# 작업 디렉토리 설정
WORKDIR /app
# 패키지 파일을 먼저 복사(더 나은 레이어 캐싱을 위해)
COPY package*.json ./
# 프로덕션 종속성만 설치
RUN npm ci --production --silent
# 애플리케이션 소스 코드 복사
COPY . .
# 보안을 위해 루트가 아닌 사용자 생성
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
# 루트가 아닌 사용자로 전환
USER nodejs
# 애플리케이션 포트 노출
EXPOSE 3000
# 컨테이너 상태를 모니터링하기 위한 상태 확인
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js || exit 1
# 애플리케이션 시작
CMD ["node", "server.js"]
Dockerfile 모범 사례
캐싱 효율성을 위해 순서가 중요합니다. 자주 변경되는 명령(예: COPY . .)을 Dockerfile의 끝 부근에 배치하세요. 이렇게 하면 소스 코드만 변경될 때 Docker가 종속성 설치를 위해 캐시된 레이어를 재사용할 수 있습니다.
.dockerignore 파일을 사용하여 빌드 컨텍스트에서 불필요한 파일을 제외하세요:
node_modules
npm-debug.log
.git
.env
*.md
.DS_Store
이렇게 하면 Docker가 컨테이너에 필요하지 않은 파일을 복사하지 않도록 하여 빌드 속도가 빨라지고 이미지 크기가 줄어듭니다.
다단계 빌드
컴파일된 언어 또는 빌드 도구가 필요한 애플리케이션의 경우 다단계 빌드를 사용하여 최종 이미지를 작게 유지하세요:
# 빌드 단계
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 프로덕션 단계
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
최종 이미지에는 빌드 도구나 소스 코드가 아닌 프로덕션 종속성과 컴파일된 아티팩트만 포함됩니다.
다중 컨테이너 애플리케이션을 위한 Docker Compose
실제 애플리케이션은 단일 서비스로 구성되는 경우가 거의 없습니다. 일반적으로 웹 애플리케이션, 데이터베이스, 캐시, 메시지 큐 및 기타 지원 서비스가 필요합니다. Docker Compose를 사용하면 단일 YAML 구성 파일을 사용하여 다중 컨테이너 애플리케이션을 정의하고 관리할 수 있습니다.
다음은 일반적인 웹 애플리케이션 스택을 위한 완전한 docker-compose.yml입니다:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: unless-stopped
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
restart: unless-stopped
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
restart: unless-stopped
volumes:
pgdata:
redisdata:
Docker Compose 작업하기
백그라운드에서 모든 서비스 시작:
dock