들어가기 전에

리액트 공식문서로 배워보자 #2, Introducing JSX

다음 변수 선언의 형태는 뭘까요?

const element = <h1>Hello world!</h1>;

이 웃긴 태그 문법은 문자열도 HTML도 아닙니다.

JSX라 불리는 것입니다. JSX는 자바스크립트의 확장 문법입니다. UI가 어떻게 보일지 기술하기 위해서, 우리는 리액트와 함께 쓰는 것을 권장합니다.JSX는 템플릿 언어의 기억을 다시 한번 떠올리게 할 수도 있지만 사실 자바스크립트의 모든 힘을 갖고 있습니다.

왜 JSX인가?

리액트는 렌더링 로직은 본질적으로 다른 UI로직과 결합된다는 사실을 받아들입니다. 이벤트가 어떻게 다루어지는지, 상태가 시간이 지남에 따라 어떻게 변화하는지, 보여줄 데이터가 어떻게 준비되는지에 대한 로직 말이죠.

마크업과 로직을 분리된 파일에 넣는 방식으로 작성하는 '부자연스럽게 분리된' 기술들과 다르게, 리액트는 마크업과 로직을 둘 다 포함하는 '컴포넌트'라 불리는 느슨하게 결합된 유닛들로 우리의 문제를 분리했습니다. (역자 주: 여기서 분리란 뜻은 컴포넌트의 독립성을 의미한다고 봐야할 것 같습니다.) 추가 섹션에서 우린 다시 컴포넌트에 대한 내용으로 돌아갈 것이지만 만일, JS 내부에 마크업 언어를 작성하는 것이 불편하다면, 이 영상이 어느정도 설득력이 있을 수도 있겠습니다.

JSX 내장 표현식

아래 예제에서, 우리는 name이라는 변수를 정의했습니다. 그리고 JSX 내부에서 {}로 감싸는 방식으로 변수를 이용했습니다.

const name = 'Josh Perez';
const element = <h1>Hello {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);

어떤 유효한 자바스크립트 표현식이든 JSX내부에서 {}안에 넣어 사용할 수 있습니다. 예를 들면, 2 + 2, user.firstName, formatName(user)는 모두 유효한 자바스크립트 표현식입니다.

아래 예제에서, 우리는 자바스크립트 함수 호출의 결과를 삽입했습니다. formatName(user)<h1> 엘리먼트 내부에 넣었습니다.

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

CodePen에서 직접 실행해보기

우리는 가독성을 위해 JSX를 몇개의 줄로 나눕니다. 꼭 필요한 작업은 아니지만, JSX 작업을 할 때는, 자동 세미콜론 삽입의 함정에 빠지지 않기 위해 괄호 안에 코드를 넣는 것을 권장합니다.

JSX도 표현식입니다.

컴파일 이후에, JSX 표현식은 일반적인 JavaScript 함수 호출이 되고, 자바스크립트 오브젝트로 취급됩니다.

이 사실이 의미하는 것은 if 문 그리고 for 반복문 안에서 JSX를 사용할 수도 있고, 변수에 할당할 수도 있고, 인자로 받을 수도 있고 함수의 반환 값으로도 사용할 수도 있다는 것입니다.

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

JSX, 속성 표기하기

속성으로 문자열 리터럴을 표기하기 위해서 따옴표(quotes)를 사용할 것입니다.

const element = <div tabIndex="0"></div>;

또 자바스크립트 표현을 속성 안에 넣기 위해 {}괄호를 사용할 수도 있습니다.

const element = <img src={user.avatarUrl}></img>;

자바스크립트 표현식을 속성에 넣을 때, {}괄호 옆에는 따옴표를 넣지 마세요. 첫번째 예처럼 문자열 리터럴 값을 넘기기 위해서 따옴표를 넣던지 자바스크립트 표현식을 넣기 위해서 {}괄호를 쓰던지 하나만 하셔야 합니다. 같은 속성에서 두개 다 하지 마세요.

경고
JSX는 자바스크립트보다 HTML에 더욱 가깝기 때문에, React DOM은 속성을 표기할 때 HTML 속성 이름 대신에 camelCase 네이밍 컨벤션을 씁니다.

예를 들면, class는 JSX에서는 className이 됩니다. 그리고 tabindextabIndex가 됩니다.

JSX, 자식(Children) 표기하기

만일, 태그가 비어있다면, XML처럼 />로 즉시 닫을 수 있을 겁니다.

const element = <img src={user.avatarUrl} />;

JSX태그가 자식을 포함합니다.

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

JSX는 Injection 공격을 방지합니다.

유저 입력을 JSX안에 내장시키는 것이 안전합니다.

const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

기본으로, React DOM은 렌더링하기 전에 JSX안에 내장된 어떤 값들이라도 escape 시킵니다. 따라서 React DOM은 어플리케이션 내부에 명시적으로 쓰여지지 않은 어느 것도 inject할 수 없음을 보장합니다. 렌더링 되기 전, 모든 것이 string으로 변환됩니다. 이러한 특성은 XSS(cross-site-scripting) 공격을 막는 데 도움을 줍니다.

JSX는 오브젝트를 나타냅니다.

Babel은 JSX를 React.createElement() 호출로 컴파일합니다.

다음 두가지 예제는 동일합니다.

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React.createElement()는 버그가 없는 코드를 작성하도록 돕기 위해 몇가지 체크를 하지만 근본적으로 다음과 같은 오브젝트를 만들어냅니다.

// 알아둬야 할 것: 이 구조는 단순화(simplified) 됐습니다.
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

이 오브젝트들을 "리액트 엘리먼트(React Elements)"라고 부릅니다. 화면에서 무엇이 보고싶은 지에 대한 기술(description)이라고 생각하시면 됩니다. 리액트는 이 오브젝트들을 읽고 DOM을 건설하기 위해 오브젝트들을 사용합니다. 그리고 계속 최신 상태로 유지합니다.

다음 섹션에서는, 리액트 엘리먼트들을 DOM에 렌더링 하는 것에 대해 둘러볼 것입니다.

팁:
에디터에서 "Babel" 언어 정의를 사용하는 것을 권장합니다. 그래야 ES6와 JSX코드가 제대로 하이라이팅 될 것입니다. 이 웹사이트는 Oceanic Next 색 스키마를 사용합니다.