2022 OSAM 해커톤 사전 온라인 교육에서 배운 내용입니다.
모르는 내용만 발췌하여 정리한 것이기 때문에 내용의 연결성이 부족한 점 양해 부탁드립니다.
React스럽게 리팩토링하기 : 노마드코더 ReactJS 초반부 정리
Adding TypeScript | Create React App
설정 파일 빠르게 작성하고, 설치 수작업으로 했는데 한번에 잘 작동되는게 너무 멋있다
- eslint 설치 https://eslint.org/docs/latest/user-guide/getting-started
- .eslintrc.js로 설정
- prettier 설치 https://prettier.io/docs/en/install.html
- prettier-eslint 충돌 해결 https://prettier.io/docs/en/integrating-with-linters.html
- .prettierrc.json으로 설정
- typescript 설치 https://www.typescriptlang.org/download
- tsconfig.json으로 설정
- @types/react @types/react-dom 설치
- babel 설치(CLI, webpack) https://babeljs.io/setup#installation
- babel.config.json으로 설정 → preset 받기
- webpack 설치 https://webpack.js.org/guides/getting-started/
- webpack.config.json로 설정(entry:
./src/index.tsx
, output:bundle.js
) https://webpack.js.org/guides/getting-started/#using-a-configuration- ts-loader(tsx→jsx) 설치 및 webpack.config.json에 module 추가 https://github.com/TypeStrong/ts-loader
- babel-loader(jsx→js) 설치 및 webpack.config.json에 module 추가 https://webpack.kr/loaders/babel-loader
- babel preset-react 추가로 설치 및 webpack.config.json에 preset 추가 https://babeljs.io/docs/en/babel-preset-react
- webpack dev-server 설치 https://webpack.kr/configuration/dev-server/
- webpack.config.json에 static(정적 파일 디렉토리 위치), hot(수정 있을 시 자동 새로고침), open(브라우저 자동으로 켬) 옵션 주기 https://webpack.kr/configuration/dev-server/#devserverstatic
npm start
테스트용 스크립트 넣기 (package.json)"start": "webpack serve"
이전 트리를 버리고 완전히 새로운 트리를 구축한다.
ex) <a>
에서 <img>
로, <Article>
에서 <Comment>
로, 혹은 <Button>
에서 <div>
로 바뀌는 것 모두 트리 전체를 재구축하는 경우
<div>
<Counter />
</div>
<span>
<Counter />
</span>
이전 Counter
는 사라지고, 새로 다시 마운트가 될 것입니다.
동일한 내역은 유지하고 변경된 속성들만 갱신합니다. 예를 들어,
<div className="before" title="stuff" />
<div className="after" title="stuff" />
이 두 엘리먼트를 비교하면, React는 현재 DOM 노드 상에 className
만 수정한다.
style
이 갱신될 때, React는 또한 변경된 속성만을 갱신한다. 예를 들어,
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
위 두 엘리먼트 사이에서 변경될 때, React는 fontWeight
는 수정하지 않고 color
속성 만을 수정한다.
DOM 노드의 처리가 끝나면, React는 이어서 해당 노드의 자식들을 재귀적으로 처리한다.
렌더링 간 state는 유지되며, props는 갱신된다.
기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성합니다. 예를 들어,
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React는 두 트리에서 <li>first</li>
가 일치하는 것을 확인하고, <li>second</li>
가 일치하는 것을 확인한다. 그리고 마지막으로 <li>third</li>
를 트리에 추가한다.
하지만 위와 같이 단순하게 구현하면, 리스트의 맨 앞에 엘리먼트를 추가하는 경우 성능이 좋지 않다. 하나만 바꾸면 되는데 통째로 다 바꿔야하기 때문이다.
이러한 문제를 해결하기 위해, React는 key
속성을 지원한다. key는 다음과 같이 생성된다.
// Counter.tsx
import * as React from "react";
interface CounterProps {
title: string;
}
interface CounterState {
num: number;
}
class Counter extends React.Component<CounterProps, CounterState> {
constructor(props: CounterProps) {
super(props);
// state를 만드는 법. 무조건 constructor에서만 가능
this.state = {
num: 0,
}
}
handleClick = () => {
this.setState((prev => {
return {
num: prev.num + 1,
};
});
};
render() {
return (
<div>
<h1>{this.props.title}</title>
<h3>Count: {this.state.num}</h3>
<button onClick={this.handleClick}>Plus</button>
</div>
);
}
}
export default Counter;
// index.tsx
import * as React from "react";
import { createRoot } from "react-dom/client";
import Counter from "./Counter";
const rootNode = document.getElementById("root");
const reactRoot = createRoot(rootNode!);
reactRoot.render(<Counter title="이건 Counter야" />);
Counter.tsx
함수형 컴포넌트로 마이그레이션// Counter.tsx
import * as React from "react";
interface CounterProps {
title: string;
}
function Counter(props: CounterProps): React.ReactElement {
const [count, setCount] = React.useState(0);
const add = () => setCount((prev) => prev+1);
return (
<div>
<h1>{props.title}</title>
<h3>Count: {count}</h3>
<button onClick={add}>Plus</button>
</div>
);
}
export default Counter;
**Counter
를 arrow function으로 만들어보자!**
// Counter.tsx
import * as React from "react";
interface CounterProps {
title: string;
}
const Counter: React.FC<CounterProps> = ({title}) => {
const [count, setCount] = React.useState(0);
const add = () => setCount((prev) => prev+1);
return (
<div>
<h1>{title}</title>
<h3>Count: {count}</h3>
<button onClick={add}>Plus</button>
</div>
);
};
export default Counter;
Logic을 완전히 분리해보자!
count 말고 다른 state가 생겨도 Counter 함수 안에서 만들텐데, 함수형 컴포넌트에서는 굳이 여러가지 state가 한 컴포넌트 안에 존재할 필요 없음.
// Counter.tsx 수정된 부분만
import { useCounter } from "./hooks/useCounter";
// const [count, setCount] = React.useState(0);
const { count, add } = useCounter();
// hooks/useCounter.ts
import * as React from "react";
interface UseCounterReturnType {
count: number;
add: () => void;
}
function useCounter(): UseCounterReturnType {
const [count, setCount] = React.useState(0);
const add = () => setCount((prev) => prev+1);
return {
count,
add
};
}