개발새발
js 파일 생성하기 본문
test.tsx를 JavaScript로 트랜스파일하기
아래 예시는 동일한 소스 test.tsx를 tsconfig.json 의 "jsx" 옵션만 바꿔가며 npx tsc 로 컴파일했을 때 어떤 JS 결과물이 생성되는지를 비교‧분석한 것이다. 모든 예시는 TypeScript 4.5 이상을 전제로 설명한다.
1. 기본 세팅
$ tree -L 1
.
├── tsconfig.json
└── test.tsx
test.tsx 안에는 일반적인 함수 컴포넌트가 들어 있다고 가정한다.
// test.tsx
import { useState, useCallback, useRef, useEffect } from 'react';
const Form = ({ children, onSubmit }) => (
<form onSubmit={onSubmit}>{children}</form>
);
const WordRelay = () => {
/* …중략… */
return (
<>
<div>{word}</div>
<Form onSubmit={onSubmitForm}>
<input ref={inputEl} value={value} onChange={onChange} />
<button>입력!</button>
</Form>
<div>{result}</div>
</>
);
};
export default WordRelay;
2. "jsx": "react-jsx" (새 런타임 · 기본 권장)
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx", // 새 런타임
"outDir": "dist"
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime"); // 자동 삽입
const react_1 = require("react");
const Form = ({ children, onSubmit }) => (
(0, jsx_runtime_1.jsx)("form", { onSubmit, children })
);
const WordRelay = () => {
const [word, setWord] = (0, react_1.useState)('제로초');
/* …중략… */
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [
(0, jsx_runtime_1.jsx)("div", { children: word }),
/* …생략… */
]});
};
- 특징
- react/jsx-runtime 가 한 줄 삽입되고, 모든 JSX는 jsx · jsxs 함수 호출로 변환된다.
- import React from 'react' 가 더 이상 필요 없다. (컴포넌트 코드에서 React 식별자를 직접 쓰지 않는 한)
- 번들러에 따라 tree-shaking 효율이 좋다.
3. "jsx": "react" (옛 런타임 · 호환 목적)
{
"compilerOptions": {
"jsx": "react", // old-school runtime
"outDir": "dist"
}
}
"use strict";
var __importStar = /* tslib 헬퍼 */ ;
Object.defineProperty(exports, "__esModule", { value: true });
const React = __importStar(require("react")); // 전체 네임스페이스 가져옴
const Form = ({ children, onSubmit }) =>
React.createElement("form", { onSubmit }, children);
const WordRelay = () => {
const [word, setWord] = React.useState('제로초');
/* …중략… */
return React.createElement(React.Fragment, null,
React.createElement("div", null, word),
/* …생략… */
);
- 특징
- 반드시 React 변수(네임스페이스)가 필요하므로 원본 소스에 import React from 'react' 구문을 직접 작성해야 한다.
- 모든 JSX가 React.createElement 호출로 변환되어 구버전 도구 체인과 호환된다.
4. "jsx": "react-native" (네이티브 전용)
{
"compilerOptions": {
"jsx": "react-native", // RN 전용
"outDir": "dist"
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = require("react"); // 훅용 import만 남음
const Form = ({ children, onSubmit }) => (
/* JSX 그대로 보존됨 */
<form onSubmit={onSubmit}>{children}</form>
);
const WordRelay = () => {
const [word, setWord] = (0, react_1.useState)('제로초');
/* …중략… */
return (
<>
<div>{word}</div>
<Form onSubmit={onSubmitForm}>
<input ref={inputEl} value={value} onChange={onChange} />
<button>입력!</button>
</Form>
<div>{result}</div>
</>
);
};
- 특징
- JSX가 그대로 남는다. React Native 번들러(Metro)가 이후 단계에서 JS→네이티브 뷰로 변환하기 때문.
- TypeScript는 훅 사용을 위해 import { useState … } from 'react' 만 유지한다.
- 브라우저용 번들에는 바로 쓸 수 없는 코드이므로 RN 프로젝트 전용 설정이다.
5. 정리 — 어떤 옵션을 언제 쓸까?
jsx 값 | 주요 용 | React import 필요 여부 | 출력 형태 |
react-jsx | 권장: CRA·Vite 등 모던 웹 | ❌ (자동) | jsx/jsxs |
react-jsxdev | 개발 전용, Fast-Refresh + 에러 디버그 | ❌ | jsxDEV |
react | 레거시 도구 체인 (예: Webpack 4 + Babel 7 이전 preset-react) |
⭕ | React.createElement |
preserve | Babel 등 다른 트랜스파일러에게 JSX 변환을 위임 | 원본 유지 | JSX 그대로 |
react-native | React Native | ❌ (React 객체 불필요) | JSX 그대로 |
TIP
- react-jsx(react-jsxdev)로 옮기면 import React from 'react' 를 지워도 ESLint가 경고하지 않는다.
- Babel을 추가로 쓰지 않는 순수 tsc 빌드라면 react-jsx가 가장 간단·빠르다.
- 라이브러리(컴포넌트 패키지) 배포 시에는 소비자가 어떤 빌드 체인을 쓰는지 불확실하므로 소스는 JSX 그대로(preserve) 두고, 번들 단계에서 UMD·ESM 각각을 생성하는 전략을 권장한다.
결론
- 웹 앱이라면 react-jsx 가 현재 표준이다.
- 구형 레거시 환경 유지보수가 필요하면 react.
- React Native 프로젝트는 react-native.
- Babel / SWC 등 외부 트랜스파일러가 JSX를 처리한다면 preserve.
옵션 하나만 달리해도 결과 JS가 크게 달라지므로, 프로젝트 구성·배포 대상·도구 체인을 먼저 정리한 뒤 tsconfig.json 의 jsx 값을 선택해야 한다.
'Typescript' 카테고리의 다른 글
React 직접 타이핑 하기 (0) | 2025.05.30 |
---|---|
JSX 타입 이해하기 (0) | 2025.05.24 |
React Hooks 분석하기 (0) | 2025.05.24 |
React 타입 분석하기 (1) | 2025.05.24 |
axios의 타입을 어떻게 찾았는지 이해하기 (0) | 2025.05.17 |