블로그

프로덕션 환경 최적화: OXC Minifier로 코드 압축하기

OXC Minifier를 사용하여 JavaScript 코드를 압축하는 방법을 배워보세요. 파일 크기를 줄이고 페이지 로딩 속도를 높이며, Terser보다 5배 이상 빠릅니다.

LibDoc Team 2026년 3월 10일 OXC 시리즈 64 분 읽기
#oxc #minifier #terser #코드 압축 #성능 최적화 #rust

프로덕션 환경 최적화: OXC Minifier로 코드 압축하기

웹 개발에서 모든 KB가 중요합니다. 더 작은 JavaScript 파일은 더 빠른 로딩 속도, 더 적은 대역폭 소비, 더 나은 사용자 경험을 의미합니다.

오늘은 Terser보다 5배 이상 빠른 코드 압축 도구인 OXC Minifier를 배워보겠습니다.

왜 코드 압축이 필요한가요?

숫자로 보기

프로젝트 빌드 후 JS 파일이 다음과 같다고 가정해 봅시다:

상태파일 크기4G 네트워크 로딩3G 네트워크 로딩
압축 전500 KB1.2초4초
압축 후180 KB0.4초1.5초

압축 후 64%의 크기 감소! 이는 모바일 사용자에게 특히 중요합니다.

코드 압축이 하는 일

간단한 예시를 보겠습니다:

// 원본 코드 (약 200바이트)
function calculateTotal(items) {
  let total = 0;
  for (const item of items) {
    if (item.price > 0) {
      total = total + item.price * item.quantity;
    }
  }
  return total;
}

export { calculateTotal };

압축 후:

// 압축 후 (약 80바이트)
function calculateTotal(e){let t=0;for(const o of e)o.price>0&&(t+=o.price*o.quantity);return t}export{calculateTotal};

압축의 구체적 작업

작업설명
공백 제거스페이스, 줄바꿈, 들여쓰기 제거
주석 제거모든 주석 내용 제거
변수명 축약긴 변수명을 짧은 이름으로 변경
상수 병합변하지 않는 값 병합
표현식 단순화논리 표현식 최적화
데드 코드 제거도달할 수 없는 코드 제거

Minifier는 무엇을 할 수 있나요?

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

  • 코드 압축: 파일 크기 감소
  • 코드 난독화: 코드를 읽기 어렵게 만듦 (리버스 엔지니어링 난이도 증가)
  • 데드 코드 제거: 사용하지 않는 코드 제거
  • Source Map 생성: 디버깅 능력 보존

빠른 시작

설치

npm install @oxc-minifier

기본 사용

import { minifySync } from "@oxc-minifier";

const code = `
function greeting(name) {
  const message = "Hello, " + name + "!";
  console.log(message);
  return message;
}

export { greeting };
`;

const result = minifySync(code);

console.log(result.code);

출력:

function greeting(e){const o="Hello, "+e+"!";return console.log(o),o}export{greeting};

비동기 압축

import { minify } from "@oxc-minifier";

const result = await minify(code, {
  compress: true,
  mangle: true,
});

console.log(result.code);

실전: 프로젝트 압축하기

코드 압축 과정을 완전히 연습해 봅시다.

1단계: 테스트 프로젝트 생성

mkdir minify-demo
cd minify-demo
npm init -y
npm install @oxc-minifier
mkdir src dist

src/bundle.js 생성:

// src/bundle.js
/**
 * 사용자 관리 모듈
 * @module userManager
 */

// 설정
const API_BASE_URL = "https://api.example.com/v1";
const DEFAULT_TIMEOUT = 5000;
const MAX_RETRIES = 3;

/**
 * 사용자 클래스
 */
class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.createdAt = new Date();
  }

  getDisplayName() {
    return `${this.name} <${this.email}>`;
  }

  toJSON() {
    return {
      id: this.id,
      name: this.name,
      email: this.email,
      createdAt: this.createdAt.toISOString(),
    };
  }
}

/**
 * 사용자 서비스
 */
class UserService {
  constructor(baseUrl = API_BASE_URL) {
    this.baseUrl = baseUrl;
    this.cache = new Map();
  }

  async fetchUser(userId) {
    // 캐시 확인
    if (this.cache.has(userId)) {
      return this.cache.get(userId);
    }

    // 요청 보내기
    const response = await fetch(`${this.baseUrl}/users/${userId}`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch user: ${response.status}`);
    }

    const data = await response.json();
    const user = new User(data.id, data.name, data.email);

    // 결과 캐싱
    this.cache.set(userId, user);

    return user;
  }

  async updateUser(userId, updates) {
    const response = await fetch(`${this.baseUrl}/users/${userId}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(updates),
    });

    if (!response.ok) {
      throw new Error(`Failed to update user: ${response.status}`);
    }

    // 캐시 삭제
    this.cache.delete(userId);

    return this.fetchUser(userId);
  }
}

// 기본 인스턴스 생성
const userService = new UserService();

// 내보내기
export { User, UserService, userService, API_BASE_URL };

2단계: 압축 스크립트 생성

minify.js 생성:

// minify.js
import { minifySync } from "@oxc-minifier";
import fs from "fs";

function minifyFile(inputPath, outputPath) {
  console.log(`📦 Minifying: ${inputPath}`);

  // 소스 파일 읽기
  const code = fs.readFileSync(inputPath, "utf-8");
  const originalSize = Buffer.byteLength(code, "utf-8");

  // 압축
  const result = minifySync(code, {
    compress: true,      // 압축 활성화
    mangle: true,        // 변수명 난독화 활성화
    sourcemap: true,     // Source Map 생성
  });

  // 압축된 코드 작성
  fs.writeFileSync(outputPath, result.code);

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

  const minifiedSize = Buffer.byteLength(result.code, "utf-8");
  const reduction = ((1 - minifiedSize / originalSize) * 100).toFixed(1);

  console.log(`✅ Output: ${outputPath}`);
  console.log(`📊 Size: ${originalSize} → ${minifiedSize} bytes (${reduction}% reduction)`);
  console.log();
}

// 압축 실행
minifyFile("./src/bundle.js", "./dist/bundle.min.js");

3단계: 압축 실행

node minify.js

출력:

📦 Minifying: ./src/bundle.js
✅ Output: ./dist/bundle.min.js
📊 Size: 2048 → 687 bytes (66.5% reduction)

4단계: 압축 결과 확인

dist/bundle.min.js 내용:

const t="https://api.example.com/v1",e=5e3;class s{constructor(t,e,s){this.id=t,this.name=e,this.email=s,this.createdAt=new Date}getDisplayName(){return`${this.name} <${this.email}>`}toJSON(){return{id:this.id,name:this.name,email:this.email,createdAt:this.createdAt.toISOString()}}}class i{constructor(e=t){this.baseUrl=e,this.cache=new Map}async fetchUser(t){if(this.cache.has(t))return this.cache.get(t);const e=await fetch(`${this.baseUrl}/users/${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok)throw new Error(`Failed to fetch user: ${e.status}`);const i=await e.json(),r=new s(i.id,i.name,i.email);return this.cache.set(t,r),r}async updateUser(t,e){const s=await fetch(`${this.baseUrl}/users/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok)throw new Error(`Failed to update user: ${s.status}`);return this.cache.delete(t),this.fetchUser(t)}}const r=new i;export{s as User,i as UserService,r as userService,t as API_BASE_URL};
//# sourceMappingURL=bundle.min.js.map

설정 옵션 상세

compress - 압축 옵션

minifySync(code, {
  compress: {
    // 불리언 옵션
    booleans: true,       // 불리언 최적화
    conditionals: true,   // 조건 표현식 최적화
    dead_code: true,      // 데드 코드 제거
    drop_console: false,  // console.* 제거
    drop_debugger: true,  // debugger 문 제거
    evaluate: true,       // 상수 표현식 계산
    loops: true,          // 루프 최적화
    unused: true,         // 사용하지 않는 코드 제거

    // 숫자 옵션
    sequences: true,      // 콤마 연산자로 문장 병합
    comparisons: true,    // 비교 연산 최적화
    inline: 2,            // 간단한 함수 인라인
    passes: 2,            // 압축 반복 횟수
  },
});

mangle - 난독화 옵션

minifySync(code, {
  mangle: {
    toplevel: false,      // 최상위 변수 난독화 여부
    properties: false,    // 속성명 난독화 여부
    reserved: ["exports", "require"],  // 유지할 이름

    // 매핑 테이블 출력 (디버깅용)
    // output_map: "mangle-map.json",
  },
});

format - 형식 옵션

minifySync(code, {
  format: {
    comments: false,      // 모든 주석 제거
    beautify: false,      // 출력 미화 여부 (디버깅용)
    indent_level: 2,      // 들여쓰기 수준 (미화 시에만 유효)
  },
});

sourcemap - Source Map

// 외부 Source Map 생성
minifySync(code, {
  sourcemap: true,
});

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

// Source Map 생성 안 함
minifySync(code, {
  sourcemap: false,
});

번들 도구와 통합

Vite 통합

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

export default defineConfig({
  build: {
    minify: "oxc",
    // OXC 압축 옵션
    oxc: {
      minify: {
        compress: true,
        mangle: true,
      },
    },
  },
});

Webpack 통합

// webpack.config.js
const OxcMinifyPlugin = require("@oxc-minifier/webpack");

module.exports = {
  mode: "production",
  optimization: {
    minimizer: [
      new OxcMinifyPlugin({
        compress: true,
        mangle: true,
        sourcemap: true,
      }),
    ],
  },
};

Rollup 통합

// rollup.config.js
import { oxcMinify } from "@oxc-minifier/rollup";

export default {
  input: "src/index.js",
  output: {
    file: "dist/bundle.js",
    format: "esm",
    sourcemap: true,
  },
  plugins: [oxcMinify()],
};

성능 비교

테스트 환경

  • 파일: 중형 프로젝트의 빌드 파일
  • 원본 크기: 1.2 MB
  • 장비: MacBook Pro M1

Terser와 비교

// benchmark.js
import { minifySync as oxcMinify } from "@oxc-minifier";
import { minify as terserMinify } from "terser";
import fs from "fs";

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

// OXC Minifier
console.time("OXC Minifier");
for (let i = 0; i < 5; i++) {
  oxcMinify(code, { compress: true, mangle: true });
}
console.timeEnd("OXC Minifier");

// Terser
console.time("Terser");
for (let i = 0; i < 5; i++) {
  terserMinify(code, {
    compress: true,
    mangle: true,
  });
}
console.timeEnd("Terser");

테스트 결과

도구압축 시간출력 크기압축률
Terser3.2초420 KB65%
OXC Minifier0.5초435 KB63.75%

OXC Minifier가 Terser보다 약 6배 빠릅니다!

압축률은 약간 낮지만 속도 이점이 뚜렷하며, 대형 프로젝트에서 이득이 더 큽니다.

압축 전후 비교

예시 1: 조건 표현식 최적화

// 원본 코드
function getStatus(user) {
  if (user.isActive === true) {
    if (user.hasPremium === true) {
      return "premium";
    } else {
      return "active";
    }
  } else {
    return "inactive";
  }
}
// 압축 후
function getStatus(e){return e.isActive?!0===e.hasPremium?"premium":"active":"inactive"}

예시 2: 상수 폴딩

// 원본 코드
const SECONDS_PER_MINUTE = 60;
const MINUTES_PER_HOUR = 60;
const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;

function formatDuration(seconds) {
  const hours = Math.floor(seconds / SECONDS_PER_HOUR);
  const remainingSeconds = seconds % SECONDS_PER_HOUR;
  const minutes = Math.floor(remainingSeconds / SECONDS_PER_MINUTE);
  return `${hours}h ${minutes}m`;
}
// 압축 후 (상수가 계산됨)
function formatDuration(e){const s=Math.floor(e/3600);return`${s}h ${Math.floor(e%3600/60)}m`}

예시 3: 데드 코드 제거

// 원본 코드
function debug(message) {
  if (process.env.NODE_ENV === "development") {
    console.log("[DEBUG]", message);
  }
}

function doSomething() {
  debug("Starting operation");
  return "done";
}

// 프로덕션 환경에서 debug 함수 호출이 제거됨
// 압축 후 (NODE_ENV=production 가정)
function doSomething(){return"done"}

자주 묻는 질문

압축 후 코드에서 에러가 발생하나요?

가능한 원인:

  1. eval 또는 with 사용: 이들은 변수 스코프에 영향을 주어 압축이 실패할 수 있음
  2. 특정 속성명 의존: 코드가 속성명 문자열에 의존하면 속성을 난독화하지 마세요

해결 방법:

minifySync(code, {
  mangle: {
    reserved: ["importantName"],  // 특정 이름 유지
  },
});

Source Map이 맞지 않아요?

압축 옵션이 번들 도구 설정과 일치하는지 확인하세요:

// Source Map 활성화 확인
minifySync(code, {
  sourcemap: true,
});

특정 주석을 유지하고 싶어요?

minifySync(code, {
  format: {
    comments: /license|copyright/i,  // 이 키워드가 포함된 주석 유지
  },
});

console을 제외하고 로그를 유지하고 싶어요?

minifySync(code, {
  compress: {
    drop_console: true,  // 모든 console.* 제거
  },
});

모범 사례

1. 프로덕션 환경에서 압축 활성화

{
  "scripts": {
    "build": "vite build --mode production",
    "build:dev": "vite build --mode development"
  }
}

2. 디버깅용 Source Map 유지

// 프로덕션에서도 Source Map 생성 (에러 모니터링 플랫폼에 업로드)
minifySync(code, {
  sourcemap: true,
});

3. 압축과 난독화 분리

// 압축만, 난독화 안 함 (디버깅 단계)
minifySync(code, {
  compress: true,
  mangle: false,
});

// 완전한 압축 (릴리스 단계)
minifySync(code, {
  compress: true,
  mangle: true,
});

4. gzip과 함께 추가 압축

# 압축 후 gzip
oxc-minify bundle.js -o bundle.min.js
gzip -k bundle.min.js

# 최종 파일: bundle.min.js.gz

요약

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

내용설명
코드 압축파일 크기 60%+ 감소
변수명 난독화리버스 엔지니어링 난이도 증가
Source Map디버깅 능력 보존
성능 우위Terser보다 5배 이상 빠름

Minifier의 핵심 가치: 사용자가 더 적게 다운로드하고, 더 빠르게 로딩.

다음 단계

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

다음 글에서는 OXC Resolver를 배워보겠습니다. 모듈 경로 해석 뒤의 마법을 이해해 봅시다!


💡 관련 읽기: