한줄 요약
JSX는 React 에서 제공하는
React.createElement()
함수에 대한 문법적 설탕이다!JSX 란?
React 에서 javascript 에 마크업을 넣을 수 있게 해주는 확장 문법이다. JSX는 React에서 제공하는
React.createElement()
함수에 대한 문법적 설탕을 제공할 뿐이다. JSX 코드는 Babel 과 같은 transcompiler에 의해 (@babel/preset-react 라는 Babel의 plug-in 에 의해) 컴파일 되어 javascript 코드로 변환 된다. 다음과 같은 코드는
const element = ( <h1 className="greeting"> Hello, world! </h1> );
Babel 에 의해 다음과 같이 컴파일 된다.
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
React.createElement()
React.createElement( type, [props], [...children] )
인자로 주어지는 type 에 따라 React 엘리먼트를 생성해서 반환하는 함수이다.
type
은 태그의 이름 문자열( ‘div’
, ‘span’
등), React 컴포넌트 타입, 또는 React Fragment 타입 중 하나가 올 수 있다.[props]
는 컴포넌트의 props 들이고, […children]
은 태그 하위의 자식들이다.JSX 특징
- JSX의 html 태그 와 같아 보이는 것은 엘리먼트 라고 하며, javascript 상에서 plain object로 취급 된다.
- React.createElement() 는 버그가 없는 코드를 작성하는데 도움이 되도록 몇가지 검사를 수행하며, 다음과 같은 객체를 생성한다.
// 주의: 다음 구조는 단순화되었습니다. const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };
- javascript 표현식을 포함 할 수 있다. JSX 자체도 표현식으로 취급된다.
- React DOM 은 HTML 어트리뷰트를 직접 사용하지 못하고, camelCase로 프로퍼티를 이름 짓는다. 예를 들어 JSX 에서는 class 가
className
이 되고, tabindex는tabIndex
가 된다.
- HTML 태그 처럼 자식을 포함할 수 있고, 태그가 비어있다면,
/>
를 이용해 바로 닫아주어야 한다.
- JSX는 주입 공격을 방지한다.
- React DOM 은 JSX 에 삽입된 모든 값을 렌더링하기 전에 이스케이프 하기 때문에, 애플리케이션 단에서 명시적으로 작성되지 않은 내용은 중입되지 않는다. 그래서 XSS 공격을 방지할 수 있다.
JSX에서의 React Element 타입 지정시 유의사항
JSX 태그의 첫 부분은 React element의 타입을 결정한다. (아래 코드에서의 MyComponent 부분)
<MyComponent />
- React가 스코프 내에 존재해야 한다.
- React.createElement() 를 사용하기 때문에, React 를 import 해야 한다.
import React from 'react'; import CustomButton from './CustomButton'; function WarningButton() { // return React.createElement(CustomButton, {color: 'red'}, null); return <CustomButton color="red" />; }
코드 상에서 실제로 React 는 사용되지 않지만, 주석문에서 나와있는 것과 같이 컴파일 되기 때문에 React 가 import 되어야 한다.
- JSX 타입을 위한 점표기법 사용
- JSX 내에서도 점 표기법을 사용하여 React 컴포넌트를 참조 할 수 있다.
import React from 'react'; const MyComponents = { DatePicker: function DatePicker(props) { return <div>Imagine a {props.color} datepicker here.</div>; } } function BlueDatePicker() { return <MyComponents.DatePicker color="blue" />; }
- 사용자 정의 컴포넌트는 반드시 대문자로 시작해야한다.
- 엘리먼트가 소문자로 시작하는 경우에는
<div>
나<span>
같은 내장 컴포넌트 라는 것을 뜻하며, 컴파일 될 때‘div’
나‘span’
같은 문자열 형태로React.createElement
에 전달된다.<Foo/>
와 같이 대문자로 시작하는 경우에는React.createElement(Foo)
의 형태로 컴파일 되며 javascript 파일 내에 사용자가 정의했거나 import 한 컴포넌트를 가리킨다. - 즉, 컴파일 될 때, 소문자로 시작하는 경우와 대문자로 시작하는 경우 처리가 다르기 때문에 반드시 대문자로 시작해야 한다.
4. 실행 중에 사용자 정의 컴포넌트 선택하는 방법
React element 타입에 일반적인 표현식은 사용할 수 없다.
import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // 잘못된 사용법입니다! JSX 타입은 표현식으로 사용할 수 없습니다. return <components[props.storyType] story={props.story} />; }
하지만, 타입을 대문자로 시작하는 변수에 지정하면 가능하다.
import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // 올바른 사용법입니다! 대문자로 시작하는 변수는 JSX 타입으로 사용할 수 있습니다. const SpecificStory = components[props.storyType]; return <SpecificStory story={props.story} />; }
JSX 안에서의 prop 사용시 유의사항
- javascript 표현식을 props 에서 사용할 수 있다.
- 문자열 리터럴
- 문자열 리터럴을 prop 으로 넘겨줄 수 있다.
//두 표현은 동일한 표현 <MyComponent message="hello world" /> <MyComponent message={'hello world'} />
문자열 리터럴을 넘겨줄 때, 그 값은 HTML 이스케이프 처리가 되지 않는다.
//두 표현은 동일한 표현 <MyComponent message="<3" /> <MyComponent message={'<3'} />
- props 의 기본값은 true
- prop에 어떤 값도 넘기지 않을 경우, 기본 값은 true로 들어간다.
- props 에 spread 연산자로 객체 전체를 넘길 수 있다.
//두 표현은 동일한 표현 function App1() { return <Greeting firstName="Ben" lastName="Hector" />; } function App2() { const props = {firstName: 'Ben', lastName: 'Hector'}; return <Greeting {...props} />; }
JSX 에서 자식사용시 유의사항
JSX 에서 여닫는 태그 사이의 엘리먼트 들은
props.children
이라는 특수한 prop 으로 넘겨진다.JSX 에서 자식은 다음과 같은 타입들이 될 수 있고 유의점은 다음과 같다.
- 문자열 리터럴
- 다음과 같은 문자열이 태그 사이에 있다면 props.children 에는 “Hello world!” 라는 문자열이 들어가게 된다. HTML 은 이스케이프 처리가 되지 않는다.
<MyComponent>Hello world!</MyComponent>
- JSX
- JSX 엘리먼트를 자식으로 넘겨줄 수 있다. 엘리먼트들로 이루어진 배열도 가능하다.
render() { // 리스트 아이템들을 추가적인 엘리먼트로 둘러쌀 필요 없습니다! return [ // key 지정을 잊지 마세요 :) <li key="A">First item</li>, <li key="B">Second item</li>, <li key="C">Third item</li>, ]; }
- javascript 표현식
- 함수
- 함수 또한 자식으로 활용 할 수 있다.
// 자식 콜백인 numTimes를 호출하여 반복되는 컴포넌트를 생성합니다. function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); }
- boolean, null, undefined
- 유효한 표현이지만 렌더가 되지 않을 뿐이다.
//아래의 JSX 표현식들은 동일하게 렌더링됩니다. <div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{undefined}</div> <div>{true}</div>
해당 타입을 화면에 출력하고 싶다면 문자열로 전환해야 한다.
<div> My JavaScript variable is {String(myVariable)}. </div>
17 버전에서의 새로운 방식의 JSX Transform
JSX 는 17 버전에서 새로운 방식으로 transform 할 수 있게 된다. 새 transform 방식은 완전히 선택적으로 사용 할 수 있으며, 좀 더 개선된 점이 있다.
- React 를 import 하지 않아도 JSX 를 사용 할 수 있다.
- setup에 따라서, 컴파일된 결과가 번들 사이즈를 살짝 개선 시켜 줄 수 있다.
- React 에 대한 당신이 배워야할 컨셉들을 줄여줄 것이다.
참조의 Introducing the New JSX Transform 문서를 통해 자세한 적용 방법이나 개선점을 확인 할 수 있다. 사용성에서는 React를 import 부분 외에는 크게는 달라지지 않은듯 하다.
참조