[React.JS] 강좌 11편 Express.js 서버 + 개발 서버 Hot Module Replacement 사용하기


liste

지금까지, React.js 에 대한 기본적인 공부는 어느정도 마쳤습니다. 이제, REST API 를 만들어서 서버와의 통신에 손을 뻗을 차례입니다! 지금까지는 webpack-dev-server 에만 의존해왔습니다. 이걸로는 서버 작업을 전혀 할 수가 없었죠. 이번 강좌에서는 React.js 를 Node.js 환경의 인기있는 웹프레임워크 중 하나인 Express.js 서버와 함께 사용하는 방법에 대해서 알아보겠습니다.

이 강좌에서는 서버사이드 코드 또한 ES6 으로 작성 할 것이며, 서버가 개발모드 일 때는, Express.js 서버와 webpack-dev-server 를 함께 실행하며, webpack-dev-server 에 proxy를 적용하여 해당 서버에서도 Express.js 서버에 구현된 라우트에 접근 하는 방법을 알아볼것입니다.

지난강좌들에선 기존의 webpack-dev-server 에서 특별한 설정을 하지 않아서 React.js 컴포넌트 코드가 수정 될때 모든 스크립트 자체가 새로고침 됐었는데요, 이번강좌에서는 더 나아가 react-hot-loader 를 적용하여 바뀐 컴포넌트만 리로딩하는 방법을 배우겠습니다.

주의: 이 강좌에서는 Express.js 에 대한 자세한 설명은 생략할 것입니다. 이에 대한 지식이 부족하신분들은 Express.js 메뉴얼 혹은 제 블로그에 작성된 Node.js 강좌 를 읽어주세요.

# 시작하기

# 로컬모듈 설치

이번 강좌에서는 비어있는 폴더에서부터 프로젝트를 작성하겠습니다.

# Node.js 프로젝트 생성하기
$ npm init

# 의존모듈 설치하기
$ npm install --save express react react-dom

# 개발 의존 모듈 설치하기
$ npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react react-hot-loader webpack webpack-dev-server

기존 React.js 프로젝트에서 사용하지 않던 모듈 두가지:

  • express: 웹 프레임워크 모듈
  • react-hot-loader특정 컴포넌트파일만 리로딩 할 수 있게 해주는 모듈

# 글로벌 모듈 설치

$ npm install -g babel-cli

babel-cli 모듈은 ES6 문법으로 작성된 코드를 ES5 문법으로 컴파일시켜주는 babel 모듈을 커맨드라인 인터페이스에서 사용 할 수 있게 해줍니다. 이는 나중에 서버사이드 코드를 빌드 할 때 사용됩니다.

이 강좌에서는 package.json 에 빌드스크립트를 작성하고 npm run <script> 기능을 통하여 처리하는 방식을 사용하겠습니다.

babel-cli 를 사용하지 않더라도, Node 버전을 최신버전으로 업그레이드하여 컴파일 과정을 생략해도 되고 (단, 아직 노드 최신버전은 일부모듈들에는 호환되지 않으며, 93% 의 ES6 문법만 호환됩니다. 또는, babel-node 모듈을 사용하여 ES6 코드를 직접 실행하셔도 됩니다. (단, babel-node 는 production 모드에서는 권장되지 않습니다) 그 외에도, gulp 모듈을 설치하여 gulp-babel 을 통하여 컴파일하는 방법도있습니다. gulp 를 아직 잘 모르시고, 사용법을 배우고싶은 분들은 GULP 강좌 를 읽어주세요.


# 설정하기

이 프로젝트의 babel-cli 과 webpack 은 설정파일을 필요로합니다.

# .babelrc

{
    "presets": ["es2015"]
}

# webpack.config.js

module.exports = {
    // 가장 처음 읽을 스크립트파일
    // 여기서부터 import 되어있는 다른 스크립트를 불러온다.
    entry: './src/index.js',

    // 파일을 합치고 ./public/bundle.js 에 저장한다.
    output: {
        path: __dirname + '/public',
        filename: 'bundle.js'
    },

    // ES6 문법과 JSX 문법을 사용한다
    module: {
        loaders: [
            {
                test: /\.js$/,
                loader: 'babel',
                exclude: /node_modules/,
                query: {
                    cacheDirectory: true,
                    presets: ['es2015', 'react']
                }
            }
        ]
    }
};

# 디렉토리 이해하기

./
├── .babelrc                # babel 설정파일
├── build                   # 서버 빌드 디렉토리
├── package.json		
├── public                  # 클라이언트 디렉토리
│    ├── bundle.js          # 컴파일된 스크립트
│    └── index.html         # 메인 페이지
├── server                  # 서버 디렉토리 (ES6)
│    ├── main.js            # 서버 사이드 메인 스크립트
│    └── routes
│        └── posts.js       # 예제 라우터
├── src
│    ├── App.js             # App 컴포넌트
│    └── index.js           # 클라이언트 사이드 메인 스크립트
├── webpack.config.js       # webpack 설정파일
└── webpack.dev.config.js   # webpack-dev-server 를 위한 설정파일

파일을 먼저 만들고 진행하는것을 선호한다면 다음 명령어를 통해 미리 만드세요.

$ mkdir build server public src server/routes && touch public/index.html server/main.js server/routes/posts.js src/App.js src/index.js webpack.dev.config.js

물론, 강좌를 진행하면서 하나 하나 새로 만들어가도 무방합니다.


# 서버 사이드 코드 작성하기

# server/main.js

import express from 'express';

const app = express();

let port = 3000;


// 경로 '/' 로 들어오는 요청들은 public 폴더로 정적 라우팅합니다.
app.use('/', express.static(__dirname + '/../public'));

app.get('/hello', (req, res) => {
    return res.send('Can you hear me?');
});

// 라우트 예제입니다.
import posts from './routes/posts';
app.use('/posts', posts);


const server = app.listen(port, () => {
    console.log('Express listening on port', port);
});

# server/routes/posts.js

import express from 'express';

const router = express.Router();

router.get('/', (req,res) => {
    res.send('posts');
});

router.get('/read/:id', (req, res) => {
    res.send('You are reading post ' + req.params.id);
});

export default router;

# 서버 사이드 코드 컴파일 하기

$ babel server --out-dir build

프로젝트 루트 디렉토리에서 이 코드를 통해 컴파일 해보세요. 잘 됐나요?

# 서버 테스트 해보기

$ node build/main.js
Express listening on port 3000

서버를 테스팅해봅시다. 현 상태로는 아직 public 폴더를 작성하지 않았으니 빈페이지가 뜨는것이 정상입니다.

한번 http://localhost/hello 페이지를 열어보세요.


# 클라이언트 사이드 코드 작성하기

# public/index.html

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
        <title>React App on Express Server</title>
    </head>

    <body>
        <div id="root"></div>
        <script src="bundle.js"></script>
    </body>
</html>

# src/App.js

import React from 'react';

export default class App extends React.Component {
    render() {
        return (
            <h1>This is HOT!</h1>
        )
    }
}

# src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

let rootElement = document.getElementById('root');

ReactDOM.render(<App/>, rootElement);

# webpack 을 통하여 코드를 컴파일하고 합치기

# webpack 을 이미 글로벌설치를 한 상태라면 디렉토리를 생략하고 webpack 만 입력해도 됩니다.
$ ./node_modules/.bin/webpack

컴파일이 성공적으로 완료됐다면, 한번 다시 서버를 열어서 http://localhost/ 페이지를 열어보세요.

제대로 된 페이지가 뜨나요?


# NPM 스크립트 작성하기

서버 및 클라이언트 사이드 코드 컴파일을 명령어를 그때그때 일일히 입력하기엔 귀찮죠?

간편하게 실행 할 수 있게끔 package.json 파일에서 스크립트를 작성해봅시다.

/* ... */
  "scripts": {
    "clean": "rm -rf build public/bundle.js",
    "build": "babel server --out-dir build && ./node_modules/.bin/webpack",
    "start": "NODE_ENV=production node ./build/main.js",
    "development": "NODE_ENV=development node ./build/main.js"
  },
/* ... */

스크립트를 실행 할 때는 npm run <script-name> 으로 실행합니다.

start 스크립트에선 NODE_ENV 를 production 으로 설정하고 development 스크립트에선 development 로 설정합니다.

Node.js 을 잘 아시는 분에게는 익숙하겠지만, 이 부분은 Node Application 에서 환경 상태를 확인하여,
환경에 따라 다른 작업을 설정하고 싶을 때 사용합니다.

저희는 development 모드일때는 webpack-dev-server 도 함께 실행하도록 설정 할 것입니다.

Window 에서는 NODE_ENV 를 설정하는 방법이 다릅니다:

"start": "set NODE_ENV=production&&node ./build/main.js",
"development": "set NODE_ENV=development&&node ./build/main.js"

# 서버사이드 코드 수정하기 – 개발모드 만들기

# 개발모드 전용 webpack 설정파일 작성하기

저희는 webpack-dev-server 에선 다른 config 을 사용 할 것이므로, 새로운 config 파일인 webpack.dev.config.js 를 만들어주세요.

편의를 위하여 기존 파일을 복사하고 필요한 부분만 수정하겠습니다.

$ cp webpack.config.js webpack.dev.config.js

왜 다른 config 를 사용하나요?

기존 config 는 output인 bundle.js 를 public 디렉토리에 저장하도록 설정이 되어있습니다.
webpack-dev-server 에서도 동일한 설정을 적용한다면, public 에 있는 파일이 계속 덮어씌워지겠죠? 저희 webpack-dev-server 에선 bundle.js 를 메모리에 저장한후, 나중에 브라우저에서  bundle.js 를 요청 할 시 public 디렉토리에 이미 있는 bundle.js 보다 우선권을 가져서 메모리에 있는걸 리턴하게됩니다.

또한, 추후 react-hot-loader 를 통해 변경된 컴포넌트만 리로드 하는 시스템을 구현할 건데요, production 모드에선 이게 필요하지 않으므로 다른 config 를 설정합니다.

# webpack.dev.config.js

 var webpack = require('webpack');

module.exports = {

    entry: [
        './src/index.js',
        'webpack-dev-server/client?http://0.0.0.0:3001',
        'webpack/hot/only-dev-server'
    ],

    output: {
        path: '/',
        filename: 'bundle.js'
    },

    devServer: {
        hot: true,
        filename: 'bundle.js',
        publicPath: '/',
        historyApiFallback: true,
        contentBase: './public',
        proxy: {
            "**": "http://localhost:3000"
        }
    },

    plugins: [
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ],

    module: {
        loaders: [
            {
                test: /\.js$/,
                loaders: ['react-hot', 'babel?' + JSON.stringify({
                    cacheDirectory: true,
                    presets: ['es2015', 'react']
                })],
                exclude: /node_modules/,
            }
        ]
    }
};
  • LINE 1: webpack 플러그인을 사용하기위하여 해당 모듈을 import 합니다.
  • LINE 5-9: webpack-dev-server 의 hot-module-replacement 를 지원하기위해 entry에 추가해줍니다. webpack-dev-server 의 포트를 7번 줄의 뒷부분에 적어줘야 HMR이 제대로 작동합니다.
  • LINE 12: 메모리에 저장하기 위하여 path를 ‘/’ 로 설정합니다.
  • LINE 16-25: webpack-dev-server 를 위한 설정입니다. proxy 부분은 Express.js 서버 URI를 넣어주어야합니다.
  • LINE 28-31HMR 을 사용하기위한 webpack 플러그인들입니다.
  • LINE 34-43: 바뀐부분은 ‘react-hot’ 로더를 추가한거밖에 없습니다. 단, 여러 모듈을 한꺼번에 적용하기 때문에 babel 을 위하여 따로 query 를 하진 못하고 ? 뒤에 JSON.stringify(query) 를 추가하여 query를 추가합니다.

 

주의: 최근 react-hot-loader 가 업데이트 되어서, 그냥 설치하시면 “react-hot-loader”: “^3.0.0-beta.3” 가 설치됩니다.

설치 하실 때, npm install –save react-hot-loader@1.3.0 을 하시거나, 버전 3을 쓰고 싶다면 수정을 다음과 같이 하세요:

    module:{
        loaders: [
            {
                test: /.js$/,
                loader: 'babel',
                exclude: /node_modules/,
                query: {
                    cacheDirectory: true,
                    presets: ['es2015', 'react'],
                    plugins: ["react-hot-loader/babel"]
                }
            }
        ]
    },

# 서버 메인파일 수정하기

자, 이제 저희는 NODE_ENV 값이 development 이면 webpack-dev-server를 실행하도록 서버 메인파일을 수정해보겠습니다.

import express from 'express';
import WebpackDevServer from 'webpack-dev-server';
import webpack from 'webpack';

const app = express();
const port = 3000;
const devPort = 3001;


if(process.env.NODE_ENV == 'development') {
    console.log('Server is running on development mode');

    const config = require('../webpack.dev.config');
    let compiler = webpack(config);
    let devServer = new WebpackDevServer(compiler, config.devServer);
    devServer.listen(devPort, () => {
        console.log('webpack-dev-server is listening on port', devPort);
    });
}
app.use('/', express.static(__dirname + '/../public'));

app.get('/hello', (req, res) => {
    return res.send('Can you hear me?');
});


import posts from './routes/posts';
app.use('/posts', posts);

const server = app.listen(port, () => {
    console.log('Express listening on port', port);
});

# 테스트하기

$ npm run build
$ npm run development

다음, 브라우저로 페이지를 열은 후, src/ 디렉토리 내의 App.js를 수정해보세요.

어때요? 페이지가 새로고침 되지 않고도 내용이 변경되었나요? (http://localhost:3001/ 로 들어가세요)

webpack-dev-server 의 주소에서 Express.js 에서 구현한 라우터에 접근해보세요: http://localhost:3001/posts/read/5

잘 됐나요?


마치면서..

수고하셨습니다. 이번 강좌는 Node.js 강좌에 더 가까웠던 것 같군요.

이 포스트에서 사용된 코드는 GitHub 에서 열람가능합니다.

다음 강좌에서는 오늘 만들었던 프로젝트 환경에서 react-router 를 제대로 사용하는 방법과,
Ajax 도구를 통하여 Express.js 서버에서 구현한 REST API 를 사용하여 데이터를 서버와 교류하는 방법에 대해서 알아보겠습니다.

질문이 있거나 포스트에 오탈자가 있으면 언제나 덧글 달아주세요 🙂

 

Reference

  1. “Webpack-dev-server”. Webpack.
  2. “Hot module replacement with webpack”. Webpack.
  3. “React-hot-loader”gaearon@GitHub
  4. “Webpack HMR Tutorial”Modern JS with React

liste

  • 오랜만에 놀러왔는데 신규 강좌가 생겼네요~재밌게 잘 봤습니다~^^
    그리고 포스트 예제 코드 중 # server/routes/posts.js <– 에 express 가져올 때 simport라고 해놓으셨는데 오타 맞죠?? ^^;

    • 오타 지적 감사합니다 🙂

  • 설거지의 달인

    좋은 글 감사드립니다. 시간을 많이 절약할 수 있었습니다. (–)(__)

    • 도움이 되어서 영광이에요 🙂

  • Robinson Park

    좋은 강좌 감사드립니다!

  • 박원배

    열정과 노력이 돋보이는 정말 친절한 글들 감사드려요!!

  • Seongkuk Park

    “build”: “babel server –out-dir build && ./node_modules/.bin/webpack”,
    이부분 “build”: “babel server –out-dir build && webpack”, 이렇게 바꿔주시는게 좋을듯 해요~

    전자로 돌려봤는데,
    ‘.’은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는..

    이런 명령어 오류가 뜨네요
    github에는 후자로 되어있어서 오류가 안 뜨네요

    오류방지를 위해 고치는것도 좋을거 같아요
    (유저분들이 왠만치 webpack을 글로벌로 깔았을 것을 전제하고..?)

  • Seongkuk Park

    그리고 npm development 치면 아래와 같은 오류가 뜨네요;

    ‘NODE_ENV’은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는..

    혹 문제가 무었인지 아시나요??

    • 윈도우는 NODE_ENV=development node ./build/main.js 대신 set NODE_ENV=development&&node ./build/main.js 이렇게 해주세요.

      start도 마찬가지입니다

      추후 이 포스트 수정하겠습니다..

      그나저나 dev때는 nodemon을 사용하게 해줘야했는데 여기서 그걸 까먹었네요 ㅋㅋ

      • Seongkuk Park

        ㅎㅎ 감사합니다 .ㅅ.

  • Seongkuk Park

    오류가 뭔지 찾았습니다.. set NODE_ENV=development && node ./build/main.js 이렇게 세팅을 해주니까
    “development ” 해서 공백까지 같이 세이브되네요..
    그래서 비교 연산자 안 먹혔어요;

    • 넹 ㅋㅋㅋ 거기에 공백이 있으면 안돼요
      근데 공백이 있으면 왜 안되는지 몰랐는데
      그런 이유 때문이였군요

  • Seongkuk Park

    흠.. 하지만 여전히 페이지가 새로고침 안 하고 결과 변경은 안되네요
    그리고 http://localhost:3000/posts/read/5 이렇게 고쳐주시면 더 좋을거 같아요.
    포트 번호 안 입력하니까 페이지가 안 뜨네요.ㅅ.

    • 포트 3001로 들어가보세요

      • Seongkuk Park

        포트 3001 들어가서 했는데 새로고침해야지 변한게 뜨네요ㅜ

  • BJ Kim

    예제 클론 받아서 돌려봤는데 3001 포트에서는 express route로 등록한 posts 로 들어가면 404 에러가 납니다.

    http://localhost:3001/posts/read/5

    GET http://localhost:3001/posts/read/bundle.js 404 (Not Found)

    • 우선 그 문제는 index.html 에 bundle.js 를 불러오는 부분에 아마 ‘./bundle.js’ 이렇게 상대경로를 넣으셨을꺼에요.
      ‘bundle.js’ 로 작성을 하셔야 다른 라우트에섣 잘 불러와질거에요.

      둘째로는.. 최근 변경된 코드가있어요.
      webpack.dev.config.js 에 proxy 설정하는 부분에

      proxy: {
      “**”: “http://localhost:3000”
      }

      이렇게 별이 두개로 변경되었습니다.
      webpack-dev-middleware 가 업그레이드 됐었어요.

  • finrir

    감사합니다.

  • lai on

    리액트 강좌 잘 듣고 있습니다. 조금 주제와 다를지도 모르겠지만 질문해도 될까요?
    React 없이 ExpressJS + webpack으로 개발용 boilderplate를 만들어보고 있습니다.
    index.js에 .scss를 require해서 쓰고 있고 .scss는 .css 파일들을 import하고 있습니다. 이 경우에는 css파일을 수정해도 HMR이 안되네요. 반드시 npm run build를 거쳐야 하는게 상당히 버거로운데 방법이 있을까요?

    • Webpack 의 로더가 제대로 설정이 되어있다면 scss 가 수정 될 때 잘 리로딩이 될거예요
      저의 예제 프로젝트 https://github.com/velopert/saysomething/tree/master/client 에서도 webpack dev server에서 hmr이 잘 되었습니다.

      아, 그리고 참고로 지금 이 게시글의 경우엔 서버파일에서 webpack-dev-server 도 불러와서 개발환경에선 express서버도 열고 개발서버도 열고 하는 방식으로 했는데

      제가 나중에는 결국 클라이언트와 서버 프로젝트를 아예 분리시키는게 낫다는 결론을 내렸습니다.

      • 될성부른나무

        velopert 님 강좌보며 공부하고 있습니다. 클라이언트와 서버프로젝트를 분리하셨다고 하셨는대 그 이유를 여쭈어 봐도 될까요?

        • 우선, 초반에는 이 강좌에서 했었던것처럼, 서버와 클라이언트를 한 패키지 안에서 사용을 했었는데요, 그 이유는 개발환경에서 명령어 한줄로 webpack dev server와 express를 실행하고싶어서였는데요,

          한번 create-react-app을 사용하여 만든 프로젝트에서 여기에서 했던것처럼 해보려고했더니 조금 번거롭더라구요, 그래서 우연히 아예 분리시켜서 진행을 해봤는데 편한점이 여럿 있더라구요.

          1. 클라이언트에서 사용하는 패키지와 서버에서 사용하는 패키지들을 구분 할 수 있다.

          package.json이 다르니, 그냥 바로 확인 할 수 있죠.

          2. 서버를 nodemon으로 실행하기 위해서
          한 파일에서 webpack dev서버와 express를 실행하게 한다면 express 부분이 수정되었을때 불필요한 클라이언트 사이드 코드도 다시 번들링 과정을 거치게됩니다.

          3. 기타… 설정과정이 매우 편하고 구조가 간단해짐. 설정 할 때 하나만 신경 쓰면 되니까 편했던것 같습니다.

          딱히 특별한 이유는 없고 한번 그렇게 해보니까 편했습니다..ㅎㅎ 개발할때는 터미널을 두개 열어서 작업해야하긴 하지만 그래도 괜찮아요.

  • tokata

    여러모로 많이 배워갑니다.
    + npm스크립트 작성할 때 cross-env모듈을 사용하면 윈도우에서 혼동이 좀 줄어들 것 같아요

    • 그것도 괜찮겠네요.
      그런데 이 게시물자체가 좀 outdated 되고있어서…

      시간날때 업데이트 해봐야겠습니다 ㅎㅎ

  • 윤인규

    안녕하세요 초보입니다~~ 궁금한게 있습니다. webpack으로 client쪽을 babel로 bundle.js로 만드는데 server side쪽도 webpack entry point를 다르게 해서하면 위에 babel server out-dir build && webpack 이부분을 webpack만으로도 가능하지 않나요??

  • 강좌 잘 보고있습니다 오타가 있어 제보드립니다

    cp webpack.config.js wepack.dev.config.js

    > cp webpack.config.js webpack.dev.config.js

  • 물외한인

    항상 강좌 잘 보고 있습니다ㅎㅎ
    아래에 게시글이 outdated 되간다는 말씀을 보고 여쭤봅니다. 지금 시점에서 expess로 리액트를 쓰는데 본문에 쓰여진 설명에서 개선되어야 하는 부분이 있을까요?

    • 그냥 제 기준에서 outdates 된것입니다. 더 편한 방법을 알게돼서요.

      1. 이 강좌에서는 Express와 React를 같은 프로젝트 디렉토리 안에서 사용했는데, 차라리 디렉토리를 분리시키는게 더 편하다는 결론을 내렸습니다.

      2. NPM스크립트를 작성할때 환경변수를 설정할때에 윈도우 커맨드와 bash에서 명령어가 다릅니다. cross-env로 처리해도 되지만 새로 script파일을 저장해서 .js 자체에서 환경변수를 설정하는게 저는 더 편한것같습니다.

      요즘은 바빠서 게시물 업데이트를 못하고있습니다. 시간이 좀 나면 전체적으로 업데이트를 좀 할 생각입니다.

  • 김현준

    이거 보면서 따라해봤는데.. 프록시가 제대로 안먹힌건지 몰라도 계속 Error occured while trying to proxy to: localhost:3001/ 라고 에러 나오네요

  • HAN JONG KO

    벨로퍼트님 블로그 글 항상 좋은것만 써주셔서 매우 감사하게 생각하고 있습니다.
    제가 일하는 곳에서, 핫로더를 적용 못 하고 있어서, 제가 맡아서 해결해보기로 했는데,
    이 가이드와, 핫로더 깃헙 튜토리얼과, 몇몇 이슈들 참고하여 진행했더니..
    (버전 1.3.1, webpackDevServer 5000포트, hapi 백엔드 3003포트)

    번들요청까지 프록시 되어 버리는 문제가 생겼습니다.
    파일 워치해서 웹팩 돌리는 것 까지는 잘 되는 것 같습니다.
    그런데, 노트 서버 로그에서는 3003번 포트로 요청이와서 404 날렸다는 로그가 뜨고,
    크롬 콘솔에서는 5000번으로 요청했는데, 404 받았다. 라고 뜹니다.
    프록시 규칙에 !*.hot-loader.json을 넣어주면 될 것 같았는데, 생각대로 안 됩니다.
    프록시 규칙 여러개 넣는법 혹시 아시는지 여쭈어봅니다..

    핫로더 자체도 문서가 되게 빈약하고,
    웹팩도 문서가 이리저리 퍼져있어서 찾기 되게 힘드네요.
    참고하신 링크도 여쭈어 봐도 될런지..

    다시한번, 블로그 운영해주셔서 감사합니다.
    군대 전역하고 일주일 만에 다시 일터에서 남들에게 도움 되는 만큼 일하게 된건
    이 블로그 힘이 컸기 때문이라 생각합니다.

    • 제가 포스트를 작성한 이후로, react-hot-loader 도 업데이트 됐구.. create-react-app 을 사용하는추세로 바뀌기도했고..해서 수정해야 할 글이 좀 있네요 ㅎㅎ

      일단, 프록시 규칙을 여러개 설정 할 필요는 없을거라고 생각해요. 만약에, 프록시 설정이 잘 되어있다면, bundle 파일은 우선순위로, webpack-dev-server 에 있는 번들파일을 보여주게 되거든요. 그래서, 제가 보기엔 설정 자체가 조금 잘못된것같은데..

      제가 추천드리고 싶은건, 우선 create-react-app 으로 프로젝트를 만들고, (이걸로 만들면 프록시 설정 할 때 package.json 에서 “proxy”: “http://localhost:4000” 를 넣어주기만 하면 돼요 (예: https://github.com/velopert/whotalk.us/blob/master/whotalk-frontend/package.json 하단)

      그리구 react-hot-loader 3 를 사용하시구… https://github.com/gaearon/react-hot-loader/blob/next/docs/README.md 를 참고해서 적용하세요.

      (참고로 babel 설정은 package.json 에서 할 수있어요)

      저는 요즘 create-react-app 으로 주로 작업하구, 리액트핫로더는 사용하고 있지 않기 떄문에 정확한 답변을 드리기가 힘드네요 ㅠ
      투두리스트에 넣어놓고 나중에 기회가 되면 포스트를 작성해보도록 하겠습니다.

      새해 복 많이 받으세요~

      • HAN JONG KO

        설정이 아예 잘못되었을 거란 얘기에, 의심가는 property 다시한번 하나하나 들여다보고, 문서 찾아내서(기어이) 해결하는데 성공했습니다.
        결론은, output.path devServer.contentBase, devServer.publicPath가 조화를 이루어야 하며, proxy또한 필요합니다.
        먼저 개발환경 셋팅하신분이, js파일만 webpack 돌릴거라 생각하시고 output.path를 static파일 서빙하는 디렉토리의 하위 디렉토리인 js 디렉토리로 지정하셨더군요. 이게 문제였습니다. 그래서 publicPath를 /js/로 맞춰주어야 했구요. 이렇게 다시 변경 되는 번들 파일을 줄 수 있게 되었으나, 핫로드는 되지 않았는데, 핫로드는 /[hash].hot-loader.json 요청과 /[id].[hash].hot-loader.js 요청을 처리해야 했는데, 이게 publicPath로 요청되어야 하는데, 이게 방금 /js/로 바꿨는데, react-hot-loader는 /로 알고 있죠. 핫로더 쪽에서 바꿔줄 방법은 보이지 않아, 프록시에 예외를 넣어줘야 했습니다.
        아래는 제가 해결한 방법입니다.(코드)

        
                proxy: {
                    "**": {
                        target: `http://localhost:${backEndport}`,
                        bypass: function (req) { // ,res, proxyOptions
                            if (/(.*)(.hot-update.js|json)$/.test(req.url)) {
                                return `/js${req.url}`;
                            }
                            return false; // 이 외는 (bypass 하지 않는다 === proxy 한다.)
                        }
                    }
                }
        

        프록시 규칙에 바로 url을 주지 않고 객체로 넘긴뒤, bypass callback에서 프록시 안할 request 잡아내고, 다른 url로 변경해서 던져 주면 되겠다 싶더라구요.

        결과는 성공, 때로는 그냥 말 한마디로도 큰 도움이 되는군요.

        새해 복 많이 받으세요!

        • 성공하셨다니 축하드립니다!
          저는 어제 핫로더 베타버전이랑 리액트 라우터랑 같이 사용을 하려니 라우터측에서 충돌하는 이슈가 있어서 한참 삽질했었는데..

          결국 경고메시지 무시하는 코드 작성하는걸로 마무리했습니다 ㅎㅎㅎ

          해외 개발자들도 그렇게 하더라구요 작동하는데엔 문제없어서

          화이팅 입니당~

    • 곧 제 사이드프로젝트를 진행하는 라이브 코딩 방송을 할껀데, (http://www.youtube.com/c/MinjunKim/live)
      한번 다뤄볼게요! 저도 궁금해서 한번 해보고싶네요. 라이브로 볼 수있으면 좋고… ㅋㅋㅋ 못보더라도 기록으로 남으니까 나중에 참조해보세요! (3편입니다)

      Firebase 를 사용할것이기에 프록시 설정은 하지 않지만.. 프록시 설정은 아래 덧글에서도 언급했다시피 설정만 간단하게 해주면 작동 잘하니까요!

  • 최홍석

    webpack 버전에 따라
    webpack.optimize.OccurenceOrderPlugin() is not a constructor 에러가 나신다면
    webpack.optimize.OccurrenceOrderPlugin() 이렇게 바꾸시고 사용하시면 되요
    이름이 바뀌었네요
    webpack 2.2.1, webpack-dev 2.4.1 기준입니다

  • Time Spot

    오타가 있네요.. http://localhost/hello 가 아니라 http://localhost:3000/hello .. 서버 사이드 코드 컴파일을 왜 하는지 모르겠지만..

  • Time Spot

    .. 웹팩에서 플러그인 명칭이 바뀌었다고 써있는거 같아요.. OccurenceOrderPlugin —> OccurrenceOrderPlugin

  • Elebit

    하나씩 보며 많은 정보를 얻고있습니다.
    현 시점에서 쭉 따라가도면 babel -> babel-loader로 react-hot -> react-hot-loader로 대신 쓰라는 오류가 뜨고,
    플러그인인 NoErrorsPlugin 도 파기되서 NoEmitOnErrorsPlugin로 대신 쓰라는 오류가 떠서 정보공유차원으로 댓글납깁니다

  • Johnny Koo

    velopert 님 강좌 잘 듣고 있습니다. 몇달 전에 velopert 님 다니시는 회사 대표님? 이 잠깐 학원에 오셔서 인사 드리면서 velopert 님 때문에 학원생들이 React 도움을 많이 받았다고 말씀드렸지요. ㅎㅎ

    다름이 아니라 이번 강좌의 보일러 플레잇을 받아서 사용중에 있는데요. npm script 3개를 한 번에 할 수 있는 방법은 없을까요?
    npm run clean
    npm run build
    npm run development
    를 현재는 하나씩 터미널에서 실행해서 하고 있거든요.. 그냥 & 로 연결해서 하니 error가 나더라구요. 아마도 build 가 끝나기 전에 run development를 하는 것 같아요 & 려 연결해서 3개의 명령어를 순차적으로 하는 것 같긴 한데.. error 가 안나게 하나가 끝나고 순차적으로 명령을 실행할 수 있는 방법이 있는지 궁금합니다.

    감사합니다.

    • Johnny Koo

      && 로 되네요! 이런, 생각보다 간단해서 놀랐습니다.

      npm run clean && npm run build && NODE_ENV=development node ./build/main.js

  • Johnny Koo

    velopert 님, 나중에 시간이 된다면 일전에 언급하신 client & server 쪽 package.json 을 분리하는 강좌를 간단하게나마 올려주실 수 있나요? 단순히 package.json을 분리하는 것은 알겠는데 위 HMR setup 과 연관되서는 잘 모르겠네요… 지금 velopert님 강좌 따라가서 개발하고 package.json script 를 node 에서 nodemon으로 바꿔서 쓰고 있는데 client 쪽 코드 를 save하면 nodemon이 쓸데없이 server를 재시동하고, server관련 코드를 save 하면 webpack 이 client 쪽까지 다시 compile을 해 버리네요…ㅜㅜ client 랑 server folder structure 를 분리해서 쓰고 있는데도 말이죠..