본문으로 건너뛰기
2024. 7. 19·약 5분

TypeScript는 JSX를 어떻게 변환할까요?

TypeScript · JSX · React · 변환 결과 네 장이 소실점 하나를 향해 늘어선 그림

TypeScript의 jsx 옵션은 JSX를 그대로 둘지, React.createElement나 자동 JSX 런타임 호출로 바꿀지를 정합니다. tsconfig.json의 compilerOptions.jsx에 설정하며, 아래 표에서 다섯 가지 옵션의 추가 시점과 출력 방식을 비교합니다.

옵션추가 시점설명
preserve초기부터 존재JSX를 변환하지 않고 그대로 유지.
react초기부터 존재JSX를 React.createElement로 변환.
react-jsxTypeScript 4.1React 17+의 새로운 JSX Transform을 사용하여 jsx 함수 호출로 변환.
react-jsxdevTypeScript 4.1개발 환경에서 디버깅 정보를 추가한 JSX Transform을 사용.
react-nativeTypeScript 2.2React Native 프로젝트에서 사용.

preserve​

  • JSX를 변환하지 않고 그대로 유지합니다.
  • 출력 파일에 JSX가 그대로 남아있습니다.
  • 보통 Babel 등 다른 도구에 JSX 처리를 맡길 때 사용합니다.

입력:

const element = <div>안녕하세요?</div>;

출력:

const element = <div>안녕하세요?</div>;

tsconfig.json 의 jsx 옵션 자동완성 목록. preserve, react, react-jsx 등이 보인다

Next.js는 JSX 변환을 자체 빌드 과정에서 처리하므로 jsx: "preserve"를 사용합니다. 다른 값으로 바꾸면 Next.js가 다시 preserve로 되돌릴 수 있습니다.


react​

  • JSX를 React의 React.createElement 호출로 변환합니다.
  • React 16 이하 또는 React.createElement가 필요한 환경에서 사용됩니다.
  • 이 방식을 사용할 경우 React를 반드시 import해야 합니다.

입력:

import React from "react";
const element = <div>안녕하세요?</div>;

출력:

import React from "react";
const element = React.createElement("div", null, "안녕하세요?");

react-jsx​

  • TypeScript 4.1 이상에서 지원하는 설정으로, React 17+ 및 백포트된 16.14.0/15.7.0/0.14.10 이상에서 사용할 수 있습니다.
  • React의 새로운 JSX 변환 방식(React 17의 JSX Transform)을 사용합니다.
  • 일반적으로 jsx·jsxs 런타임 호출로 변환하므로 JSX만을 위해 React를 import할 필요가 없습니다. 일부 key와 spread 조합에서는 createElement로 변환될 수 있습니다.

입력:

const element = <div>안녕하세요?</div>;

출력:

import { jsx as _jsx } from "react/jsx-runtime";

const element = _jsx("div", { children: "안녕하세요?" });

react-jsxdev​

react-jsx와 같은 자동 런타임을 사용하되 개발용 디버깅 정보를 추가합니다.

  • 디버깅 목적으로 개발 환경(Development Mode)에서 사용합니다.
  • 생성된 JSX 코드에 디버깅에 유용한 정보(예: 파일 이름, 라인 번호 등)를 포함합니다.

입력:

const element = <div>Hello, world!</div>;

출력:

import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";

const element = _jsxDEV(
"div",
{ children: "Hello, world!" },
undefined,
false,
{
fileName: "src/App.js",
lineNumber: 2,
columnNumber: 17,
},
this
);

react-native​

React Native 프로젝트에서 JSX를 처리하기 위한 옵션입니다.

  • preserve와 마찬가지로 JSX를 유지하지만 출력 확장자는 .jsx 대신 .js입니다.
  • TypeScript는 JSX를 변환하지 않고 그대로 유지하며, React Native의 Babel 설정에서 변환을 처리합니다.


자동 JSX 런타임을 도입한 이유​

요약​

  • JSX만을 위해 React를 가져올 필요가 없습니다.
  • 번들에서 불필요한 import를 줄일 수 있습니다.
  • React.createElement에 묶이지 않은 전용 런타임을 통해 이후 변환 방식을 확장할 수 있습니다.

React import가 필요 없는 이유​

JSX Transform 도입 이전에는 JSX를 사용하기 위해 React를 반드시 import해야 했습니다. React 17의 자동 런타임을 사용하면 JSX만을 위해 React를 import할 필요가 없습니다.


1-1. 이전 방식 (React 16 이하)

JSX는 React.createElement로 변환되었기 때문에 React를 반드시 import해야 했습니다:

import React from "react";

const App = () => <div>Hello, world!</div>;

1-2. JSX Transform 도입 이후 (React 17 이상)

React import 없이도 JSX가 동작합니다.

const App = () => <div>Hello, world!</div>;

번들 크기 개선과 향후 최적화 기반​

JSX Transform은 기존 React.createElement 방식의 제약을 걷어내도록 설계되었습니다.


2-1. 기존 방식 (React.createElement)

기존 변환기는 JSX를 다음과 같은 React.createElement 호출로 바꿨습니다.

입력:

import React from "react";

const element = <div>Hello, world!</div>;

출력:

import React from "react";

const element = React.createElement("div", null, "Hello, world!");

이 방식은 JSX를 사용하는 모든 파일에서 React를 import해야 했고, React.createElement가 허용하는 동작 범위가 넓어 React가 내부 최적화를 시도하기 어려웠습니다.


2-2. 새로운 방식 (JSX Transform)

React 17의 JSX Transform은 전용 런타임 함수인 jsx를 호출하는 코드를 생성합니다.

입력:

const element = <div>Hello, world!</div>;

출력:

import { jsx as _jsx } from "react/jsx-runtime";

const element = _jsx("div", { children: "Hello, world!" });

다만 이 변화로 성능이 극적으로 좋아지지는 않습니다. React import가 필요 없어져 개발 경험이 나아지고 번들 크기가 조금 줄었으며, 향후 React가 내부 최적화를 도입할 기반이 마련되었습니다.


2-3. React 17 이후의 새로운 기능 지원

전용 JSX 런타임이 생기면서 이후 React 기능과 변환 방식을 확장할 여지가 생겼습니다.

  • React 팀은 기존 React.createElement API에만 의존하지 않고 변환 결과를 확장할 수 있습니다.
  • 이 런타임은 JSX 컴파일 방식에 관한 것이며, 서버에서 실행되는 컴포넌트 모델인 React Server Components와는 다른 개념입니다.

2-4. 변환 도구와 개발 환경 설정

Babel·SWC·TypeScript 같은 변환 도구에서 자동 런타임을 설정해야 합니다. ESLint도 React import가 필요 없다는 전제에 맞게 구성합니다. Webpack이나 Vite 자체가 이 변환만으로 빌드 속도 향상을 보장하지는 않습니다.



정리​

TypeScript가 JSX 변환까지 맡는 프로젝트라면 react-jsx로 자동 런타임을 사용할 수 있습니다. JSX만을 위한 React import가 필요 없어집니다.

React 프로젝트라도 Next.js처럼 다른 도구가 JSX 변환을 맡는다면 preserve 등 해당 도구가 요구하는 설정을 따릅니다.

옵션설명장점제한사항
preserveJSX를 변환하지 않고 그대로 유지.외부 트랜스파일러와의 호환성TypeScript에서 JSX 처리 안함.
reactJSX를 React.createElement로 변환.React 16 이하와 호환.React Import가 필수.
react-jsxReact 17+의 새로운 JSX Transform을 사용하여 jsx 함수 호출로 변환.React Import 불필요, 최신 방식.React 17+ 및 백포트된 16.14.0, 15.7.0, 0.14.10 이상에서 사용 가능.
react-jsxdevreact-jsx와 비슷하지만, 디버깅 정보를 추가한 JSX Transform을 사용.디버깅에 유용.프로덕션 환경에서는 사용하지 않음.
react-nativeReact Native 환경에서 JSX를 변환하지 않고 그대로 유지.React Native 환경에서 사용 가능.출력된 JSX를 다른 도구에서 변환해야 함.

좋은 사람들과 재미있는 일을 하며 열정적이고 즐겁게 살고 싶은 개발자