프론트엔드 개발자를 위한 Docker로 React 개발 및 배포하기
Docker + React Tutorial: From Development to Production Workflow
원제: Docker + ReactJS tutorial: Development to Production workflow + multi-stage builds + docker compose
이 포스트는 Youtube의 Sanjeev Thiyagarajan라는 분이 올려주신 Docker + ReactJS tutorial 영상을 쉽게 따라하고 이해할 수 있도록 정리한 내용이다.
prerequisite
- 원본 영상은 Windows, 본 설명은 Mac OS 환경에서 진행된다.
- NodeJS
- Docker
- CRA 등을 통해 초기화된 리액트 앱 (또는 본인의 프로젝트)
- Docker, Container 개념에 대한 기본 지식
1) Setting Docker Container
1-1) hub.docker.com
hub.docker.com은 docker에서 관리하는 컨테이너 이미지 레지스트리이다. node, nginx 등 기본 이미지를 쉽게 받을 수 있는 저장소 개념이다.
리액트 앱을 위한 컨테이너를 만들기 때문에 nodeJS를 컨테이너의 기본 이미지로 한다.
- node 이미지를 다운로드 받아 이미지를 커스터마이징 하는 것
- 특정 개발 환경을 제공하는 이미지들을 pre-built container라고 한다.
- 프로젝트 root 디렉토리에
Dockerfile
을 만든다. - Dockerfile은 이미지를 빌드하기 위한 모든 단계를 레이어별로 명시한다.
Dockerfile
# 가져올 이미지를 정의
FROM node:14
# 경로 설정하기
WORKDIR /app
# package.json 워킹 디렉토리에 복사 (.은 설정한 워킹 디렉 토리를 뜻함)
COPY package.json .
# 명령어 실행 (의존성 설치)
RUN npm install
# 현재 디렉토리의 모든 파일을 도커 컨테이너의 워킹 디렉토리에 복사한다.
COPY . .
# 각각의 명령어들은 한줄 한줄씩 캐싱되어 실행된다.
# package.json의 내용은 자주 바뀌진 않을 거지만
# 소스 코드는 자주 바뀌는데
# npm install과 COPY . . 를 동시에 수행하면
# 소스 코드가 조금 달라질때도 항상 npm install을 수행해서 리소스가 낭비된다.
# 3000번 포트 노출
EXPOSE 3000
# npm start 스크립트 실행
CMD ["npm", "start"]
# 그리고 Dockerfile로 docker 이미지를 빌드해야한다.
# $ docker build .
2) Build Docker Image
리액트 앱 root 디렉토리에서 IDE 터미널 커맨드라인에서 아래 명령어를 실행한다.
$ docker build .
build 뒤의 구두점은 현재 디렉토리에 이미지를 만들겠다는 이야기다. 각 단계들은 한 번 실행되면 캐싱되기 때문에 두번째 빌드부터는 더 빠르게 실행된다.
다음 명령어로 도커 이미지를 찾아보자.
$ docker image ls
이미지에 이름을 정해주지 않아 <none>
으로 나온다. docker 이미지를 지우고 다시 만들어본다.
$ docker image rm <docker-image-id>
rm 다음 도커 이미지의 이름 또는 아이디를 입력하여 이미지를 삭제한다.
$ docker build -t react-image .
-t 옵션으로 이름(태그)를 만들어 다시 빌드한다.
결과가 캐싱되어 있어 훨씬 빠르게 이미지가 빌드되는 것을 확인할 수 있다.
3) Create Docker Container
만들어진 이미지로 도커 컨테이너를 띄우려면 아래의 명령어를 실행한다.
$ docker run -d --name <container-name> <image-name>
만들어질 컨테이너 이름은 react-app, 이미지 이름은 react-image이므로 아래와 같이 실행하게 된다.
$ docker run -d --name react-app react-image
3-1) -d
옵션 (detached)
d, --detach Run container in background and print container ID
- 컨테이너를 백그라운드(detached 모드)에서 실행하고, 실행 결과로 컨테이너 ID를 출력하는 옵션
- 이 옵션 없이 실행하면 터미널에 컨테이너 로그가 출력되고, 종료하기 위해
Ctrl + C
를 입력 해야 한다.
3-2) docker ps
docker
컨테이너의 리스트를 보여주는 명령어로 현재 가동중인 컨테이너만 보인다. 만약 모두 보고 싶다면-a
옵션을 붙인다.
4) Run Container and Port Fowarding
$ docker run ~
로 컨테이너를 실행했는데 리액트 로컬 서버인 3000번 포트에 접속이 되지 않는다 왜일까?
- 컨테이너는 호스트 환경과 격리된 파일 시스템과 네트워크를 가지기 때문
- 호스트에서 컨테이너로 접근 가능하도록 포트 포워딩을 시켜줘야 한다.
4-1) docker rm <container-name> -f
- 위 명령어는 컨테이너를 제거할 것이다.
- 보통 컨테이너를 삭제하기 전에 실행을 중단시킨 후 삭제한다.
- 중단은
$ docker stop <container-name>
을 사용한다.
4-2) 포트 포워딩
이미지로 컨테이너를 띄울때 커맨드라인에 -p hostPort:ContainerPort
옵션으로 포트 포워딩을 할 것을 명시한다.
$ docker run -d -p 3306:3000 --name <container-name> <image-name>
결과를 구분하기 위해 호스트 포트를 3306으로 지정했다.
위 명령에서 -p
의 콜론을 사이에 둔 숫자에 주목해야한다. -p 3306:3000
의 의미는 로컬 머신(127.0.0.1)의 3000번 포트로 접근하는 모든 트래픽을 도커 컨테이너의 3000번 포트로 보낸다는 뜻이다.
브라우저에서 로컬호스트 3306 포트로 접속하면 컨테이너 환경에서 실행된 리액트 로컬 서버가 앱을 잘 서빙해주는 것을 확인할 수 있다.
4-3) docker exec
- docker 컨테이너 환경에서 커맨드라인(bash) 띄우기
$ docker exec -it <container-name> bash
ls
명령어로 확인해보니 컨테이너의 리액트 앱 루트 폴더가 잘 확인된다.
- 여기서
docker exec
은 명령어를 실행하는 것
$ docker exec [option] <container-command>
exec
에-it
옵션을 쓰는 이유-i
: 표준 입출력 STDIN을 열겠다는 의미-t
: 가상 TTY(Pseudo TTY)를 통해 접속하겠다는 의미
4-4) docker ignore
도커 컨테이너에 포함시키지 않을 파일들을 명시한다. .gitignore
와 개념이 같다.
# .dockerignore
node_modules
Dockerfile
.git
.gitignore
.dockerignore
.env
.dockerignore
에 명시된 파일은 컨테이너에 포함되지 않은 것을 볼 수 있다.