ko.reactjs.org/docs/getting-started.html
시작하기 – React
A JavaScript library for building user interfaces
ko.reactjs.org
리액트 문서 다시 찬찬히 보면서 공부하기
주요개념
JSX
- JS를 확장한 문법으로 UI가 어떻게 생겨야 하는지 설명하기 위해 React와 함께 사용함. JS의 모든 기능이 포함되어 있음.
- JSX는 React 엘리먼트를 생성한다.
- 리액트에서는 이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등 렌더링 로직이 본질적으로 다른 UI로직과 연결된다는 사실을 받아들인다.
- 리액트는 별도의 파일에 마크업과 로직을 넣어 인위적으로 분리하는 대신, 둘 다 포함하는 '컴포넌트'라고 부르는 느슨한 연결 유닛을 사용한다.
- JSX없이 리액트를 사용할 수도 있다. React.createElement를 사용하면 되는데 쓰기 피곤할것이다.
- { } 중괄호로 감싸 js 표현식을 사용할 수 있다.
- JSX또한 표현식이다. 컴파일이 끝나면, JSX표현식이 정규 js 함수 호출이 되고 js 객체로 인식된다. 함수의 리턴값으로 jsx를 사용할 수 있다.
- JSX는 주입 공격을 방지한다. 삽입된 모든 값을 렌더링 하기 전에 이스케이프('<'를 '<'으로 바꾸는 등 보안에 위협이 될 문자를 안전한 문자로 치환)시킨다.
엘리먼트 렌더링
const element = <h1>Hello, world</h1>
- 브라우저의 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며 쉽게 생성할 수 있다.
- React DOM에서 React 엘리먼트와 일치시키도록 DOM을 업데이트한다.
- 컴포넌트와 엘리먼트는 다른것이다. 컴포넌트 챕터에서 설명함.
- 리액트 엘리먼트를 root라는 id의 div에 렌더링하려면 ReactDOM.render()로 전달하면 된다.
ReactDOM.render(element, document.getElementById('root'));
- React 엘리먼트는 불변객체이다. 생성 이후 엘리먼트의 자식이나 속성을 변경할 수 없다.
- UI를 업데이트 하기 위해선 새로운 엘리먼트를 생성하고 ReactDOM.render()로 전달해야 한다.
function tick( ) {
const element = (<div>Time is {new Date().toLocaleTimeString()}</div>;
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
이 코드는 매 초 엘리먼트를 생성하고 새로 리액트돔에서 렌더링한다..
- 리액트 돔은 해당 엘리먼트와 그 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다.
컴포넌트와 props
- 컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있다.
- 컴포넌트는 js 함수와 유사하다. props라는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 엘리먼트를 반환한다.
- 함수형 컴포넌트와 클래스 컴포넌트가 있다.
- 리액트 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다. 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달한다. 이 객체를 props라고 함.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
렌더링 순서
1. <Welcome name="Sara" /> 엘리먼트로 ReactDOM.render()를 호출함
2. { name : 'Sara' }를 props로 Welcome 컴포넌트 호출.
3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1> 엘리먼트를 반환함.
4. React DOM은 <h1>Hellow, Sara</h1> 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트함.
그냥 함수호출이랑 비슷하다.
- 컴포넌트 이름은 항상 대문자로 시작해야한다.
- 컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있다.
- 컴포넌트를 추출해서 재사용이 가능한 작은 컴포넌트들로 만들자.
- props는 읽기 전용이다. 모든 리액트 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야한다. 동적인 값이 필요하면 state를 쓰면 된다.
State 와 라이프사이클
- state는 props와 유사하지만, 비공개이며 컴포넌트에 의해 완전히 제어된다.
- 위의 시간을 출력하는 tick함수를 state를 사용하는 컴포넌트로 수정하면 아래와 같다.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
실행 순서
1. ReactDOM.render()가 Clock 컴포넌트의 constructor를 호출한다. Clock은 현재 시각이 포함된 객체로 this.state를 초기화 한다.
2. 리액트는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 리액트는 화면에 표시되어야 할 내용을 알게되고, Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트한다.
3. Clock 출력값이 DOM에 삽입되면, 리액트는 componentDidMount() 생명주기 메서드를 호출한다. 그 안에 Clock 컴포넌트는 매초 컴포넌트의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청한다.
4. 매초 브라우저가 tick() 메서드를 호출한다. 그 안에서 Clock 컴포넌트는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행한다. setState()호출로 리액트는 state가 변경된 것을 인지하고 화면에 표시된 내용을 인지하기 위해 render를 다시 호출한다. 이때 렌더링 출력값은 업데이트 된 시각을 포함한다.
5. Clock 컴포넌트가 돔에서 삭제된다면 리액트는 타이머를 멈추기 위해 componentWillUnMount() 생명주기 메서드를 호출한다.
- 라이프사이클 : 마운트(업데이트) -> 렌더 -> 종료시 언마운트

state 올바르게 사용하기
- 직접 State를 수정하면 컴포넌트가 리렌더링 되지 않기 때문에 setState()를 사용해야한다. this.state를 직접 지정할 수 있는 유일한 공간은 constructor
- state 업데이트는 비동기적일 수 있다. 리액트의 성능을 위해 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있다. 그리고 브라우저의 최소한으로 보장해야하는 렌더링을 위해 state 업데이트가 늦춰질 수도 있다.
- state 업데이트는 병합된다.
- 데이터는 아래로 흐르기 때문에 부모나 자식 컴포넌트에서 모두 특정 컴포넌트가 유상태인지 무상태인지 알 수 없고, 그들이 함수나 클래스로 정의되었는지에 대해 관심가질 필요가 없다. 이를 하향식 또는 단방향식 데이터흐름이라고 한다.
이벤트 처리하기
- DOM엘리먼트에서 이벤트를 처리하는 방식과 유사하다. 문법차이만 조금있다.
- React의 이벤트는 소문자 대신 캐멀 케이스를 사용한다.
- JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.
- 리액트에서는 false를 반환해도 기본 동작을 방지할 수 없다. 반드시 preventdefault를 명시적으로 호출해야 한다.
- 이벤트 핸들러의 인자 e는 합성이벤트이다. 리액트는 W3C 명세에 따라 합성이벤트를 정의하기 때문에 브라우저 호환성에 대해 걱정할 필요가 없다.
- DOM 엘리먼트가 생성된 후 리스너를 추가하기위해 addEventListener를 호출할 필요가 없다. 대신 엘리먼트가 처음 렌더링될 때 리스너를 제공하면 된다.
조건부 렌더링
- 리액트에서 원하는 동작을 캡슐화하는 컴포넌트를 만들 수 있다. 이렇게 하면 애플리케이션의 상태에 따라서 컴포넌트 중 몇 개만을 렌더링할 수 있다. 조건문으로 리턴할 컴포넌트를 선택하면 됨.
리스트와 키
- JSX내의 중괄호에서 배열의 map 함수를 활용하여 배열내에 있는 요소를 참조해 엘리먼트를 만들어 낼 수 있음.
- 이 때 각 항목에 key값을 넣어 줘야 한다. key는 리액트가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.
- key값으로 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이다.
- 안정적인 key값이 없다면 최후의 수단으로 인덱스를 key로 사용하라.
- 항목의 순서가 바뀔 수 있는 경우 key에 인덱스를 사용하는 것은 권장하지 않는다. 이로 인해 성능이 저하되거나 컴포넌트의 state와 관련된 문제가 발생할 수 있다. - medium.com/sjk5766/react-%EB%B0%B0%EC%97%B4%EC%9D%98-index%EB%A5%BC-key%EB%A1%9C-%EC%93%B0%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-3ce48b3a18fb
[React] 배열의 index를 key로 쓰면 안되는 이유
React 공식 Document를 보다가 아래 노란색으로 캡쳐한 부분을 보게 되었고 확인한 사항들을 정리합니다.
medium.com
- 리액트는 key가 동일 할 경우 동일한 DOM Element를 보여준다.
폼
- HTML 폼 엘리먼트는 폼 엘리먼트 자체가 내부 상태를 가지기 때문에, 리액트의 다른 DOM 엘리먼트와 조금 다르게 동작한다.
- HTML에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다. 리액트에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트 된다.
- 그래서 리액트는 제어 컴포넌트들에 대해 이벤트 핸들러를 따로 작성해 줘야 함.
예시 생략
State 끌어 올리기
- 종종 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야 할 필요가 있다. 이럴 때 가장 가까운 공통 조상으로 state를 끌어올리는 것이 좋다.
합성 vs 상속
- 리액트는 강력한 합성 모델을 가지고 있으며, 상속 대신 합성을 사용하여 컴포넌트 간에 코드를 재사용하는 것이 좋다.
- children prop을 사용하면 자식엘리먼트를 그대로 출력할 수 있다.
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
- 자식컴포넌트에 props 이름을 붙여서 각 컴포넌트마다 다른 기능을 수행할 수도 있다.
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
'공부' 카테고리의 다른 글
CORS (0) | 2020.12.29 |
---|---|
리액트 Docs - 고급 안내서 (0) | 2020.12.16 |
실용적인 프론트엔드 전략 3 (0) | 2020.12.10 |
실용적인 프론트엔드 테스트 전략 - 2 (0) | 2020.12.09 |
실용적인 프론트엔드 테스트 전략 - 1 (0) | 2020.12.09 |