
이 글은 모던 리액트 Deep Dive를 기반으로 작성되었습니다.
JSX는 페이스북에서 독자적으로 개발한 구문으로 흔히들 리액트에서 사용하는데 이는 리액트의 전유물이 아닌 XML과 유사한 내장형 구문으로, 리액트에 종속적이지 않은 독자적인 문법으로 보는것이 옳다.
페이스북에서 독자적으로 개발했기에 JSX는 ECMAScript로 보기는 어렵다. 따라서,다음과 같이 JSX를 포함한 코드를 실행시키면 에러가 나온다. JSX는 반드시 트랜스파일러를 거쳐 자바스크립트 코드로 변환되어야한다.
<!-- JSX가 포함된 코드-->
const Component = (
<div className="hello">
<input type="text" value="hello">
</div>
)
JSX는 JSXElement, JSXAttributes, JSXChildren, JSXStrings로 구성되어있다. 이상한 영어로 되어있지만, HTML 구조와 비슷하다.
Element를 구성하는 방법은 대표적으로 아래와 같다.
자식요소가 없는데 Self-Closing을 하지않으면 ESlint에 잡힌다.
<!-- 자식 요소를 가질 때 -->
<JSXElement>Child</JSXElement>
<!-- 자식 요소가 없을 때 : Self-Closing -->
<JSXElement/>
1) 요소의 이름은 HTML 태그명과 구분짓기 위해 첫 글자는 무조건 대문자로 시작한다.
2) 이름을 지을 수 있는 규칙은 자바스크립트 식별자 규칙과 동일하다.
<!-- 가능 -->
function Valid1() {
return <$></$>
}
function Valid2() {
return <_></_>
}
<!-- 불가능 -->
function InValid() {
return <1></1>
}
<!-- :를 통해서도 가능 대신, 한 번만 가능 -->
function Valid() {
return <foo:bar></foo:bar>
}
<!-- 불가능 -->
function InValid() {
return <foo:bar:baz></foo:bar:baz>
}
<!-- .를 통해서도 가능 대신, :와 함꼐 사용 안됨 -->
function Valid1() {
return <foo.bar></foo.bar>
}
function Valid2() {
return <foo.bar.baz></foo.bar.baz>
}
<!-- 불가능 -->
function InValid() {
return <foo.bar:baz></foo.bar:baz>
}
속성을 의미하며, 필수 값이 아니며 자바스크립트의 표현식이 들어갈 수 있다.
<Child attribute={<div>hello</div>}/> <!-- Self-Closing -->
JSXElement의 자식 값을 나타낸다. 다른 JSX의 요소가 들어가거나 자바스크립트의 표현식이 들어갈 수 있다.
export default function SampleComponent() {
return <>{'{} <>'}</>
}
HTML에서 사용 가능한 문자열은 모두 JSXStrings에서도 사용 가능하다. 자바스크립트와의 차이점은 \의 경우 자바스크립트에서는 특수 문자를 위해 사용되기에 \\로 사용하여야 한다는 것
export default function SampleComponent() {
return <>{'{} <>'}</>
}
@babel/plugin-transform-react-jsx 플러그인을 통해 변환된다.
const ComponentA = <A required={true}>Hello World</A>
const ComponentB = <>Hello World></>
const ComponentC = (
<div>
<span>Hello World</span>
</span>
)
.
.
.
<!-- 변환 -->
var ComponentA = React.createElement(
A,
{
required: true,
},
'Hello World',
)
var ComponentB = React.createElement(React.Fragment, null, 'Hello World')
var ComponentC = React.createElement(
'div',
null,
React.createElement('span', null,'Hello World'),
)
1) JSXElement를 첫 번째 인수로 선언하여 요소를 정의함
2) 옵셔널인 JSXChildren, JSXAttributes, JSXStrings는 이후 인수로 넘김
<!-- 코드 중복이 일어남 -->
function TextOrHeading({
isHeading,
children,
}: PropsWithchildrend<{ isHeading: boolean }>) {
return isHeading ? (
<h1 className="text">{children}<h1>
) : (
<span className="text">{children}<span>
)
}
.
.
.
<!-- 중복을 최소화하여 리팩토링 -->
function TextOrHeading({
isHeading,
children,
}: PropsWithchildrend<{ isHeading: boolean }>) {
return createElement(
isHeading ? 'h1' : 'span',
{ className: 'text'},
children,
)
}
위와 같은 코드처럼 JSX파일이 어떻게 변환되는지 알아둔다면 도움이 될 수 있다.
때에 따라서는 직접 createElment를 사용하여 컴포넌트를 구성하는게 더 효율적일 수 있다.