본문으로 건너뛰기

AI로 현실이 된 장기 코드 마이그레이션

LLM과 검증 루프가 수년간 미뤄진 테스트 프레임워크 마이그레이션을 몇 주 단위 작업으로 바꾸고 있습니다.

이 요약은 AI가 원문을 분석해 생성했습니다. 정확한 내용은 원문 기준으로 확인하세요.

TL;DR

OpenAI는 Asana가 Codex를 활용해 4,000개가 넘는 Enzyme 파일 중 상당 부분을 React Testing Library로 전환하고, 약 2주와 $12,000의 모델·인프라 비용으로 작업을 마쳤다고 전했습니다. Enzyme은 컴포넌트 인스턴스를 직접 조작하지만 React Testing Library는 렌더링된 DOM과 사용자 이벤트를 기준으로 삼아 테스트 코드의 구조가 크게 달라지므로 단순한 일괄 치환만으로는 부족합니다. Airbnb의 3,500개 파일 전환과 Uber의 600,000개 unit test 전환 사례처럼 LLM은 수년간 우선순위에서 밀리던 마이그레이션을 실행 가능한 작업으로 바꾸지만, 엔지니어가 자동화 단계와 검증 루프를 설계하고 예외를 처리해야 안정적인 결과를 얻을 수 있습니다.

섹션별 상세

01
OpenAI는 Asana가 Codex를 사용해 Enzyme 테스트 시스템을 약 2주 만에 제거했고 모델·인프라 비용이 약 $12,000이었다고 소개했습니다. Asana의 기존 계획은 약 6 million 달러와 최소 5년이었지만, 실제 작업은 1.5주 분량의 엔지니어링 노력으로 두 달력 주에 걸쳐 진행됐습니다. 이 사례는 장기간 방치되던 테스트 프레임워크 교체도 LLM을 활용하면 실행 가능한 작업으로 바뀔 수 있다는 문제의식에서 출발합니다.
02
Enzyme과 React Testing Library는 같은 Counter 동작을 검증하면서도 테스트의 입력과 관찰 대상이 다릅니다. Enzyme은 컴포넌트를 mount한 wrapper에서 p와 button을 찾고 click을 simulate한 뒤 상태 갱신을 확인하지만, React Testing Library는 렌더링된 DOM에서 Count 텍스트와 button role을 찾고 userEvent.click으로 사용자 흐름을 재현합니다. 따라서 단순한 카운터 테스트도 import, 이벤트 처리, 결과 조회 방식이 거의 모두 달라지며 복잡한 사용자 여정에서는 변환 난도가 더 커집니다.
03
Airbnb는 3,500개의 Enzyme 컴포넌트 테스트 파일을 LLM과 여러 단계의 자동화 루프로 약 6주에 걸쳐 React Testing Library로 옮겼습니다. 첫 반복에서 4시간 만에 파일의 75%를 처리했고, 남은 테스트에는 더 정교한 refactor pipeline을 적용해 4일 동안 전체의 97%까지 진행한 뒤 마지막 3%를 엔지니어가 약 1주일 동안 마무리했습니다. 이 과정은 모델 호출만으로 끝난 것이 아니라 실패한 변환을 다시 실행하고 lint와 테스트 결과를 이용해 수정하는 검증 구조를 함께 구축한 사례입니다.
04
Asana의 6 million 달러 추정치는 파일 하나를 수작업으로 변환하고 검증하는 시간을 넉넉하게 계산한 뒤 남은 3,000개 이상의 파일과 프레임워크 흔적 제거, 모니터링, 정적 분석 도구 작업을 더해 엔지니어 시간당 비용을 곱한 값입니다. 이 방식은 여러 파일을 함께 처리할 때 생기는 효율 향상이나 LLM 사용을 반영하지 않은 사전 AI 기준선이므로 실제 비용보다 클 가능성이 있습니다. 다만 핵심은 정확한 금액보다 기존 방식으로는 우선순위가 낮아 수년간 미뤄질 작업이었다는 점이며, Asana는 이런 경험을 바탕으로 다른 장기 마이그레이션에도 LLM을 활용할 계획입니다.
05
기사에 인용된 Uber와 Bun 사례는 AI 보조 마이그레이션의 적용 범위가 테스트 프레임워크를 넘어선다는 점을 보여줍니다. Uber는 두 명의 엔지니어와 AI를 사용해 15 million lines of code에 걸친 600,000개 unit test를 JUnit 4에서 JUnit 5로 4개월 만에 옮겼고, Bun은 Zig 코드 530,000 lines를 Rust로 두 주 만에 변환하면서 API 비용 $165K를 기록했습니다. 빠른 전환은 오래된 라이브러리와 새 라이브러리를 함께 지원해야 하는 기간을 줄이지만, 엔지니어가 계획을 세우고 검증 루프를 설계하며 전체 과정에 참여해야 한다는 조건은 그대로 남습니다.

이미지 분석

React의 Counter 컴포넌트가 useState로 카운터를 관리하고 버튼 클릭 때마다 값을 1씩 증가시키는 코드입니다.
Screenshot

이미지는 initialCount를 초기 상태로 사용하는 Counter 컴포넌트와 Count 텍스트, Increment 버튼을 담고 있습니다. 버튼의 onClick 핸들러는 함수형 상태 갱신으로 현재 값에 1을 더하며, 뒤이어 나오는 테스트 프레임워크 비교의 공통 대상이 됩니다.

React의 Counter 컴포넌트가 useState로 카운터를 관리하고 버튼 클릭 때마다 값을 1씩 증가시키는 코드입니다.

Enzyme으로 Counter를 mount하고 버튼 클릭 뒤 Count가 2에서 3으로 바뀌는지 확인하는 테스트 코드입니다.
Screenshot

테스트는 mount로 컴포넌트 wrapper를 만든 뒤 wrapper.find로 p와 button을 조회합니다. act 안에서 simulate('click')을 실행하고 wrapper.update를 호출한 다음, 텍스트가 Count: 3인지 검증해 컴포넌트 중심 접근을 드러냅니다.

Enzyme으로 Counter를 mount하고 버튼 클릭 뒤 Count가 2에서 3으로 바뀌는지 확인하는 테스트 코드입니다.

React Testing Library로 렌더링된 화면에서 버튼을 찾아 실제 사용자 클릭을 수행하고 Count: 3을 확인하는 테스트 코드입니다.
Screenshot

이미지는 render와 screen을 사용해 DOM을 준비하고 getByText로 초기 출력을 확인합니다. userEvent.setup과 getByRole로 이름이 Increment인 버튼을 찾아 비동기 클릭을 수행한 뒤, DOM에 Count: 3이 존재하는지 검증합니다.

React Testing Library로 렌더링된 화면에서 버튼을 찾아 실제 사용자 클릭을 수행하고 Count: 3을 확인하는 테스트 코드입니다.

Enzyme 테스트 코드가 React Testing Library 방식으로 바뀌는 과정을 좌우 비교로 나타낸 코드 diff입니다.
Diagram

왼쪽에서는 mount, act, wrapper.find, simulate, update, unmount 같은 Enzyme 중심 코드가 제거됩니다. 오른쪽에서는 render, screen, userEvent, getByRole, 실제 클릭 흐름이 추가되며 테스트가 컴포넌트 인스턴스가 아니라 렌더링된 화면을 기준으로 전환됩니다.

Enzyme 테스트 코드가 React Testing Library 방식으로 바뀌는 과정을 좌우 비교로 나타낸 코드 diff입니다.

각 파일마다 Enzyme 여부, Jest 통과, lint, 타입 오류를 순서대로 확인하며 LLM이 코드를 고치는 마이그레이션 절차도입니다.
Diagram

절차는 Enzyme 사용 여부를 확인해 RTL로 변환하고, Jest를 통과시키며, lint --fix를 실행한 뒤 남은 lint 오류와 TypeScript 타입 오류를 차례로 수정하는 구조입니다. 각 단계의 실패를 LLM 수정 단계로 되돌리는 검증 루프가 파일 완료 조건으로 연결되어 있어, 반복 실행 가능한 자동화 파이프라인의 형태를 갖춥니다.

각 파일마다 Enzyme 여부, Jest 통과, lint, 타입 오류를 순서대로 확인하며 LLM이 코드를 고치는 마이그레이션 절차도입니다.

용어 해설

컴포넌트 테스트(Component Testing)
UI 컴포넌트 자체의 인스턴스와 내부 상태를 중심으로 동작을 검증하는 테스트 방식입니다. Enzyme은 컴포넌트를 직접 마운트한 뒤 내부 요소를 찾아 상태 변화와 출력을 확인합니다.
Document Object Model
브라우저가 HTML 문서를 트리 구조의 데이터로 표현한 모델입니다. React Testing Library는 컴포넌트 인스턴스가 아니라 실제로 렌더링된 DOM과 사용자의 상호작용을 기준으로 테스트를 수행합니다.
LLM 보조 마이그레이션(LLM-assisted Migration)
LLM을 사용해 기존 코드와 테스트를 새 프레임워크에 맞게 변환하는 작업입니다. 모델이 반복적인 코드 수정과 오류 보정을 맡고, 엔지니어는 검증 루프와 예외 처리를 설계합니다.
검증 루프(Verification Loop)
변환 결과를 테스트, lint, 타입 검사 같은 자동화된 단계로 반복 확인하는 절차입니다. 마이그레이션 결과에서 오류를 찾으면 LLM이 수정하고 다시 검사를 실행해 파일 단위 완성도를 높입니다.
API 비용(API Cost)
LLM API 호출에 사용한 모델 추론 비용입니다. 기사에서는 엔지니어 인건비와 별도로 Codex 마이그레이션에 약 $12,000, Bun 마이그레이션에 $165K가 들었다고 비교합니다.

코드 예제

javascript
import React, { useState } from 'react';

export function Counter({ initialCount = 0 }) {
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount((value) => value + 1)}>
        Increment
      </button>
    </div>
  );
}

React Counter 컴포넌트가 상태를 저장하고 버튼 클릭마다 카운터를 1씩 증가시킵니다.

javascript
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { Counter } from './Counter';

describe('Counter - Enzyme', () => {
  it('increments the count when clicked', () => {
    const wrapper = mount(<Counter initialCount={2} />);

    expect(wrapper.find('p').text()).toBe('Count: 2');

    act(() => {
      wrapper.find('button').simulate('click');
    });
    wrapper.update();

    expect(wrapper.find('p').text()).toBe('Count: 3');

    wrapper.unmount();
  });
});

Enzyme 테스트는 컴포넌트를 mount한 wrapper에서 요소를 찾고 클릭 이벤트를 simulate해 상태 변화를 검증합니다.

javascript
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

describe('Counter - React Testing Library', () => {
  it('increments the count when clicked', async () => {
    const user = userEvent.setup();

    render(<Counter initialCount={2} />);

    expect(screen.getByText('Count: 2')).toBeInTheDocument();

    await user.click(
      screen.getByRole('button', { name: 'Increment' }),
    );

    expect(screen.getByText('Count: 3')).toBeInTheDocument();
  });
});

React Testing Library 테스트는 렌더링된 화면에서 텍스트와 버튼 역할을 찾고 실제 사용자 클릭에 가까운 방식으로 결과를 확인합니다.

javascript
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

describe('Counter - React Testing Library', () => {
  it('increments the count when clicked', async () => {
    const user = userEvent.setup();

    render(<Counter initialCount={2} />);

    expect(screen.getByText('Count: 2')).toBeInTheDocument();

    await user.click(
      screen.getByRole('button', { name: 'Increment' }),
    );

    expect(screen.getByText('Count: 3')).toBeInTheDocument();
  });
});

Enzyme 테스트에서 React Testing Library 테스트로 바뀌는 핵심은 컴포넌트 wrapper 조작을 DOM 조회와 사용자 이벤트로 대체하는 데 있습니다.

기술

  • OpenAI Codex
  • Enzyme
  • React Testing Library
  • React
  • Jest
  • Claude 3.7 Sonnet
  • GPT-5.6 Sol
  • Claude Fable 5
  • JUnit 4
  • JUnit 5
  • Zig
  • Rust
  • TypeScript
  • JavaScript

활용 사례

  • Enzyme 테스트를 React Testing Library 테스트로 전환
  • 지원이 중단된 JUnit 4에서 JUnit 5로 마이그레이션
  • JavaScript 코드베이스의 TypeScript 전환
  • Zig 기반 코드의 Rust 전환
  • 오래된 라이브러리와 프레임워크 흔적 제거
AI 분석 전체 내용 보기

AI 요약 · 북마크 · 개인 피드 설정 — 무료

출처 · 인용 안내

원문 발행 2026. 08. 28.수집 2026. 08. 28.출처 타입 RSS

인용 시 "요약 출처: AI Trends (aitrends.kr)"를 표기하고, 사실 확인은 원문 보기 기준으로 진행해 주세요. 자세한 기준은 운영 정책을 참고해 주세요.