Ruby에서 Class 키워드를 제거해야 하는 5가지 이유

Dave Thomas, Start writing Ruby (stop using classes). San Francisco Ruby Conference 2025.

작성자
Evil Martians
발행일
2025년 12월 27일

핵심 요약

  • 1 Ruby는 객체 지향 언어이지 클래스 지향 언어가 아니므로, 객체 팩토리가 아닌 경우 클래스 대신 모듈, 함수, 또는 Struct를 활용하여 코드의 결합도를 낮추고 가독성을 향상해야 합니다.
  • 2 Active Record와 같이 디자인 패턴의 이름을 따거나, 상태가 없거나, 생성 후 유효하지 않거나, 단순히 데이터 컨테이너 역할을 하는 클래스는 Ruby의 다형성을 제대로 활용하지 못하며 불필요한 복잡성을 초래합니다.
  • 3 클래스 사용을 줄이고 함수형 프로그래밍 접근 방식을 도입하면 코드의 결합도가 감소하고, 메서드 크기가 작아지며, 테스트 및 디버깅이 훨씬 용이해지는 등 상당한 개발 이점을 얻을 수 있습니다.

도입

본 발표는 Ruby 개발자들이 'class' 키워드를 사용하는 방식에 대한 근본적인 의문을 제기하며, 많은 개발자에게 익숙한 관행에 도전합니다. 발표자는 Ruby가 객체 지향(Object-Oriented) 언어이지 클래스 지향(Class-Oriented) 언어가 아님을 강조하며, C++와 같은 언어의 영향으로 인해 Ruby에서 클래스가 과도하게 사용되고 있다고 지적합니다. Alan Kay의 객체 지향 개념을 인용하며, Ruby의 유연성을 최대한 활용하기 위해서는 클래스 사용에 대한 재고가 필요하다고 주장합니다. 이는 코드의 불필요한 복잡성과 결합도를 줄이기 위한 중요한 논의의 시작입니다.

발표자는 Ruby에서 ‘class’ 키워드 사용을 지양해야 하는 구체적인 규칙들을 제시합니다. 이러한 규칙들은 기존의 Ruby 및 Rails 개발 관행에 대한 비판적 시각을 담고 있습니다.

1. 객체 팩토리가 아니라면 클래스가 아니다

  • 클래스의 유일한 목적은 객체를 생성하는 ‘객체 팩토리’여야 합니다. 인스턴스 변수, 인스턴스 메서드, 생성자가 없는 클래스(예: 체크섬 계산기)는 모듈로 대체되어야 합니다. 모듈은 extend self를 통해 self. 호출을 줄이고, private 키워드를 사용하여 내부 메서드를 은닉할 수 있어 더 나은 캡슐화를 제공합니다.

2. 디자인 패턴의 이름을 딴 것이라면 클래스가 아니다

  • C++의 한계를 극복하기 위해 고안된 디자인 패턴(예: 팩토리, 데코레이터)은 Ruby의 내장 기능을 통해 자연스럽게 구현되는 경우가 많아 불필요합니다. ActiveRecord::Base와 같은 추상 기본 클래스(Abstract Base Class)는 C++나 Java와 달리 Ruby에서 필요성이 적습니다. Ruby는 강력한 타입 검사가 없고 믹스인(Mix-in)을 지원하기 때문입니다.

  • Person < ActiveRecord::Base와 같은 상속은 ‘Person이 Active Record Base이다’라는 잘못된 의미를 부여하며, 단순히 메서드 주입을 위한 게으른 방식입니다. 대신 include ActiveModel::Validations와 같이 필요한 모듈만 명시적으로 포함하여 불필요한 780개 이상의 메서드 주입을 피해야 합니다.

3. 상태가 없다면 클래스가 아니다

  • ApplicationJob의 서브클래스처럼 perform이라는 단일 함수만 가지고 다른 함수를 호출하는 클래스는 단순히 함수를 담는 ‘버킷’에 불과합니다. 이러한 클래스는 상태를 가지지 않으므로 클래스가 아닌 모듈이나 독립적인 함수로 구현되어야 합니다.

  • 생성자에서 인스턴스 변수를 설정하고 다른 함수에서 이를 사용하는 클래스도 마찬가지입니다. 이는 객체를 생성하고 즉시 사용하는 불필요한 복잡성을 추가합니다. 대신 인수를 직접 받아 처리하는 함수로 대체하는 것이 더 명확하고 효율적입니다.

4. 생성 후 유효하지 않다면 클래스가 아니다

  • 객체가 생성된 직후 유효하지 않은 상태(예: buildernil로 초기화된 Director 클래스)이며, 추가적인 설정 메서드 호출이 필요한 경우 이는 클래스로서 적합하지 않습니다. 이러한 설계는 구현 세부 사항을 외부에 노출하고 객체의 사용성을 저해합니다. 모든 필요한 정보를 한 번에 받아 객체를 유효한 상태로 만드는 단일 함수/모듈 패턴이 더 바람직합니다.

5. 데이터 버킷이라면 클래스가 아니다

  • 단순히 데이터를 담는 ‘PORO’(Plain Old Ruby Object) 역할을 하는 클래스는 attr_accessor를 반복하여 정의하는 대신 Ruby의 Struct나 불변 데이터 구조를 위한 Data를 사용해야 합니다. Struct에 메서드를 추가할 수도 있지만, 비즈니스 로직은 date_of_birth와 같은 속성을 가진 모든 객체를 처리할 수 있는 독립적인 함수로 분리하여 Ruby의 다형성을 활용하는 것이 좋습니다. 이는 코드를 더 일반적이고 테스트하기 쉽게 만듭니다.

Elixir의 영향과 장점

  • 발표자는 Elixir와 같은 함수형 언어 경험을 통해 클래스 없는 코드 작성의 이점을 깨달았다고 언급합니다. 이 접근 방식은 코드의 결합도를 현저히 낮추고, 메서드 크기를 줄이며, ‘Fat Model’ 문제를 해결하고, 메서드 수준의 다형성을 제공하며, 파일 구조를 유연하게 만듭니다. 결과적으로 디버깅 및 테스트가 훨씬 쉬워집니다. 컨트롤러나 모델 테스트 시 필요한 복잡한 컨텍스트 설정 없이 독립적인 메서드를 직접 호출할 수 있기 때문입니다.

결론

결론적으로 발표자는 개발자들이 'class' 키워드를 사용할 때마다 잠시 멈춰서 진정으로 클래스가 필요한지 자문해야 한다고 강력히 권고합니다. 클래스가 절대적으로 '악'은 아니지만, 현재 사용량의 90% 정도는 다른 방식으로 대체될 수 있으며, 이를 통해 코드 품질이 비약적으로 향상될 것이라고 주장합니다. 추가적으로, 개발 초기 단계에서는 복잡한 디렉토리 구조 대신 `main.rb` 파일에서 인라인 코드를 작성하고, 필요할 때 리팩토링하며, 클래스와 모듈의 중첩을 피하고, 데이터 그룹화를 위해 해시(Hash)를 먼저 사용해 볼 것을 제안합니다. 궁극적으로 이러한 실천을 통해 개발 과정에서 더 많은 재미를 느끼고 더 나은 코드를 작성할 수 있다는 메시지를 전달합니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!