[React] - Wep App 개발에 적합한 환경만들기

Lee Jeong Min·2021년 12월 6일
1
post-thumbnail

Not Suitable Application Development

Web App 개발에 적합하지 않은 (현재 시점에서의) 웹 환경

현재 아무것도 없이 순수 React JS 컴포넌트들을 불러오게 된다면, 수많은 모듈 파일들을 다시 서버에 요청을 하여 받아오는 것이기 때문에 비효율적이다.

적합하지 않은 이유

  1. 브라우저 호환성

오늘 날 웹 브라우저 환경에서 정식으로 모듈을 사용할 수 있게 되었지만, 모든 브라우저에서 지원하는 것은 아님

참고: https://caniuse.com/es6-module

  1. 앱을 빌드하는 환경이 기본적으로 제공되지 않기 때문
  • 모듈 번들링
    웹 어플리케이션의 크기가 커짐에 따라 유지보수를 위해 모듈로써 파일을 관리하게 되는데, 이 과정에서 모듈 종속성의 올바른 순서를 추적하고 로드하는 번거로움이 커지게 된다. 이 모듈로된 파일들을 여러번 걸쳐서 요청하여 불러올 수 없기 때문에 파일을 한번에 번들링하여 갖고와야하는데, 웹 브라우저 환경은 모듈 번들링 기능을 제공하지 않는다.

  • 트리 쉐이킹
    사용되지 않는 코드 조각은 번들 크기를 불필요하게 키우므로 번들 과정에서 제거해야한다. 이는 최종 번들에서 사용되지 않을 코드를 제거하는 것을 목표로 하는데 올바르게 완료되면 번들 크기를 줄여 앱의 실행 속도를 향상시킬 수 있다. 그러나 웹 브라우저 환경은 트리 쉐이킹 기능을 제공하지 않는다.

  • 코드 분할
    번들링을 하여 모듈파일들을 한번에 묶을 수 있지만, 앱이 커지면 번들또한 커지기 때문에 이를 나누는것이 필요하다. 그래서 런타임 중에 나뉘어진 번들을 동적으로 불러와야하는데 웹 브라우저 환경은 코드 분할 기능을 제공하지 않는다.

  • 코드 최적화, 소스맵
    코드 최적화를 위해 공백 또는 긴 변수, 함수 이름 등을 모두 축소 또는 제거하여 파일의 크기를 줄여야 한다. 하지만 이 코드는 압축되어서 사람이 읽기 어려워 디버깅이 쉽지않아 코드를 추적 가능한 소스맵이 필요하다. 소스맵은 디코더처럼 작동하여 축소된 코드를 구문분석하는데, 소스맵에는 원본 파일의 행에 대한 참조도 포함되어 있어 버그를 추적하기에 용이하다. 하지만 웹 브라우저 환경은 코드 압축 및 소스맵 기능을 기본 제공하지 않는다.

https://reactjs.org/docs/optimizing-performance.html#gatsby-focus-wrapper

Web App 개발에 적합한 환경 만들기

손쉽게 환경 구성하는 방법(CRA 사용)

npx create-react-app 프로젝트이름 --template 템플릿이름

cra-template-typescript // 타입 스크립트 리액트 템플릿

참고: https://create-react-app.dev/

직접 환경 구성하는 방법(Webpack 직접 하나하나 설정)

새로운 폴더 생성 후

다음의 명령어로 package.json을 생성후, 개발환경에서 사용할 webpack, webpack-cli, webpack-dev-server를 설치한다.

yarn init

yarn add --dev webpack webpack-cli webpack-dev-server
{
  "private": true,
  "name": "config-manual-react-dev-env",
  "version": "1.0.0",
  "description": "React 앱 개발 환경 메뉴얼 구성",
  "scripts": {
    "start": "",
    "serve": "",
    "bundle": "",
    "watch": "",
    "build": "",
    "clear": ""
  },
  "keywords": [
    "react",
    "webpack",
    "babel",
    "sass",
    "postcss"
  ],
  "author": "hustle-dev",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.64.4",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  }
}
// package.json
"bundle": "webpack --target web",

다음과 같이 설정하였다면, 각 alias에 맞는 명령어들이 필요하다. bundle의 경우 기본 target이 web이라 webpack만 작성하여도 되지만 명시하기 위해 --target web을 작성한다.

https://webpack.js.org/configuration/target/#root

// package.json
"bundle": "webpack --target web --mode development",

모드를 devlopment로 설정하여 개발환경에서 사용함을 명시한다.

"watch": "yarn run bundle -- --watch",
"build": "webpack build --target web --mode production",

yarn run build로 실행시키면 배포용으로 되어 코드가 압축되어 있는 모습을 확인할 수 있다.

"start": "yarn run serve -- --open",
"serve": "webpack serve --port 3000 --static --target web --mode development",

serve에서는 바로 열리지 않지만 start명령어에서는 open 명령어를 뒤에 추가해주어 브라우저에서 바로 열리도록 만들어 준다.

webpack 구성 파일 만들기

빌드(production 용)할때 sourceMap은 필요가 없다. 따라서 dev 파일에는 source-map 속성을 주고 build에서는 devtool에 false를 줌

참고: https://webpack.js.org/configuration/devtool/#root

webpack/config.build.js

const buildConfig = {
  target: ['web'],
  mode: 'production',
  devtool: 'eval',
};

module.exports = buildConfig;

webpack/config.dev.js

const devConfig = {
  target: ['web'],
  mode: 'development',
  devtool: 'source-map',
};

module.exports = devConfig;

서버를 돌릴때는 --watch 옵션을 주지 않아도 webpack dev server로 인해 변경시 자동적으로 반영해준다.

"serve": "webpack serve --config webpack/config.server.js",

webpack/config.server.js

const serverConfig = {
  target: ['web'],
  mode: 'development',
  devtool: 'source-map',
  devServer: {
    port: 3000,
    static: ['dist'],
  },
};

module.exports = serverConfig;

참고: https://webpack.js.org/configuration/dev-server/#devserverstatic

정적 파일들을 사용하기 위해서는 static 옵션에 정적파일 사용을 위한 폴더를 명시해준다.

그러나 위와 같이 모든 config들을 일일히 작성한다면 중복이 많이 발생하여 비효율이 생긴다. 이를 위해 webpack-merge를 사용한다.

참고: https://www.npmjs.com/package/webpack-merge

React를 위한 패키지 설치

참고: https://reactjs.org/docs/create-a-new-react-app.html#recommended-toolchains

일반적으로 리액트 환경을 구성하기 위해 추천하는 방법은 Create React App을 사용하는 것이다. 그러나 현재는 웹팩을 사용하여 직접 구성하고 있기 때문에 다음과 같은 패키지들의 설치가 필요하다.

yarn add react react-dom

이후 index.js 파일을 만들어 다음과 같이 작성해준다.

index.js에

import React from 'react';
import ReactDOM from 'react-dom';


// App Component
function App(props) {
    return (
        // JSX
    )
}

// --------------현재 바벨이 없기때문에 JSX를 사용 X----------------------------------
  
import React, { createElement as h } from 'react';
import ReactDOM from 'react-dom';

// App Component
function App(props) {
  return h('div', { className: 'app' }, h('h1', null, props.greetingMessage));
}

// ReactDOM render
ReactDOM.render(h(App, { greetingMessage: '안녕! React 😀' }), document.getElementById('root'));

현재 바벨이 없기 때문에 JSX문법을 사용하지 못하기 때문에 React.creatElement로 코드를 작성해야 한다. 이후 구조 분해 할당으로 createElement as h로 꺼내와서 다음과 같이 코드를 작성한다.

prettier 설치

이후 프리티어를 위해 패키지를 설치한다.

yarn add --dev prettier

다음과 같이 .prettierrc 파일을 작성해준다.

module.exports = {
    // 화살표 함수 식 매개변수 () 생략 여부 (ex: (a) => a)
    arrowParens: 'always',
    // 닫는 괄호(>) 위치 설정
    // ex: <div
    //       id="unique-id"
    //       class="container"
    //     >
    bracketSameLine: false,
    // 객체 표기 괄호 사이 공백 추가 여부 (ex: { foo: bar })
    bracketSpacing: true,
    // 행폭 설정 (줄 길이가 설정 값보다 길어지면 자동 개행)
    printWidth: 80,
    // 산문 래핑 설정
    proseWrap: 'preserve',
    // 객체 속성 key 값에 인용 부호 사용 여부 (ex: { 'key': 'xkieo-xxxx' })
    quoteProps: 'as-needed',
    // 세미콜론(;) 사용 여부
    semi: true,
    // 싱글 인용 부호(') 사용 여부
    singleQuote: true,
    // 탭 너비 설정
    tabWidth: 2,
    // 객체 마지막 속성 선언 뒷 부분에 콤마 추가 여부
    trailingComma: 'es5',
    // 탭 사용 여부
    useTabs: false,
  };

eslint 설치

npx eslint --init

위 명령어를 사용하여 eslint설치와 .eslintrc.js 설정을 파일을 생성한다.

이후 eslint 규칙으로 prettier를 실행하고 차이를 개별 eslint 문제로 보고하기 위해 eslint-plugin-prettier를, JSX 요소의 접근성 규칙을 정적 추상구문 트리로 체크하기 위해 eslint-plugin-jsx-a11y를, 불필요하거나 prettier와 충돌할 수 있는 모든 규칙을 끄기 위해 eslint-config-prettier를 설치한다.

yarn add --dev eslint-plugin-prettier eslint-plugin-jsx-a11y eslint-config-prettier

또한 eslint-plugin-prettier의 추천 설정을 위해 다음과 같은 rules 들을 가져와서 설정해준다.

'prettier/prettier': 'error',
'arrow-body-style': 'off',
'prefer-arrow-callback': 'off',

바벨 설치

이제 JSX 문법과 최신문법 트랜스파일링을 위해 바벨을 설치한다.

yarn add --dev babel-loader @babel/{core,preset-env,preset-react}

참고: https://babeljs.io/docs/en/babel-preset-react#docsNav

react 개발에 babel 필요하여 preset-react와 webpack에서 babel-loader가 필요하기 때문에 설치!

.babelrc.js 설정

// common js 방식으로 통합
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
};

jsx 문법으로 바꾸고 실행하면 오류 --> webpack babel loader config 설정 필요!

config.dev.js

  // 모듈 > 규칙(들) > (개별) 로더 구성
  // (어떤 파일? 제외 항목? 어떤 로더 사용? 옵션은?)
  module: {
    rules: [
      //babel-loader 구성
      {
        test: /\.jsx?$/i,
        exclude: /(node_modules|dist)/,
        use: 'babel-loader',
        // 옵션은 이미 .babelrc에서 주었기 때문에 안해주어도 됨
      },
    ],
  },

참고: https://webpack.kr/loaders/babel-loader/

React에서 style 어트리뷰트 스타일링

참고: https://ko.reactjs.org/docs/dom-elements.html#style

위 사진과 같이 React Jsx에서 스타일링을 사용하는 경우, 문자열 대신 카멜 케이스 프로퍼티를 가진 JS 객체를 사용하는 것이 일반적이다. 또한 스타일에는 autoprefixer가 붙지 않는데, 이는 웹팩으로 직접 일일히 설정할 시, postcss plugin의 autoprefixer 같은 것들이 필요함을 알 수 있다.(CRA에서는 다해줌 https://create-react-app.dev/docs/post-processing-css/)

스타일링 예시

export const RandomCountUpApp = ({ count, isComplete }) => {
  const completeStyle = !isComplete
    ? null // 리액트 컴포넌트는 null이면 화면에 안나옴 (어트리뷰트도 마찬가지)
    : {
        animationName: 'none',
      };
  return (
    <div className="randomCountUp">
      <output style={completeStyle}>{count}</output>
    </div>
  );
};
profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글