목록으로

로컬 환경에서 Docker Container로 서버 띄우기

약 8분 읽기Development#Daily#Docker댓글로 이동

도커로 개발 환경 구축하기

main
date
Jul 17, 2024
slug
local-docker-container
author
status
Public
tags
Daily
Docker
summary
도커로 개발 환경 구축하기
type
Post
thumbnail
category
Development
updatedAt
Oct 12, 2025 10:44 AM
유니서베이 마이그레이션 작업을 진행하면서 노드 버전 관련 이슈가 발생하였다.
 
기존 유니서베이의 경우 Node 14 버전을 사용해야 하고 새롭게 생성한 Next 프로젝트의 경우 Node 18^의 버전이 필요하다. 각각의 프로젝트의 노드 버전이 다를 경우에 로컬에 도커 컨테이너를 띄워서 활용해볼 수 있다.
개발을 하고 있는 Next와 Nest의 경우는 로컬로 실행하고 이미 만들어져 있는 기존 유니서베이만 로컬 컨테이너로 실행시킨다.
 
도커 파일 작성 경험이 많이 부족하기 때문에 우선 도커 파일이 무슨 역할을 하는지 알아보려고 한다.

도커 파일

 
notion image
 
도커파일은 도커 이미지가 어떤 단계 를 거쳐 빌드되야 하는지를 담고있는 텍스트 파일이다. 도커파일에 나열된 명령문을 차례대로 수행하여 이미지를 생성해준다.
 
도커파일을 작성할 때 일반적으로 아래와 같은 절차를 따르게 된다.
  • 베이스 이미지 선택
    • → 이미지를 구성할 때 베이스가 되는 이미지를 선택하고 이 이미지에 필요한 것들 붙여나간다.
  • 필요한 파일 및 명령어 추가
    • → 베이스 이미지 위에 필요한 파일 또는 명령어, 환경 변수를 넣는다.
  • 작업 디렉토리 설정
    • → 이미지 내에서 작업 디렉토리를 설정하여 파일을 추가하거나 명령을 실행하는 위치를 설정한다.
  • 명령 실행
    • → 이미지 내에서 실행할 명령을 정의한다. (RUN, CMD, COPY, ENTRYPOINT 등)
  • 포트
    • → 이미지가 실행될 때 개방할 포트를 지정한다.

주요 명령어

FROM : 베이스 이미지 설정한다.

하나의 도커 이미지는 base 이미지부터 시작해서 기존 이미지위에 새로운 이미지를 중첩해서 여러 단계의 이미지 층을 쌓아가며 만들어간다.
FROM 명령문은 이 base 이미지를 지정해주기 위해서 사용되는데, 보통 Dockerfile 내에서 최상단에 위치한다.
FROM node:18

WORKDIR : 작업 디렉토리 설정한다.

WORKDIR <이동할 경로> # app 라는 디렉토리로 이동한다고 했을 때 WORKDIR /app
WORKDIR 명령문은 쉘(shell)의 cd 명령문처럼 컨테이너 상에서 작업 디텍토리로 전환을 위해서 사용된다.  작업 디렉토리를 전환하면 그 이후에 등장하는 모든 RUN, CMD, ENTRYPOINT, COPY, ADD 명령문은 해당 디렉토리 기준으로 실행된다.

RUN : 컨테이너 내에서 실행할 명령어 정의

 
RUN ["<커맨드>", "<파라미터1>", "<파라미터2>"] RUN <전체 커맨드> # npm install RUN npm install
RUN 명령문은 이미지 빌드 과정에서 필요한 커맨드를 실행하기 위해서 사용된다.

ENV : 환경 변수 설정

ENV NODE_ENV production

COPY : 파일이나 디렉토리를 이미지 내부로 복사

COPY <src>... <dest> COPY ["<src>",... "<dest>"] # pacakage.json 복사 COPY package.json package.json # 이미지를 빌드한 디렉터리의 모든 파일을 컨테이너의 app/ 디렉터리로 복사 WORKDIR app/ COPY . .
COPY 명령문은 호스트 컴퓨터에 있는 디렉터리나 파일을 Docker 이미지의 파일 시스템으로 복사하기 위해서 사용된다. 절대 경로와 상대 경로를 모두 지원하며, 상대 경로를 사용할 때는  WORKDIR 명령문으로 작업 디렉터리를 어디로 전환을 해놨는지 고려해야한다.

ADD : COPY와 유사하지만 추가 기능을 지원. URL로부터 파일을 다운로드하거나 압축 파일을 풀 수 있다.

EXPOSE : 컨테이너가 개방할 포트 지정한다.

EXPOSE <포트> EXPOSE <포트>/<프로토콜> # 3000 port 사용 EXPOSE 3000
EXPOSE 명령문은 네트워크 상에서 컨테이너로 들어오는 트래픽(traffic)을 리스닝(listening)하는 포트와 프로토콜를 지정하기 위해서 사용된다. 프로토콜은 TCP와 UDP 중 선택할 수 있는데 지정하지 않으면 TCP가 기본값으로 사용된다.
여기서 주의할 점은 EXPOSE 명령문으로 지정된 포트는 해당 컨테이너의 내부에서만 유효하며, 호스트(host) 컴퓨터에서는 이 포트를 바로 접근을 할 수 있는 없다. 호스트 컴퓨터로부터 해당 포트로의 접근을 허용하려면, docker run 커맨드를 -p 옵션을 통해 호스트 컴퓨터의 특정 포트를 포워딩(forwarding)시켜줘야 한다.

CMD : 컨테이너가 실행될 때 실행할 명령어 지정.

CMD ["<커맨드>","<파라미터1>","<파라미터2>"] CMD ["<파라미터1>","<파라미터2>"] CMD <전체 커맨드>
CMD 명령문은 해당 이미지를 컨테이너로 띄울 때 디폴트로 실행할 커맨드나, ENTRYPOINT 명령문으로 지정된 커맨드에 디폴트로 넘길 파라미터를 지정할 때 사용한다.

ENTRYPOINT : 컨테이너가 실행될 때 항상 실행할 명령어

ENTRYPOINT ["<커맨드>", "<파라미터1>", "<파라미터2>"] ENTRYPOINT <전체 커맨드> # npm start ENTRYPOINT ["npm", "start"]
ENTRYPOINT  명령문은 이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용한다.다
 

VOLUME : 호스트와 컨테이너 간 볼륨을 공유

 

Docker 파일 생성

 
먼저 Docker 파일을 생성한다. (프로젝트의 경우 frontend로 되어있다.)
notion image
 
# dcoker > front.development.Dockerfile FROM node:14 WORKDIR /app/frontend COPY ../frontend . RUN npm install ENV NODE_ENV=development EXPOSE 8080 CMD ["npm", "run", "serve"]
# 이미지 빌드 # docker build [옵션] <경로> docker build . # 현재 디렉토리 위치해서 도커파일을 이용하여 build 하겠다. # 컨테이너 실행 docker run -p <로컬 포트>:<컨테이너 포트> <docker image ID>
 
<경로>는 도커파일이 위치한 디렉토리의 경로를 나타내고, 옵션은 이미지 빌드 과정을 조정하고 세부 사항을 지정하는 데 사용된다.
일반적으로 사용되는 옵션들은 다음과 같다:
  • -t, --tag: 빌드된 이미지에 태그를 지정. 이미지이름:태그 형식으로 사용.
  • -f, --file: 사용할 도커파일의 경로를 지정다. 기본값은 ./Dockerfile
  • -q, --quiet: 빌드 진행 상황 메시지를 출력하지 않음
  • --build-arg: 도커파일 내에서 ARG 지시문에 전달할 빌드 인자를 지정.
 
 
하지만 추후에 백엔드도 컨테이너로 실행할 수도 있으니 docker compose를 활용하기로 한다.
 
💡
docker compose 는 도커 컨테이너를 여러개 돌릴 수 있도록 해주는 도구
 
# docker > docker-compose.yml version: '3.8' services: frontend: build: context: .. dockerfile: docker/front.development.Dockerfile ports: - "8080:8080" volumes: - "../frontend:/app/frontend" environment: - LOCAL_DOCKER=docker - NODE_ENV=development
 
  • version: 도커 컴포즈 파일 버전 명시
  • services: 서비스 명시 (사용할 컨테이너들)
  • build: 이미지 빌드
  • ports: 포트 매칭
  • volumes: 호스트(로컬 IDE)와 컨테이너 간의 파일 시스템 공유

스크립트 실행하기

 
# docker compose run docker-compose up
 
위와 같이 파일을 생성하고 위 스크립트를 실행하면 로컬에서 컨테이너가 실행된다.
 
notion image
 
localhost:8080 으로 접속을 할 경우 로컬에서 실행했던 것과 동일하게 실행되는 것을 알 수 있다.
하지만 문제가 있다. 8080의 경우 컨테이너에서 실행시킨 것이고 백엔드 서버의 경우 local로 실행시켰을 때 서로 HTTP 통신을 할 수가 없다.
 
notion image
 
localhost라는건 결국 자기 자신을 가리키는 것인데 컨테이너의 localhost와 PC에서 실행시킨 locahost가 서로 다르기 때문이다.
 
docker-compose 파일을 보면 환경 변수로 LOCAL_DOCKER을 명시한 것을 알 수 있는데 위의 이슈 때문에 api 호출 경로를 변경을 해줘야 한다.
 
'use strict'; const dockerRun = process.env.LOCAL_DOCKER === 'docker'; const targetApi = dockerRun ? 'http://host.docker.internal:3033' : 'http://localhost:3033'; module.exports = { chainWebpack: (config) => { config.module.rules.delete('eslint'); config.plugin('html').tap((args) => { args[0].title = 'Unisurvey'; return args; }); config.plugin('provide').use(require('webpack').ProvidePlugin, [ { 'window.Quill': 'quill/dist/quill.js', Quill: 'quill/dist/quill.js', }, ]); }, devServer: { host: '0.0.0.0', disableHostCheck: true, proxy: { '/api': { target: targetApi, changeOrigin: true, }, '^/uploads': { target: 'http://localhost:3033', changeOrigin: true, }, }, }, css: { loaderOptions: { sass: { additionalData: ` @import "@/assets/css/component/utils/colors.scss"; @import "@/assets/css/component/utils/font.scss"; `, }, }, }, };
 
호스트 명을 host.docker.internal 변경해주면 컨테이너와 localhost 간의 HTTP 통신이 정상적으로 동작한다.
 
host.docker.internal는 사용자의 로컬을 의미하며 도커 내부적인 로직에 의해 로컬 시스템의 ip로 매핑되게 된다.
 
만약 백엔드 서버도 컨테이너를 띄워놓고 서로 호출을 하는 상황이라면 host.docker.internal 이 부분을 docker-compose 에 작성한 서비스 명으로 변경해주면 된다.
 
'use strict'; const dockerRun = process.env.LOCAL_DOCKER === 'docker'; const targetApi = dockerRun ? 'http://backend:3033' : 'http://localhost:3033';
 
예시로 backend라는 이름으로 서비스명을 지었다면 위와 같은 형태가 될 것이다.
 
version: '3.8' services: frontend: build: context: .. dockerfile: docker/front.development.Dockerfile ports: - "8080:8080" volumes: - "../frontend:/app/frontend" environment: - DEV_ENV=docker - NODE_ENV=development backend: build: context: .. dockerfile: docker/back.development.Dockerfile ports: - "3033:3033" volumes: - "../backend:/app/backend" env_file: - ../backend/.env.development
 
Docker Compose 스크립트에 서비스를 명시했으니까 서비스 명으로 변경되는 것이지 만약 그냥 컨테이너를 사용한다면 컨테이너 네트워크에서 IP를 확인해서 명시해주면 된다.
 
그 이외에도 네트워크 그룹을 만들어서 컨테이너끼리 묶어주는 작업도 있는 것 같은데 이 방법은 해보지 않았으니 패스