본문 바로가기

Docker

docker-compose 를 활용한 react 및 nodejs express 배포

반응형

옛날 spring, jsp 시절에는maven(or gradle) 빌드로 나온 war 결과를 tomcat 에 배포하면 쉽게 배포가 되었다. 하지만 react 와 nodejs express 를 개발하고 배포하려고 보면, 어떻게 배포해야할지 난감할 때가 있다. express 앱은 pm2 로 배포해도 될 것 같고, react는 build 이 후 serve로 배포해도 될 것 같고.. 방법은 다양한 것 같다. 

하지만 나는 docker-compose 를 활용하여 nginx 를 통한 프록시 설정까지 고려하여 한번에 배포해보려고 한다.

 

※ 필수 설치 : docker 가 설치되어있어야 한다. [윈도우 도커 설치] 참고

 

[Docker (1)] window10 Docker 설치하기(윈도우 10 도커 설치)

[Docker (1)] window10 Docker 설치하기(윈도우 10 도커 설치) 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ Window10 도커 설치 ] 입니다. : ) 도커 설치하기 ▶ 1. 도커란? 도커 설치와 관련된 포스팅 이기.

goddaehee.tistory.com

 

테스트 소스 구성 하기 전 먼저 전체 패키지 구조를 확인하자.

 

 

express 구성을 위해 서버 구동 파일 추가 (./packages/server)

# server 디렉토리를 구성하고 npm 프로젝트로 초기화 및 express 모듈 설치
mkdir packages/server
cd packages/server
npm init
npm i express nodemon
const express = require('express');
const app = express();

app.use(express.json());

// proxy 설정 확인을 위한 test api 구성
app.get('/api/list', (req, res) => {
    res.json([
        { id: 'test1', name: '테스트1' },
        { id: 'test2', name: '테스트2' },
        { id: 'test3', name: '테스트3' },
        { id: 'test4', name: '테스트4' },
    ]);
});

app.get('/api/item', (req, res) => {
    res.json({
        id: 'test1',
        name: '테스트1'
    });
});

app.listen(8080, (err) => {
    if (err) {
        console.log('err 발생');
    }

    console.log('정상구동');
});

소스 변경 감지를 위한 nodemon 설정 파일 추가 (./packages/server/nodemon.json)

{
  "watch": ["./"],
  "exec": "node server.js",
  "ext": "js json yaml",
  "ignore" : ["./log/"]
}

package.json 파일에 scripts 추가

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "server": "nodemon"
  },

server 실행 (port 8080)

npm run server

 

react 구성을 위한 cra(create-react-app) 를 활용 (./packages/client)

npx create-react-app ./packages/client
cd packages/client
# api 테스트를 위해 proxy middleware 및 axios 설치
yarn add http-proxy-middleware axios

개발용 proxy 설정 (./packages/client/src/setupProxy.js)

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
    app.use(
        createProxyMiddleware('/api', {
            target: 'http://localhost:8080',
            changeOrigin: true
        }),
    );
};

api 테스트를 위해 CRA 순정의 ./src/App.js 에서

// ./packages/client/src/App.js
import { useState, useEffect } from 'react';
import axios from 'axios';
import logo from './logo.svg';
import './App.css';

function App() {
    const [list, setList] = useState([]);
    const [item, setItem] = useState({});

    useEffect(() => {
        (async () => {
            const { data: resultList } = await axios.get('/api/list');
            setList(resultList);

            const { data: result } = await axios.get('/api/item');
            setItem(result);
        })();
    }, []);

    return (
        <div className="App">
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo"/>
                <p>
                    Edit <code>src/App.js</code> and save to reload.
                </p>
                <a
                    className="App-link"
                    href="https://reactjs.org"
                    target="_blank"
                    rel="noopener noreferrer"
                >
                    Learn React
                </a>

                <div>List</div>
                {list.map((item, idx) => (
                    <div key={idx}>{`${item.id}/${item.name}`}</div>
                ))}

                <div>item</div>
                <div>{`${item.id}/${item.name}`}</div>
            </header>

        </div>
    );
}

export default App;

37~43번째 줄 추가

yarn start

리액트 구동 후 http://localhost:3000 으로 접속하면 아래와 같은 화면을 볼 수 있다.

API 요청 및 응답이 정상적으로 이루어지는 것을 확인할 수 있다.

 

이 후 리액트 프로젝트를 빌드 해 둔다.

yarn build

 

그럼 이제 프록시 설정도 하면서 react 와 express 를 docker-compose 로 어떻게 배포하는지 확인해볼 것이다.

 

./nginx/nginx.conf 파일을 만든다.

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    upstream docker-server {
        server server:8080;
    }
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }
        location /api {
            proxy_pass         http://docker-server;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /socket {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_pass http://docker-server;
        }
    }
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}

여기서 설명이 필요한 부분만 보겠다.

    upstream docker-server {
        server server:8080;
    }

도커로 구동한 server 컨테이너명을 통해 upstream 포워드 구성

 

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }

nginx 에 접속 시 / 로 접근하면 react 에서 build 한 정적파일이 열림

 

        location /api {
            proxy_pass         http://docker-server;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        }

/api 로 접근하면 upstream 으로 포워딩한 docker-server(백엔드서버) 로 요청

 

※ nginx.conf 파일에 대한 설정 및 설명은 다른 문서가 많으니 참고해서 본인 시스템에 맞는 설정으로 하면 된다.

 

server 내 Dockerfile 을 생성한다. (./packages/server/Dockerfile)

FROM node:15.11.0-alpine # node 15 version 사용
MAINTAINER SONGJANG

RUN mkdir /app
WORKDIR /app # 기본 경로 설정
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json

RUN npm install --no-cache

# 서버 소스 복사
COPY . /app

# 실행 명령어
CMD ["npm", "run", "server"]

 

이 후 프로젝트 최상위 경로에 docker-compose-yml 파일을 생성한 뒤 해당 내용을 입력한다.

version: '3.3'

services:
  web:
    image: nginx:latest # nginx 이미지
    container_name: web # 컨테이너 명
    restart: "on-failure" # 구동 실패 시 재시작
    ports:
      - 80:80
    volumes: # 볼륨 설정
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./packages/client/build:/usr/share/nginx/html

  server:
    build: # Dockerfile 빌드
      context: ./packages/server # Dockerfile 빌드 경로
    container_name: server # 컨테이너 명
    restart: "on-failure"
    expose:
      - 8080 # 도커 내부적 포트
    volumes: # 볼륨 설정
      - './packages/server:/app'
      - '/app/node_modules'
    environment: # 환경변수 설정
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true
    stdin_open: true
    tty: true

 

docker-compose 파일 작성이 완료되었으면, 프로젝트 최상위 경로에서 docker-compose 를 실행시킨다.

docker-compose up -d

정상 실행이 된다면 컨테이너가 이상없이 발행 되고 http://localhost 로 접속하면 화면을 조회할 수 있게 된다.

 

 

docker-compose.yml 에서 react 의 build 디렉토리를 볼륨 마운트 했기 때문에 yarn build 로 리액트의 수정사항을 즉각 반영 할 수 있다. 또한 server의 전체 소스를 볼륨 마운트 했기 때문에 소스가 수정되면 nodemon 에 의해 재구동 후 반영됨을 확인할 수 있다.

 

※ 오타 수정이나 질문은 댓글로 부탁드립니다. 블로그 내용 및 소스는 마음껏 퍼가셔도 됩니다^^

 

전체소스: https://github.com/sbjang123456/docker-fullstack

 

sbjang123456/docker-fullstack

docker-compose 를 통한 프론트(react) 및 백엔드(nodejs) 컨테이너 발행 - sbjang123456/docker-fullstack

github.com

 

반응형