TypeScript가 JSX를 변환하는 방법들과 JSX Transform에 대해

Wonkook Lee·2025년 1월 13일
1

Libraries and Frameworks

목록 보기
1/3
post-thumbnail

TypeScript가 JSX를 변환하는 방법들과 JSX Transform에 대해

TypeScript 4.1 이상부터는 JSX 코드를 트랜스파일링하기 위한 다양한 옵션을 제공합니다. 이러한 옵션은 React의 다양한 환경(예: React 16, React 17+, React Native)에 맞춰 JSX를 효율적으로 처리하는 데 사용됩니다. 이번 글에서는 TypeScript가 JSX를 처리하는 방식과 React 17에서 도입된 새로운 JSX Transform의 이점을 설명합니다.

  • preserve
  • react
  • react-jsx
  • react-jsxdev
  • react-native

이 옵션들은 tsconfig.json 파일의 compilerOptions에 설정하는 jsx 속성에서 사용할 수 있습니다. 설정에 따라 TypeScript가 JSX 코드를 변환하거나 유지하는 방식이 달라집니다.

아래는 옵션별 TypeScript 스펙 추가 시점과 간략한 설명입니다.

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

preserve

  • JSX를 변환하지 않고 그대로 유지합니다.
  • 출력 파일에 JSX가 그대로 남아있습니다.
  • 보통 Babel 등 다른 도구에서 JSX를 처리하도록 위임하는 경우 사용합니다.
// 입력
const element = <div>안녕하세요?</div>;

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

Next.js에서 tsconfig.json의 ‘jsx’ 옵션을 ‘preserve’로 설정하지 않으면 자동으로 ‘preserve’로 바꿔줍니다.
그 이유는 Next.js에서 자체 구현한 JSX transform을 제공하기 때문입니다.


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 이상에서 지원하는 설정으로, React17 이상에서 사용됩니다.
  • React의 새로운 JSX 변환 방식(React 17의 JSX Transform)을 사용합니다.
  • React.createElement를 사용하지 않으며, React를 import하지 않아도 됩니다.
// 입력
const element = <div>안녕하세요?</div>;

// 출력
import { jsx as _jsx } from 'react/jsx-runtime';

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

react-jsxdev

React 17+의 새로운 JSX Transform을 사용하면서 디버깅 정보를 추가하기 위해 사용합니다.

  • 디버깅 목적으로 개발 환경(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를 처리하기 위한 옵션입니다.

  • React Native 환경에서는 브라우저 DOM 대신 Native Components를 사용하므로 JSX 변환 방식이 다릅니다.
  • TypeScript는 JSX를 변환하지 않고 그대로 유지하며, React Native의 Babel 설정에서 변환을 처리합니다.


JSX를 변환하는 방법이 React.createElement에서 JSX Transform으로 변경된 이유

요약

  • React Import 필요성을 제거하여 개발자 경험(DX)을 개선.
  • 런타임 성능 최적화를 통해 더 빠른 실행과 적은 오버헤드를 제공.
  • 향후 React 기능 확장을 지원하는 유연성과 호환성 확보.
  • 코드가 더 간결하고 유지보수하기 쉬워짐.

1. React Import 불필요

JSX Transform 도입 이전에는 JSX를 사용하기 위해 React를 반드시 import해야 했습니다. 그러나 React 17부터는 React Import 없이 JSX를 사용할 수 있도록 변경되었습니다.


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>;

2. 성능 최적화

JSX Transform은 기존 React.createElement 방식보다 더 효율적으로 동작하도록 설계되었습니다.


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

JSX를 사용할 때마다 React는 다음과 같이 동작했습니다:

import React from 'react';

const element = <div>Hello, world!</div>;
// 컴파일 후
const element = React.createElement('div', null, 'Hello, world!');

이 방식은 런타임에서 React.createElement를 호출하여 객체를 생성하므로 추가적인 오버헤드가 있었습니다.

여기서 말하는 오버헤드란 아래와 같습니다.

  • 런타임에서 createElement가 호출될 때마다 연산이 반복
  • 추가적인 React 객체 참조로 불필요한 메모리 소비
  • React 객체 내 다른 API가 번들에 포함될 가능성이 있음 (no tree shaking)

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

React 17의 JSX Transform은 런타임 오버헤드를 줄이기 위해 더 효율적인 코드를 생성합니다:

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

// 컴파일 후
import { jsx as _jsx } from 'react/jsx-runtime'; // 컴파일러가 자동으로 런타임에서 넣어주는 import문

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

이로써 React 자체의 오버헤드를 줄이고, JSX 변환 과정을 더 최적화하며, 코드 크기를 줄이고 실행 속도를 개선합니다.


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

React 17은 JSX Transform을 통해 향후 추가될 React 기능 및 변경사항에 대한 더 나은 지원을 제공합니다.

  • React 팀이 기존 React.createElement API에 의존하지 않고 새로운 기능을 쉽게 추가할 수 있는 유연성을 확보.
  • React의 향후 업데이트와 새로운 컴파일러(예: React Server Components)와의 호환성을 더 쉽게 보장.

2-4. 타사 도구 및 빌드 환경과의 호환성

JSX Transform은 React 외의 다른 빌드 도구와도 더 나은 통합을 제공합니다.

  • Babel: JSX Transform은 Babel 플러그인을 통해 더 쉽게 통합.
  • ESLint: React Import가 필요 없으므로 불필요한 import에 대한 경고를 줄임.
  • Webpack/Vite: 최신 빌드 도구와 더 빠르고 간결하게 동작.


Recap

React 17 이상을 사용 중이라면 react-jsx를 사용하는 것이 권장됩니다. 이는 React의 새로운 JSX 변환 방식을 활용하며, React를 명시적으로 import하지 않아도 되기 때문입니다.

React 외의 환경(Babel 기반 프로젝트 등)에서는 preserve를 사용하여 다른 도구에서 JSX를 처리하도록 위임할 수 있습니다.

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



Wonkook Lee
Frontend Engineer
LinkedIn

profile
Software Engineer | Former Industrial Designer

0개의 댓글