|
19 | 19 | # 08. 좋은 리액트 코드 작성을 위한 환경 구축하기
|
20 | 20 | ## 8.1 ESLint를 활용한 정적 코드 분석
|
21 | 21 | ### 8.1.1 ESLint 살펴보기
|
| 22 | +#### ESLint는 어떻게 코드를 분석할까? |
| 23 | +1. 자바스크립트 코드를 문자열로 읽는다 |
| 24 | +2. 자바스크립트 코드를 분석할 수 있는 파서parser로 코드를 구조화한다 |
| 25 | +3. 2번에서 구조화한 트리를 AST(Abstract Syntax Tree)라 하며, 이 구조화된 트리를 기준으로 각종 규칙과 대조한다 |
| 26 | +4. 규칙과 대조했을 때 이를 위반한 코드를 알리거나(report) 수정한다(fix) |
| 27 | + |
| 28 | +자바스크립트 분석 파서 espree를 기본으로 사용 |
| 29 | + |
| 30 | +타입스크립트의 경우 @typescript-eslint/typecript-estree espree 기반 파서 |
| 31 | + |
| 32 | +ESLint 규칙과 plugins(특정한 규칙의 모음) |
22 | 33 | ### 8.1.2 eslint-plugin과 eslint-config
|
| 34 | +#### eslint-plugin |
| 35 | +eslint-plugin 접두사로 시작하는 플러그인은 특정 프레임워크나 도메인과 관련된 규칙을 모아놓은 패키지 |
| 36 | +- eslint-plugin-import: 자바스크립트에서 다른 모듈을 불러오는 import와 관련된 다양한 규칙을 제공 |
| 37 | +- eslint-plugin-react: 리액트 관련 규칙을 제공 ex. JSX 배열에 키를 선언하지 않아 경고(react/jsx-key) |
| 38 | + |
| 39 | +코드 정적 분석 도구라서 key가 유니크한 값인지 확인은 못하지만, 존재 여부를 확인할 수 있음 |
| 40 | + |
| 41 | +#### eslint-config |
| 42 | +eslint-plugin을 한데 묶어서 완벽하게 한 세트로 제공하는 패키지 |
| 43 | + |
| 44 | +- eslint-config-airbnb |
| 45 | +- @titicaca/triple-config-kit |
| 46 | +- eslint-config-next |
| 47 | + |
| 48 | + |
23 | 49 | ### 8.1.3 나만의 ESLint 규칙 만들기
|
| 50 | +#### 이미 존재하는 규칙을 커스터마이징해서 적용하기: import React를 제거하기 위한 ESLint 규칙 만들기 |
| 51 | +``` |
| 52 | +// .eslintrc.js |
| 53 | +module.exports = { |
| 54 | + rules: { |
| 55 | + 'no-restricted-imports: [ |
| 56 | + 'error', |
| 57 | + { |
| 58 | + paths: [ |
| 59 | + { |
| 60 | + name: 'react', // 모듈명 |
| 61 | + importNames: ['default'], // 모듈의 이름 |
| 62 | + message: |
| 63 | + "import React from 'react'는 react 17부터 더 이상 필요하지 않습니다. 필요한 것만 react로부터 import해서 사용해 주세요.", |
| 64 | + }, |
| 65 | + ], |
| 66 | + }, |
| 67 | + ], |
| 68 | + }, |
| 69 | +} |
| 70 | +] |
| 71 | +] |
| 72 | +``` |
| 73 | +#### 완전히 새로운 규칙 만들기: new Date를 금지시키는 규칙 |
| 74 | +``` |
| 75 | +/** |
| 76 | +* @type {import('eslint').Rule.RuleModule} |
| 77 | +*/ |
| 78 | +module.exports = { |
| 79 | + meta: { |
| 80 | + type: 'suggestion', |
| 81 | + docs: { |
| 82 | + description: 'disallow use of the new Date()', |
| 83 | + recommended: false, |
| 84 | + }, |
| 85 | + fixable: 'code', |
| 86 | + schema: [], |
| 87 | + messages: { |
| 88 | + message:'new Date()는 클라이언트에서 실행 시 해당 기기의 시간에 의존적이라 정확하지 않습니다. 현재시간이 필요하다면 ServerDate()를 사용해 주세요.', |
| 89 | + }, |
| 90 | + }, |
| 91 | + create: function (context) { |
| 92 | + return { |
| 93 | + NewExpression: function (node) { // new 생성자가 실행될 때 ESLint 실행 |
| 94 | + if (node.callee.name === 'Date' && node.arguments.length === 0) { |
| 95 | + context.report({ |
| 96 | + node: node, |
| 97 | + messageId: 'message', |
| 98 | + fix: function (fixer) { |
| 99 | + return fixer.replaceText(node, ServerDate()') |
| 100 | + }. |
| 101 | + }), |
| 102 | + } |
| 103 | + }, |
| 104 | + } |
| 105 | + }, |
| 106 | +} |
| 107 | +``` |
24 | 108 | ### 8.1.4 주의할 점
|
| 109 | +#### Prettier와의 충돌 |
| 110 | +Prettier는 줄바꿈, 들여쓰기, 작은따옴표와 큰따옴표등 코드의 포매팅을 도와주는 도구 |
| 111 | + |
| 112 | +HTML, CSS, 마크다운, JSON 등 다양한 언어에서 적용 가능 |
| 113 | + |
| 114 | +ESLint는 코드의 잠재적인 문제가 될 수 있는 부분을 분석 |
| 115 | + |
| 116 | +자바스크립트에서만 작동 |
| 117 | + |
| 118 | +Prettier와 ESLint가 서로 충돌하지 않게 규칙을 선언해야함 |
| 119 | + |
| 120 | +자바스크립트나 타입스크립트는 ESLint에, 그 외는 Prettier에 맡겨 관여 파일을 분리 + eslint-plugin-prettier를 통해 Prettier 규칙 적용 |
| 121 | + |
| 122 | +#### 규칙에 대한 예외 처리, 그리고 react-hooks/no-exhaustive-deps |
| 123 | +일부 코드에서 특정 규칙을 임시로 제외시키고 싶다면 eslint-disable- 주석을 사용 |
| 124 | +``` |
| 125 | +// 특정 줄 제외 |
| 126 | +console.log('hello world'); // eslint-disable-line no-console |
| 127 | +
|
| 128 | +// 다음 줄 제외 |
| 129 | +// eslint-disable-next-line no-console |
| 130 | +console.log('hello world'); |
| 131 | +
|
| 132 | +// 특정 여러 줄 제외 |
| 133 | +/* eslint-disable no-console */ |
| 134 | +console.log('hello'); |
| 135 | +console.log('world'); |
| 136 | +/* eslint-disable no-console */ |
| 137 | +
|
| 138 | +// 파일 전체에서 제외 |
| 139 | +/* eslint-disable no-console */ |
| 140 | +console.log('hello'); |
| 141 | +``` |
| 142 | +- eslint-disable-line no-exhaustive-deps: 의존 배열이 필요한 훅에 의존성 배열을 제대로 선언했는지 확인하는 역할 |
| 143 | +- typescript-eslint/no-explicit-any |
| 144 | + |
| 145 | +#### ESLint 버전 충돌 |
| 146 | +ESLint의 의존성은 peerDependencies로 명시하도록 권장 |
| 147 | + |
25 | 148 | ### 8.1.5 정리
|
| 149 | + |
26 | 150 | ## 8.2 리액트 팀이 권장하는 리액트 테스트 라이브러리
|
| 151 | +백엔드의 경우 서버나 데이터베이스에서 원하는 데이터를 올바르게 가져올수 있는지, 데이터 수정 간 교차 상태나 경쟁 상태가 발생하지는 않는지, 데이터 손실은 없는지, 특정 상황에서 장애가 발생하지 않는지 등을 테스트 |
| 152 | + |
| 153 | +화이트박스 테스트로, 작성한 코드가 의도대로 작동하는지 확인해야하며, AUI에서 주로 수행 |
| 154 | + |
| 155 | +프론트엔드는 일반적인 사용자와 동일하거나 유사한 환경에서 사용자가 수행할 주요 비즈니스 로직이나 모든 경우의 수를 고려해야함 |
| 156 | + |
| 157 | +블랙박스 형태로, 코드가 어떻게 됐든 상관없이 의도한 대로 작동하는지를 확인해야함 |
| 158 | + |
| 159 | +시나리오가 어느 정도 정해져 있는 백엔드와 달리 사용자에게 완전히 노출된 영역이므로 어떻게 작동할지 최대한 예측해서 확인해야함 |
27 | 160 | ### 8.2.1 React Testing Library란?
|
| 161 | +DOM Testing Library와 리액트를 기반으로 한 테스트를 수행하기 위해 만들어진 테스팅 라이브러리 |
| 162 | + |
| 163 | +DOM Testing Library는 Node.js와 같이 HTML이 없는 자바스크립트만 존재하는 환경에서 HTML과 DOM을 사용할 수 있도록 해주는 jsdom 라이브러리를 기반으로 만들어짐 |
| 164 | +``` |
| 165 | +const jsdom = require('jsdom'); |
| 166 | +
|
| 167 | +const { JSDOM } = jsdom; |
| 168 | +const dom = new JSDOM('<!DOCTYPE html><p>Hello World</p>') |
| 169 | +
|
| 170 | +console.log(dom.window.document.querySelector('p').textContent) // "Hello World" |
| 171 | +``` |
28 | 172 | ### 8.2.2 자바스크립트 테스트의 기초
|
| 173 | +테스트 코드: 작성한 코드가 코드를 작성했던 당시의 의도와 목적에 맞는지 확인하는 코드 |
| 174 | +1. 테스트할 함수나 모듈을 선정한다 |
| 175 | +2. 함수나 모듈이 반환하길 기대하는 값을 적는다 |
| 176 | +3. 함수나 모듈의 실제 반환 값을 적는다 |
| 177 | +4. 3번의 기대에 따라 2번의 결과가 일치하는지 확인한다 |
| 178 | +5. 기대하는 결과를 반환한다면 테스트는 성공이며, 만약 기대와 다른 결과를 반환하면 에러를 던진다 |
29 | 179 | ### 8.2.3 리액트 컴포넌트 테스트 코드 작성하기
|
30 | 180 | ### 8.2.4 사용자 정의 훅 테스트하기
|
31 | 181 | ### 8.2.5 테스트를 작성하기에 앞서 고려해야 할 점
|
|
0 commit comments