JSX는 React 팀이 사용자 인터페이스를 선언적으로 설명하기 위해 개발한 JavaScript용 XML 유사 구문 확장입니다. 템플릿 엔진과 달리 JSX는 일반 JavaScript 함수 호출로 컴파일되어 마크업 내에서 언어의 모든 기능을 활용할 수 있습니다. React, 2024에 따르면 JSX는 모든 최신 React 프레임워크와 라이브러리에서 지원됩니다.
핵심 포인트
JSX(JavaScript XML)는 JavaScript용 구문 확장으로, 2013년 React에서 처음 도입되었습니다. 개발자가 선언적 스타일로 사용자 인터페이스의 구조를 설명하고 마크업과 로직을 하나의 파일에 결합할 수 있게 해줍니다.
JSX 이전에는 개발자가 문자열 템플릿(Mustache, Handlebars)을 사용하거나 document.createElement를 통해 수동으로 DOM 요소를 만들었습니다. 두 접근 방식 모두 단점이 있었습니다: 문자열 템플릿은 타입 검사가 되지 않았고, 수동 DOM 생성은 번거롭고 오류가 발생하기 쉬웠습니다.
React는 다른 길을 선택했습니다 — 코드에 직접 마크업을 포함시키는 것입니다. 이 결정은 커뮤니티에서 논란을 불러일으켰지만, 시간이 지나면서 표준이 되었습니다. 오늘날 JSX는 React뿐만 아니라 SolidJS, Preact, Qwik에서도 사용되며 Vue에서도 플러그인을 통해 사용됩니다.
JSX 요소는 HTML 태그처럼 보이지만 JavaScript에 직접 포함됩니다. 각 요소는 세 가지 인수(요소 유형, props 객체, 자식)를 가진 React.createElement 호출로 트랜스파일됩니다. 하나의 루트 요소는 필수 규칙입니다: JSX 표현식은 하나의 부모 요소 또는 프래그먼트를 반환해야 합니다.
import React from 'react';
// 기본 JSX - 단일 루트 요소
const element = (
<div className="app">
<h1>Hello, World!</h1>
<p>Welcome to React</p>
</div>
);
추가 DOM 노드가 필요하지 않은 경우 React.Fragment 또는 단축 구문 <></>를 사용하세요. 프래그먼트는 실제 DOM 요소를 생성하지 않고 자식 노드만 그룹화합니다. 이는 추가 래퍼가 HTML 구조를 깨뜨릴 수 있는 테이블과 리스트에서 특히 유용합니다.
function Columns() {
return (
<>
<td>First</td>
<td>Second</td>
</>
);
}
모든 JavaScript 표현식은 중괄호 { }를 통해 JSX에 포함됩니다. 내부에서는 변수, 함수 호출, 삼항 연산자 및 배열 메서드를 사용할 수 있습니다. 조건부 렌더링은 삼항 연산자 또는 논리 AND를 통해 수행됩니다.
function Greeting({ name, isLoggedIn }) {
const items = ['Red', 'Green', 'Blue'];
return (
<div>
{/* Expression with variable */}
<h1>Hello, {name}</h1>
{/* Conditional rendering */}
{isLoggedIn ?
<p>Welcome back!</p :
<button>Login</button>
}
{/* Render array */}
<ul>
{items.map(item =>
<li key={item}>{item}</li>
)}
</ul>
</div>
);
}
핵심 제한 사항 — 중괄호 내에서는 표현식만 사용할 수 있고 명령문은 사용할 수 없습니다. if/else, for, switch를 사용할 수 없으며 삼항 연산자, 논리 연산자 및 배열 메서드만 사용할 수 있습니다. 화살표 함수를 포함한 함수 호출이 허용됩니다.
JSX는 시각적으로 HTML과 유사하지만 근본적인 차이점이 있습니다. 속성은 camelCase로 작성됩니다 — class 대신 className, onclick 대신 onClick, tabindex 대신 tabIndex. 이는 JSX가 JavaScript로 트랜스파일될 때 속성 이름에 하이픈을 사용할 수 없기 때문입니다.
| HTML | JSX | 이유 |
|---|---|---|
class="wrapper" | className="wrapper" | class는 JS의 예약어 |
onclick="handle()" | onClick={handle} | JS 속성의 camelCase |
style="color: red" | style={{ color: ‘red’ }} | 문자열 대신 스타일 객체 |
<input disabled> | <input disabled={true}> | 명시적 불리언 속성 |
또 다른 중요한 차이점은 자체 닫기 태그입니다. HTML에서는 일부 태그(br, hr, input)를 닫지 않고 남겨둘 수 있습니다. JSX에서는 모든 자체 닫기 태그에 닫는 슬래시가 있어야 합니다: <br />, <input />. SVG 속성 이름도 camelCase로 변환됩니다: strokeWidth, fillOpacity.
브라우저는 JSX를 직접 이해하지 못합니다. 실행 전에 JSX 코드는 트랜스파일러 — Babel, TypeScript 또는 esbuild를 거칩니다. 각 JSX 요소는 createElement 또는 jsx/runtime 호출로 변환됩니다.
// 원본 JSX
const element = <h1 className="title">Hello</h1>;
// 트랜스파일 후
const element = React.createElement(
'h1',
{ className: 'title' },
'Hello'
);
React 17부터 React를 가져올 필요가 없는 새로운 JSX 변환이 도입되었습니다. jsx-runtime의 자동 가져오기를 통해 import React from ‘react’ 없이 컴포넌트를 작성할 수 있습니다. Babel과 TypeScript는 {"runtime": "automatic"} 구성을 통해 이 변환을 지원합니다.
JSX가 React와 연관되어 있지만 다른 프레임워크에서도 사용합니다. SolidJS는 Virtual DOM 없이 실제 DOM 노드로 컴파일되는 JSX를 사용합니다. Preact는 완전한 JSX 지원을 제공하는 React의 경량 대안입니다. Vue는 @vue/babel-plugin-jsx를 통해 JSX를 지원하지만 주요 템플릿은 template입니다.
핵심 차이점은 표현식 의미론입니다. React에서는 JSX 표현식이 매 렌더링마다 실행됩니다. SolidJS에서는 JSX가 한 번 컴파일되고 업데이트가 신호를 통해 추적됩니다. 이는 성능에 영향을 미칩니다: SolidJS는 Virtual DOM과 재조정이 필요하지 않아 JSX 렌더링이 더 빠릅니다.
TypeScript에서는 JSX가 언어 수준에서 지원됩니다. .tsx 확장자를 가진 파일은 자동으로 JSX 구문을 처리합니다. Props 타입 지정은 TypeScript + JSX의 주요 장점 중 하나입니다: IDE가 예상 속성, 콜백 유형 및 props 필수 여부를 제안합니다.
읽고 유지 관리하기 쉬운 JSX 코드를 작성하세요. 복잡한 표현식을 별도의 변수나 함수로 추출하면 가독성이 향상되고 마크업과 별도로 로직을 테스트할 수 있습니다. 불필요한 div 래퍼 대신 프래그먼트를 사용하세요.
들여쓰기 규칙 — 중첩된 요소에 대한 들여쓰기를 유지하세요. 각 중첩 수준은 하나의 들여쓰기 수준입니다. 닫는 태그는 여는 태그와 같은 수준에 있어야 합니다. 긴 속성에는 줄 바꿈을 사용하세요.
매 렌더링마다 생성되는 props의 인라인 화살표 함수를 피하세요. 이러한 경우 콜백을 useCallback으로 감싸거나 함수를 컴포넌트 외부로 이동하세요. 복잡한 컴포넌트는 더 작은 것으로 분할하세요 — 각 컴포넌트는 인터페이스의 하나의 논리 단위를 처리해야 합니다.
자주 묻는 질문
네, Babel 플러그인 @babel/plugin-transform-react-jsx를 통해 JSX를 모든 라이브러리용으로 구성할 수 있습니다. SolidJS, Preact, Nerv 및 기타 프레임워크는 자체 런타임으로 JSX를 사용합니다. 완전한 독립성을 위해 pragma를 자체 함수로 설정할 수 있습니다.
class는 JavaScript의 예약어입니다. JSX는 createElement로 트랜스파일되며, 두 번째 인수는 props 객체를 전달합니다. class 속성은 ES6 클래스 구문과 충돌하므로 React가 className을 선택했습니다.
템플릿 문자열 또는 classnames 라이브러리를 사용하세요: className={`btn ${isActive ? ‘active’ : ‘’}`}. 복잡한 로직의 경우 객체와 배열을 허용하는 npm 패키지 clsx 또는 classnames를 설치하세요.
JSX는 표현식만 지원하고 명령문은 지원하지 않습니다. 조건에는 삼항 연산자, 논리 AND (&&) 또는 논리 OR (||)을 사용하세요. 복잡한 로직의 경우 조건을 별도의 변수나 함수로 추출하세요.
key 속성은 React가 다시 렌더링할 때 리스트 요소를 식별하는 데 도움을 줍니다. key가 없으면 React는 변경 시 전체 리스트를 다시 렌더링합니다. 고유한 key를 사용하면 React가 DOM 노드를 재사용하고 요소를 다시 만들지 않고 재정렬합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.