JSX(Javascript XML) 란?
보통 리액트를 통해 JSX를 접하기에 JSX가 리액트의 전유물로 생각하는 경우가 많다. 하지만 JSX는 리액트에서 사용하는 것은 맞지만 리액트와는 독립적으로 동작하는 문법이다.
JSX는 ECMAScript 자바스크립트 표준의 일부는 아니기 때문에 코드를 바로 실행하면 에러가 발생한다.
그렇기 때문에 JSX는 반드시 트랜스파일러를 거쳐야 JS 런타임이 이해할 수 있는 JS 코드로 변환 된다.
JSX는 HTML이나 XML을 JS 내부에 표현하는 것이 유일한 목적은 아니며 JSX 내부에 트리 구조로 표현하고 싶은 다양한 것들을 작성해 두고, JSX를 트랜스파일 과정을 거쳐 JS가 이해할 수 있는 코드로 변경하는 것이 목적이다.
JSX의 정의
JSX는 기본적으로 JSXElement, JSXAttributes, JSXChidren, JSXStrings라는 4가지 컴포넌트를 기반으로 구성돼 있다.
JSXElement
JSX를 구성하는 가장 기본 요소로 HTML의 요소와 비슷한 역할을 하며 아래의 형태중 하나를 따라야 한다.
JSXOpeningElement
<JSXElement JSXAttributes(optional)>
JSXClosingElement
</JSXClosingElement>
JSXSelfClosingElement
<JSXElement JSXAttributes(optional)/>
JSXFragment
<>JSXChildren(optional)</>
리액트의 요소명은 반드시 대문자로 시작해야 사용이 가능하다.
JSXElement 표준에 없는 내용지만, 리액트에서 HTML 태그명과 사용자가 만드는 컴포넌트 태그명을 구분하기 위해 이런 방식을 채택했다.
JSXElementName
JSXElement의 요소 이름으로 쓸 수 있는 것을 의미한다.
JSXIdentifier: JSX 내부에서 사용할 수 있는 식별자를 의미하며 JS 식별자 규칙과 동일하다.
function Valid1(){
return <$></$>
}
function Valid2(){
return <_></_>
}
//불가능
function Invalid1(){
return <1></1>
}
JSXNamespacedName: ":"을 이용해 서로 다른 식별자를 이어주는 것도 하나의 식별자로 취급되며 ":"로 묶을 수 있는 것은 한개만 묶을 수 있다.
function valid(){
return <foo:bar></foo:bar>
}
function invalid(){
return <foo:bar:baz></foo:bar:baz>
}
JSXMemberExpression: "."을 통해 서로 다른 식별자를 이어주는 것도 하나의 식별자로 취급되며 여러 개 이어서 하는 것이 가능하다. 하지만 ":"와 "."을 이어서 사용하는 것은 불가능하다.
function valid(){
return <foo.bar></foo.bar>
}
function valid2(){
return <foo.bar.baz></foo.bar.baz>
}
function invalid(){
return <foo:bar.baz></foo:bar.baz>
}
JSXAttribute
JSXElement에 부여할 수 있는 속성을 의미한다. 필수 값은 아니다.
JSXSpreadAttributes : JS의 전개 연산자와 동일한 역활을 한다.
JSXAttribute : 속성을 나타내는 키와 값으로 짝을 이루어서 표현
function Child({attribute}) {
return <div>{attribute}</div>
}
export default function App(){
return (
<div>
<Child attribute=<div>hello</div> />
</div>
)}
아래의 방식처럼 사용하지 않아도 오류는 나지 않는다. 아래의 코드 방식은 prettier의 규칙이다.
<Child attribute={<div>hello</div>} />
JSXChildren
JSXElement의 자식 값을 나타낸다.
JSXStrings
JS에서 \는 특수문자를 처리하기 위해 사용하지만 HTML에서는 \는 아무런 제약 사항이 없다. 이는 JSX는 HTML의 내용을 쉽게 JSX를 가져올 수 있게 의도적으로 설계되었으므로 \를 이스케이프 문자열로 처리하고 있지 않다.
JSX 예제
다음 예제는 모두 유효한 JSX 구조를 띠고 있다.
// 하나의 요소로 구성된 가장 단순한 형태
const ComponentA = <A>안녕하세요.</A>
// 자식이 없이 SelfClosingTag로 닫혀있는 형태도 가능하다.
const ComponentB = <A />
// 옵션을 { } 와 전개 연산자로 넣을 수 있다.
const ComponentC = <A {...{ required: true }} />
// 옵션명만 넣어도 가능하다.
const ComponentD = <A required />
// 옵션명과 속성을 넣을 수 있다.
const ComponentE = <A required={false} />
const ComponentF = (
<A>
{/* 문자열은 쌍따옴표및 홀따옴표 모두 가능하다. */}
<B text="리액트" />
</A>
)
const ComponentG = (
<A>
{/* 옵선의 값으로 JSXElement를 넣는 것 또한 올바른 문법이다. */}
<B optionalChildren={<>안녕하세요.</>} />
</A>
)
const ComponentH = (
<A>
{/* 여러개의 자식도 포함할 수 있다. */}
안녕하세요
<B text="리액트" />
</A>
)
이 외에도 리액트 애에서는 유효하지 않거나 사용되는 경우가 거의 없는 문법도 JSX 문법 자체로는 유효하다.
function ComponetA() {
return <A.B></A.B>
}
function ComponentB() {
return <A.B.C></A.B.C>
}
function ComponentC() {
return <A:B.C></A:B.C>
}
function ComponetD() {
return <$></$>
}
function ComponetE() {
return <_></_>
}
JSX의 변환
JS에서 JSX가 변환되는 방식을 알려면 @babel/plugin-transform-react-jsx 플러그인을 알아야한다.
이 플러그인은 JSX 코드를 아래와 같이 JS코드로 바꿔준다.
이렇게 JSX를 JS 코드로 변환하는데에는 특징이 있다.
- JSXElement를 첫 번째 인수로 선언해 요소를 정의한다.
- 옵셔널인 JSXChildren, JSXAttributes, JSXStrings는 이후 인수로 넘겨주어 처리한다.
위 특성을 활용하면 조건부 랜더링에서 요소 전체를 감싸지 않더라도 처리할 수 있다.
import { createElement, PropsWithChildren } from "react";
function TextOrHeading({
isHeading,
children,
}: PropsWithChildren<{ isHeading: boolean }>) {
return isHeading ? (
<h1 className="text">{children}</h1>
) : (
<span className="text">{children}</span>
);
}
//단순 props여부에 따라 children요소만 달라지는 경우 코드 중복이 일어난다.
import { createElement } from "react";
function TextOrHeading({
isHeading,
children,
}: PropsWithChildren<{ isHeading: boolean }>) {
return createElement(
isHeading ? 'h1' : 'span',
{className : 'text'},
children
)
}
//불필요한 코드중복 X
이렇게 JSX 반환값이 결국 React.createElement로 귀결된다는 사실을 파악한다면 이런 식으로 쉽게 리팩터링할 수 있다.
'React > 모던 리액트 Deep Dive' 카테고리의 다른 글
리액트의 렌더링과 메모이제이션 (1) | 2024.11.08 |
---|---|
클래스 컴포넌트와 함수 컴포넌트 (0) | 2024.11.05 |
가상 DOM과 리액트 파이버 (2) | 2024.10.30 |
[React Deep Dive] 타입스크립트 (1) | 2024.10.17 |
[React Deep Dive] 동등 비교 (0) | 2024.10.08 |