"제 컴퓨터에서는 되는데요?"
개발을 시작한 지 얼마 안 됐을 때, 팀원에게 코드를 넘겨받고 실행해보려다 에러 메시지만 잔뜩 만난 경험, 혹시 있지 않나요?
ModuleNotFoundError: No module named 'xxx'
Error: ENOENT: no such file or directory
...
분명히 같은 코드인데, 내 환경에선 실행이 안 됩니다. 상대방은 "저는 되는데요?"라고 하고, 나는 한 시간 넘게 환경 설정을 뒤지고 있습니다. Node.js 버전이 다르거나, Python 버전이 미묘하게 달랐거나, 어떤 라이브러리가 전역으로 설치되어 있거나 없거나. 이 상황이 반복되다 보면 "왜 코드보다 환경 세팅에 더 오래 걸리는 거지?"라는 생각이 자연스럽게 듭니다.
Docker는 바로 이 문제에서 출발합니다. "어떤 환경에서든 똑같이 실행되게 하자"는 단순하지만 강력한 아이디어입니다. 이 글에서는 Docker가 무엇인지, 왜 개발자들이 그렇게 좋아하는지를 초급 개발자 눈높이에서 풀어보겠습니다.
Docker란 무엇인가 — 컨테이너 개념부터 이해하기
Docker를 한 문장으로 정리하면, "애플리케이션과 그 실행 환경을 하나의 패키지로 묶어주는 도구" 입니다.
여기서 핵심 개념이 컨테이너(Container) 입니다. 컨테이너는 코드, 런타임, 라이브러리, 설정 파일 등을 하나로 묶어서 어느 환경에서든 동일하게 실행할 수 있게 해줍니다. 마치 음식 배달을 시킬 때 음식만 오는 게 아니라 용기, 수저, 소스까지 세트로 오는 것과 비슷합니다. 받는 사람 입장에선 따로 준비할 게 없죠.
컨테이너와 자주 비교되는 개념이 가상 머신(VM, Virtual Machine) 입니다.
graph TD
subgraph VM["가상 머신 구조"]
HOST_OS1["Host OS"] --> HYPERVISOR["Hypervisor"]
HYPERVISOR --> GUEST_OS1["Guest OS 1\n(수백 MB ~ GB)"]
HYPERVISOR --> GUEST_OS2["Guest OS 2\n(수백 MB ~ GB)"]
GUEST_OS1 --> APP1["App A"]
GUEST_OS2 --> APP2["App B"]
end
subgraph DOCKER["Docker 컨테이너 구조"]
HOST_OS2["Host OS"] --> DOCKER_ENGINE["Docker Engine"]
DOCKER_ENGINE --> CONTAINER1["Container 1\n(수십 MB)"]
DOCKER_ENGINE --> CONTAINER2["Container 2\n(수십 MB)"]
CONTAINER1 --> APP3["App A"]
CONTAINER2 --> APP4["App B"]
end
VM은 운영체제(OS) 자체를 통째로 가상화합니다. 그래서 무겁고, 시작하는 데 시간이 걸립니다. 반면 Docker 컨테이너는 Host OS의 커널을 공유하면서 프로세스 수준에서 격리됩니다. 덕분에 훨씬 가볍고, 시작도 빠릅니다. VM이 아파트 건물 전체를 새로 짓는 거라면, 컨테이너는 이미 있는 건물에 독립된 방 하나를 만드는 느낌입니다.

Dockerfile, 이미지, 컨테이너 — 세 가지 개념의 관계
Docker를 처음 배울 때 용어가 헷갈리는 경우가 많습니다. Dockerfile, 이미지(Image), 컨테이너(Container), 이 세 가지는 어떻게 다를까요?
비유를 하나 들어볼게요.
- Dockerfile = 레시피 (어떻게 만들지 쓴 문서)
- 이미지(Image) = 레시피대로 만든 완성된 냉동식품 (실행 가능한 패키지)
- 컨테이너(Container) = 냉동식품을 전자레인지에 돌려서 실제로 먹고 있는 상태 (실행 중인 인스턴스)
Dockerfile을 작성하면, docker build 명령어로 이미지를 만들고, 그 이미지를 docker run으로 실행하면 컨테이너가 됩니다.
간단한 Node.js 앱을 예로 들면, Dockerfile은 이렇게 생겼습니다:
# 베이스 이미지 지정 (Node.js 18버전 사용)
FROM node:18-alpine
# 컨테이너 안의 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 먼저 복사 (캐시 효율을 위해)
COPY package*.json ./
# 의존성 설치
RUN npm install
# 나머지 소스 코드 복사
COPY . .
# 앱이 사용할 포트 명시
EXPOSE 3000
# 컨테이너 실행 시 실행될 명령어
CMD ["node", "index.js"]
이 파일 하나가 있으면, 팀원 누구든 docker build와 docker run 명령어 두 개로 동일한 환경에서 앱을 실행할 수 있습니다. Python 버전 맞추거나, 라이브러리 따로 설치할 필요가 없습니다.

실제 개발 환경에서 Docker를 쓰면 어떻게 달라질까
Docker가 이론적으로는 멋지게 들리는데, 실제로 어떤 상황에서 체감이 되는지 궁금하실 수 있습니다.
상황 1: 새 팀원 온보딩
기존에는 새 팀원이 합류하면 README를 보면서 Node 버전 맞추고, 데이터베이스 설치하고, 환경 변수 설정하는 데 반나절이 걸리는 경우가 흔합니다. Docker Compose를 사용하면, 아래처럼 명령어 하나로 앱 서버와 데이터베이스가 함께 뜹니다.
# docker-compose.yml 예시
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
docker compose up
이 명령어 하나면 앱 서버와 PostgreSQL 데이터베이스가 함께 실행됩니다. 새 팀원 입장에선 DB를 따로 설치할 필요가 없습니다.
상황 2: "내 컴퓨터에서는 됐는데" 문제 해결
로컬에서 개발한 코드가 서버에 배포되면 갑자기 안 되는 상황, 개발자라면 한 번쯤 겪어봤을 겁니다. Docker를 쓰면 개발 환경과 배포 환경이 동일한 이미지를 기반으로 실행되기 때문에 이런 불일치가 크게 줄어듭니다. 로컬에서 잘 돌아가던 컨테이너가 서버에서도 그대로 실행됩니다.
상황 3: 여러 프로젝트 환경 충돌 방지
Python 2가 필요한 레거시 프로젝트와 Python 3.11이 필요한 신규 프로젝트를 동시에 작업해야 할 때, 기존엔 가상환경을 여러 개 관리하거나 pyenv 같은 도구를 써야 했습니다. Docker를 쓰면 각 프로젝트가 완전히 격리된 컨테이너에서 실행되기 때문에, 어떤 버전이든 충돌 없이 공존할 수 있습니다.
Docker를 처음 써볼 때 알아두면 좋은 것들
처음 Docker를 접하면 막연하게 느껴질 수 있어서, 실제로 해보면서 "아, 이래서 쓰는구나" 싶었던 순서를 공유해봅니다.
1. Docker Desktop 설치부터
로컬에서 Docker를 쓰려면 Docker Desktop을 설치하는 것이 가장 쉬운 출발점입니다. 설치하면 docker CLI 명령어를 바로 쓸 수 있습니다.
2. 공식 이미지를 먼저 당겨보기
직접 Dockerfile을 짜기 전에, Docker Hub에 있는 공식 이미지를 실행해보는 것도 좋은 연습입니다.
# nginx 웹서버를 로컬 8080 포트로 실행해보기
docker run -p 8080:80 nginx
이 명령어 하나로 nginx 서버가 뜹니다. 브라우저에서 localhost:8080에 접속하면 nginx 기본 페이지가 보입니다. 설치도 없이요.
3. 컨테이너 안을 직접 들여다보기
# 실행 중인 컨테이너 안으로 들어가기
docker exec -it [컨테이너ID] /bin/sh
컨테이너를 마치 별도의 리눅스 환경처럼 들어가서 파일도 보고 명령어도 실행해볼 수 있습니다. 이렇게 하다 보면 컨테이너가 "가상 컴퓨터 비슷한 것"이라는 감이 오기 시작합니다.

마무리 — Docker, 배워두면 어디서든 쓰인다
정리해보면 이렇습니다.
| 개념 | 한 줄 요약 |
|---|---|
| 컨테이너 | 코드 + 실행 환경을 하나로 묶은 격리된 프로세스 |
| Dockerfile | 이미지를 만드는 레시피 파일 |
| 이미지 | 빌드된 실행 패키지 (배포 단위) |
| Docker Compose | 여러 컨테이너를 한 번에 정의하고 실행하는 도구 |
Docker가 처음엔 "그냥 환경 맞추는 도구" 정도로 보일 수 있습니다. 그런데 쓰다 보면 협업, 배포, 테스트 자동화 등 개발의 거의 모든 영역에 연결된다는 걸 느끼게 됩니다. 특히 CI/CD 파이프라인이나 쿠버네티스(Kubernetes) 같은 컨테이너 오케스트레이션 도구로 가는 기초이기도 합니다.
"일단 Docker run 한 번 해봤다"는 경험이 쌓이다 보면, 어느 순간 팀 프로젝트에서 Dockerfile을 자연스럽게 작성하고 있는 자신을 발견하게 될 겁니다. 처음엔 어렵게 느껴져도 직접 손을 움직여보는 게 제일 빠른 길이었습니다.
'make' 카테고리의 다른 글
| 변수에서 클로저까지 — 자바스크립트 클로저 동작 원리 완전 정복 (0) | 2026.07.27 |
|---|---|
| 클라우드 없이 라즈베리파이로 24시간 AI 에이전트 직접 만들어보기 (0) | 2026.07.23 |
| 부트캠프 수료 후 이력서 첫 줄, 이렇게 쓰면 서류에서 떨어집니다 (0) | 2026.07.22 |
| "fix: 수정함" — 신입 개발자가 가장 많이 하는 깃 커밋 메시지 실수와 고치는 법 (0) | 2026.07.22 |
| 코드리뷰 지적이 무서웠던 나에게 — 주니어 개발자가 피드백을 받아들이는 법 (0) | 2026.07.21 |