🚨 next 9.4 이상부터는 새롭게 환경변수를 추가하는 방법이 나왔습니다.
nextjs.org/docs/basic-features/environment-variables
dotenv 패키지를 설치할 필요 없이
.env.local 파일을 최상단에 생성한 후 환경 변수를 작성하시고, process.env.[이름] 꼴로 사용하시면 됩니다.
🚨 custom server를 만들어보려면 프론트 최상위 파일에 server.js를 만들고 아래의 지시를 따르면됩니다.
이 부분은 전에 다른 포스트에서도 다룬 바 있으니 참고합시다!
nextjs.org/docs/advanced-features/custom-server
1. next.config.js 구성과 bundle-analyzer를 통한 번들 크기 확인 + tree shaking + gzip 압축
nextjs.org/docs/api-reference/next.config.js/introduction
공식 문서에서 살펴보시면 아시겠지만 내용이 많습니다.
Environment Variables, Base Path, Rewirtes, Redeirects, Custom Headers 등등.
이 부분은 가이드보다는 직접 공식 문서과 웹팩 문서를 보고 서비스마다 필요한 부분을 교체하거나 추가하는 방식으로 작성해야 합니다.
커스텀 웹팩 세팅 부분은 이 문서를 참고합시다.
nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
여기에 저는 bundle-analyzer를 감싸서 사용해보겠습니다.
https://www.npmjs.com/package/@next/bundle-analyzer
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({
compress: true,
webpack(config) {
console.log(config);
let prod = process.env.NODE_ENV === "production";
return {
...config,
mode: prod ? "production" : "development",
devtool: prod ? "hidden-source-map" : "eval",
};
},
});
npm run build를 통해서 빌드를 하고나면 자동으로 분석 결과를 보여줍시다.
빌드 결과물의 analyze 쪽에서 확인하실 수 있습니다.
parsed size를 보면 됩니다. 각 번들(파일)당 500kb 이하가 되는 것이 좋습니다. 인터넷 속도가 빠른 곳에서는 1mb 정도도 괜찮다고 합니다. 현재 저 위의 결과물에서는 500kb 이상 되는 것이 없지만 moment가 워낙 커보이기 때문에 줄여보도록하기로 했습니다. 이러한 과정을 tree shaking이라고 합니다.
만약 큰 파일의 경우에는 tree shaking을 키워드로 검색해보시면 됩니다.실제로 사용하는 모듈만 로딩하는 것을 말합니다. 예를 들어 antd 아이콘 번들이 너무 크기가 크므로 and icon tree shaking 관련 정보를 찾아보시면 됩니다. 그러나 tree shaking 자체가 안되는 패키지도 많습니다. 이를 감안하고 사용하시던가, 아니면 아예 들어내야 합니다.
moment.js 관련 tree shaking은 검색하니까 바로 나오네요. 우리는 ContextReplacementPlugin를 사용해보겠습니다.
https://github.com/jmblog/how-to-optimize-momentjs-with-webpack#bonus
https://gist.github.com/iamakulov/59d88d00404259abb83daaf51b70cb07
우선 사용할 locale을 지정하여 사용하는 언어팩만 지정하여 사용하는 양을 줄이고
import moment from "moment";
moment.locale("ko");
웹팩에도 ContextReplacementPlugin 플러그인을 통해 필요한 부분만 지정합시다.
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
const webpack = require("webpack");
module.exports = withBundleAnalyzer({
compress: true,
webpack(config) {
console.log(config);
let prod = process.env.NODE_ENV === "production";
return {
...config,
mode: prod ? "production" : "development",
devtool: prod ? "hidden-source-map" : "eval",
plugins: [
...config.plugins,
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /^\.\/ko$/),
],
};
},
});
줄인 결과 57.7KB로 줄어든 것을 보실 수 있습니다. 이 외에 다른 패키지의 용량이 너무 크다면 tree shaking 해주시면 됩니다.
tree-shaking에 대한 좋은 글을 찾아 여기에 공유해봅니다.
gzip으로 압축하기
1/3 ~ 1/4 정도로 번들 용량을 압축해줍니다. (!)
npm install compression-webpack-plugin --save-dev
https://webpack.js.org/plugins/compression-webpack-plugin/
압축한 결과 빌드 내용물 중 .gz 가 붙은 것이 보입니다.
최종적으로 next.config.js는 다음과 같습니다.
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
const webpack = require("webpack");
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = withBundleAnalyzer({
compress: true,
webpack(config) {
console.log(config);
// 배포환경인가?
const prod = process.env.NODE_ENV === "production";
// 플러그인 관련 설정
const plugins = [
...config.plugins,
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /^\.\/ko$/),
];
if (prod) {
plugins.push(new CompressionPlugin());
}
return {
...config,
mode: prod ? "production" : "development",
devtool: prod ? "hidden-source-map" : "eval",
plugins: plugins,
};
},
});
2. redux devtools 등 redux state값을 노출하는 내용 제거하기
직방 같이 redux state가 노출되는 일을 막기 위해서 NODE_ENV가 production일 때 Devtools를 사용하지 않도록 만들었습니다. 또, next-redux-wrapper가 상당히 verbose하기 때문에(이거야 간단한 로그 읽기도 힘들 정도로 많습니다.) prod일 때는 debug가 false로 설정하도록 하여 좀 조용히 만들어주기로 했습니다.
const prod = process.env.NODE_ENV === "production";
const configureStore = () => {
const logger = createLogger();
const enhancer = prod
? compose(applyMiddleware(thunk))
: compose(composeWithDevTools(applyMiddleware(logger, thunk)));
const store = createStore(rootReducer, enhancer);
return store;
};
const wrapper = createWrapper(configureStore, { debug: !prod });
npm run build/start하신 후에 devtools에 불이 꺼졌는지 확실히 확인해줍시다.
여담인데 티스토리는 배포 환경인데도 이걸 안 꺼뒀네요. 똑똑하신 분들이라서 문제 없는 부분만 노출시키신 건지 자세한 상황은 모르겠지만 일단 꺼주는 게 원칙입니다.
3. 빌드 및 배포
주의하실 점이, Next는 SSR이기 때문에 프론트에도 서버가 필요합니다.
따라서 백엔드만 pm2로 돌리고, 해당 서버 인스턴스를 종료할 경우 접속할 수 없게 됩니다.
next 프론트를 배포하기 위해서는 우선 커스텀 서버를 만들어 둬야 합니다.
darrengwon.tistory.com/539?category=891029
여기에 만들어 두신 후 진행합시다.
실행 script 부분에서는
npm run build => npm run start 두 명령어를 치기 귀찮다면 "prestart" 를 지정해두면 됩니다.
(저는 매번 prestart를 npm run build로 지정해두고 프로젝트를 시작합니다)
프론트 쪽의 package.json의 script는 다음과 같습니다.
테스트하실 때는 로그 꼭 달아줍시다. 안 달아주면 왜 안되는지 알 수가 없습니다.
"scripts": {
"dev": "next dev",
"prepm2-dev": "cross-env ANALYZE=false next build",
"pm2-dev": "pm2 start server.js -e testLog/err.log -o testLog/out.log --watch",
"build": "cross-env NODE_ENV=production ANALYZE=true next build",
"prestart": "npm run build",
"start": "cross-env NODE_ENV=production pm2 start server.js -i 1 --name \"next-front\""
},
백엔드 쪽의 package.json의 script는 다음과 같습니다. 따로 실행해주면 아주 번거로우니 concurrently를 이용합니다.
dev는 로컬에서 소스 코드로 개발할 때 사용하는 코드이고
test는 로컬에서 배포 직전의 환경과 가장 가까운 환경을 조성하기 위한 테스트 용입니다.
prod는 실제 배포 환경입니다. 리눅스라서 cross-env를 해줘도 되고 안해줘도 됩니다. 저는 그냥 두겠습니다. ㅎ
"scripts": {
"front": "cd .. && cd front && npm run dev",
"server": "nodemon --exec node app.js",
"dev": "concurrently --kill-others-on-fail \"npm run front\" \"npm run server\"",
"-----test-----": "------------------",
"testServer": "cross-env NODE_ENV=production pm2 start app.js -i -1 --watch",
"test": "concurrently --kill-others-on-fail \"npm run testServer\" \"npm run prodFront\"",
"-----prod-----": "------------------",
"prodServer": "cross-env NODE_ENV=production pm2 start app.js -i -1 --watch",
"prodFront": "cd .. && cd front && npm run start",
"prod": "concurrently --kill-others-on-fail \"npm run prodServer\" \"npm run prodFront\""
},
'React, Next, Redux > ▲ Next.js' 카테고리의 다른 글
next/router event 감지 및 이를 활용한 page loader (0) | 2020.12.10 |
---|---|
Image Optimization (0) | 2020.12.05 |
Next의 Pre-rendering, SSG와 SSR에 대한 설명 (0) | 2020.09.09 |
next Built-In CSS Support(css 모듈, Sass Support, etc) (0) | 2020.09.08 |
Reusing the built-in error page (next/error) (0) | 2020.08.20 |