ts react sass eslint prettier

  • react 시작할 때 편하게 하려고 만듦
  • 내가 사용하려고 만들어둔 템플릿
  • react + ts + eslint(airbnb) + prettier + sass
  • 필요 없는 테스트 코드 삭제 및 favicon 설정해두었다
  • 리액트로 만들 새로운 앱 초기 설정하는 김에 템플릿으로 만들었다

설정한 순서

1
2
3
4
5
6
7
8
9
10
npx create-react-app . --template typescript

// eslint, prettier
npx install-peerdeps --dev eslint-config-airbnb
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

// sass(scss)
yarn add node-sass
yarn add prettier-plugin-style-order
  • 필요 패키지 설치
package.json
1
2
3
4
5
6
{
// ...
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"eslint": "7.2.0", // <-- 삭제
  • react-scripts에서 "eslint": "^7.11.0"를 사용한다고 수동으로 eslint를 설치하지 말라고 한다
1
yarn
  • package.json 에서 eslint를 삭제하고 yarn으로 종속성을 다시 설치한다
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
// ...
"eslintConfig": {
"plugins": [
"react",
"@typescript-eslint"
],
"extends": [
"airbnb",
"airbnb/hooks",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/react",
"plugin:prettier/recommended"
],
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"import/no-unresolved": "off",
"import/extensions": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"no-use-before-define": "off",
"react/jsx-filename-extension": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto",
"singleQuote": true,
"tabWidth": 2
}
]
}
},
  • package.json 에서 eslint 관련 설정을 해준다
.vscode/settings.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"typescript.tsdk": "node_modules\\typescript\\lib",

// 프리티어 확장도구로 인한 자동 포맷팅 비활성화
"editor.formatOnSave": false,

// 파일 저장시 자동으로 lint 및 포맷팅
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},

// css파일에서 저장시 자동으로 포맷팅
"[scss]": {
"editor.formatOnSave": true
}
}
  • settings.json 을 설정한다

react에서 Map을 state로 사용할 때

맞는 예
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const [state, setState] = React.useState(new Map());

const add = (key, value) => {
setState((prev) => new Map([...prev, [key, value]]));
};


const upsert = (key, value) => {
setState((prev) => new Map(prev).set(key, value));
}

const delete = (key) => {
setState((prev) => {
const newState = new Map(prev);
newState.delete(key);
return newState;
});
}

const clear = () => {
setState((prev) => new Map(prev.clear()));
}
틀린 예
1
2
3
4
5
6
7
8
const [state, setState] = React.useState(new Map());

const add = (key, value) => {
state.set(key, value);
setState(state);
};

// ...
  • 원시 타입이 아닌 객체를 state로 다룰 때 실수하기 쉽다
  • 완전히 새로운 레퍼런스?를 넣어줘야 상태가 변경되었다고 판단하고 리렌더링 된다

배열

array-state-example.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// work
const add = (value) => {
setState((prev) => {
return [...prev, value];
});
};

// not work
const add = (value) => {
setState((prev) => {
prev.push(value);
return prev;
});
};

참고

react로 만들어본 퇴근시간 계산기

기능

메인 페이지

  • 목표 시각까지 남은 시간 계산
  • url param을 통해 목표 시각 세팅
  • 목표 시각이 지나면 퇴근하라는 메시지를 뛰움

about 페이지

  • 링크 공유 기능
  • 퇴근 시간 설정 기능
  • 자동 클립보드 복사

깃허브 페이지 spa 세팅

배경이미지 출처

해결한 문제들

gsap 적용안됨

Share.tsx
1
2
3
4
5
import { Power3, TimelineLite } from 'gsap';
import CSSPlugin from 'gsap/CSSPlugin'; // 추가

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const C = CSSPlugin; // 추가
  • 로컬에서는 애니메이션이 잘 실행되었는데, 빌드 후 애니메이션이 안 나오는 문제 해결

후기

  • 어느 정도 리액트에 익숙해진 것 같다
  • 주로 사용하는 패턴이 생긴 것 같다
  • 엄청 간단해 보이지만 이것저것 신경 쓴 것 같은데, 기록을 안 해놨다 ㅠㅠ
  • 나는 코드에 조금의 변화가 생겨도 커밋을 했었는데,,
  • 이번에는 커밋을 안 하고 한 번에 몰아서 마지막에 파일별로 커밋을 했다
  • 그래서 해결한 문제들이 기억이 잘 안 난다.. ㅠㅠ

신경 썼던 것들

input element

  • about 페이지에서는 링크를 생성하는 부분이 있다
  • 처음에는 input width가 좁았었다. 그래서 고민했던 것이…
  • input 넓이보다 안의 내용이 더 길면은 끝부분이 감춰진다

  • input에서 포커스를 해제하면 위 그림처럼,
  • 계속 앞쪽으로 포커스가 자동으로 이동했다

  • 나는 input 내용의 가장 뒤쪽,
  • url param이 변하는 것에 포커스가 가도록 만들려고 이것저것 해봤는데…
  • 결국엔 그냥 input width를 늘려버리고 끝을 냈다…

github pages SPA

  • 깃허브 페이지는 기본적으로 SPA를 지원하지 않는다
  • react-router-dom 으로 여러 경로들을 만들어 두면 index 말고는 404 페이지로 리디렉트 된다
  • 그래서 찾아보니까,,.!
404.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var segmentCount = 1;

var l = window.location;
l.replace(
l.protocol +
'//' +
l.hostname +
(l.port ? ':' + l.port : '') +
l.pathname
.split('/')
.slice(0, 1 + segmentCount)
.join('/') +
'/?p=/' +
l.pathname
.slice(1)
.split('/')
.slice(segmentCount)
.join('/')
.replace(/&/g, '~and~') +
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function (l) {
if (l.search) {
var q = {};
l.search
.slice(1)
.split('&')
.forEach(function (v) {
var a = v.split('=');
q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
});
if (q.p !== undefined) {
window.history.replaceState(
null,
null,
l.pathname.slice(0, -1) + (q.p || '') + (q.q ? '?' + q.q : '') + l.hash
);
}
}
})(window.location);
  • 원리는 index 경로 외에 나머지 경로로 들어오면 404페이지를 반환하는데,
  • 커스텀 404페이지에서 스크립트를 통해서 index페이지로 리디렉트 하게 한다
  • 이때, url params 및 query string을 가공한다
  • index.html에서는 404.html로부터 넘겨받은 값으로 라우팅이 적용된 화면을 보여준다
  • 이걸 만든 사람은 정말 대단한 것 같다. ㄷㄷ;

소스코드

참고

github pages spa

react url params 사용하기

example.tsx
1
2
3
4
5
// 전
res = await myFetch(`get/${location.pathname.split('/')[2]}`);

// 후
res = await myFetch(`get/${id}`);
  • 처음에는 url params에 어떻게 접근할까하다가 location객체에서 수동으로 파싱해서 썼었는데

  • ‘react-router-dom’ 에서 useRouteMatch라는 메서드를 통해서 자동으로 파싱하고 필요한 차일드에 넘겨주었다

parent.tsx
1
2
3
4
5
6
7
8
9
10
11
12
// 최상위, 컴포넌트 밖
type MatchParams = {
id: string;
};

// 컴포넌트 안 최상위
const match = useRouteMatch<MatchParams>('/start/:id');

// 렌더 부분, 컴포넌트 안 return 부분
<Route path="/start/:id">
<Start id={match?.params.id || ''} />
</Route>;
child.tsx
1
2
3
4
5
6
7
8
9
// 최상위, 컴포넌트 밖
type Props = {
id: string;
};

// 컴포넌트 선언부
function Start({ id }: Props) {
// ...
}
  • 차일드에서 useRouteMatch를 사용해보았는데, useEffect에서 무한루프에 걸려서
  • 부모에서 전달해주었다

참고

Select elements must be either controlled or uncontrolled.

Select elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled select element and remove one of these props.

에러나는 코드
1
2
3
4
<select value={state} defaultValue="kr" onChange={changeLang}>
<option value="kr">한국어</option>
<option value="en">English</option>
</select>
에러 안나는 코드
1
2
3
4
<select value={state} onChange={changeLang}>
<option value="kr">한국어</option>
<option value="en">English</option>
</select>
  • defaultValue prop을 삭제하자
  • Select elements는 컨트롤되거나 안되게 둘중 하나만 하라는 건데..
  • value prop와 defaultValue prop는 같이 사용할 수 없다고 한다

참고

Cannot use JSX unless the '--jsx' flag is provided.ts(17004)

  • react typescript에서 tsconfig가 계속 자동수정되는 문제 해결하기
오류 메시지 모음
1
2
3
4
5
6
Cannot use JSX unless the '--jsx' flag is provided.ts(17004)

Specify JSX code generation: 'preserve', 'react', 'react-jsx', 'react-jsxdev' or'react-native'. Requires TypeScript version 2.2 or later.

The following changes are being made to your tsconfig.json file:
- compilerOptions.jsx must be react-jsx (to support the new JSX transform in React 17)

  • .ts, .tsx 확장자의 아무 파일이나 열고,
  • F1 > TypeScript 검색
  • TypeScript: Select TypeScript Version... 선택
  • 주의 : (타입스크립트 관련 파일이여야 위 명령이 검색된다)

  • Use Workspace Version 을 선택해준다.
.vscode/settings.json
1
2
3
4
{
// (...)
"typescript.tsdk": "node_modules\\typescript\\lib"
}
  • .vscode/settings.json 가보면 이렇게 업데이트 된것을 확인할 수 있다

TMI

상황

  • react typescript eslint airbnb rule 로 개발하는데
  • yarn start로 시작을 하면 자동으로 tsconfig.json의
  • compilerOptions > jsx 가 자동으로 “react-jsx”로 업데이트 됬다
  • 그런데 나는 “react-jsx”일 때 빨간줄이 마구 그어졌다
  • 그래서 yarn start 이후에 수동으로 “react”로 변경해 주었었다…

해결

  • 워크스페이스의 typescript가 사용되지 않아서 발생한 문제였다
  • compilerOptions > jsx 가 “react-jsx” 여도 빨간줄이 안생긴다!

참고

react Each child in a list should have a unique key prop.

  • key prop 에 대한 글이 아니고, 단순히 에러 메시지를 뛰우지 않기위해 하는 단순무식한 글이다.

example.js
1
2
3
4
5
6
7
<div className="product__rating">
{Array(rating)
.fill()
.map((_, i) => (
<p key={i}>✨</p>
))}
</div>
  • 반복문을 통해 자식 엘리먼트를 생성할 때,

    Each child in a list should have a unique key prop.

  • 이 오류 콘솔에 뜨게된다
  • 콘손에 빨간 오류가 보기 싫어서
  • 인덱스 값으로 key prop을 명시해준다

참고

react에서 html 자동완성 쓰기, emmet 사용하기

settings.json
1
2
3
4
5
6
{
// (...)
"emmet.includeLanguages": {
"javascript": "javascriptreact" // <-- react에서도 emmet 사용가능하도록
}
}
  • settings.json에 추가한다
  • 추가로 "emmet.triggerExpansionOnTab": true, 설정을 추가하면 tab키를 눌러 자동완성할 수 있다

참고

react typescirpt eslint prettier airbnb style guide 적용하기

방법 바로 보기

react typescirpt eslint prettier airbnb style guide 적용 방법은 여기로…
https://github.com/chinsun9/typescript-react-eslint-prettier-practice

TMI

상황

  • 진짜 여러 튜토리얼을 따라해도 뭔가 잘 안됬다
  • 차근차근 따라하기보다 세팅을 빨리 끝마치고 싶은 생각에 복붙을 시전했다
  • 일단 뭔가 쫌 되야 이렇구나 생각할텐데 잘안되서 머리가 아팠다

문제 & 원인

vscode setting.json

  • eslint없이 prettier 를 사용하면서 설정해둔 설정값
  • format on save 이것때문에, 린트 이후에 prettier가 다시 코드를 포맷팅하는 문제
.vscode/setting.json
1
2
3
4
5
6
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.formatOnSave": false
}
  • 처음에는 이거를 둘다켜놨엇다
  • 기존에 eslint에서 fix한거 formatOnSave가 다시 prettier 포맷터로 다시 포맷해서 생겼었다

.eslintrc.js

Parsing error: “parserOptions.project” has been set for @typescript-eslint/parser. The file does not match your project config: .eslintrc.js. The file must be included in at least one of the projects provided.

  • 이유를 모르겠으나. .js 방식으로하면 위 오류가 떠서 안됬다
  • 그래서 단순하게 .json방식으로 바꾸었다. 그랬더니 해결됬다…

알게된 것

  • eslint-config-prettier 를 통해 별도 .prettierrc 파일없이
  • eslintrc에서 prettier 설정이 가능하다
  • reportWebVitals.ts 이런 자동생성된 파일은 린트를 적용시킬 필요가없다
  • 파일 최상단에 /* eslint-disable */를 적어 무시해주자
  • 빨간줄이 생기면 희소식이다. 빨간줄 조차 안생기면 eslintrc 파일에 문제가 생긴거다
  • vscode도 재시작해보자
  • “source.fixAll.eslint”: true 설정으로 eslint에서 포맷팅을 실행할 수 있다
  • 쓸모없다고 생각하는 룰은 그냥 꺼버리자
  • 마우스를 올려서 어떤 룰인지 확인하고 .eslintrc > rules 에 해당 룰을 적고 0으로 세팅한다

세팅법 - 여기까지 리액트 프로젝트

https://github.com/chinsun9/typescript-react-eslint-prettier-practice

  • readme.md에 설정하는 방법을 정리해두었다
  • 커밋내역을 보면 명령어 입력 순서가 나와있으니 참고하면 좋을 것 같다

참고

react eslint prettier airbnb style guide 적용하기

terminal
1
2
3
4
5
6
npx create-react-app my-react-app
cd my-react-app

exec 3<&1;bash <&3 <(curl https://raw.githubusercontent.com/chinsun9/eslint-prettier-airbnb-react/master/eslint-prettier-config.sh 2> /dev/null)

yarn start
  • 누군가 자동으로 세팅하는 스크립트를 제작해두었다
  • 명령어 3개만 입력하면 세팅이 끝난다
  • windows에서는 gitbash에서 해당 명령을 실행하면 된다
  • 추가로 eslint 확장도구가 설치되어 있어야한다
  • 추가로, prettier 확장도구를 통해 자동포맷팅을 사용하는 사람들은
.vscode/settings.json
1
2
3
4
5
6
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.formatOnSave": false
}
  • "editor.formatOnSave": false ; prettier 확장도구로 인한 포맷팅은 비활성화해주고,
  • "source.fixAll.eslint": true ; eslint에서 포맷팅을 하도록한다

TMI

따라해도 나오는 오류

Replace onPerfEntry with (onPerfEntry)eslint (prettier/prettier)

  • src/reportWebVitals.js 파일에서 1개의 파라미터만 받는 함수에 괄호로 묶어주라는 오류가 나올 것이다
  • 이것만 고쳐주면 잘 동작한다
  • 혹시 빨간줄이 계속남아있거나, 안된다면 vscode를 다시 실행해보자

계속된 세팅 실패

error log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        leadingComments: [
{
type: 'Line',
value: ' If you want to start measuring performance in your app, pass a function',
start: 270,
end: 344,
loc: [SourceLocation],
range: [Array]
},
{
type: 'Line',
value: ' to log results (for example: reportWebVitals(console.log))',
start: 345,
end: 406,
loc: [SourceLocation],
range: [Array]
},
{
type: 'Line',
value: ' or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals',
start: 407,
end: 481,
loc: [SourceLocation],
range: [Array]
}
],
}
],
parent: null
}
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! react-practice@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the react-practice@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\chin_\AppData\Roaming\npm-cache\_logs\2020-11-17T06_40_37_514Z-debug.log
  • eslint-config-airbnb 만 설치하면 위 오류가 뜨면서 실행조차 안됬다
  • 이유는 모르겠다

eslint-prettier-airbnb-react 포크

.eslintrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"rules": {
// ...
"prettier/prettier": [
"error",
{
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"endOfLine": "auto" // <-- 추가
}
]
// ...
}
}
  • "endOfLine": "auto"를 추가해어 해결하였다

.prettierrc 파일 불필요

  • eslint-config-prettier 을 사용해서 prettierrc 파일없이 eslint 설정만으로도 가능하다고한다
  • eslintrc.json에 rule 아래 prettier/prettier이 있는데, 여기에 내가 필요한 포맷팅 설정을 해두면된다

여기까지 리액트 프로젝트

참고