앞서의 두 포스팅은 Quick Start에 있는 내용이었고 이번 번역할 JSX In Depth는 Advanced Guides에
있는 내용이입니다. 하지만 JSX를 한 번에 살펴보는 것이 좋을 것 같아 바로 이 JSX In Depth를 번역합니다.
JSX 심화
기본적으로 JSX는 단지 React.createElement(component, props, ...children)
함수에 대한
문법적인 편의성만을 제공한다. 아래 JSX 코드를 보자
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
이 코드는 컴파일 후 다음과 같이 바뀐다.
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
만일 태그에 자식이 없다면 아래 코드와 같이 self-closing 태그(/>)를 사용할 수 있다.
<div className="sidebar" />
이 코드는 아래와 같이 컴파일된다.
React.createElement(
'div',
{className: 'sidebar'},
null
)
특정 JSX가 컴파일 후 어떻게 javascript 코드로 바뀌는지를 테스트해보고 싶다면 the online Babel compiler
를 사용하면 된다.
Specifying The React Element Type
JSX 태그의 처음 부분은 React element의 type을 결정한다.
대문자로 시작된 JSX 태그는 React Component를 가리킨다. 이러한 태그들은 해당 이름으로 된 변수들의
직접적인 참조로 컴파일되기 때문에 만일 <Foo />
라는 JSX 표현식을 사용하게 되면 Foo
라는 객체는
같은 scope 내에 있어야 한다.
React Must Be in Scope
JSX가 React.createElement
함수를 호출하도록 컴파일되기 때문에 React
라이브러리는 JSX 코드의
scope 내에 있어야 한다.
예를 들면 아래 코드는 비록 React
와 CustomButton
이 javascript로부터 직접 참조되지 않더라도
2개의 import문이 모두 필요하다.
mport React from 'react';
import CustomButton from './CustomButton';
function WarningButton() {
// return React.createElement(CustomButton, {color: 'red'}, null);
return <CustomButton color="red" />;
}
자바 스크립트 번들을 사용하지 않고 React를 스크립트 태그로 추가했다면 이미 React
글로벌의 scopre 내에
있다고 할 수 있을 것이다.
Using Dot Notation for JSX Type
JSX 내에서 React 컴포넌트는 점(.) 표시로 참조될 수 있다. 이 것은 많은 React 컴포넌트를 추출할 수 있는
단일 모듈을 사용할 때 편리하다. 예를들면 만일 MyComponents.DatePicker
가 컴포넌트라면 아래 예제와
같이 JSX에서 직접 사용 가능하다
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" />;
}
User-Defined Components Must Be Capitalized
element가 소문자로 시작한다면 이것은 <div>
나 <span>
같은 기존 html 태그로 참조하게 될 것이고 그 결과
React.createElement에 ‘div’
나 ‘span’
같은 문자열로 전달될 것이다.
하지만 <Foo />
와 같이 대문자로 시작을 한다면 React.createElement(Foo)
형태로 컴파일 될 것이고
작성된 javascript 파일에서 선언되거나 import된 component와 상응하게 될 것이다.
따라서 component 이름은 대문자로 시작하는 것을 권장한다. 만일 작성된 코드에 소문자로 시작되는
component가 존재한다면 그것을 JSX에 사용하기 전에 대문자로 시작되는 변수에 먼저 할당을 해야 한다.
예를들면 아래 코드는 정상적으로 작동하지 않을 것이다.
import React from 'react';
// Wrong! This is a component and should have been capitalized:
function hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
return <hello toWhat="World" />;
}
위의 코드를 수정하기 위해서는 hello
라는 이름을 Hello
로 바꾸고 <Hello />
로 그 것을 참조해야 한다.
import React from 'react';
// Correct! This is a component and should be capitalized:
function Hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Correct! React knows <Hello /> is a component because it's capitalized.
return <Hello toWhat="World" />;
}
Choosing the Type at Runtime
일반적인 표현식을 React element type으로 사용할 수 없다. 만일 일반적인 표현식을 element type으로
사용하고 싶다면 먼저 그 표현식을 대문자로 시작하는 변수에 할당하여야 한다. 이러한 상황은 종종 prop을
기반으로 하여 서로 다른 컴포넌트를 렌더링하고자 할 때 발생한다.
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Wrong! JSX type can't be an expression.
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) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
Props in JSX
JSX에서 prop을 지정하는 방법에는 몇가지가 있다.
JavaScript Expressions
javascript의 표현식을 {}
로 둘러 싸서 prop으로 전달할 수 있다. 아래 예에서 보자.
<MyComponent foo={1 + 2 + 3 + 4} />
MyComponent
에서 props.foo
는 10
이 된다. 1+2+3+4
가 {}
둘러싸여 javascript 연산으로 평가되었기
때문이다.
if
문이나 for
루프는 javascript의 표현식이 아니기 때문에 JSX 내에서 직접 사용할 수는 없다. 대신에 다음의
예제처럼 코드를 이용하여 처리할 수 있다.
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} is an {description} number</div>;
}
String Literals
문자열도 prop으로 전달할 수 있다. 아래의 두 JSX표현은 동일한 것이다.
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
문자열로 전달할 때 그 문자열은 HTML escaping 처리되지 않는다. 따라서 아래 두 코드도 동일하다.
<MyComponent message="<3" />
<MyComponent message={'<3'} />
이런 동작들 사이에 서로 관련성은 없으며 다만 완벽한 설명을 위해 언급된 것 뿐이다.
Props Default to "True"
만일 prop에 아무런 값을 넘기지 않는다면 prop은 기본적으로 true
라는 값을 갖게 된다. 아래의 두
JSX 표현식은 동일하다.
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
일반적으로 이러한 표현은 ES6의 축약 표현에서 {foo}
가 {foo: foo}
를 의미하기 때문에 권장하지 않는다.
대신에 {foo: true}
와 같이 명시적으로 사용해야 한다. 이러한 동작은 단지 HTML의 동작과 일치하도록 하기
위한 것일 뿐이다.
Spread Attributes
이미 object 형태의 props
이 존재하고 이것을 JSX로 전달하려고 한다면 전체 props object를 모두
전달하기 위한 전개(spread) 연산자로 …
를 사용할 수 있다. 아래의 두 컴포넌트는 동일한 동작을 한다.
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
전개(Spread) 연산자는 generic containers를 만들때 유용하다. 그러나 이러한 사용이 코드를 더 복잡하게
만들 수도 있다. 다수의 관련 없는 props를 컴포넌트에 넘김으로써 관리하기가 힘들어질 수 있기 때문이다.
따라서 이러한 문법은 반드시 필요한 부분에만 적절하게 사용하여야 한다.
Children in JSX
여는 태그와 닫는 태그가 있는 JSX 표현에서 이 두 태그 사이에 있는 내용은 props.children
라는 특별한
prop으로 전달된다. children을 전달하는데는 몇가지 다른 방법이 있다.
String Literals
우선 여는 태그와 닫는 태그 사이에 문자열을 넣을 수 있으며, 이 때의 props.children
는 단지 문자열로만
취급된다. 이러한 방식은 기존 HTML 요소를 사용하는 경우 유용하다. 아래의 예를 보자
<MyComponent>Hello world!</MyComponent>
이 코드는 유효한 JSX 표현식이며 MyComponent
의 props.children
의 값은 단순히 “Hello world!”라는
문자열이다. HTML은 escaping되지 않으므로 일반적으로 아래 코드와 같이 HTML을 작성하듯이 JSX를 작성할
수 있다.
<div>This is valid HTML & JSX at the same time.</div>
JSX는 각 줄의 첫 번째와 마지막에 있는 공백은 모두 제거한다. 또한 비어있는 줄도 제거한다. 태그에 인접한
줄바꿈도 제거된다. 문자열 중간에 있는 줄바꿈은 하나의 공백으로 처리된다. 이런 이유로 아래의 모든 코드는
동일하다.
<div>Hello World</div>
<div>
Hello World
</div>
<div>
Hello
World
</div>
<div>
Hello World
</div>
JSX Children
JSX elements도 children으로 사용을 할 수 있으며 이러한 표현은 중첩된 컴포넌트를 표현하는데 유용하다.
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
서로 다른 유형의 children을 함께 사용할 수 있기 때문에 문자열 children을 JSX children과 함께 사용할 수
있다. 이러한 방식은 JSX를 HTML과 유사하게 사용할 수 있는 또다른 방법이며 따라서 아래의 코드는 JSX로도
또 HTML로도 유효한 표현식이다.
<div>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
React 컴포넌트는 다수의 React elements를 리턴할 수 없다. 하지만 하나의 JSX 표현식에는 다수의
children이 있을 수 있기 때문에 다수의 요소로 컴포넌트를 렌더링 하고 싶다면 위의 코드와 같이 div
로
씌운 중첩된 구조를 만들면 된다.
JavaScript Expressions
{}
로 감싼다면 어떠한 javascript 표현식이라도 children으로 전달할 수 있다. 아래 예제들은 동일한 코드이다.
<MyComponent>foo</MyComponent>
<MyComponent>{'foo'}</MyComponent>
이러한 방식은 임의의 길이를 갖는 JSX 표현식의 목록을 렌더링 하는데 유용하다. 아래 예제는 HTML 목록을
렌더링하는 코드이다.
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
javascript 표현식은 다른 유형의 children과 함께 섞어서 사용할 수 있다. 이 것은 문자열 template 대신에
사용하면 유용하다.
function Hello(props) {
return <div>Hello {props.addressee}!</div>;
}
Functions as Children
보통 JSX에 삽입되는 javascript 표현식은 문자열이나 React element나 이들의 목록으로 평가된다. 그러나props.children
은 렌더링 방법에 대해 알 필요가 없다는 점 뿐만 아니라 어떤 종류의 데이터도 전달할 수
있다는 점에서 다른 종류의 prop과 동일하게 작동한다. 예를들어 컴포넌트에서 callback을 props.children
으로 사용할 수도 있는 것이다.
// Calls the children callback numTimes to produce a repeated component
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>
);
}
커스텀 컴포넌트에 전달된 children은 렌더링이 진행되기 전에 React가 해석할 수 있는 것이라면 어떤 형태로도
바뀔 수 있다. 이러한 방식은 일반적인 사용법은 아니지만 JSX가 수행할 수 있는 작업을 확장하고자 할 때 유용하게
사용될 수 있다.
Booleans, Null, and Undefined Are Ignored
false
, null
, undefined
그리고 true
등도 모두 유효한 children들이다. 이 것들은 별도로 렌더링 하지
않는다. 아래 JSX 표현식들은 모두 동일하게 렌더링된다.
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
이러한 표현들은 React elements를 조건에 따라 렌더링할 때 유용하다. 아래 예제는 만일 showHeader가
true이면 <Header />
만 렌더링한다.
<div>
{showHeader && <Header />}
<Content />
</div>
한 가지 주의할 점은 숫자 0
과 같이 false를 의미하는 듯한 값은 React에서 렌더링 된다는 점이다. 예를들면
아래의 코드에서 만일 props.messages
가 비어있는 배열이라면 props.messages.length
는 0
이
되어 false로서 기능하는 것이 아니라 0
이 출력되게 된다.
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
반대로 false
, true
, null
그리고 undefined
과 같은 것들을 화면상에 출력하기 위해서는 먼저
이것들을 문자열로 변환해야 한다.
<div>
My JavaScript variable is {String(myVariable)}.
</div>
'Study > React' 카테고리의 다른 글
React 살펴보기 #6 (0) | 2017.03.27 |
---|---|
React 살펴보기 #5 (0) | 2017.03.04 |
React 살펴보기 #4 (0) | 2017.02.17 |
React 살펴보기 #2 (0) | 2017.02.06 |
React 살펴보기 #1 (0) | 2017.02.06 |