webpack 빌드를 하지 않는 Webpacker 적용기

easylogic
10 min readFeb 9, 2021

Ruby on Rails(이하 줄여서 RoR) 에서 프론트엔드 개발을 위해서 webpacker 를 많이 사용합니다.

하지만 webpacker 를 사용해서 프론트엔드 개발을 하게 되면 백엔드와 연관된 설정들로 인해서 체크해야하는 비용이 계속 늘어나게 됩니다.

webpacker 내부에서 webpack 빌드를 분리함으로써 좀 더 유연하게 프론트엔드 개발할 수 있는 방법을 제시해 보려고 합니다.

왜 분리를 시작하게 되었나요?

webpacker 는 RoR 환경에서 webpack으로 빌드한 프론트엔드 application 을 적용할 수 있게 해주는 라이브러리입니다.

webpacker 에서 webpack 을 분리하다니 약간 말이 안 되는 상태인데요. 말 그대로입니다.

로컬 webpack 의 빌드 환경을 분리하고 빌드된 결과물만 적용시킬려고 합니다.

현재 docker 시스템 위에 RoR 을 사용하고 있습니다. 프론트엔드 개발환경이 webpacker 와 같이 실행되고 있어서 프론트엔드만 개발하는데 많은 리소스가 드는 상황이었습니다.

백엔드 측에서도 화면 상의 결과물을 확인 하려면 프론트엔드 소스를 매번 빌드를 해야하는 상황이 계속 되고 있었습니다. 그래서 중간에 예측 불가능한 오류가 발생하면 서로 대응하는데 오래 걸리는 문제가 발생하고 있었습니다.

이렇게오류가 발생할 수 있는 상황을 사전에 차단하고 백엔드/프론트엔드 모두 개발 생산성을 높이기 위한 방법으로, webpacker 에서 webpack 빌드 환경을 분리 하는 것을 고민하게 되었습니다.

나아가 외부에 이미 나가있는 webpack 이 빌드한 리소스를 webpacker 에서 바로 사용할 수 있게 함으로써 프런트의 빌드 없이도 백엔드 서버를 정상적으로 실행 할 수 있도록 할 예정입니다.

어떻게 할 수 있을까요?

webpacker 에서 webpack 을 분리 할려면 사전 전제가 하나 필요합니다.

그 것은 webpacker 가 webpack 빌드 결과물로 나온 manifest.json 사용한다는 점입니다. manifest.json 을 기준으로 화면상에 스크립트를 로드해주게 됩니다.

실제로 적용되는 방법을 한 번 살펴 보겠습니다.

먼저 webpacker 가 manifest.json 를 자동으로 인식할 수 있는 구조로 만드는 것입니다.

webpacker 는 로컬에 존재하는 manifest.json 을 인식해서 javascript_pack_tag, stylesheet_pack_tag 함수를 사용해서 화면상에 webpack 으로 빌드된 결과물의 링크를 만들어 줍니다.

그래서 실행될 때 manifest.json 의 존재를 미리 알고 있어야 하는 데요, 보통 자체 내장된 webpack 빌드를 사용하게 됩니다.

이 때 자체 빌드되는 webpack을 사용하지 않고 이미 빌드된 결과물이나 RoR과 상관 없는 곳에서 webpack 빌드 결과물을 가지고 올 수만 있으면 webpack 을 내장하지 않고도 webpacker 를 실행할 수 있게 됩니다.

기본 컨셉은 아래의 그림과 같습니다.

  1. webpacker 가 인식 할 수 있게 manifest.json 을 webpack resource 에서 다운로드 받습니다.
  2. nginx 에서 /webpack/ 주소로 온 것을 실제 webpack resource 쪽으로 proxy 해줍니다.

코드화 시키기

실제로 코드로 만들어서 적용시켜 보겠습니다. 먼저 이미 빌드된 webpack 리소스가 있는 곳을 가정합니다.

sample : https://cdn.webpack.resource/webpack/manifest.json

RoR 시작전에 manifest.json 받기

manifest.json 은 webpack 으로 빌드된 리소스들 목록을 모아놓은 json 파일입니다.

{
"0.0744d1333637ff98f6f0.js": "/webpack/0.0744d1333637ff98f6f0.js",
"1.0744d1333637ff98f6f0.js": "/webpack/1.0744d1333637ff98f6f0.js",
"2.0744d1333637ff98f6f0.css": "/webpack/2.0744d1333637ff98f6f0.css",
"2.0744d1333637ff98f6f0.js": "/webpack/2.0744d1333637ff98f6f0.js",
....
}

위와 같은 형태로 빌드된 리소스 목록이 정리되어 있습니다.

mkdir -p public/webpackrm -rf public/webpack/manifest*.jsonwget — no-check-certificate -P public/webpack https://cdn.webpack.resource/webpack/manifest.json

wget 으로 원격지의 manifest.json 을 받아서 원하는 디렉토리에 넣어둡니다.

ps. curl 명령어는 아래를 참고하세요.

curl -k -o public/webpack/manifest.json --compress https://cdn.webpack.resource/webpack/manifest.json

webpacker.yml 설정하기

default: &default
public_output_path: webpack
extract_css: true

webpacker 가 webpack 의 디렉토리를 인식할 수 있게 public_output_path 를 설정해줍니다.

extract_css: true 를 해주면 stylesheet_pack_tag 를 사용할 때 css 도 같이 출력해주게 됩니다.

extract_css 옵션에 대한 자세한 사항은 https://github.com/rails/webpacker/blob/master/docs/v4-upgrade.md#upgrading-projects-with-custom-webpack-setups-that-use-only-the-view-helpers 여기를 참고해주세요.

Nginx Proxy 설정하기

location ~ ^/webpack/(.*)$ {
resolver 1.1.1.1 ipv6=off;
proxy_pass https://cdn.webpack.resource/webpack/$1;
proxy_no_cache 1;
proxy_cache_bypass 1;
}

RoR 설정이 끝나고 나면 Nginx 에서 proxy 설정을 해줍니다.

Nginx 에서 proxy_pass 를 특정 uri 로 하게 되면 DNSresolver 를 꼭 붙여줘야 합니다. (1.1.1.1 은 클라우드플레어 dns 입니다). proxy 내부에서는 cache 설정을 하지 않습니다.

여기까지 설정으로 인해서 webpacker 는 이미 빌드된 결과물에 대해서 로컬에서 빌드하지 않고도 사용할 수 있게 되었습니다.

그럼 이제 로컬에서 webpack-dev-server 를 어떻게 뛰우는지 같이 보시죠.

webpack-dev-server 사용하기

로컬 프론트엔드 개발환경을 위해서 webpack-dev-server 를 사용해야합니다.

이유는 소스코드가 수정 되었을 때 자동으로 재시작 할 수 있게 하기 때문입니다.

간단하게 webpack-dev-server 설정을 살펴보겠습니다.

output: {
filename: '[name].js',
path: path.join('your public directory', 'webpack'),
publicPath: '/webpack/'
},
devServer: {
header: {
'Cache-Control’: ‘no-cache, no-store, must-revalidate',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
},
host: '0,0,0,0',
hot: true,
https: true,
inline: true,
port: 3035,
sockPort: 3035,
disableHostCheck: true,
},
plugins: [
….,
new ManifestPlugin({
publicPath: path.join('your public directory', 'webpack'),
writeToFileEmit: true,
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
]

필요한 속성을 몇가지 설명을 드리겠습니다.

output.filename: 빌드된 js 파일의 이름

output.path : 빌드된 결과물이 저장될 패스

output.publicPath : 빌드된 결과물의 로드될 주소

devServer.hot: 자동 새로고침 하기 위한 옵션입니다.

devServer.https: 는 로컬 개발환경에 따라 다릅니다. 개발환경도 https 를 사용하시면 활성화 시켜주세요.

devServer.port: webpack-dev-server 가 실행되는 포트입니다.

devServer.sockPort: webpack-dev-server 가 웹소켓으로 브라우저랑 통신하는 포트입니다.

devServer.disableHostCheck: 이 옵션을 켜두시면 아이피로도 접속할 수 있게 됩니다.

ManifestPlugin 으로 manifest.json 을 생성을 하게 됩니다.

MiniCssExtractPlugin 으로 최종 결과물인 css 파일을 생성하게 됩니다.

이제 webpack-dev-server 를 뛰우게 되면 https://127.0.0.1:3035/webpack/manifest.json 형태로 manifest.json 을 다운로드 받을 수 있게 됩니다.

로컬에서 빌드되는 리소스들은 [hash] 를 넣지 않고 [name]으로 고정을 시키게 되면, manifest.json 가 나중에 인식이 되더라도 동일한 리소스를 바라 볼 수 있게 됩니다. no-cache 설정으로 리소스를 로딩하게 하면 매번 새로운 리소스로 로딩할 수 있습니다.

Docker 내부에서 RoR 을 사용하고 Docker 외부에 프론트엔드 개발환경이 있을 때도 적용할 수 있습니다.

맺음말

지금까지 webpacker 에서 webpack 빌드를 분리하는 방법을 알아 보았습니다.

webpacker 에서 webpack 코드를 분리 함으로써 몇 가지 이점이 생겼습니다.

  1. 백엔드는 프런트엔드 실행을 위해서 webpack을 매번 빌드 하지 않아도 됩니다.
  2. 프론트엔드는 자체 webpack 빌드 환경을 가지고 에러에 더 민첩하게 대응할 수 있게 되었습니다. (백엔드와 소스를 분리해서 관리할 수 있게 되었습니다.)
  3. 프론트엔드 환경을 Docker 안에서 관리하지 않아도 되게 되었습니다.
  4. 백엔드/프론트엔드 간에 알 수 없는 상태의 오류를 고민하는 시간이 줄어 들게 되었습니다.

이로써 백엔드와 프론트엔드 개발 환경을 좀 더 유연하게 가져갈 수 있게 되었습니다.

읽어주셔서 감사합니다.

--

--