이번에는 개발환경에서는 리액트 앱을 개발하고 만든 리액트 앱을 테스트 및 배포를 해보자.
간단하게 보면 개봘환경에서 개발 -> 개발 된 것을 테스트 -> Production 환경에 배포
- 더 자세한 흐름으로 알아보자.
- 개발환경에서 개발 -> Github 에 소스를 push -> Featrue 브랜치에서 Main 브랜치에 Pull 요청
- Travis CI 에서 Main 브랜치에서 push 된 코드를 가져가서 개발된 소스가 잘 작동하는지 먼저 Test 를 한다.
- 만약 테스트가 성공하면 호스팅 사이트(AWS , Azure , Google...)로 보내서 배포를 한다.
리액트 앱 설치하기
- node 를 받으면 리액트는 매우 간단하게 설치할 수 있다.
- npx create-react-app DIR_NAME (리액트를 설치하고자 하는 디렉토리 이름)
- 만약 현재 있는 경로에 설치하고 싶다면 npx create-react-app ./
다른 명령어들
- npm run start : 실행
- npm run test : 테스트 (개발 완료 후 문제가 있는지 없는지 확인)
- npm run build
-> 테스트도 완료가 됐다면 배포를 해서 다른 사람들도 이용할 수 있도록 하는 배포 명령어
-> 실행하면 배포를 할 때 사용할 수 있는 build 폴더와 그 안에 많은 파일들이 생성된다.
이제는 도커를 이용하여 리액트 앱을 실행해보자.
도커를 이용하여 리액트 앱 실행하기
도커로 어플을 실행하기 위한 흐름
- 도커 이미지 생성 -> 이미지를 이용해 컨테이너 만들기 -> 컨테이너 안에서 앱을 실행
- 도커 이미지를 생성하기 위해서는 Dockerfile 을 작성
현재까지는 Dockerfile 을 그냥 한 가지만 만들었지만, 실제로는 개발 단계를 위한 Dockerfile 과 실제 배포 이후를 위한 Dockerfile 을 따로 작성하는게 좋다.
- 이제는 그냥 Dockerfile 이 아닌 Dockerfile.dev 라는 파일로 작성해보자.
개발 환경에서의 도커 파일 작성은 현재까지 도커 파일 작성했던 것과 똑같이 하면 된다.
#Dockerfile.dev
FROM node:alpine
WORKDIR /usr/src/app
COPY pacage.json ./
RUN npm install
COPY ./ ./
CMD ["npm" , "run" , "start"]
- 전에 배운 내용이지만 COPY package.json ./ 을 해주는 이유는?
- COPY ./ ./ 이후에 npm install 을 하게 되면 약간의 소스 변경을 하고 나서 종속성도 계속 다운로드 받기 때문에 먼저 종속성 부분을 먼저 COPY 해오고 npm install 을 한다.
- npm run start 로 실행되기 떄문에 CMD 부분에 넣어준다.
이렇게 Dockerfile.dev 를 작성하고 나면 이 도커 파일로 이미지를 만들면 된다.
하지만 에러가 발생한다..
- 그 이유는 원래 이미지를 빌드할 때 해당 디렉토리만 정해주면 Dockerfile 을 자동으로 찾아서 빌드를 하지만, 현재는 Dockerfile 이 아닌 Dockerfile.dev 밖에 없다.
- 그래서 자동으로 도커 파일을 찾지 못해 에러가 발생한다.
- 이러한 문제를 해결하기 위해서는 build 할 때 어떠한 파일을 참조할지 알려주면 된다.
- 임의로 알려주는 방법은 빌드를 할 때 그냥 docker build . 이 아닌 -f 옵션을 사용해야 한다.
docker run -f Dockerfile.dev ./
- -f 는 이미지를 빌드 할 때 쓰일 도커 파일을 임의로 지정해준다.
- 그래서 이런식으로 -f 옵션을 사용하면 정상적으로 이미지 빌드가 된다.
팁
- 현재 디렉토리에 보면 node_modules 라는 폴더가 있는데 이것을 삭제해도 괜찮다.
- 그 이유는 node_modules 에는 리액트 앱을 실행할 떄 필요한 모듈들이 들어있지만 이미지를 빌드할 때 이미 npm install 로 모든 모듈들을 도커 이미지에 다운 받기 떄문에 굳이 로컬 머신에 node_modules 을 필요로 하지 않는다.
- node_modules 자체가 사이즈가 크기 떄문에 지워주도록 하자.
생성된 도커 이미지로 리액트 앱 실행해보기
docker build -f Dockerfile.dev -t supportkim/docker-react-app ./
docker run supportkim/docker-react-app
리액트는 기본적으로 3000번 포트로 실행되기 때문에 3000번 포트로 실행해보면 에러가 발생한다..
- 이유는 이전과 같이 포트 매핑을 해줘야 하는데 안 해줬기 때문이다.
- 컨테이너 안에서 실행되고 있는 리액트 앱에 도달하지 못한 것이다.
- 즉 브라우저의 3000번 포트와 컨테이너 안에 있는 3000번 포트를 매핑해줘야 한다.
docker run -it -p 3000:30000 supportkim/docker-react-app
- 리액트쪽에서 업그레이드를 해서 -it 옵션을 꼭 사용해야한다.
- 실행해보면 정상적으로 동작하는 것을 확인할 수 있다.
도커 볼륨을 이용한 소스 코드 변경
- COPY 를 했을때는 로컬에 있던 파일들을 도커 컨테이너에 "복사"
- Volume 은 도커 컨테이너에서 로컬에 있는 파일들을 "매핑(참조)"
docker run -it -p 3000:3000 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app supportkim/docker-react-app(IMAGE_NAME)
- 첫 번째 -v 는 호스트 디렉토리에 node_modules 는 없기 때문에 매핑하지 말라고 하는 것
- 두 번째 -v 는 pwd 경로에 있는 디렉토리 혹은 파일을 /usr/src/app 경로에서 참조하라는 것 (WORKDIR 에서 pwd 에 있는 파일들을 참조하자는 것)
소스 코드를 바꾸면 빌드하지 않아도 바로 반영 되는 것을 알 수 있다.
도커 컴포즈로 좀 더 간단하게 앱 실행 하기
- 앞서 리액트 앱을 실행할 때 위에 있는 명령어처럼 매우 길어서 불편한 부분이 있다.
- 이걸 간단히 하기 위해서 Docker Compose 파일을 작성해보자.
- Docker compose 파일에서 도커 파일 사용 + 포트 맵핑을 해주면 된다.
version: '3'
services:
react:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
stdin_open: true
- 작성한 후 docker-compose up 명령어만 입력하면 어플리케이션이 실행된다.
리액트 앱 테스트 하기
- npm run test : 리액트 앱에서 테스트 진행
도커를 이용한 리액트 앱에서 테스트 진행
- docker build -f dockerfile.dev .
- docker run -it IMAGE_NAME npm run test
여기서 좀 더 나아가서 테스트도 소스 코드 변경하면 자동으로 반영되는 것 처럼 테스트 소스도 추가 하면 바로 반영되었으면 좋을텐데 어떻게 할 수 있을까?
- 기존에 있던 테스트 말고 새로운 테스트 소스 코드를 만든후 다시 테스트를 진행해도 추가가 되지 않는다.
- 어플리케이션을 껐다가 다시 켜도 테스트가 더 추가로 진행되지 않는다.
- 바로 반영 하기 위해서 똑같이 Volume 을 사용하면 된다.
소스 코드 변경을 위해서 Volume 을 이용했듯이 이번에도 Volume 을 이용하면 되는데, Test 를 위한 컨테이너를 Compose 파일에 하나 더 만들어주면 된다.
tests:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- /usr/src/app/node_modules
- ./:/usr/src/app
command: ["npm" , "run" , "test"]
- 다시 docker-compose up --build 로 켜주면 리액트 컨테이너와 테스트 컨테이너가 켜지고 테스트 2개 모두 진행이 된다.
- 만약 테스트를 하나 더 늘리고 저장을 하면 바로 3개의 테스트가 진행이 된다.
운영환경을 위한 Nginx
현재까지는 리액트앱을 개발 환경에서 다뤄봤는데 이제는 운영 환경(배포 후)을 다뤄보자.
-그전에 Nginx 라는 것을 알아야한다.
Nginx 가 필요한 이유?
- 먼저 개발환경에서 리액트가 실행되는 과정과 운영 환경에서 리액트가 실행되는 과정이 다르다.
- 개발 환경에서는 브라우저에서 3000번 포트로 요청이 리액트 컨테이너로 가면 컨테이너 안에 개발 서버에서 해당 요청을 받고 뷰를 보여준다.
- 하지만 운영 환경에서는 리액트 컨테이너 안에 개발 서버라는 것이 없기 떄문에 뷰를 보여줄 수 없다.
- 이때 Nginx 라는 웹 서버가 개발 환경의 개발 서버 역할을 해주는 것이다.
그렇다면 왜 개발환경 서버와 운영환경 서버를 다른걸 써야할까?
- 개발서버를 그대로 운영 환경에서도 써도 될 것 같은데 왜 그럴까?
- 개발에서 사용하는 서버는 소스를 변경하면 자동으로 전체 앱을 다시 빌드해서 변경 소스를 반영해주는 것 같이 개발 환경에 특화된 기능들이 있기에 그러한 기능이 없는 Nginx 서버보다 더욱 적합하다.
- 운영환경에서는 소스를 변경할 떄 다시 반영해줄 필요가 없고 개발에 필요한 기능들이 필요하지 않기에 더 깔끔하고 빠른 Nginx 를 웹 서버로 사용한다.
운영 환경 도커 이미지를 위한 Dockerfile 작성
- 위에서 운영 환경에서는 Nginx 가 필요하다는 것을 알게됐기 떄문에 이제는 Nginx 를 포함하는 리액트 운영환경 이미지를 생성해보자.
- npm run build 명령어를 실행하면 build 파일이 생성되는데, 이 빌드 파일을 Nginx 서버가 브라우저에서 보일 수 있게 해준다.
# 여기 FROM 부터 다음 FROM 전까지는 모두 builder stage 라는 것을 명시
FROM node:alpine as builder
WORKDIR '/usr/src/app'
COPY package.json .
RUN npm install
COPY ./ ./
RUN npm run build
FROM nginx
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
- 위에 Dockerfile 은 크게 두 개로 구분할 수 있다.
- 첫 번째 단계는 빌드 파일들을 생성하는 Builder Stage
- 두 번쨰 단계는 Nginx 를 가동하고 첫 번째 단계에서 생성된 빌드 폴더의 파일들을 웹 브라우저의 요청에 따라 제공해주는 Run Stage
- 첫 번째 FROM 부터 다음 FROM 나오기 직전이 Builder Stage
- 두 번째 FROM 부터 Run Stage
Builder Stage
- builder stage 란 빌드 파일들을 생성하는 것인데 생성된 파일과 폴더들은 /usr/src/app/build 로 들어가게 된다.
- as builder 으로 다음 FROM 나오기 전까지가 builder stage 라는 것을 알려준다.
Run Stage
- FROM nginx 는 nginx 를 위한 베이스 이미지를 명시해준다.
- --from=builder 는 다른 Stage 에 있는 파일을 복사할 때 다른 Stage 이름을 명시해줘야 하기 때문에 위에 있는 builder 를 복사해온다.
- builder stage 에서 생성된 파일들은 /usr/src/app/build 에 들어가게 되며 그곳에 저장된 파일들을 /usr/share/nginx/html 로 복사를 시켜줘서 nginx 가 웹 브라우저의 http 요청이 올떄 마다 알맞은 파일을 전해줄 수 있게 만든다.
- /usr/share/nginx/html 로 복사해주는 이유는 이 장소에 파일을 넣어 두면 Nginx 가 알아서 Client 에서 요청이 들어올 때 알맞은 정적 파일들을 제공해준다.
- 설정으로 장소를 바꿀 수 있다.
해당 Dockerfile 을 만들고 build 한 후 docker run -p 8080:80 supportkim/docker-react-app 명령어를 실행하면 정상적으로 실행이 된다.
앞으로는 테스트 하고 배포를 해보자!
참고자료