본문 바로가기
Front_End/Webpack

Spring Boot 환경에서 webpack dev server 사용하기

by Havi 2017. 3. 24.

Spring Boot에서 Webpack을 사용하며 script 개발시 webpack --watch를 사용하여 디버깅하였습니다. Boot의 devtool를 설정하여 live reloading이 가능하였지만 watch기능이 bundle파일을 아예 새로 만들어 주는 형식이라 실시간으로 변경을 감지하지 못하였고 그에 따라 디버깅이 굉장히 불편했습니다. 
프론트 개발시에도 실시간 디버깅을 위해 webpack-dev-server를 사용하였고 이제는 F5를 손에서 떼버리게 되었습니다. (부끄럽게도 처음에는 Spring 환경에서 webpack-dev-server를 못쓰는줄 알았습니다...) 모든 코드는 github에 있으며 아래는 이에 관한 설정내용입니다.

저의 주 개발환경이 freemarker(Server Tmeplate Engine)을 사용하기 때문에 프론트와 백엔드를 따로 분리 하지 않고 하나의 프로젝트로 개발하였습니다.

1. webpack-dev-server란?

webpack-dev-server는 말그대로 임시 개발 서버를 하나 띄워서 live reloading을 제공합니다. 의존성추가와 몇 개의 옵션 추가로 손쉽게 사용할 수 있습니다. webpack --watch(writing disk)는 bundle파일을 새로 만들지만 webpack-dev-server(writing memory)는 memory에 bundle 파일을 올려놓고 변경사항이 생길때 마다 이에 대한 결과를 live reload시켜 줍니다. 고로 훨씬 좋은 performance를 보여줍니다.

2. webpack-dev-server 설정

npm install webpack-dev-server --save-dev
<script type="text/javascript" src="bundle.js"></script>

paskage.json script 설정

{
	...
	"scripts": {
	    "start": "webpack-dev-server --progress --inline",
	    "watch": "webpack -d --watch",
	    "dev": "webpack -d",
	    "prod": "webpack -p",
	    "test": "echo \"Error: no test specified\" && exit 1"
	  }
	...
}

webpack.config.js에 추가 / 혹은 webpack.dev.config.js를 따로 두어 로컬개발시에만 적용

module.exports = {
    devtool: 'inline-source-map',
    devServer: {
        historyApiFallback: true,
        compress: true,
        publicPath: '/',
        host: "0.0.0.0",
        port: 3000,
        proxy: {
            "**": "http://localhost:8080"
        }
    },
    plugins: [
        new webpack.NamedModulesPlugin() //브라우저에서 HMR 에러발생시 module name 표시
    ]
}

devtool option

디버깅을 위한 source mapping style을 선택할 수 있습니다. 변환되기 전, 변환된 후 혹은 각각의 성능에 따라 devtool option을 선택할 수 있습니다.

자세한 설명은 devtool docs를 참고하세요. 저에게 도움이 되었던 양권성님의 블로그도 참고하시면 성능 테스트까지 확인하실 수 있습니다.

devServer option

  • historyApiFallback: HTML5 History API를 사용해야 한다면 'true'값으로
  • progress: console에 진행사항 표시(%)
  • quiet: error, warning을 console에서 안보이게 합니다.
  • compress: enable gzip compress(압축)
  • publicPath
    • 브라우저에서 bundle 파일이 위치한 경로를 잡아줘야 합니다.(중요합니다 이것 때문에 삽질을...)
    • http://localhost:3000/static/bundle.js에 위치한다면 다음과 같이 잡습니다.
     publicPath: "/static/"
    
    • HMR(Hot Module Replacement)을 사용한다면 전체 경로를 잡아줘야 합니다.
    publicPath: "http://localhost:3000/static/bundle.js"
    
  • host: 디폴트는 localhost. 만약 외부의 다른 host를 잡고 싶다면 0.0.0.0.
  • proxy: proxying하기 원하는 URL을 설정해 줄 수 있습니다. 현재 백엔드 서버 호스트를 설정해 주었습니다. 자세한 설명은 여기를 참고하세요.

webpack-dev-server는 중요한 옵션들을 CLI를 사용하여 설정할 수 있습니다. 설정이 유연하게 제공되어 자칫 충돌을 일으킬 수 있습니다.(저 또한 HMR설정에 삽질을 많이 했습니다...) CLI설정은 여기를 참고하세요.

HMR(Hot Module Replacement)

webpack-dev-server의 또 하나의 장점인 HMR은 특정 모듈의 변화를 감지하여 변경된 부분만 페이지 reload 없이 빠르게 변경해 줍니다. HMR은 HMR을 구현하는 'loaders'에서만 작동됩니다.('react-hot-loader', 'style-loader' etc) 
설정방식이 다양하기 때문에 본인에게 맞는 것, 그리고 되는 것으로 취사선택하시면 됩니다. react-hot-loader 방식은 여기를 참조하시면 빠르게 세팅가능합니다.

HMR원리를 잘 설명하고 싶지만 너무 이야기가 길어지므로...이 블로그를 참고하세요.

작동방법

npm start
Spring Boot Run

npm start 실행

결과 화면

3. 삽질해소에 도움이 되었던 참조 사이트


'Front_End > Webpack' 카테고리의 다른 글

Spring Boot 환경에서 webpack dev server 사용하기  (10) 2017.03.24
이해하기 쉬운 Webpack 가이드  (8) 2017.02.02

댓글10

  • Favicon of https://lhb0517.tistory.com BlogIcon Mr.star 2017.10.13 17:32 신고

    안녕하세요, 올려주신 글을 보고 궁금한 점이 있어 질문드리게 되었습니다.
    저도 지금 spring boot 어플리케이션 내부에 프론트 엔드는 webpack 으로 bundling 하여 사용하고 있는데, bundle.js 파일(output)이 실시간으로 spring boot 가 구동중인 server에 반영이 안 됩니다.
    webpack-dev-server로 구동한 3000번 포트의 로컬 서버에는 실시간으로 반영이 되고 있는데, spring boot가 구동중인 8080번 로컬 서버에서 사용하는 bundle.js 파일은 한참 뒤에 업데이트가 되는데 제가 잘못 알고 있는 건가요?
    설정은 올려주신 코드와 리로딩 관련한 부분은 거의 같게 했는데도 잘 안되어 여쭤봅니다.
    제가 해본 방법은 proxy 의 설정을 이렇게, 저렇게("/", "*", "/*", 등등)도 바꿔보고 publicPath도 http를 포함한 절대경로도 적어보고 상대경로도 적어보고 했는데 잘 안 되네요.
    spring boot 프로젝트 내의 html 코드에서 <scirpt src="http://localhost:3000/web/src/main/resources/static/js/bundle.js"></script>
    의 자바스크립트 로딩을 시도하면 404 not found 에러가 나서 이 방법도 안 되더라구요.
    webpack --watch 로 돌리면 시간은 좀 걸리지만 나중에 바뀌긴 합니다.
    참고로 spring boot는 spring-devtools 를 사용하여 live reloading을 사용하고 있습니다.

    힌트가 될 만한 조언 해주실 수 있을까요?

    감사합니다.

    답글

    • Favicon of https://haviyj.tistory.com BlogIcon Havi 2017.10.17 09:13 신고

      안녕하세요 star님!
      답변 늦게 올린점 우선 사과드립니다ㅠㅠ
      이미 답을 찾으셨을 수도 있지만...우선
      이 글을 쓴 이유는 watch를 사용하는 것보다 더 편리하고 빠르게 개발이 가능하기 때문에 쓴 것입니다.
      webpack dev server(3000)를 사용한다는 것은 bundle.js파일을 새로 만드는 것이 아니라 수정된 사항을 즉시 라이브 로딩하여 개발하는데 도움을 주기 위한 부분입니다. 즉, 저희가 스프링 어플리케이션(8080포트)을 사용하여 localhost:8080을 띄워 접속하는 것처럼 webpack dev server(3000포트)가 localhost:3000을 띄워서 접속을 할 수 있게 해줍니다. 하지만, webpack dev server는 프론트 코드만 띄워져 있기 때문에 실제와 동일한 테스트 환경을 위해 백엔드 로직을 프록시로 태워서 프론트는 3000포트에 있는 서버를 통해서, 백엔드는 8080포트를 통해서 프로세스가 흘러간다고 생각하시면 이해하기 쉽습니다.
      여기서 질문의 핵심으로 webpack dev server는 bundle.js라는 물리적인 파일을 만드는 것이 아니라 메모리에 bundle.js파일을 올려놓기에 빠르지만 실제로 파일은 바뀌지 않습니다.
      절대경로는 실제 배포될 경우 도메인이 다르기 때문에 다음과 같이 상대경로로 잡으시고 개발을 진행하시면 됩니다.

      <scirpt src="/resources/static/js/bundle.js"></script>

      결과적으로 webpack dev server로 개발을 쭉 진행하면서 마지막 배포전에 백엔드 및 프론트를 빌드하시면 수정한
      코드데로 bundle.js가 완성되어 war(or jar)파일이 완성됩니다.

      (제가 자세히 설명드리고 싶은 마음에 길이 길어졌는데...그래도 이해 안가시는 부분이 있으시면 언제든 댓글 부탁드립니다. 읽어주셔서 감사합니다!)

    • Favicon of https://lhb0517.tistory.com BlogIcon Mr.star 2017.10.17 09:51 신고

      Havi님, 친절하고 자세한 답변 감사합니다. 웹팩이 처음이라 webpack-dev-server가 필요한 이유를 고민하지 못해 어떤 원리인지 이해하는데 다소 시간이 걸렸던것 같습니다. 우선 Havi님 방법 말고 다른 방법으로 적용하여 개발하고 있긴 한데 Havi님 방법이 더 올바른 방법 같아요.
      추가로 궁금한 점이 있는데 그러면 webpack.config.js 에서 devServer.proxy 속성을 백엔드 서버:port로 지정하고 개발 단계에서는 8080포트가 아닌 3000번 포트로 접속하여 개발을 하게 되는 건가요?
      참고로 제 현재 개발 환경은 spring boot, pebble view template(확장자 : .peb :: HTML이 아니에요.), webpack 으로 구성되어 있는데 in-memory webpack-dev-server가 3000번 포트로 구동이 되고 거기로 *.peb 파일을 요청해보면 스프링 부트 서버인 8080번 포트를 프록시 타지 않아서요. 프록시 설정이 잘못된 건지, 백엔드에서도 spring active profile이 개발일때는 프론트에 대한 proxy를 설정해야하는 지 잘 모르겠습니다.
      신입 개발자라 용어 같은게 틀린 점이 있다면 너그럽게 양해 부탁드립니다. ㅜㅜ 그리고 다시 한 번 답변 감사합니다!

    • Favicon of https://lhb0517.tistory.com BlogIcon Mr.star 2017.10.17 10:23 신고

      Havi님, 제가 이해와 설정을 잘못 했던 부분이 있었던 것 같습니다. 방금 3000번 포트의 백엔드 로직은 8080번 포트를 프록시 타도록 설정했더니 잘 됩니다! 정말 감사드립니다.
      그리고 참고로 제가 개발하고 있는 프로젝트에는 로그인 인증 코드가 붙어 있어서 host 이름이 localhost일 경우에는 redirection infinite loop이 발생하기 때문에 hosts 파일을 수정해서 별도의 도메인으로 127.0.0.1을 접속하도록 했는데 "Invalid Host Header" 메시지가 뜬 것을 devServer.disableHostCheck: true 속성으로 해결했습니다.
      혹시 다른 분들께서 비슷한 어려움을 겪으신다면 도움이 되시길...

      Havi님 다시 한 번 감사드립니다! 저는 기초가 너무 약하네요 ㅜㅜ

    • Favicon of https://haviyj.tistory.com BlogIcon Havi 2017.10.20 09:47 신고

      아닙니다. 하나씩 해나가는 모습이 멋지신거 같아요ㅎㅎ
      추가적으로 도움되는 정보주셔서 감사합니다!!

  • Yonghyun 2018.02.02 12:01

    안녕하세요 현재 학부 4학년으로 미흡하지만 개발자로 꿈을 키우는 학생입니다.

    vue.js 와 Spring을 이용해서 웹을 제작하고 있습니다.

    현재 하고 있는 기능이 유저들이 이미지를 업로드하면 서버에서 이미지를 저장시키고 DB에는 경로를 저장해두어

    다른 뷰페이지에 저장된 이미지들을 보여주는 작업을 하고있습니다.

    현재 고민하고 있는 문제로는 front에서 ajax방식으로 get을 요청하면 이미지의 경로들이

    넘어오는데 이 경로를 img src="/to/the/path"형식으로 지정하면 webpack에서 돌고있는

    devServer로 인해 경로를 찾을 수 없다고 합니다..

    ex)localhost:8080/User/Desktop/......./ <- 그냥 경로만 넣으면 크롬에는 잘뜹니다 ㅠㅠ..

    만약 배포한다면 가상서버를 사용하지 않으니 문제없겠지만

    개발도중에 이러한 문제를 테스트 할 수 있는 방법이 알고싶어서 질문드립니다 ㅠㅠ..


    답글

    • Favicon of https://haviyj.tistory.com BlogIcon Havi 2018.02.03 19:53 신고

      안녕하세요, yonghyun님
      말씀하신 문제는 본문에서 다루고 있는데요ㅎㅎ
      proxy: {
      "**": "http://localhost:8080"
      }
      devserver 설정중에서 위의 proxy 옵션을 현재 개발하고 있는 포트인 8080으로 설정한다면 문제가 해결될거 같습니다.
      혹시나 제가 잘못 파악했으면 다시 댓글 주세요. 화이팅!

  • V 2018.04.08 20:45

    올려주신 포스팅이 많은 도움이 되었습니다.
    감사합니다.
    답글

  • s 2018.12.16 21:51

    안녕하세요..블로그 참고하며 공부하고있는데 ㅠㅠ 막힘이좀생겨서요 다름아니고 webpack dev server 이용해서
    front 서버를 띄웠는데 이게 hmr 이 적용이안되더라구요..기존 spring에서 서버띄운거랑 동일하게만 되네요..
    조금 막연하긴하지만 이럴때 체크해야되는 부분이 어디일가요? ㅠㅠ 너무 오래 삽질중이라..참고로 폴더구조는
    front폴더가 src랑 동일위치에 있습니다..
    프로젝트
    --front
    --src

    ..ㅠㅠ..

    답글