리액트 공식문서 주요개념 부분에 JSX 소개를 읽고 정리한 글입니다.
React에는 아래와 같은 희한한 문법을 볼 수 있다.
const element = <h1>Hello, world!</h1>;
이는 문자열도 HTML도 아니고, JSX라 하며 JS를 확장한 문법이다. JSX라고 하면 템플릿 언어가 떠오를 수도 있지만, JS의 모든 기능이 포함되어 있다.
JSX는 React "엘리먼트"를 생성한다.
React에서는 본질적으로 렌더링 로직이 UI로직(이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등)과 연결된다는 사실을 전제한다.
React는 별도의 파일에 마크업과 로직을 분리하는 대신, '컴포넌트'라고 부르는 유닛으로 관심사를 분리한다.
React는 JSX 사용이 필수가 아니지만, 대부분의 사람은 JS 코드 안에서 UI 관련 작업을 할 때 시각적으로 더 도움이 된다고 생각한다.
React 사용 시, JSX는 필수가 아니다. 단지 JSX는 React 엘리먼트를 생성하는 React.createElement(component, props, ...children)
을 호출하기 위한 문법적 설탕이다.
아래의 코드를 보자.
jsx 사용 코드
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
)
jsx 사용 안한 코드
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
컴포넌트는 문자열이나 React.Component
의 하위 클래스 또는 컴포넌트를 위한 일반 함수로 제공된다.
함수형 컴포넌트, 클래스형 컴포넌트를 말하는 듯
React.createElement
를 다음과 같이 줄여서 사용할 수도 있다.
const h = React.createElement;
ReactDOM.render(
h('div', null, 'Hello World'),
document.getElementById('root')
);
const name = 'JeongMin Lee';
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
이 예시에는
name
이라는 변수를 선언한 후 중괄호로 감싸 JSX안에 사용
JSX의 중괄호 안에는 유효한 모든 JS 표현식을 넣을 수 있다.
문이아닌 표현식이라는 점에 유의하자.
아래 예시에서는 JS함수 호출의 결과인 formatName(user)
라는 표현식의 값을 <h1>
엘리먼트에 포함한다.
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'JeongMin',
lastName: 'Lee'
}
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
)
위와 같이
element
의 JSX를 여러 줄로 나눌때, 자동 세미콜론 삽입을 피하기 위해 괄호를 묶는것을 권장한다.
컴파일이 종료되면, JSX 표현식이 정규 JS 함수 호출이 되고 JS 객체로 인식된다.
즉, JSX를 if
구문 및 for
loop 안에 사용하고, 변수에 할당하고, 인자로서 받아들이고, 함수로부터 반환할 수 있다.
const getGreeting = user => {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
어트리뷰트에 따옴표를 이용해 문자열 리터럴을 정의할 수 있다.
const element = <div tabIndex="0"></div>;
중과호를 사용해 어트리뷰트에 JS 표현식을 삽입할 수도 있다.
const element = <img src={user.avatarUrl}></img>;
어트리뷰트에 JS표현식을 삽입할 때 중괄호 주변에 따옴표를 입력하지 말고, 따옴표 또는 중괄호 중 하나만 사용하고 동일한 어트리뷰트에 두 가지를 동시에 사용하면 안된다.
JSX는 HTML보다 JS에 가까워서 ReactDOM은 HTML 어트리뷰트 이름 대신
camelCase
프로퍼티명 규칙을 사용한다.
class =>className
, tabindex =>tabIndex
태그가 비어있다면(자식이 비어있다면) XML 처럼 />
를 이용해 바로 닫아 주어야한다.
const element = <img src={user.avatarUrl} />;
// 자식 포함할 때
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX에 사용자 입력을 삽입하는 것은 안전하다.
const title = response.potentiallyMaliciousInput;
const element = <h1>{title}</h1>;
기본적으로 ReactDOM은 JSX에 삽입된 모든 값을 렌더링하기 전, 이스케이프 처리를 한다.(<
를 <
로 바꾸는 것과 같은 작업) 또한 모든 항목은 렌더링 되기 전에 문자열로 변환된다. 이러한 특성으로 XSS 공격을 방지할 수 있다.
Babel은 JSX를 React.createElement()
로 컴파일 시킨다. 따라서 아래의 코드가 다음과 같이 변환된다.
// JSX 문법
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// Babel 사용 후 JSX => React.createElement 문법
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
React.createElement()
는 버그가 없는 코드를 작성하는데 도움이 되도록 몇 가지 검사를 수행하여 기본적으로 다음과 같은 객체를 생성한다.
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
공식문서에선 이 예제로 사용된 객체는 단순화된 구조라고 설명한다.
이러한 객체를 React 엘리먼트라고 하며, 화면에서 보고 싶은 것을 나타내는 표현이고, React는 이 객체를 읽어서 DOM을 구성하고 최신 상태로 유지하는데 사용한다.