-
webpack 중요 속성들 정리.node.JS 2022. 12. 29. 12:58
1. devServer
요즘 vite나 bun 등 제각의 강점과 빠른 빌드 속도를 가진 다양한 번들링 툴이 개발 및 출시되고있기는 하지만 아직 번들링 생태계에서 가장 큰 비중을 차지하는 것은 webpack이 아닐까 생각합니다. 이 webpack은 webpack.config.js라는 파일 내부에 설치, 지정된 속성이 정의됩니다. 만약 터미널 커맨드로 프로젝트를 설치할 경우에는 보통 webpack.config.js 파일은 숨김처리 되어있기 때문에 우리는 이 파일에 대해 쉽게 접근할 수 없으며, 그 중요성과 존재를 알아차리기 힘들지만 사실 이 파일은 우리가 프로젝트를 만들도록 돕는데 큰 역할을 합니다. 여기서 webpack을 정의하는 속성 중 가장 중요한 요소 중 하나는 devServer가 아닐까 생각합니다.
우리가 vue 등의 SPA 사이트 프로젝트를 만들거나 CRA로 리액트 프로젝트를 설치할 때 vue, js, ts 등의 모듈 파일 확장자를 번들링하여 하나의 프론트 개발서버로 가시화해주는 역할을 바로 webpack에서 devServer라는 녀석이 수행합니다. 즉, 우리가 터미널에 몇 단어를 입력하는 것으로써 프로젝트 패키지를 손쉽게 생성할 때 webpack이 자동적으로 설치되며 이 devServer 속성 역시 함께 설치되어 로컬 브라우저에서 프론트 서버를 띄울 수 있게 합니다.
너무 설명이 장황한데, 일단 만들어보고 어떤 녀석인지 확인하는게 제일 좋을것 같습니다.
npm i -D webpack-cli webpack-dev-server
위 명령어로 웹팩을 터미널로 다룰 수 있도록 해주는 라이브러리와 devServer를 구동할 수 있도록 돕는 라이브러리를 설치합니다.
설치 후에 node_modules 내부의 devServer 디렉토리에서 무엇이 로컬에 서버를 띄우고 번들링 결과물을 실시간으로 반영하는지 알 수 있었습니다. 핵심은 아무래도 SockJSClient, WebSocketClinet 파일들인것 같네요. 소켓 통신을 사용해 실시간으로 개발자가 의도한 사항들을 로컬 서버에 반영하고 그 결과(성공, 에러) 등을 브라우저에 반영하는 것입니다!
그렇다면 우리는 이 devServer가 제공하는 기능들을 커스터마이징할 수도 있어야겠죠.
이것은 webpack.config.js 파일 내부에서 devServer 속성을 사용해 가능합니다.
// webpack.config.js mode: "development", devServer: { contentBase: path.join(__dirname, "dist"), publicPath: "/", host: "dev.domain.com", overlay: true, port: 8080, stats: "errors-only", historyApiFallback: true, },
어짜피 각 사항들은 구글링을 통해 알 수 있으니 중요한 내용만 꼽아 보겠습니다.
publicPath: 해당 어플리케이션의 기본 라우팅 경로.
port: 프론트 서버를 구동해 로컬에 띄우는 프로세스의 포트 번호.
historyApiFallback: 이 어플리케이션이 SPA일 경우, 404 에러 발생 시 기본 경로(index.html)로 재라우팅 할 것인지 여부.
뿐만 아니라 devServer는 html, js 파일을 제공하는 것 외에 서버로서의 기능도 수행할 수 있습니다.
이런 경우 node.js의 Express와 매우 흡사한데요, 제가 이 방면에 지식이 많지 않지만 app 객체가 컨트톨러로서 동작해 res 객체의 json 함수로 필요한 정보들을 json화하여 클라이언트에 보낼 수 있도록하는 서버의 기능도 제공합니다.
webpack이 프론트 개발도구로서만 사용되는지 알고있었던 저에게는 새로운 사실이었습니다.
devServer: { host: "dev.domain.com", overlay: true, before: app => { app.get("/api/users", (req, res) => { res.json([ { name: "Jack", age: 12 }, { name: "Chris", age: 17 } ]); }) } }
이렇게 되면 localhost:포트번호/api/users를 호출했을 때 위의 예시 객체를 응답받을 수 있다는 것입니다!
또한, 이 webpack의 devServer를 활용해 CORS에러를 제거할 수도 있습니다.
이 CORS에러는 다들 아시다시피 도메인이 다른 두 어플리케이션이 http 통신으로 정보를 주고받을 때, 서버에서 응답한 결과를 브라우저가 안전하지 않다고 판단하여 에러를 던지는 웹개발자들에게 아주 악명 높은 에러입니다. 만약 현재 프론트 개발 서버의 포트가 3000번이고 백엔드 서버의 개발 포트번호가 8080번이라고 가정할 시, 도메인 localhost:3000에서 localhost:8080으로 요청을 보내는 것이기 때문에 브라우저는 두 어플리케이션의 도메인이 일치하지 않아 보안 상 문제가 있다고 판단하여 우리는 그 결과물을 확인할 수 없게됩니다.
따라서, 일반적으로 CORS 문제를 해결하기 위해 백엔드의 컨트롤러에 응답 객체의 header에 access-allow-control-origin 속성을 넣고 그 값으로 와일드카드 문자 또는, 프론트 서버의 도메인을 넣어 문제를 해결하는 것이 일반적입니다. 이것의 목적은 서버에서 허가된 클라이언트 도메인의 값을 지정하여 해당되는 도메인에 접속한 브라우저는 CORS에러를 발생시키지 않도록 하는 것입니다. 그러나 우리는 서버에서 이런 해결방법을 사용하지 않고 webpack의 설정을 활용하여 문제를 해결할 수 있습니다.
devServer: { proxy: { "/api": "http://localhost:8081" } }
이런식으로 devServer에 proxy 설정을 추가해 CORS에러를 우회하는 방식입니다. proxy의 사전적 의미는 '간접적인', '간접의' 이라는 뜻을 가지고 있는데요. 이렇게되면 /api 경로를 포함한 모든 클라이언트의 요청들은 요청 주소가 프록시 서버로 인해 내부적으로 localhost:8081로 교체됩니다. 즉, 요청의 origin 도메인이 localhost:8081이고 응답 서버의 도메인 역시 localhost:8081이라면 서버에서는 이 요청이 같은 도메인에서 온 것이라고 인식하여 응답 시에 브라우저가 CORS에러를 발생하지 않도록 정상적인 응답을 클라이언트에게 보내줍니다. 이것이 바로 proxy 설정이 하는 역할입니다! 즉, 정리하자면 본래 브라우저가 서버에 자원을 요청 시 클라이언트 서버의 도메인을 그대로 가지고 서버에 전송했다면, proxy 속성을 지정한 경우는 프론트엔드의 서버가 요청의 도메인을 바꾸어 서버가 이 요청이 같은 도메인에서 왔다고 인식하게끔 바꿔주는 역할을 한다고 볼 수 있습니다. 이 내용은 제가 가고싶었던 회사의 기술면접에서 대답하지 못한 내용이라 더욱 기억에 남을 것 같습니다.
이 이미지를 보면 webpack의 proxy 속성이 무엇인지 이해하는데 좀 더 수월할 것 같습니다. 출저) 웹팩 핸드북
2. hot
다음은 webpack의 hot 속성에 대해 짧게 이야기해보려고합니다. 이 속성은 devServer 속성의 값인 객체에서 지정해줄 수 있으며, 우리가 webpack을 사용해 프로젝트를 구현하는데 리소스의 활용 측면에서 아주 큰 도움을 주는 속성입니다.
// webpack.config.js: module.exports = { devServer = { hot: true, }, }
속성 지정은 이렇게 해줍니다. 매우 단순합니다. 그렇다면 이 속성은 어떤 역할을 하는 것일까요? webpack, 혹은, vsc의 live server 등을 사용하지 않고 아주 전통적인 방식으로 프론트 개발을 하던 방식을 생각해봅시다. 우리는 그러면 js, css, html 파일들의 내용이 변경되었을 때 이를 확인하기 위해 브라우저를 새로고침해야합니다. 만약 webpack을 사용한다고 해도 devServer의 hot 속성을 사용하지 않는다면 우리는 일일히 변경사항을 반영하기 위해 브라우저를 새로고침해야할 것입니다. 그런데 우리는 react, vue 등의 터미널 명령어를 통해 설치한 프로젝트 패키지로 작업을 할 때, 변경 사항을 확인하기 위해 새로고침을 하지 않고 키보드를 사용해 저장 버튼만 누르면 해당 내용이 화면에 바로 반영되는 것을 모두 경험했을 것입니다. 즉, 우리가 터미널 명령어로 설치하는 프로젝트들은 이미 hot 속성이나 그와 비슷한 속성들이 모두 반영되어 있다고 할 수 있습니다.
그렇다면 어떻게 devServer의 hot 속성이 작동되는지 간단하게 알아볼 필요가 있습니다.
// result.js const result = { async render() { const res = await axios.get("/api/user"); return `<div>${user.id}` - ${user.name}</div>; } } export default result;
// app.js import result from "./result"; if (module.hot) { console.log("Hot module ON."); module.hot.accept("./result", () => { body.innerHTML = await result.render(); }) };
프로젝트를 로드했을 때, hot 속성이 켜져있으면 if문의 코드들이 실행되고 accept 메소드는 변화를 감지할 모듈 파일과 감지 이후 실행할 함수를 인자로 받습니다. 즉, 위의 코드처럼 result 모듈의 변화를 감지해 그 변화를 html에 반영할 수 있다는 것입니다. 이 방식은 모듈로서 나눠진 부분만 선택적으로 화면에 반영하기때문에 브라우저가 새로고침되지 않고도 변화를 화면에 반영할 수 있습니다. 위의 예시에서 만약 개발자가 result 함수에서 호출하는 axios의 경로를 바꿔주거나 반환되는 태그를 의미하는 문자열을 수정하면 아래 app.js에서 result 모듈의 변화를 감지하는 accept 함수가 실행될 것입니다. 그리고 이 변경사항들은 새로고침 없이 바로 부분적으로 개발자의 브라우저에 반영됩니다. 이 방식은 컴퓨터 자원을 아끼게 해주고, 개발자의 피로도 역시 낮춰줄 수 있기에 아주 중요한 속성이라고 생각합니다.
3. optimization
빌드 최적화는 아주 중요합니다. 빌드된 결과물의 용량에 따라 사용자들이 어플리케이션을 다운받고 사용하기 전까지의 시간이 영향을 받으며 이는 서비스의 사용자 인식에 직접적으로 연관되기 때문입니다. webpack의 mode를 production으로 지정하고 빌드를 시행하면 설정된 여러 플러그인들이 모듈화된 js파일들과 css파일들을 하나의 파일로 연결하면서 파일의 용량을 줄이기 위해 압축을 진행합니다. 전의 포스트에서 작성했듯이 그러한 역할을 실행하는 플러그인들은 webpack의 plugins라는 속성에서 설정됩니다. 그러나 모든 플러그인들이 plugins 속성에서 설정되는 것은 아닙니다. 특정 플러그인들은 optimization이라는 별도의 속성에서 지정 및 사용이 가능합니다.
// webpack.config.js: const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); module.exports = { optimization: { minimizer: mode === "production" ? [ new OptimizeCSSAssetsPlugin(), new TerserPlugin({ terserOptions: { compress: { drop_console: true, // 콘솔 로그를 제거한다 }, }, })] : [], }, }
최적화를 수행할 플러그인들은 optimization 속성 내부에 minimizer 속성을 추가로 설정하여 그 안에 지정해주어야합니다. 위 코드 블럭의 OptimizeCSSAssetsPlugin 플러그인은 프로젝트 내부의 모든 css파일들에서 모든 공백을 제거하여 압축을 진행해주는 플러그인을 optimazation 속성에 지정한 것입니다. 이 방식은 plugins 속성의 플러그인의 사용법과 마찬가지로 new 연산자를 통해 인스턴스를 생성하는 형식으로 최적화를 수행합니다. 마찬가지로 TerserPlugin은 생성자에 객체를 인자로 넣어 최적화 설정을 커스텀해줄 수 있습니다.
'node.JS' 카테고리의 다른 글
자주 사용되는 webpack plugins 정리. (0) 2022.11.07 webpack 내용 정리. (2) (0) 2022.10.17 webpack 내용 정리. (0) 2022.09.23 npm 명령어 정리 (0) 2022.09.22 get, post 통신 해보기 (0) 2022.02.22