JSX는 JavaScript의 확장 문법입니다. JSX는 템플릿 언어와 비슷해 보이지만, JavaScript의 강력한 기능들을 모두 사용할 수 있습니다. JSX는 React.createElement()의 호출을 통해 일반 JavaScript 객체인 “React 엘리먼트”(React element)로 컴파일됩니다. JSX에 대한 기본 소개는 여기에서 확인할 수 있으며 JSX에 대한 자세한 튜토리얼은 여기에서 확인할 수 있습니다.
React DOM은 HTML 어트리뷰트(attribute) 이름 대신 캐멀케이스(camelCase)를 네이밍 컨벤션으로 사용합니다. 예를 들어, JSX에서 tabindex는 tabIndex로 작성합니다. class 어트리뷰트는 JavaScript의 예약어이므로 className으로 작성합니다.
기본적으로 자바스크립트의 코드 용량을 줄이기 위해 min.js로 압축을 시킨다.
코드의 들여쓰기나 띄어쓰기를 다 없애서 한줄로 압축
JavaScript 컴파일러(Compiler)는 JavaScript 코드를 변환하고 다른 형식으로 JavaScript 코드를 반환합니다. 일반적으로 ES6 문법을 구형 브라우저에서도 동작할 수 있도록 변환하는 데 많이 사용합니다. Babel은 React와 함께 가장 널리 사용되는 컴파일러입니다.
사용자가 요청을 하면 서버는 웹 애플리케이션을 구성하는 파일들을 보내게 됩니다. 웹 애플리케이션를 구성하는 파일의 양이 많다면, 사용자의 요청에 응답하는 시간이 길어지게 됩니다.
서버가 파일 1개를 요청하고 응답하는데 1초가 걸린다고 가정합니다. 그럴 경우 100개의 파일을 응답하는 데 100초, 1000개의 파일을 응답하는 데 1000초의 시간이 걸리게 됩니다.
거기에 수많은 사용자가 웹 사이트를 이용할 경우, 응답을 제때 하지 못해 네트워크 병목현상이 일어날 수 있습니다.
만약 파일의 양을 줄이기 위해 하나의 JS 파일에 모든 변수, 함수를 추가한다고 하면 속도 면에선 빨라져 네트워크 병목 현상을 피할 수 있을 것입니다. 하지만 개발자의 입장에선 수 천, 수 만줄의 코드를 유지보수 해야 하기 때문에 가독성 면에서 지옥이 따로 없을 것입니다.
이처럼 가독성, 유지 보수를 위해 파일을 분리하면 네트워크 병목현상이 일어날 것이고, 응답 시간을 위해 파일 수를 줄이면 가독성, 유지 보수가 힘들어지는 딜레마에 빠지게 됩니다. 그렇다면 두 마리 토끼를 다 잡는 방법은 없을까요?
이와 같은 문제들을 해결하기 위해 번들러(Bundler) 가 등장하였습니다. 번들러를 간단히 설명하면, 여러개의 파일을 하나의 파일로 묶어주는 역할을 한다고 말할 수 있습니다. 번들러는 대표적으로 webpack, parcel, browserify가 있습니다. 저는 주로 사용되고 있는 webpack을 중심으로 살펴보겠습니다.
툴체인 = CRA
엘리먼트는 React앱의 가장 작은 단위이다.
<div id="root"></div>
이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 “루트(root)” DOM 노드라고 부릅니다.
React 엘리먼트를 루트 DOM 노드에 렌더링하려면 둘 다 ReactDOM.render()로 전달하면 됩니다.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
React 엘리먼트는 불변객체입니다. 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없습니다. 엘리먼트는 영화에서 하나의 프레임과 같이 특정 시점의 UI를 보여줍니다.
지금까지 소개한 내용을 바탕으로 하면 UI를 업데이트하는 유일한 방법은 새로운 엘리먼트를 생성하고 이를 ReactDOM.render()로 전달하는 것입니다.
React DOM은 해당 엘리먼트와 그 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트합니다.
React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있습니다.
const element = <Welcome name="Sara" />;
React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달합니다. 이 객체를 “props”라고 합니다.
다음은 페이지에 “Hello, Sara”를 렌더링하는 예시입니다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
이 예시에서는 다음과 같은 일들이 일어납니다.
<Welcome name="Sara" />
엘리먼트로 ReactDOM.render()
를 호출합니다.
React는 {name: 'Sara'}
를 props로 하여 Welcome
컴포넌트를 호출합니다.
Welcome
컴포넌트는 결과적으로 <h1>Hello, Sara</h1>
엘리먼트를 반환합니다.
React DOM은 <h1>Hello, Sara</h1>
엘리먼트와 일치하도록 DOM을 효율적으로 업데이트합니다.
컴포넌트로 함수를 전달할 때 호출하지 않는지 확인합니다.
render() {
// 잘못된 방법: handleClick은 레퍼런스로 전달되지 않고 호출되었습니다!
return <button onClick={this.handleClick()}>Click Me</button>
}
위와 같은 방식이 아니라 괄호 없이 함수 그 자체를 전달해야 합니다.
render() {
// 올바른 방법 : handleClick이 레퍼런스로 전달되었습니다.
return <button onClick={this.handleClick}>Click Me</button>
}
createPortal 부분을 보고 모달을 만들어보았다
{this.state.list.map((todo, index) => (
<ToDo key={index} {...todo} />
))}
index로 놓으면 성능이 좋지 않다. 인덱스를 key로 사용 중 배열이 재배열되면 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다. 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용됩니다. 인덱스를 key로 사용하면, 항목의 순서가 바뀌었을 때 key 또한 바뀔 것입니다. 그 결과로, 컴포넌트의 state가 엉망이 되거나 의도하지 않은 방식으로 바뀔 수도 있습니다.
예를 들어.
<ul>
<li key="0">Duke</li>
<li key="1">Villanova</li>
</ul>
---------------- li 엘리먼트 하나를 맨 앞에 추가했을 때
<ul>
<li key="0">Connecticut</li>
<li key="1">Duke</li>
<li key="2">Villanova</li>
</ul>
기존의 index가 하나씩 다 바뀌기 때문에 비교 알고리즘을 통해 항목들이 재배열되고 비효율적으로 동작을 할 것이다.
{this.state.list.map((todo, index) => (
<ToDo key={Math.random().toString(10).substr(2,13)} {...todo} />
))}
key는 반드시 변하지 않고, 예상 가능하며, 유일해야 합니다. 변하는 key(Math.random()으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있습니다.
{this.state.list.map((todo, index) => (
<ToDo key={todo.id} {...todo} />
))}
고유한 id면서 예상가능한 id를 부여하자..!