안녕하세요, 동료 개발자 여러분!
“어, 이상하다. 제 컴퓨터에서는 잘 되는데요?”
개발팀에서 일하다 보면 한 번쯤은 들어봤거나, 혹은 직접 말해본 경험이 있으실 겁니다. 새로운 팀원이 합류할 때마다 반나절 이상 개발 환경 설정에 시간을 쏟고, 동료와 나의 미묘하게 다른 설정 때문에 얘기치 않은 버그가 터지는 경험. 우리 모두의 숙명과도 같았죠.
하지만 더 이상은 아닙니다! 오늘 소개해드릴 Docker는 바로 이런 ‘환경의 저주’에서 우리를 해방시켜 줄 강력한 도구입니다. 복잡한 설치 과정 없이, 명령어 몇 줄만으로 누구에게나 똑같은 개발 환경을 뚝딱 만들어주는 마법. 함께 배워볼까요?
Docker가 뭔가요? 컨테이너에 담아 배달하는 소프트웨어
Docker를 이해하는 가장 쉬운 방법은 ‘소프트웨어용 배송 컨테이너’라고 생각하는 겁니다.
과거에는 서버라는 ‘집’에 운영체제, 라이브러리, 데이터베이스, 애플리케이션 등 다양한 ‘가구’를 직접 들여놓았습니다. 집(서버) 구조가 바뀌거나 가구(소프트웨어) 버전이 달라지면 모든 게 엉망이 되기 일쑤였죠.
Docker는 이 모든 가구를 ‘컨테이너’라는 표준화된 상자에 담아버립니다. 이 컨테이너 안에는 우리 앱이 돌아가는 데 필요한 모든 것(코드, 런타임, 시스템 도구, 라이브러리 등)이 포함되어 있습니다. 그리고 이 컨테이너는 노트북, 개발 서버, 클라우드 등 어디에서든 똑같이 작동합니다.
(이미지 출처: Red Hat)
이런 방식을 컨테이너화라고 부릅니다. 덕분에 우리는 더 이상 개발 환경 설정에 골머리를 앓지 않고, 오롯이 코드에만 집중할 수 있게 됩니다.
Docker로 개발 환경 자동화하기: 3단계 실전 가이드
자, 이제 이론은 충분합니다. 간단한 Node.js 애플리케이션을 Docker 컨테이너에 띄우는 과정을 통해 Docker의 강력함을 직접 체험해봅시다.
1단계: 개발 환경 설계도 그리기 (Dockerfile)
Dockerfile은 우리만의 컨테이너를 만들기 위한 ‘설계도’ 또는 ‘레시피’입니다. 텍스트 파일에 우리가 원하는 환경을 순서대로 적어주기만 하면 됩니다.
프로젝트 루트에 Dockerfile이라는 이름의 파일을 만들고 아래와 같이 작성해 보세요.
# 1. 베이스 이미지 선택
# Node.js 18 버전이 설치된 가벼운 리눅스 환경을 사용합니다.
FROM node:18-alpine
# 2. 작업 디렉토리 설정
# 컨테이너 안에서 명령어를 실행할 기본 폴더를 지정합니다.
WORKDIR /usr/src/app
# 3. 의존성 설치
# 먼저 package.json 파일을 복사해서 npm install을 실행합니다.
# (소스코드보다 먼저 복사하면, 소스코드만 변경될 때 불필요한 재설치를 피할 수 있습니다.)
COPY package*.json ./
RUN npm install
# 4. 소스코드 복사
# 프로젝트의 모든 파일을 작업 디렉토리로 복사합니다.
COPY . .
# 5. 포트 개방
# 컨테이너가 3000번 포트로 요청을 받을 수 있도록 설정합니다.
EXPOSE 3000
# 6. 컨테이너 시작 명령어
# 컨테이너가 시작될 때 실행할 명령어를 지정합니다.
CMD [ "node", "server.js" ]
2단계: 설계도로 이미지 만들기 (docker build)
이제 설계도를 바탕으로 컨테이너의 ‘재료’가 될 이미지를 만들어 봅시다. 이미지는 컨테이너를 실행하기 위한 스냅샷이라고 생각하면 쉽습니다.
터미널에서 아래 명령어를 실행하세요.
# 현재 디렉토리의 Dockerfile을 사용하여 'my-dev-app'이라는 이름의 이미지를 만든다
docker build ./ -t my-dev-app
build .: 현재 디렉토리에서Dockerfile을 찾아 이미지를 빌드합니다.-t my-dev-app: 이미지에이름:태그형식으로my-dev-app이라는 이름을 붙여줍니다.
3단계: 이미지로 컨테이너 실행하기 (docker run)
드디어 마지막 단계입니다! 방금 만든 이미지로 실제 동작하는 컨테이너를 실행해 봅시다.
# 'my-dev-app' 이미지로 컨테이너를 실행한다
docker run -d -p 3000:3000 my-dev-app
-d: 컨테이너를 백그라운드에서 실행합니다. (detached mode)-p 3000:3000: 내 컴퓨터(호스트)의 3000번 포트와 컨테이너의 3000번 포트를 연결합니다.
이제 웹 브라우저에서 http://localhost:3000으로 접속해 보세요. 여러분의 Node.js 앱이 컨테이너 안에서 멋지게 실행되고 있을 겁니다!
실전 팁: 컨테이너에서 내 컴퓨터(localhost)의 서비스 호출하기
개발을 하다 보면 컨테이너 안의 애플리케이션이 내 컴퓨터(호스트 머신)에서 실행 중인 데이터베이스나 다른 API를 호출해야 할 때가 있습니다.
이때 localhost를 그대로 사용하면 될까요? 정답은 ‘아니오’입니다. 컨테이너는 격리된 네트워크 환경을 가지기 때문에, 컨테이너의 localhost는 컨테이너 자기 자신을 가리킵니다.
이 문제를 해결하기 위해 Docker는 host.docker.internal이라는 특별한 호스트명을 제공합니다. 컨테이너 안에서 host.docker.internal로 요청을 보내면, 마법처럼 호스트 머신(여러분의 컴퓨터)으로 연결됩니다.
예를 들어, 내 컴퓨터 5432번 포트에서 PostgreSQL이 실행 중이라면, 컨테이너 안의 코드에서는 host.docker.internal:5432로 접속하면 됩니다.
마치며
오늘 우리는 Docker를 이용해 개발 환경을 코드화하고, 명령어 몇 줄로 실행하는 방법을 배웠습니다. 이제 새로운 팀원이 와도 docker run 명령어 하나만 알려주면 되고, “제 컴퓨터에서는 되는데요?”라는 말 대신 “Docker 이미지는 동일한가요?”라고 묻는, 한 단계 높은 수준의 협업이 가능해질 겁니다.
Docker의 세계는 훨씬 더 깊고 넓지만, 오늘 배운 내용만으로도 여러분의 개발 생산성은 크게 향상될 것입니다. 지금 바로 여러분의 프로젝트에 Dockerfile을 추가해보는 건 어떨까요?
답글 남기기