개인 프로젝트로 포트폴리오 웹애플리케이션을 만들기 앞서서, MERN 스택에서 첫 번째로 React를 학습할 예정이다. 가장 기초부터 학습하며 직접 UI를 구현해보자!
이번 챕터에서는 JSX와 Element에 대해 알아보자
React에서는 본질적으로 렌더링 로직이 UI로직(이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등)과 연결된다.
다시말하면, React는 마크업과 로직을 별도의 파일로 넣어 인위적으로 분리하는 대신, 둘 다 포함하는 컴포넌트라고 부르는 유닛으로 분리한다.
이러한 컴포넌트를 구현하는데 있어, 편리하게 사용할 수 있는 문법이 있다.
const element = <h1>Hello, world!</h1>;
JSX는 JavaScript를 확장한 문법으로, React element를 생성할 수 있다.
React에서 JSX의 사용은 필수가 아니지만, JavaScript 코드 안에서 UI 관련 작업을 할 때 시각적으로 도움이 된다.
표현식이란 값으로 이행하는 임의의 유효한 코드 단위를 말한다.
JSX에 중괄호를 사용하여, 유효한 모든 JavaScript 표현식을 넣을 수 있다.
아래 예시는 name이라는 변수를 선언한 후, 중괄호로 감싸 JSX 안에 사용했다.
const name = "yong";
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
또 다른 예시를 보자. JavaScript 함수 호출의 결과인 formatName(user)을 <h1> element에 포함한다.
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'yong',
lastName: 'yong'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
컴파일이 끝나면, JSX가 정규 JavaScript 함수 호출이 되고, JavaScript 객체로 인식된다.
즉, JSX를 if 조건문, for 반복문 안에 사용하고, 변수에 할당하고, 매개변수로 사용하고, 함수로부터 반환하는 등 표현식으로써 사용할 수 있다.
JSX에 attribute를 정의하는 방법은 두 가지가 있다.
따옴표 사용하기
attribute에 따옴표를 이용해 문자열 리터럴을 정의할 수 있다.
const element = <a href="example.com">link</a>;
중괄호 사용하기
중괄호를 사용하여 attribute에 JavaScript 표현식을 삽입할 수도 있다.
const element = <img src={user.avatarUrl}></img>;
참고로, JSX는 HTML보다 JavaScript에 가깝기 때문에, React DOM은 HTML의 attribute name 대신 카멜표기법을 사용한다.
예를 들어, HTML에서 class는 JSX에서 className이 되고, tabindex는 tabIndex가 된다.
JSX element는 자식 element를 포함할 수 있다.
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
기본적으로 React DOM은 렌더링하기 전에 JSX에 삽입된 모든 값을 escape하므로, 애플리케이션에서 명시적으로 작성되지 않은 내용은 삽입되지 않는다.
쉽게말해, 렌더링 되기 전에 모든 항목은 문자열로 변환되므로, XSS 공격을 방지할 수 있다.
참고로, JSX에 사용자 입력을 삽입하는 것은 안전하다.
// 사용자 입력을 받는 코드는 안전하다.
const title = response.potentiallyMaliciousInput;
const element = <h1>{title}</h1>;
Babel은 JSX를 React.createElement() 함수 호출로 컴파일한다.
예를들어, 다음의 두 방식을 동일하다.
// JSX 방식
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// JavaScript 방식
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
React.createElement()는 버그가 없는 코드를 작성하는데 도움이 되도록 몇 가지 검사를 수행하며, 기본적으로 다음과 같은 객체를 생성한다.
// 단순화한 객체. 전체 객체는 더 길다.
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
이러한 객체를 React Element라고 하며, React는 이런 객체를 읽어서 DOM을 구성하는데 사용한다.
Element는 React 앱의 가장 작은 단위로, 화면에 표시할 내용을 기술한다.
const element = <h1>Hello, world</h1>;
브라우저 DOM element와 달리 React element는 일반 객체이며 쉽게 생성할 수 있다.
React DOM은 element와 일치하도록 DOM을 업데이트한다.
HTML파일 어딘가에 <div>가 있다고 가정하자.
<div id="root"></div>
이 안에 들어가는 모든 element를 React DOM에서 관리하기 때문에 이것은 root DOM 노드라고 부른다.
React로 구현된 애플리케이션은 일반적으로 하나의 root DOM 노드가 있지만, 만약 React를 기존 앱에 통합하려는 경우에는 다수의 root DOM 노드가 있을 수 있다.
React element를 root DOM 노드에 렌더링하려면 ReactDOM.render() 메서드로 전달하면 된다.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
결론부터 말하면 불가능하다. element는 불변하는 객체이다. element를 생성한 이후에는 해당 element의 자식이나 attribute를 변경할 수 없다.
element는 스냅샷처럼 특정 시점의 UI를 보여준다. 따라서, UI를 업데이트하는 유일한 방법은 새로운 element를 생성하고, 이를 매번 ReactDOM.render() 메서드로 전달하는 것이다.
매 초 단위로 증가하는 시계를 예시로 들어보자. 아래에 구현한 tick() 함수는 setInterval() 콜백을 이용해, 초마다 ReactDOM.render()를 호출한다.
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
React DOM은 해당 element와 그 자식 element를 이전의 element와 비교하고, DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다.
위에서 구현한 시계 예시를 살펴보면, 매초 전체 UI를 다시 렌더링하도록 element를 만들었지만, React DOM은 내용이 변경된 텍스트 노드만 업데이트한다.