RBS Trace: 테스트 기반 타입 선언 자동 생성 젬

[JA] Automatically generating types by running tests / Takumi Shotoku @sinsoku_listy

3줄 요약

  • RBS Trace는 Ruby 테스트 실행 결과를 바탕으로 RBS 타입 선언을 자동으로 생성하는 젬입니다.
  • TracePoint, Binding, Prism 등의 Ruby 내부 도구를 활용하여 메서드 호출, 인자, 반환 값 정보를 수집하고 보이드 타입을 판별합니다.
  • ActiveRecord Relation 이름 문제, 보이드 타입 처리, 병렬 테스트 지원 등 다양한 기술적 난관을 해결하며 실제 Rails 애플리케이션(Redmine, Mastodon 등)에서 동작함을 확인했습니다.

본 영상은 테스트 실행을 통해 Ruby 코드의 RBS 타입 선언을 자동으로 생성하는 'RBS Trace' 젬에 대한 발표 내용을 요약합니다. 발표자는 RBS Trace의 개발 배경, 작동 방식, 구현 과정에서 마주한 문제점과 해결책, 그리고 향후 전망에 대해 상세히 설명합니다. 이 젬의 목표는 테스트만 실행하면 모든 메서드에 대한 타입 선언을 자동으로 생성하여 개발 생산성을 향상시키는 것입니다.

RBS Trace의 핵심 기능은 테스트 실행 중 메서드 호출 시 인자와 반환 값의 클래스 정보를 기록하는 것입니다. 이를 위해 Ruby의 TracePoint 라이브러리를 활용하여 모든 메서드 호출 시점에 개입합니다. 인자 값을 얻기 위해서는 Bindinglocal_variable_get 메서드를, 반환 값은 TracePointreturn_value 메서드를 사용합니다. 수집된 타입 정보는 Ruby 코드에 RBS 주석 형태로 삽입됩니다.

개발 과정에서는 여러 기술적 문제가 발생했습니다. 첫째, BasicObject를 상속받은 클래스에서 클래스 메서드 호출 시 NoMethodError가 발생하는 문제에 직면했습니다. 이는 UnboundMethodbind_call을 사용하여 해결했습니다. 둘째, ActiveRecord Relation의 name 메서드가 오버라이드되어 실제 클래스 이름이 아닌 ‘ActiveRecord::Relation’만 반환하는 문제가 있었습니다. 이 역시 UnboundMethod를 활용하여 올바른 Relation 클래스 이름을 얻도록 수정했습니다. 셋째, RBS에서 반환 값이 사용되지 않는 경우 void 타입으로 선언해야 하는데, TracePoint만으로는 이를 판별하기 어려웠습니다. 호출 위치 정보를 제공하는 caller_locations와 Ruby 코드를 파싱하는 Prism 라이브러리를 조합하여 메서드 호출이 정의부 바로 아래에 있고 마지막 호출이 아닌 경우 void로 판단하는 로직을 구현했습니다. 넷째, 병렬 테스트 환경에서의 타입 정보 수집 및 병합 문제를 해결하기 위해 각 프로세스별로 RBS 파일을 저장하고, 별도의 명령어를 통해 이 파일들을 병합한 후 최종적으로 Ruby 코드에 삽입하는 방식을 도입했습니다.

성능 측면에서 RBS Trace를 사용하면 테스트 실행 시간이 로컬 환경에서 Redmine과 Mastodon 기준 약 3.8배에서 5.4배, CI 환경에서는 소속 회사 프로젝트 기준 약 1.5배 증가했습니다. 하지만 타입 선언 생성은 한 번만 수행하면 되므로 이 정도 성능 저하는 감수할 만하다고 발표자는 판단합니다.

RBS Trace는 Redmine과 Mastodon 같은 대규모 Rails 애플리케이션에서 실제 동작함을 확인했으며, 자동 생성된 타입 선언의 정확성도 대체로 양호한 것으로 보입니다.

결론적으로 RBS Trace는 테스트 기반의 자동 RBS 타입 선언 생성이라는 유용한 기능을 제공합니다. 다양한 기술적 도전을 극복하고 실제 Rails 환경에서의 동작을 검증했습니다. 향후 RBS Collection에 포함되지 않은 젬에 대한 RBS 자동 생성 기능 등 추가 개선을 모색할 계획입니다. Ruby 및 RBS 커뮤니티의 발전에 기여하고자 하며, 사용자들에게 RBS Trace를 직접 사용해보고 피드백을 줄 것을 권장합니다.