웹팩 개발 서버에서는 HTML이나 JS의 소스 코드를 변경했을 때, 모든 소스 코드를 다시 가져오면서 화면을 다시 그려주는 방식으로 우리에게 개발 편의성을 제공했다.
그런데 SPA(Single Page Application)에서는 빈 html인 index.html
과 같은 파일에 app.js
라는 하나의 js파일로 모든 것을 로드한다.
이와 같은 경우 리프레시 후에 모든 데이터가 초기화 되어 버린다. 다른 부분을 수정했는데 내가 의도하지 않은 부분까지 리프레시되어 개발하는데 불편할 수 있다.
전체를 갱신하지 않고, 변경한 모듈만 바꿔준다면 어떨까? 핫 모듈 리플레이스먼트는 이러한 불편함을 개선하는 목적을 가진 웹팩 개발서버의 한 기능이다.
Alice
라는 유저를 검색하는 것을 테스트하려고 위와 같이 세팅한 상태에서 아래쪽에 리스트에 ---
라는 구분선이 넣고싶어졌다고 가정해보자.
위와 같이 소스코드를 바꾸고 저장하면?
모든게 새로고침되며 입력했던 Alice
가 사라진다.
핫 모듈 리플레이스먼트가 적용되면 변경된 모듈만 새로고침되고 변경하지 않은 것은 새로고침 되지 않아야 한다.
간단히 webpack.config.js
에서 devServer
에 관한 설정을 해주면 된다.
devServer: {
client: {
overlay: true,
logging: "info",
},
onBeforeSetupMiddleware: (devServer) => {
devServer.app.use(apiMocker("/api", "mocks/api"));
},
hot: true,
},
그런데 나는 이 부분에서 막혀서 한참을 헤맸다. 왜냐면, 내가 사용하던 프로젝트에서는 프로젝트 내부에 또 다른 프로젝트를 품는 형식으로 되어있는데, 뭐가 잘못된건지 계속해서 HMR이 적용되지 않았다. 결국 그냥 프로젝트 하나를 새로 만들어서 다시 적용하니 멀쩡히 잘 적용이 되었다.
import form from "./form.js";
import result from "./result.js";
document.addEventListener("DOMContentLoaded", async () => {
const formEl = document.createElement("div");
formEl.innerHTML = form.render();
document.body.appendChild(formEl);
const resultEl = document.createElement("div");
resultEl.innerHTML = await result.render();
document.body.appendChild(resultEl);
});
if (module.hot) {
console.log("핫모듈 켜짐");
module.hot.accept("./result.js", () => {
console.log("result 모듈 변경됨");
});
}
위와 같이 module.hot
에 조건문을 걸고, 핫 모듈이 켜졌을 때 제대로 로그를 찍는지, ./result.js
가 변경되었을 때 제대로 로그를 찍는지를 살펴보자.
로그가 잘 뜨면 성공이다.
그렇다면 반면에 module.hot.accept()
로 지정하지 않은 form.js
가 수정되면 어떻게 될까?
전체 페이지가 새로고침되며 위와 같은 로그가 뜬다.
app.js
import form from "./form.js";
import result from "./result.js";
const resultEl = document.createElement("div");
document.addEventListener("DOMContentLoaded", async () => {
const formEl = document.createElement("div");
formEl.innerHTML = form.render();
document.body.appendChild(formEl);
resultEl.innerHTML = await result.render();
document.body.appendChild(resultEl);
});
if (module.hot) {
console.log("핫모듈 켜짐");
module.hot.accept("./result.js", async () => {
console.log("result 모듈 변경됨");
resultEl.innerHTML = await result.render();
});
}
resultEl
을 밖으로 빼고, 전역화 시켰다. 그리고 module.hot.accept()
의 콜백함수에도 await result.render();
를 넣어주었다.
이제 result.js
에 변경사항이 있을 때, 화면 전체가 갱신되지 않고, 딱 갱신한 부분만 갱신이 된다.
모든 로더가 HMR을 지원하지 않는다. HMR 인터페이스를 구현해야지만 핫 로딩 리플레이스먼트를 지원한다. 웹팩 기본 편에서 보았던 style-loader
가 그렇다.
그래서 app.css
에서 무언가 스타일을 변경해도 HMR(Hot Module Replacement)이 잘 지원된다.
이미지와 같은 것들도 HMR을 지원하기 때문에, 이미지를 바꿔도 HMR이 잘 적용된다.