리액트 개발자라면 애용하고 계실 도구인 create-react-app 의 v2 버전이 릴리즈되었습니다! 기존에 만든 자료들을 업데이트해야 한다는 점 (특히 책... 따흑... )은 조금 귀찮긴 하지만, 좋아하는 도구가 더 멋져졌으니까, 정말 듣기좋은 소식인데요!

이 포스트에서는 v2 에서 어떠한 변화가 적용되었는지 한번 살펴보도록 하겠습니다.

업데이트의 자세한 내용은 다음 링크에서 확인 할 수 있습니다.

설치 및 사용

사용방법은 딱히 바뀌지 않았습니다.

$ npx create-react-app what-is-new
$ cd what-is-new
$ yarn start

1. Sass, CSS Module 지원

Sass 와 CSS Module 을 yarn eject 하지 않고 사용 할 수 있습니다. 저처럼 Sass를 자주 쓰시는 편이라면 정말 반가운 소식이겠네요!

Sass

Sass 를 사용하기 위해선, 사전에 node-sass 를 설치하시면 됩니다.

$ yarn add node-sass

정말 되는지 한번 써볼까요?

src/MyComponent.js

import React from 'react';
import './MyComponent.scss';

const MyComponent = () => {
  return (
    <div className="MyComponent">
      <div className="something-inside">Hello CRA v2</div>
    </div>
  );
};

export default MyComponent;

src/MyComponent.scss

.MyComponent {
  background: black;
  color: white;
  padding: 1rem;
  .something-inside {
    background: white;
    color: black;
    font-size: 2rem;
    text-align: center;
    padding: 1rem;
  }
}

src/App.js

import React, { Component } from "react";
import MyComponent from "./MyComponent";

class App extends Component {
  render() {
    return (
      <div>
        <MyComponent />
      </div>
    );
  }
}

export default App;

image.png

오호, 그냥 하니까 되는군요! 👍

CSS Module

CSS Module 은 사용 방식이 이전과 조금 다릅니다. 파일을 생성하실 때 파일이름.module.css 이런식으로 하시면 CSS Module 이 적용됩니다.

한번 해볼까요?

src/AnotherComponent.module.css

.wrapper {
  background: gray;
  color: white;
  padding: 1rem;
  font-size: 2rem;
}

src/AnotherComponent.js

import React from 'react';
import styles from './AnotherComponent.module.css';

const AnotherComponent = () => {
  return <div className={styles.wrapper}>What about CSS Module?</div>;
};

export default AnotherComponent;

src/App.js

import React, { Component } from 'react';
import MyComponent from './MyComponent';
import AnotherComponent from './AnotherComponent';

class App extends Component {
  render() {
    return (
      <div>
        <MyComponent />
        <AnotherComponent />
      </div>
    );
  }
}

export default App;

image.png

2. Babel 7 업그레이드

Babel7 로 업그레이드 되면서 빌드속도가 개선된다고 합니다. 추가적으로, preset-typescript 를 통한타입스크립트 지원자동 Polyfill 등의 기능이 도입되었습니다.

리액트 개발자들이 주목해야 될 부분은 JSX Fragment <>...</> 문법을 이제 별도의 설정 커스터마이징 없이도 사용 할 수 있다는 점 입니다.

한번 써볼까요?

src/App.js

import React, { Component } from 'react';
import MyComponent from './MyComponent';
import AnotherComponent from './AnotherComponent';

class App extends Component {
  render() {
    return (
      <>
        <MyComponent />
        <AnotherComponent />
      </>
    );
  }
}

export default App;

3. Webpack v4 업그레이드

웹팩 v4 로 업그레이드되었다고 합니다. 이로 인하여 번들링 성능도 개선될거고, 본문에 의하면 JS 번들을 더 똑똑하게 분리시킨다고 합니다. yarn build 를 입력해보니

image.png

1.chunk.js 에는 자동으로 react, react-dom 관련 코드 들이 들어있고 main.js 쪽에 우리가 만는 컴포넌트들의 코드가 들어있습니다.

옛날에 CommonsChunkPlugin 를 사용해서 해야했던 작업들이 optimization.splitChunks 라는 API 를 통하여 자동으로 이뤄진다고 하네요.

추가적으로, 비동기 코드 스플리팅을 위하여, 옛날 문법인 require.ensure 를 기존에 사용하시는 분이 계시다면 이제 그 문법은 더 이상 사용하지 못하니 import() 로 바꾸셔야겠습니다!

4. SVG 를 리액트 컴포넌트화 하여 불러오기

SVG 파일을 불러올 떄 이전처럼 그냥 불러올 수 도 있지만:

import logo from './logo.svg';

이제 컴포넌트 형태로 변환하여 불러올 수 있다고 합니다.

import { ReactComponent as Logo } from './logo.svg';

위와 같이 ReactComponent 를 불러올 수 있네요!

한번 해볼까요?

import React, { Component } from 'react';
import MyComponent from './MyComponent';
import AnotherComponent from './AnotherComponent';
import { ReactComponent as Logo } from './logo.svg';

class App extends Component {
  render() {
    return (
      <>
        <MyComponent />
        <AnotherComponent />
        <Logo />
      </>
    );
  }
}

export default App;

디자이너한테 SVG 를 전달 받았을 때 보통 직접 컴포넌트화 하거나, img 태그로 불러왔었는데, 별도의 설정 없이 이렇게 불러올 수 있으니 편리하네요!

5. proxy 설정을 커스터마이징 가능

웹팩 갸벌 서버에서 백엔드 API 를 호출 할 때 발생 할 수 있는 CORS 관련 오류를 방지하기 위하여 웹팩 개발 서버에 proxy 를 설정 할 수 있는데요, 이는 이전부터 package.json 에서 proxy 필드를 설정해주면 됐었습니다. 지금도 똑같이 하시면 되는데요:

{
  "name": "what-is-new",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "node-sass": "^4.9.3",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-scripts": "2.0.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "proxy": "https://jsonplaceholder.typicode.com"
}

이제 이 설정을 커스터마이징 할 수 있게 되었습니다. 만약에 그 중간 단계에서 추가작업을 하고 싶다면, 이 proxy 는 지우고, http-proxy-middleware 라는 모듈을 설치 후 src 디렉토리에 setupProxy.js 라는 파일을 생성하세요.

$ yarn add http-proxy-middleware axios

src/setupProxy.js

const proxy = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    proxy('/posts', {
      target: 'http://jsonplaceholder.typicode.com/',
      changeOrigin: true
    })
  );
};

여기서 사용하는 app 은 Express 서버 인스턴스입니다. 그리고 proxy 쪽에 changeOrigin 의 경우엔 jsonplaceholder 에서 Origin 을 전달하지 않으면 제대로 요청이 처리가 되지 않기 때문에 넣어주었습니다. 만약에 여러분의 API 서버에서 Virtual Host (요청 받는 Domain 이름에 따라 다른 요청 처리하기) 를 사용하고 있다면 이 설정을 해주셔야 합니다.

이제 App.js 에서 axios 를 사용하여 웹 요청을 해볼까요?

$ yarn add axios

src/App.js

import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
  state = {
    data: null
  };
  getPost = async () => {
    try {
      const response = await axios.get('/posts/1');
      this.setState({
        data: response.data
      });
    } catch (e) {
      console.log(e);
    }
  };
  componentDidMount() {
    this.getPost();
  }
  render() {
    if (!this.state.data) {
      return <div>로딩중...</div>;
    }
    const { title, body } = this.state.data;

    return (
      <div>
        <h1>{title}</h1>
        <p>{body}</p>
      </div>
    );
  }
}

export default App;

image.png

6. Good bye, IE

CRA v2 부터는 Internet Explorer 를 기본적으로 아예 지원하지 않는다고합니다. 지원이 필요하다면 react-app-polyfill 을 사용하셔야합니다.

$ yarn add react-app-polyfill

그리고 src/index.js 에서 이 스크립트를 추가하세요.

import 'react-app-polyfill/ie9'; // For IE 9-11 support
import 'react-app-polyfill/ie11'; // For IE 11 support

이 polyfill 에 대한 자세한 내용은 여기에서 확인 하실 수 있습니다.

기타

이 외에도 물론 다른 변경사항들이 있지만 딱히 크게 와닿지는 않는 변경사항이라 상세설명은 생략했습니다. 더욱 자세히 알고 싶다면 CRA v2 공식 블로그 포스트 를 참고하세요.

Yarn Plug n Play

CRA v2 리뷰하면서 처음 본건데.. node_modules 를 더 이상 사용하지 않는 yarn 의 기능인가봅니다. 이걸 실험적으로 지원한다고 하네요. 아직 어떤 이점이 있는지 모르겠지만 만약 유의미하다고 느껴지면 나중에 벨로그에 포스트 작성해보겠습니다.

최신 Node target 으로 작성된 라이브러리 사용가능

ES5 형태로 번들링되지 않는 라이브러리, 예를들어 query-string 최신 버전은 ES6 로 작성되어 npm 에 등록되어 있는 상태인데, 이전 CRA 에선 이걸 사용하면 빌드할때 오류가 났었는데요, 이제는 오류 나지 않고 사용 가능하다고 하네요.

Service worker 를 직접 켜야함

기존에는 Service worker 가 기본적으로 적용되어있었는데 이제는 직접 켜줘야만 작동합니다. index.js 를 보시면

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

저기에 최하단에 있는 unregister -> register 로 변경하시면 됩니다.

그리고 이제는 이 서비스워커가 구글의 Workbox 기반이라고 하네요. 저는 아직 Workbox 랑 친하지 않아서 자세한건 잘 모르겠습니다..

정리

CRA v2 가 더욱 쓸만해진 것 같습니다. 아, 그리고 기존에 이미 CRA v1 로 프로젝트 작업 진행중이시고.. eject 하신 분들도 많을텐데요, 제 경험상 그런 경우는 CRA v2 로 프로젝트를 아예 새로 만드시고 거기에 기존 코드를 이동시켜서 오류나는 것들 수정하는 방식으로 진행하는게 가장 편할 것 입니다. 만약에 eject 안하셨다면 react-scripts 버전만 업그레이드 하시면 됩니다 :) (참고)