Docker

Dockerfile을 어떻게 작성하고, 이미지를 빌드하는 과정은?

yj992233 2026. 5. 14. 12:59

Docker를 사용하다 보면 반드시 만나게 되는 파일이 있다. 바로 Dockerfile이다.

 

Dockerfile은 Docker 이미지를 만들기 위한 설계도다.


애플리케이션을 어떤 환경에서 실행할지, 어떤 파일을 복사할지, 어떤 명령어로 실행할지를 Dockerfile에 작성한다.

예를 들어 Node.js 애플리케이션을 실행하려면 보통 이런 환경이 필요하다.

Node.js 런타임
package.json
node_modules
애플리케이션 소스 코드
실행 명령어
 

Dockerfile은 이 과정을 코드처럼 문서화한다.

 

즉, Dockerfile은 다음 질문에 답하는 파일이다.

이 애플리케이션을 실행하려면 어떤 환경이 필요하고,
어떤 순서로 준비해야 하며,
마지막에 어떤 명령어로 실행해야 하는가?

⭐ Dockerfile이란 무엇인가?

Dockerfile은 Docker 이미지를 만들기 위한 설정 파일이다.

Docker 이미지는 컨테이너를 실행하기 위한 템플릿이다.
그리고 Dockerfile은 그 이미지를 어떻게 만들지 적어둔 파일이다.

 

비유하면 다음과 같다.

개념 비유
Dockerfile 요리 레시피
Docker 이미지 완성된 밀키트
Docker 컨테이너 실제로 조리되어 실행 중인 음식

레시피가 있어야 같은 음식을 반복해서 만들 수 있다.

마찬가지로 Dockerfile이 있으면 같은 실행 환경을 가진 이미지를 반복해서 만들 수 있다.


⭐ Dockerfile 기본 구조

Dockerfile 안에는 여러 명령어가 들어간다.

 

대표적인 명령어는 다음과 같다.

명령어 역할
FROM 어떤 기본 이미지를 사용할지 정한다
WORKDIR 컨테이너 안에서 작업할 디렉터리를 정한다
COPY 파일을 이미지 안으로 복사한다
RUN 이미지 빌드 중 실행할 명령어를 작성한다
EXPOSE 컨테이너가 사용할 포트를 문서화한다
CMD 컨테이너가 실행될 때 기본으로 실행할 명령어를 정한다
ENV 환경 변수를 설정한다

이제 Node.js Dockerfile 예시로 쉽게 이해해보자.

FROM node:20

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
 

처음 보면 낯설 수 있지만, 한 줄씩 보면 어렵지 않다.

1️⃣ FROM

FROM node:20
 

FROM은 어떤 이미지를 기반으로 새 이미지를 만들지 정한다.

 

여기서는 node:20 이미지를 사용한다.
즉, Node.js 20이 설치된 환경에서 애플리케이션을 실행하겠다는 의미다.

 

Docker 이미지는 보통 완전히 처음부터 만들지 않는다.
이미 만들어진 기본 이미지를 가져와 그 위에 필요한 설정을 추가한다.

 

2️⃣ WORKDIR

WORKDIR /app
 

WORKDIR은 컨테이너 내부에서 작업할 디렉터리를 지정한다.

이후 실행되는 COPY, RUN, CMD 같은 명령어는 기본적으로 /app 디렉터리를 기준으로 동작한다.

 

쉽게 말하면 컨테이너 안에서 다음 명령어를 실행한 것과 비슷하다.

cd /app
 

WORKDIR을 사용하면 Dockerfile이 더 깔끔해진다.

 

3️⃣ COPY

COPY package*.json ./
 

COPY는 내 컴퓨터에 있는 파일을 Docker 이미지 안으로 복사한다.

여기서는 package.json, package-lock.json 파일을 컨테이너의 /app 디렉터리로 복사한다.

 

그다음 아래 명령어가 실행된다.

RUN npm install
 

이 순서가 중요하다.

 

패키지 설치에 필요한 파일만 먼저 복사하고 npm install을 실행하면, Docker 빌드 캐시를 효율적으로 사용할 수 있다.

만약 처음부터 전체 코드를 복사하면 소스 코드가 조금만 바뀌어도 npm install이 다시 실행될 수 있다.

4️⃣ RUN

RUN npm install
 

RUN은 이미지를 빌드하는 과정에서 실행할 명령어다.

여기서는 Node.js 패키지를 설치한다.

 

즉, 이미지를 만드는 중에 다음 작업이 실행된다.

npm install
 

이 결과로 node_modules가 이미지 안에 생성된다.

 

주의할 점은 RUN은 컨테이너 실행 시점이 아니라 이미지 빌드 시점에 실행된다는 점이다.

5️⃣ COPY 전체 코드 복사

COPY . .
 

이 명령어는 현재 프로젝트의 전체 파일을 컨테이너 안의 현재 작업 디렉터리로 복사한다.

 

현재 WORKDIR이 /app이므로, 프로젝트 파일들이 컨테이너 내부의 /app 아래로 들어간다.

내 컴퓨터 프로젝트 파일
→ Docker 이미지 내부 /app 디렉터리
 

6️⃣ EXPOSE

EXPOSE 3000
 

EXPOSE는 이 컨테이너가 3000번 포트를 사용할 예정이라는 것을 나타낸다.

 

다만 EXPOSE만 작성한다고 해서 실제로 호스트의 3000번 포트가 자동으로 열리는 것은 아니다.

컨테이너를 실행할 때 포트 연결을 따로 해야 한다.

docker run -p 3000:3000 my-node-app
 

즉, EXPOSE는 “이 애플리케이션은 컨테이너 내부에서 3000번 포트를 사용한다”는 문서화에 가깝다.

7️⃣ CMD

CMD ["npm", "start"]
 

CMD는 컨테이너가 실행될 때 기본으로 실행할 명령어를 지정한다.

 

여기서는 컨테이너가 실행되면 다음 명령어가 실행된다.

npm start
 

즉, Docker 이미지를 컨테이너로 실행하면 Node.js 애플리케이션이 시작된다.


Docker 이미지 빌드하기

Dockerfile을 작성했다면 이제 이미지를 빌드할 수 있다.

프로젝트 루트 디렉터리에서 다음 명령어를 실행한다.

docker build -t my-node-app .
 

이 명령어를 나누어 보면 다음과 같다.

부분 의미
docker build Docker 이미지를 빌드한다
-t my-node-app 이미지 이름을 my-node-app으로 지정한다
. 현재 디렉터리를 빌드 컨텍스트로 사용한다

여기서 마지막의 .이 중요하다.

.은 현재 디렉터리 안의 파일들을 Docker 빌드에 사용할 수 있도록 보내겠다는 뜻이다.

 

즉, Docker는 현재 디렉터리에 있는 Dockerfile을 읽고, 필요한 파일들을 가져와 이미지를 만든다.


⭐빌드 과정은 어떻게 진행될까?

앞에서 작성한 Dockerfile을 기준으로 빌드 과정을 보면 다음과 같다.

FROM node:20

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
 

Docker는 위 명령어를 위에서 아래로 순서대로 실행한다.

1. node:20 이미지를 가져온다.
2. 컨테이너 내부 작업 디렉터리를 /app으로 설정한다.
3. package.json 파일을 복사한다.
4. npm install을 실행해 패키지를 설치한다.
5. 프로젝트 전체 코드를 복사한다.
6. 3000번 포트를 사용할 예정이라고 표시한다.
7. 컨테이너 실행 시 npm start를 실행하도록 설정한다.
 

이 과정이 끝나면 my-node-app이라는 Docker 이미지가 만들어진다.

 

빌드된 이미지는 다음 명령어로 확인할 수 있다.

docker images
 

예시 결과는 다음과 비슷하다.

REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
my-node-app     latest    abc123def456   10 seconds ago   1.1GB

⭐빌드한 이미지로 컨테이너 실행하기

이미지를 만들었다면 컨테이너로 실행할 수 있다.

docker run -p 3000:3000 my-node-app
 

이 명령어는 my-node-app 이미지를 기반으로 컨테이너를 실행한다.

 

-p 3000:3000은 포트 연결을 의미한다.

내 컴퓨터의 3000번 포트
→ 컨테이너 내부의 3000번 포트
 

즉, 브라우저에서 다음 주소로 접속할 수 있다.

http://localhost:3000
 

만약 포트 연결을 하지 않으면 컨테이너 안에서는 서버가 실행되어도, 내 컴퓨터 브라우저에서 접근하지 못할 수 있다.


⭐이미지 이름과 태그 지정하기

Docker 이미지를 빌드할 때는 이름과 태그를 함께 지정할 수 있다.

docker build -t my-node-app:1.0 .
 

여기서 my-node-app은 이미지 이름이고, 1.0은 태그다.

이미지 이름: my-node-app
태그: 1.0
 

태그를 지정하지 않으면 기본적으로 latest 태그가 붙는다.

docker build -t my-node-app .
 

이는 다음과 비슷하다.

docker build -t my-node-app:latest .
 

실무에서는 latest만 사용하기보다 버전 태그를 명확히 붙이는 것이 좋다.

 

예를 들어 다음처럼 사용할 수 있다.

my-app:1.0.0
my-app:1.1.0
my-app:2025-01-15
my-app:commit-hash
 

이렇게 하면 어떤 버전의 이미지가 배포되었는지 추적하기 쉽다.


⭐.dockerignore 파일 작성하기

Docker 이미지를 빌드할 때 모든 파일을 이미지에 넣을 필요는 없다.

 

예를 들어 Node.js 프로젝트에는 다음 파일이나 폴더가 있을 수 있다.

node_modules
.git
.env
logs
README.md
 

이 중 일부는 Docker 이미지에 들어가지 않는 것이 좋다.

 

특히 node_modules는 컨테이너 내부에서 다시 설치할 수 있으므로 보통 제외한다.
.env 같은 민감한 환경 변수 파일도 이미지에 포함되면 위험하다.

 

이때 사용하는 파일이 .dockerignore다.

 

프로젝트 루트에 .dockerignore 파일을 만들고 다음처럼 작성할 수 있다.

node_modules
.git
.env
npm-debug.log
Dockerfile
.dockerignore
 

.dockerignore는 Docker 빌드 컨텍스트에 포함하지 않을 파일을 지정한다.

 

이 파일을 잘 작성하면 이미지 빌드 속도가 빨라지고, 불필요한 파일이나 민감한 파일이 이미지에 들어가는 것을 막을 수 있다.


⭐Docker 빌드 캐시 이해하기

Docker 이미지를 빌드할 때 각 명령어는 하나의 레이어를 만든다.

 

예를 들어 다음 Dockerfile을 보자.

FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
 

Docker는 각 단계의 결과를 캐시한다.

 

만약 package.json이 바뀌지 않았다면, 다음 빌드 때 RUN npm install 결과를 다시 사용할 수 있다.

그래서 이런 순서가 중요하다.

COPY package*.json ./
RUN npm install
COPY . .
 

이렇게 하면 소스 코드만 수정했을 때는 npm install을 다시 하지 않아도 된다.

 

반대로 다음처럼 작성하면 비효율적이다.

COPY . .
RUN npm install
 

이 경우 소스 코드 파일 하나만 바뀌어도 COPY . . 단계가 변경된 것으로 인식된다. 그러면 그 뒤의 RUN npm install도 다시 실행될 가능성이 커진다.

 

즉, Dockerfile을 작성할 때는 자주 바뀌지 않는 작업을 위에 두고, 자주 바뀌는 코드를 나중에 복사하는 것이 좋다.

'Docker' 카테고리의 다른 글

Docker compose란?  (0) 2026.05.14
Docker란 무엇이고, VM과의 차이점?  (0) 2026.05.14