안녕하세요! 지난 포스팅에서는 Dockerfile을 작성해 우리만의 이미지를 만드는 방법을 알아봤습니다. 오늘은 한 걸음 더 나아가, 도커의 핵심 개념인 **이미지(Image)**와 **컨테이너(Container)**가 어떻게 상호작용하는지, 그리고 개발 과정에서 마주칠 수 있는 문제들을 어떻게 해결해야 하는지 자세히 파헤쳐 볼게요.
1. 코드 수정했는데 왜 반영이 안 될까? 🤔
지난번에 만든 Node.js 프로젝트의 HTML 코드에서 느낌표(!)를 하나 삭제했다고 가정해 봅시다.
// 코드 수정 전
let userGoal = 'Learn Docker!';
// 코드 수정 후
let userGoal = 'Learn Docker';
그리고 나서 지난 포스팅에서 사용했던 docker run -p 3002:80 [이미지 ID] 명령어로 컨테이너를 다시 실행했습니다. 하지만 웹사이트에 접속해보니 여전히 느낌표가 그대로 남아있네요. 왜 이런 현상이 일어나는 걸까요?
이유는 바로 이미지의 특성 때문입니다.
우리가 Dockerfile에 작성한 COPY . . 명령어는 docker build 명령어를 실행하는 시점의 프로젝트 코드를 그대로 이미지 내부에 복사합니다. 즉, 이미지는 빌드되는 순간의 정적인 스냅샷이라고 할 수 있죠.
FROM node
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 80
CMD ["node", "server.js"]
이미지가 한번 만들어지고 나면, 코드를 아무리 수정해도 이미지는 변하지 않습니다. 도커가 이미지를 컨테이너로 실행할 때 사용하는 코드는 빌드 시점에 복사된 코드이기 때문입니다. 따라서 수정된 코드를 컨테이너에 반영하려면 이미지를 다시 빌드(Rebuild)하는 과정이 반드시 필요합니다.
이미지는 한 번 만들어지면 수정할 수 없는 읽기 전용(Read-only) 상태가 됩니다. 이 핵심 개념을 이해하는 것이 도커를 사용하는 데 있어 가장 중요합니다.
2. 이미지를 다시 빌드하는 과정이 빠른 이유: 이미지 레이어
그렇다면 코드가 조금만 바뀌어도 이미지를 매번 새로 만들어야 하는 걸까요? 매번 처음부터 다시 빌드하면 너무 느리지 않을까 걱정할 필요는 없습니다! 도커에는 빌드 속도를 획기적으로 높여주는 특별한 개념이 있습니다. 바로 이미지 레이어(Image Layer)입니다.
도커 이미지는 Dockerfile의 각 명령어가 실행될 때마다 하나의 레이어를 생성하는 방식으로 만들어집니다.
- FROM node → 레이어 1 (Node.js 베이스 이미지)
- WORKDIR /app → 레이어 2 (작업 디렉토리 설정)
- COPY . . → 레이어 3 (코드 복사)
- RUN npm ci → 레이어 4 (의존성 설치)
- ...
이런 식으로 각 명령어의 결과가 별도의 레이어로 쌓이게 됩니다. 그리고 도커는 각 레이어의 결과를 캐시에 저장합니다.
다음에 이미지를 다시 빌드할 때, 도커는 변경되지 않은 레이어는 캐시된 결과를 재사용합니다. 예를 들어, 코드만 수정하고 package.json 파일은 그대로라면, npm install 명령어는 다시 실행되지 않고 캐시된 결과를 사용하게 됩니다.
docker build . 명령어를 실행할 때 터미널에 나타나는 CACHED 메시지가 바로 이 레이어 캐시를 사용하고 있다는 증거입니다.
레이어 기반 아키텍처는 변경 사항이 발생한 레이어와 그 이후의 레이어만 새로 빌드하고, 이전 레이어는 캐시를 사용함으로써 이미지 빌드 속도를 매우 빠르게 만들어 줍니다.
3. 레이어 캐시를 더 효율적으로 활용하는 방법
레이어 캐시의 원리를 이해했다면, Dockerfile을 더 최적화해서 빌드 속도를 더욱 빠르게 만들 수 있습니다.
COPY . . 명령어가 코드 전체를 복사하는 명령어이기 때문에, 코드 파일 중 하나라도 변경되면 이 레이어와 그 이후의 모든 레이어가 다시 빌드됩니다. 즉, 코드를 수정할 때마다 npm install이 매번 다시 실행되는 비효율적인 상황이 발생할 수 있습니다.
이를 해결하기 위해 Dockerfile의 순서를 다음과 같이 바꿔볼 수 있습니다.
FROM node
WORKDIR /app
COPY package.json . // (1) package.json 먼저 복사
RUN npm install // (2) 의존성 설치
COPY . . // (3) 나머지 코드 복사
EXPOSE 80
CMD ["node", "server.js"]
- 먼저 package.json 파일만 복사합니다.
- npm install 명령어를 실행해 의존성을 설치합니다.
- 마지막으로 나머지 모든 코드를 복사합니다.
이렇게 하면 package.json 파일이 변경되지 않는 한, npm install 레이어는 캐시를 계속 사용하게 됩니다. 코드만 수정하는 경우에는 COPY . . 레이어만 새로 빌드되고, npm install 과정은 건너뛰게 되어 빌드 시간이 훨씬 단축됩니다.
4. 컨테이너는 이미지 위에 덧씌워진 레이어

이미지가 여러 레이어로 이루어진 읽기 전용 상태라면, 컨테이너는 이 이미지 위에 쓰기 가능한(Writable) 새로운 레이어가 하나 더 추가된 것입니다.
컨테이너가 실행되면 CMD 명령어가 실행되는데, 이때 발생하는 모든 변경 사항(예: 로그 파일 생성, 데이터 저장 등)은 바로 이 최상위 쓰기 가능 레이어에 기록됩니다.
따라서 이미지는 변하지 않으면서도, 컨테이너별로 독립적인 실행 환경을 가질 수 있게 되는 것입니다.
이번 포스팅에서는 도커 이미지의 불변성, 레이어 기반 아키텍처, 그리고 이를 활용한 Dockerfile 최적화까지 깊이 있게 다뤄봤습니다. 다음 포스팅에서는 실시간으로 코드를 수정하고 컨테이너에 반영하는 더 편리한 방법에 대해 알아보겠습니다.
궁금한 점이 있다면 언제든 댓글로 남겨주세요! 😊
'DevOps' 카테고리의 다른 글
| [Docker 6편] 컨테이너의 다양한 모드와 상호작용하기 🐳 (1) | 2025.08.19 |
|---|---|
| [Docker 5편] 컨테이너와 이미지 관리 명령어 모음 🐳 (2) | 2025.08.18 |
| [Docker 3편] DockerFile로 도커 이미지 만들고 컨테이너 실행하기 (2) | 2025.08.17 |
| [Docker 2편] Dockerfile 작성해보기 (2) | 2025.08.16 |
| [Docker 1편] 내 컴퓨터에서는 잘 되는데? 개발자를 위한 도커(Docker) 이야기 (4) | 2025.08.15 |