Deploy React App by Docker 삽질기

처음 React로 토이 프로젝트를 진행하면서, Docker로 배포하면서 삽질한 기록을 정리해둔다.

1. 개발 환경에서는 npm start로 작동하지만…

RUN npm install
RUN npm run build
ENTRYPOINT ["npm", "start"]

처음에는 위의 단순한 코드로 Docker 컨테이너를 생성하고 올렸지만, Firefox에서 이상한 메세지가 나왔다.

이를 보니 Firefox에서는 https:// 에서 ws:// 로 이동하는 것이 Firefox에서는 불가능하다고 되어있다고 한다. (https://stackoverflow.com/questions/11768221/firefox-websocket-security-issue/12042843#12042843)

라이브러리 상 문제긴 하였지만, DevTools 를 실제 서버에 올리는 것도 문제가 되었기에 아래처럼 개발용일 때에만 DevTools와 Logger를 사용하도록 수정했다.

import rootReducer from "./reducers/RootReducer";

const middlewares = [];
let enhancer: any;

if (process.env.NODE_ENV === "development") {
 const createLogger = require("redux-logger").createLogger;
 const logger = createLogger();
 middlewares.push(logger);
 const compose = composeWithDevTools({
   trace: true,
   traceLimit: 100
});
 enhancer = compose(applyMiddleware(...middlewares));
}

const store = createStore(rootReducer, enhancer);
const rootElement = document.getElementById("root");

2. Production build로 올리기

1번에서 사용하던 Dockerfile로는 개발 빌드로 계속 올라가서, react-scripts build 로 빌드를 시도했을 때, 아래의 메세지가 나온다.

...
You can control this with the homepage field in your package.json.  
For example, add this to build it for GitHub Pages:

"homepage" : "...",

The build folder is ready to be deployed.
You may serve it with a static server:

npm install -g serve
serve -s build

Find out more about deployment here:

bit.ly/CRA-deploy

물론, 정석은 nginx를 올리는 것이지만 이미 서버의 앞단에 nginx가 reverse-proxy로 자리잡고 있어 단순히 static page를 올리는 것에서는 본문에 있는 serve 가 좀 더 적합해 보였다. https://github.com/zeit/serve

이를 Dockerfile로 반영하면 다음과 같았다.

RUN npm install
RUN npm i -S serve
RUN npm run-script build
ENTRYPOINT [ "serve" "-s" "build" ]

단, 이렇게 했을 때 [“serve” 를 path 에서 찾을 수 없다는 에러가 나오게 되는데, 이는 기본적으로 ENTRYPOINT 및 CMD가 /bin/sh 로 작업을 실행하기 때문에 찾을 수 없다는 로그다.

따라서 이를 해결하기 위해 직접 node_modules 에 있는 바이너리를 참조할 필요가 있다.

EXPOSE 5000

RUN npm install
RUN npm i -S serve
RUN npm run-script build
ENTRYPOINT [ "./node_modules/.bin/serve", "-s", "build" ]

이렇게 하고 빌드하니 정상적으로 실행이 되었고, nginx로 reverse proxy 연결해주니 도메인으로 접속할 수 있었다.