블로그

코드 변환 입문: OXC Transformer로 TypeScript 컴파일 구현하기

OXC Transformer를 사용하여 TypeScript를 JavaScript로 컴파일하는 방법을 배워보세요. JSX 변환과 구문 다운그레이드를 지원하며, Babel보다 더 빠르고 간단합니다.

LibDoc Team 2026년 3월 8일 OXC 시리즈 60 분 읽기
#oxc #transformer #typescript #babel #코드 변환 #rust

코드 변환 입문: OXC Transformer로 TypeScript 컴파일 구현하기

프론트엔드 개발에서 우리는 종종 코드를 “변환”해야 합니다. TypeScript를 JavaScript로, ES6 문법을 ES5로, JSX를 일반 JavaScript 함수 호출로 변환하는 것입니다.

오늘은 Babel보다 더 빠른 코드 변환 도구인 OXC Transformer를 배워보겠습니다.

코드 변환이란 무엇인가요?

왜 코드를 변환해야 하나요?

브라우저는 우리가 작성한 모든 코드를 직접 실행할 수 없습니다:

소스 코드문제해결책
TypeScript브라우저가 타입 구문을 인식하지 못함타입 제거, JS로 변환
ES6+ 문법구형 브라우저 미지원ES5로 다운그레이드
JSX브라우저가 파싱할 수 없음React.createElement로 변환

일반적인 변환 시나리오

1. TypeScript → JavaScript

// 입력: TypeScript
interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}
// 출력: JavaScript
function greet(user) {
  return `Hello, ${user.name}!`;
}

2. JSX → JavaScript

// 입력: JSX
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}
// 출력: JavaScript
function Button({ onClick, children }) {
  return React.createElement("button", { onClick }, children);
}

3. ES6+ → ES5

// 입력: ES6+
const greet = (name) => `Hello, ${name}!`;
class Person { ... }
// 출력: ES5
var greet = function(name) { return "Hello, " + name + "!"; };
function Person() { ... }

Transformer는 무엇을 할 수 있나요?

OXC Transformer는 다음 기능을 제공합니다:

  • TypeScript 타입 제거: TS를 순수 JS로 변환
  • JSX 변환: JSX 문법을 함수 호출로 변환
  • 구문 다운그레이드: 새 문법을 구문법으로 변환 (일부 지원)
  • 소스맵 보존: 디버깅을 위한 Source Map 생성

빠른 시작

설치

npm install @oxc-transform

기본 사용

import { transformSync } from "@oxc-transform";

const code = `
interface User {
  name: string;
}

function greet(user: User) {
  return \`Hello, \${user.name}!\`;
}
`;

const result = transformSync(code, {
  lang: "ts",
});

console.log(result.code);

출력:

function greet(user) {
  return `Hello, ${user.name}!`;
}

비동기 변환

import { transform } from "@oxc-transform";

const result = await transform(code, {
  lang: "ts",
});

console.log(result.code);

실전: TypeScript를 JavaScript로 컴파일하기

TypeScript 컴파일 과정을 완전히 연습해 봅시다.

1단계: TypeScript 프로젝트 생성

mkdir ts-project
cd ts-project
npm init -y
npm install @oxc-transform
mkdir src

src/index.ts 생성:

// src/index.ts
interface Config {
  apiUrl: string;
  timeout: number;
  debug?: boolean;
}

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

class ApiClient {
  private config: Config;

  constructor(config: Config) {
    this.config = config;
  }

  async request<T>(method: HttpMethod, path: string): Promise<T> {
    const url = `${this.config.apiUrl}${path}`;

    const response = await fetch(url, {
      method,
      headers: {
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    return response.json();
  }
}

// 사용 예시
const client = new ApiClient({
  apiUrl: "https://api.example.com",
  timeout: 5000,
});

export { ApiClient, Config, HttpMethod };

2단계: 컴파일 스크립트 생성

build.js 생성:

// build.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";

function compileTypeScript(inputPath, outputPath) {
  // 소스 파일 읽기
  const code = fs.readFileSync(inputPath, "utf-8");

  // 변환
  const result = transformSync(code, {
    lang: "ts",
    // JSX 보존 (있는 경우)
    jsx: "react",
    // 대상 ES 버전
    target: "es2020",
    // Source Map 생성
    sourcemap: true,
  });

  // 출력 디렉토리 확인
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 출력 파일 작성
  fs.writeFileSync(outputPath, result.code);

  // Source Map 작성
  if (result.map) {
    fs.writeFileSync(`${outputPath}.map`, result.map);
  }

  console.log(`✓ Compiled: ${inputPath} → ${outputPath}`);
}

// 컴파일
compileTypeScript("./src/index.ts", "./dist/index.js");

3단계: 컴파일 실행

node build.js

출력:

✓ Compiled: ./src/index.ts → ./dist/index.js

생성된 dist/index.js 확인:

class ApiClient {
  #config;
  constructor(config) {
    this.#config = config;
  }
  async request(method, path) {
    const url = `${this.#config.apiUrl}${path}`;
    const response = await fetch(url, {
      method,
      headers: {
        "Content-Type": "application/json"
      }
    });
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
  }
}
const client = new ApiClient({
  apiUrl: "https://api.example.com",
  timeout: 5e3
});
export { ApiClient };

다음을 확인할 수 있습니다:

  • TypeScript 타입 정의가 제거됨
  • private 필드가 # 프라이빗 필드 문법으로 변경됨
  • 코드 구조는 유지됨

설정 옵션 상세

lang - 언어 타입

transformSync(code, {
  lang: "js",   // JavaScript
  lang: "jsx",  // JavaScript + JSX
  lang: "ts",   // TypeScript
  lang: "tsx",  // TypeScript + JSX
});

target - 대상 ES 버전

transformSync(code, {
  // 지원하는 대상 버전
  target: "es5",
  target: "es2015",
  target: "es2016",
  target: "es2017",
  target: "es2018",
  target: "es2019",
  target: "es2020",
  target: "es2021",
  target: "es2022",
  target: "esnext",
});

jsx - JSX 변환 모드

// React 클래식 모드
transformSync(code, {
  jsx: "react",  // React.createElement(...)
});

// React 자동 런타임
transformSync(code, {
  jsx: "automatic",
  jsxImportSource: "react",  // 자동 import
});

// 커스텀 JSX 팩토리
transformSync(code, {
  jsx: "react",
  jsxFactory: "h",           // React.createElement 대신 h(...)
  jsxFragment: "Fragment",
});

sourcemap - 소스맵

transformSync(code, {
  sourcemap: true,    // Source Map 생성
  sourcemap: "inline", // 인라인 Source Map
});

JSX 변환 상세

OXC Transformer는 JSX 변환을 완전히 지원합니다.

React 클래식 모드

// 입력
function App() {
  return (
    <div className="container">
      <h1>Hello, World!</h1>
      <button onClick={() => alert("clicked")}>Click me</button>
    </div>
  );
}
// 출력 (클래식 모드)
function App() {
  return React.createElement(
    "div",
    { className: "container" },
    React.createElement("h1", null, "Hello, World!"),
    React.createElement("button", { onClick: () => alert("clicked") }, "Click me")
  );
}

React 자동 런타임

// 설정
transformSync(code, {
  lang: "jsx",
  jsx: "automatic",
  jsxImportSource: "react",
});
// 출력 (자동 런타임)
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";

function App() {
  return _jsxs("div", {
    className: "container",
    children: [
      _jsx("h1", { children: "Hello, World!" }),
      _jsx("button", { onClick: () => alert("clicked"), children: "Click me" })
    ]
  });
}

Vue JSX

transformSync(code, {
  lang: "tsx",
  jsx: "react",
  jsxFactory: "h",
  jsxFragment: "Fragment",
});

실전 미니 프로젝트: 일괄 컴파일

더 실용적인 일괄 컴파일 도구를 만들어 봅시다.

일괄 컴파일 스크립트 생성

// batch-compile.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";

const config = {
  inputDir: "./src",
  outputDir: "./dist",
  extensions: [".ts", ".tsx"],
  target: "es2020",
};

function compileFile(inputPath, outputPath) {
  const code = fs.readFileSync(inputPath, "utf-8");

  const lang = inputPath.endsWith(".tsx") ? "tsx" : "ts";

  const result = transformSync(code, {
    lang,
    target: config.target,
    jsx: "automatic",
    jsxImportSource: "react",
    sourcemap: true,
  });

  // 출력 디렉토리 확인
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 파일 확장자 변경 .ts -> .js
  const jsOutputPath = outputPath.replace(/\.tsx?$/, ".js");

  fs.writeFileSync(jsOutputPath, result.code);

  if (result.map) {
    fs.writeFileSync(`${jsOutputPath}.map`, result.map);
  }

  console.log(`✓ ${inputPath} → ${jsOutputPath}`);
}

function walkDir(dir, callback) {
  const files = fs.readdirSync(dir);

  for (const file of files) {
    const filePath = path.join(dir, file);
    const stat = fs.statSync(filePath);

    if (stat.isDirectory()) {
      walkDir(filePath, callback);
    } else if (config.extensions.some((ext) => file.endsWith(ext))) {
      callback(filePath);
    }
  }
}

function build() {
  console.log("🚀 Starting build...\n");

  // 출력 디렉토리 정리
  if (fs.existsSync(config.outputDir)) {
    fs.rmSync(config.outputDir, { recursive: true });
  }
  fs.mkdirSync(config.outputDir, { recursive: true });

  // 모든 파일 컴파일
  walkDir(config.inputDir, (inputPath) => {
    const relativePath = path.relative(config.inputDir, inputPath);
    const outputPath = path.join(config.outputDir, relativePath);
    compileFile(inputPath, outputPath);
  });

  console.log("\n✨ Build complete!");
}

build();

package.json에 추가

{
  "scripts": {
    "build": "node batch-compile.js",
    "dev": "node batch-compile.js && node dist/index.js"
  }
}

Babel과의 비교

기능 비교

기능BabelOXC Transformer
TypeScript 컴파일
JSX 변환
구문 다운그레이드✅ 완전🚧 일부 지원
플러그인 시스템✅ 풍부❌ 아직 없음
Polyfill 주입
커스텀 변환

성능 비교

// benchmark.js
import { transformSync as oxcTransform } from "@oxc-transform";
import * as babel from "@babel/core";

const code = fs.readFileSync("./large-file.ts", "utf-8");

// OXC
console.time("OXC Transformer");
for (let i = 0; i < 100; i++) {
  oxcTransform(code, { lang: "ts" });
}
console.timeEnd("OXC Transformer");

// Babel
console.time("Babel");
for (let i = 0; i < 100; i++) {
  babel.transformSync(code, {
    presets: ["@babel/preset-typescript"],
  });
}
console.timeEnd("Babel");

결과:

OXC Transformer: 150ms
Babel: 2800ms

OXC Transformer가 Babel보다 약 18배 빠릅니다!

마이그레이션 제안

상황제안
순수 TypeScript 컴파일OXC로 충분
간단한 JSX 프로젝트OXC로 충족 가능
ES5로 구문 다운그레이드 필요일단 Babel 사용
커스텀 플러그인 필요Babel 계속 사용
대형 프로젝트 점진적 마이그레이션혼합 사용 가능

혼합 사용 방안

{
  "scripts": {
    "build": "oxc-transform ./src --out-dir ./dist-oxc && babel ./src --out-dir ./dist-babel --presets @babel/preset-typescript"
  }
}

자주 묻는 질문

데코레이터는 어떻게 처리하나요?

OXC는 현재 데코레이터 지원이 제한적입니다. 프로젝트에서 데코레이터를 사용한다면 일단 Babel이나 TypeScript 컴파일러를 사용하는 것을 권장합니다.

생성된 코드 형식이 안 예뻐요?

OXC Transformer는 변환에 집중하며, 포맷팅은 담당하지 않습니다. Oxfmt와 함께 사용할 수 있습니다:

# 먼저 변환, 그 다음 포맷팅
oxc-transform ./src --out-dir ./dist
oxfmt "./dist/**/*.js" --write

Source Map이 작동하지 않아요?

변환 시 Source Map을 활성화하고 경로가 올바른지 확인하세요:

transformSync(code, {
  sourcemap: true,
  filename: "source.ts",  // 소스 파일명 지정
});

번들 도구와 통합

Vite 통합

OXC는 공식 Vite 플러그인을 제공합니다:

// vite.config.js
import { defineConfig } from "vite";
import oxc from "vite-plugin-oxc";

export default defineConfig({
  plugins: [oxc()],
});

Rollup 통합

// rollup.config.js
import oxc from "rollup-plugin-oxc";

export default {
  input: "src/index.ts",
  output: {
    dir: "dist",
    format: "esm",
  },
  plugins: [oxc()],
};

요약

본문에서는 OXC Transformer의 핵심 사용법을 소개했습니다:

내용설명
TypeScript 컴파일타입 제거, JS 생성
JSX 변환React.createElement 또는 자동 런타임으로 변환
설정 옵션lang, target, jsx, sourcemap
성능 우위Babel보다 18배 이상 빠름

Transformer의 핵심 가치: Rust의 속도로 코드 변환 완료.

다음 단계

Transformer의 더 많은 설정 옵션을 알고 싶으신가요? **OXC 한국어 문서 - Transformer 챕터**를 방문하세요.

다음 글에서는 OXC Minifier를 배워보겠습니다. 프로덕션 코드를 더 작고 빠르게 만들어 봅시다!


💡 관련 읽기: