Next.js
 
⛏⛏⛏
현재 작성 중인 페이지입니다.
 

1. emotion 라이브러리 설치

 
디자인 시스템, UI 라이브러리 구현에 css-in-js를 사용하는 게 편리해 emotion을 사용하기로 결정했습니다.
 
위 링크의 .babelrcpackage.json 의 의존 모듈을 추가해 손쉽게 emotion을 추가할 수 있었습니다.
 
next.js의 기본 환경에서는 emotion을 제공하지 않습니다.
emotion 라이브러리와 emotion 환경을 위한 babel-plugin을 설치해줍니다.
// CLI

npm i @emotion/react @emotion/styled
npm i --save-dev @emotion/babel-plugin
 
위에서 설치한 babel-plugin을 사용하려면 nextjs의 default babel config를 변경해줘야합니다.
아래와 같이 babelrc 파일을 복붙했습니다.
// .babelrc
{
  "presets": [
    [
      "next/babel",
      {
        "preset-react": {
          "runtime": "automatic",
          "importSource": "@emotion/react"
        }
      }
    ]
  ],
  "plugins": ["@emotion/babel-plugin"]
}
 
 
babelrc 파일에 대한 추가 설명입니다. ( 좌측 토글탭을 클릭해주세요! )
 
만약 다음과 같은 개념이 익숙치 않으시다면 이 레포의 README.md를 살펴보시는 것을 강력히 추천드립니다.
  • ast
  • parser
  • transformation
 
최대한 알고 있는 내용과 새롭게 자료를 조사해 내용을 단순화해보려 했는데 아직 babel을 제대로 이해하지 못 해 설명이 부족한 것 같습니다. ( 추후에 이 레포를 공부해 수정해보겠습니다. )
 
babel은 parser를 통해 js파일을 파싱해 ast를 생성하고, 여러 plugin에 정의된 내용을 통해 ast를 transform해 새로운 ast를 생성하며 최종적으로 code를 generate합니다.
 
한 편, 페이스북팀은 js로 돔을 쉽게 생성하기 위해 jsx 문법을 개발했습니다.
 
babel의 기본 parser는 정해진 알고리즘에 의해 ast를 생성하는데요, 기본 알고리즘으로는 jsx 문법을 해석할 수 없습니다.
따라서 jsx 문법과 같은 새로운 문법들을 구현할 때, babel이 이를 이해할 수 있도록 문법 지침서(babel syntax plugins)를 함께 개발합니다. babel에서 syntax에 대한 plugin을 사용하게 되면 기본 parser가 지원하지 않는 문법을 이해하고 파싱해 ast를 생성할 수 있게 되는 것입니다.
이렇게 생성된 ast는 transform에 관여하는 plugin과 옵션에 의해 새로운 ast로 생성되고 이 final ast를 통해 code를 generate하게 됩니다.
 
next/babel presets는 위와 같은 여러 plugins 패키지들과 babel config options를 포함합니다.
이 기본 config에 emotion/babel-plugin를 plugin으로 추가하게 되면, 새롭게 설치한 @emotion/babel-plugin을 적용해 js파일을 컴파일하게 됩니다.
플러그인은 ast에서 필요한 부분에 대해서만 ast 노드를 traversing한다고 합니다.
또한 plugins이 적용되는 순서는 presets, plugins이 babelrc 파일에 작성된 순서라고 합니다.
 

2. ESLint

 

2.1 getting started 따라하기

 
eslint는 버그를 유발하는 코드 패턴을 방지하고 코드의 일관성을 유지하기 위한 linting 툴입니다.
 
parser를 이용해 AST를 생성한다는 점에서 babel과 유사하지만, babel은 어플리케이션 구동에 필요한 코드로 변환해주는 컴파일러 역할을 하는 반면, eslint는 생성된 AST를 이용해 코드의 타당성을 평가해 설정된 규칙에 어긋나는 코드 패턴들을 개발자에게 알려주는 linting 툴입니다.
 
공식 문서에서는 global하게 eslint를 설치하는 것을 권장하지 않아 글로벌로 설치된 패키지 모듈을 삭제해줬습니다. ( 프로젝트별로 설정하게되는 config가 달라지기 떄문에 매번 로컬 설치하는 것을 권장하는 것 같습니다. )
 
글로벌로 설치된 패키지 확인 후
npm list -g --depth=0
아래 명렁어로 글로벌로 설치된 패키지를 삭제해줬습니다.
npm uninstall -g eslint
 
이제 설치해보겠습니다.
 
먼저 eslint 모듈을 추가하고
npm i eslint --save-dev
 
eslint의 config 파일을 생성하기 위해 아래 코드로 eslint의 init api를 실행합니다.
// 아래 명령어는 package.json 파일이 생성된 경우에만 동작한다고 합니다.
// nextjs 프로젝트를 create할 때 package.json이 생성되기 때문에 이대로 입력해줍니다.

npx eslint --init
 
--init flag로 eslint의 init api를 실행하게 되면, 몇 가지 기본적인 eslint 옵션들을 cli 창에서 쉽게 설정할 수 있고 설정 옵션에 따라 config 파일까지 생성해줍니다.
 
아래 option으로 설정했더니
to check syntax and find problems
js modules (import/export)
React
Typescript : no
Browser
config file format : json
eslint-plugin-react@latest : yes
 
아래와 같은 .eslintrc.json 파일이 생성됐습니다.
cli에서 입력한 option을 반영해 json파일에 코드가 생성된 것 같습니다. ( 세부적인 내용들은 이후에 docs를 천천히 살펴보려합니다. )
{
  "env": {
    "browser": true, // browser
    "es2021": true // js modules (import/export)
  },
  "extends": ["eslint:recommended", "plugin:react/recommended"], // eslint, react에서 적용하길 추천하는 기본 규칙들
  "parserOptions": { // React
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react"], // React
  "rules": {}
}
 
여기까지 진행이 완료됐다면 아마 여러 에러가 발생할 것입니다. ( 우선은 무시해주세요! 후술하겠습니다. )
 
eslint가 파싱/AST 생성/린팅할 때 필요한 환경 정보가 .eslintrc.json 에 추가되었으니, 린팅에 반영할 규칙들을 "rules"에 설정해주면 됩니다.
// "규칙명" : ["에러 레벨", "옵션"] 형태로 작성

"rules": {
        "semi": ["error", "always"],
        "quotes": ["error", "double"]
}
 
위와 같이 config 파일에 사용할 규칙들을 정의할 수 있고,
파일 단위로 적용할 규칙이 있는 경우라면 아래와 같이 주석 처리해주면 된다고 합니다.
// someFile.js

/* eslint eqeqeq: "off", curly: "error" */
or
/* eslint eqeqeq: 0, curly: 2 */
 
이상하게도 rule을 작성하기 전부터 여러 에러가 발생하고, 작성하지 않은 rule에 대해서도 에러가 발생하기도 하는데요.
eslint의 init api에 의해 생성된 .eslintrc.json 파일에 extends 옵션이 추가되어있기 떄문입니다.
('2.2 extends와 plugins 옵션의 차이'에서 더 자세하게 정리해봤습니다.)
// extends에 적용되는 각각의 옵션(config file)은
// 옵션에서 정의한 규칙들을 포함합니다.
{
  "extends": ["eslint:recommended", "plugin:react/recommended"],
}
 
아래 코드는 docs에서 제공하는 rules 작성 예시입니다.
extends에 추가한 플러그인에 포함되지 않은 새로운 규칙은 추가되고, 중복되는 규칙은 override되며, 원하는 규칙은 disable할 수 있다고 합니다.
  // ex

	"extends": "eslint:recommended",
  "rules": {
      // enable additional rules
      "indent": ["error", 4],
      "linebreak-style": ["error", "unix"],
      "quotes": ["error", "double"],
      "semi": ["error", "always"],

      // override configuration set by extending "eslint:recommended"
      "no-empty": "warn",
      "no-cond-assign": ["error", "always"],

      // disable rules from base configurations
       "for-direction": "off",
	  }
 

2.2 "extends""plugins" 옵션의 차이 ?

 
두 옵션이 구분되지 않아 이 링크를 참고해 정리해봤습니다.
 
eslint에서 여러 린팅 규칙들을 제공하지만, 여러 개발 환경에 따라( ex) "우린 react를 사용할거야!" ) 코드 작성 시 필요한 린팅 규칙들이 끊임없이 생겨날텐데요. 이러한 확장성을 위해 eslint에서는 "plugins" 옵션을 제공해줍니다.
 
즉, eslint config에 plugin을 추가하면 eslint 기능이 확장되는 셈입니다.
아래와 같이 eslint-plugin-react를 설치하고 사용할 plugin을 추가하면, eslint 기능이 확장되어 eslint의 기본 규칙뿐만 아니라 리엑트 관련 규칙들을 린팅할 수 있게 됩니다.
// package.json
eslint-plugin-react : ...

// .eslintrc.json
"plugins": [
	"react"
] 
 
한편, "extends" 옵션은 여러 규칙들이 정의되어 있는 config file을 공유할 수 있도록 도와줍니다.
 
아래 예시는 eslint-plugin-react 플러그인을 통해 eslint 규칙을 확장시키고,
eslint의 recommended config file과 eslint-plugin-react의 recommended config file을 적용하는 설정입니다. 해당 config file에는 여러 추천 규칙들이 사용되기 떄문에, 별도의 rule을 추가하지 않더라도 여러 규칙에 대해 linting하게 됩니다.
//package.json
eslint-plugin-react


// .eslintrc.json
"plugins": [
  "react"
],
"extends": [
    "eslint:recommended",
    "plugin:react/recommended"
],
만약 위와 같이 여러 config file을 사용할 경우 앞에 위치한 config file이 base가 되어 recursive하게 merge해 result config를 생성하고 이를 사용하게 된다고 합니다.
 

2.3 eslint-config-airbnb 추가하기

 
eslint에서 유명한 config인 airbnb config를 추가해보겠습니다.
 
유능한 개발자분들이 다수의 프로젝트를 경험하며 정의한 config이기에 최대한 이를 따르고, 꼭 수정이 필요한 규칙들은 근거를 바탕으로 수정해나갈 계획입니다.
 
먼저 패키지를 설치하기에 앞서, github README.md를 훑어봅니다.
 
airbnb-config는 ECMAScript 6+와 React에서 사용되는 문법들에 대한 규칙들을 포함한다고 합니다.
즉, airbnb 팀에서는 eslint의 기본 기능에서 plugins로 기능을 확장시킨 후, 확장된 규칙들을 포함하는 config file을 작성해 프로젝트를 진행 중이고 eslint-config-airbnb라는 오픈 소스로 config file을 공유하고 있는 것입니다.
 
만약 제가 eslint-config-airbnb 패키지만을 설치하고 아래와 같이 config file을 작성하게 된다면 config file을 extend해 airbnb 팀의 config를 merge하게 되지만, 이 config에 필요한 여러 플러그인들이 설치되지 않았기 때문에 에러가 발생할 것입니다.
// eslint-config-airbnb는 총 3개의 config files를 export한다고 합니다.
// 아래 예시에서는 3개의 파일 중 메인 config와 hooks config을 사용합니다.

extends: ["airbnb", "airbnb/hooks"]
 
따라서 README.md에서는 아래 플러그인들을 설치할 것을 안내해줍니다.
  • eslint-plugin-import : es6+의 import/export 구문 지원
  • eslint-plugin-react
  • eslint-plugin-react-hooks
  • eslint-plugin-jsx-a11y
 
아래 코드를 입력할 경우 최신 버전의 eslint-config-airbnb 패키지를 사용하기에 필요한 여러 peerDependencies를 알려줍니다.
npm info "eslint-config-airbnb@latest"

// 이렇게 출력됩니다.
{
  eslint: '^5.16.0 || ^6.8.0 || ^7.2.0',
  'eslint-plugin-import': '^2.22.1',
  'eslint-plugin-jsx-a11y': '^6.4.1',
  'eslint-plugin-react': '^7.21.5',
  'eslint-plugin-react-hooks': '^4 || ^3 || ^2.3.0 || ^1.7.0'
}
 
npm 5+ 버전 이상일 경우 아래 명령어를 사용하면 된다고 합니다.
npx install-peerdeps --dev eslint-config-airbnb
 
제 npm 버전을 확인해보겠습니다.
npm -v  // 6.14.8
 
앞자리가 6입니다.
npx install-peerdeps --dev eslint-config-airbnb
 
.eslintrc.json을 수정해보겠습니다.
예상대로라면 config의 모든 내용을 지우고 extends만 작성해도 될 것 같습니다.
{
  "extends": ["airbnb", "airbnb/hooks"]
}
 
이 세 줄을 작성하기 위해 정말 오랜 시간이 걸렸습니다..
현타가 쎄게 오긴 하지만..
 
에러가 잘 보입니다!
 
 

2.4 발생한 몇몇 에러 fix 해보기

 
프로젝트를 진행하며 규칙을 수정하게 되겠지만, 우선 보이는 에러를 처리해보며 eslint 사용법을 익혀보겠습니다.
 
2.4.1 linebreak-style
 
error 1 : Expected linebreaks to be 'LF' but found 'CRLF'.eslintlinebreak-style
 
코드에디터/버전컨트롤시스템/운영체제마다 line break를 처리하는 방식이 달라 이를 일관성 있게 유지하기 위한 옵션입니다.
 
린팅을 적용하니 새롭게 알게되는 실무적인 지식들이 차곡차곡 쌓일 것만 같습니다.
 
git에서는 이를 자동적으로 처리해준다는 내용이 있습니다.
Many versioning systems (like git and subversion) can automatically ensure the correct ending. However to cover all contingencies, you can activate this rule.
 
모두 git을 사용하니 사용할 필요가 없지만, 우선 error를 처리해보겠습니다.
 
quik fix를 이용해 disable시켰봤습니다.
 
for this line : // eslint-disable-next-line linebreak-style 주석이,
for the entire file : /* eslint-disable linebreak-style */ 주석이 위치한 파일에 작성됩니다.
fix all auto-fixable problems : 관련된 모든 안티 패턴을 best practice 코드로 바꿔주는 것 같은데요. line break의 경우, 에디터상에서 코드를 바꿔 해결할 수 없기 떄문에 아무런 변화가 없는 것 같습니다.
 
vscode 하단 탭을 이용해 config를 수정하면 에러가 해결됩니다.
 
지금까지 에러를 어떻게 처리할 수 있는지 테스트하기 위해 여러 방법을 시도해봤습니다.
 
이제 이 불필요한 rule을 제거해보겠습니다.
규칙명은 linebreak-style입니다.
 
https://eslint.org/docs/user-guide/configuring/rules#configuring-rules를 참고해 아래처럼 rules를 작성하면 off되는 것을 확인할 수 있습니다.

{
  "extends": ["airbnb", "airbnb/hooks"],
  "rules": {
    "linebreak-style": 0
  }
}
 
깃에서 이를 자동적으로 처리해주는 것을 확인하기 위해 github을 찾아봤습니다.
로컬에서 설정하는 방법과 레포별로 end of line(eol)을 처리해주는 방법이 있습니다.
 
  1. 로컬 환경 : git global config의 core.autocrlf 옵션이 커밋되는 코드를 unix style로 일치시킨다고 합니다.
    1. 이는 git config core.autocrlf true 로 설정할 수 있습니다. (아마 true값이 default일 것 같습니다. default는 false라고 합니다. ) config 관련 링크입니다( 여기에선 default 값을 찾지 못 했습니다. )
       
      만약 window를 사용하는 제가 이를 false 로 설정해두고 커밋한 내용을, macOS를 사용하는 팀원이 수정해 커밋하는 상황이 발상핸다면 linebreak에 의한 충돌이 발생할 것 같습니다.
       
2. repo별 설정
 
위와 같은 문제를 방지하기 위해 repo에 .gitattributes 파일을 작성했습니다. github에서 제공해주는 example입니다. option 또한 설명해줍니다.
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text

# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf

# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
 
2.4.2 react-in-jsx-scope
 
React 17 이상의 버전에서는 Reactimport하지 않아도됩니다.
 
 
이전 버전의 jsx는 다음의 두 가지 문제가 있었다고 합니다.
  • jsx 문법이 babel에 의해 React.createElement 함수로 변형되었기 때문에 jsx 문법 사용 시 반드시 React를 import해줘야 한다는 번거로움
  • React.createElement 함수의 성능상 한계점
 
현재 버전에서는 jsx를 아래와 같이 처리합니다.
jsx가 babel에 의해 변환될 때 jsx-runtime을 import하는 코드라인을 생성하고, jsx_runtime의 jsx 함수를 이용해 react element를 생성하는 것 같습니다.
function App() {
  return <h1>Hello World</h1>;
}
This is what the new JSX transform compiles it to:

// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}
 
2개의 에러를 처리해봤는데요. 이처럼 eslint의 rule이 왜 적용되는지를 찾아보며 진행하는 프로젝트에 필요하지 않는 규칙들은 off시키고, 근거를 남겨 문서 또는 주석으로 남겨두면 될 것 같습니다.
 
 
 
to do reference