Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Ruby on Rails 다형성(Polymorphism) 완벽 가이드: 개념부터 실전 구현까지

객체지향 프로그래밍(OOP)으로 애플리케이션을 개발해 본 경험이 있다면, '다형성(Polymorphism)'이라는 개념을 실제로 활용했거나 최소한 한 번쯤은 들어보셨을 것입니다.

과학이나 컴퓨터 공학 교과서에서나 볼 법한 용어라 어렵게 느껴질 수 있지만, 사실 많은 개발자가 정확한 개념을 이해하지 못한 채 다형성을 구현하고 있기도 합니다.

이 글에서는 특히 Ruby on Rails 환경에서의 다형성을 심도 있게 다룹니다. 구체적으로 살펴볼 내용은 다음과 같습니다.

  • 실생활에서의 다형성
  • OOP 관점에서 바라본 프로그래밍의 다형성
  • Rails 애플리케이션에 다형성을 적용하여 고품질 코드를 유지하는 방법

그럼 시작해 보겠습니다!

실생활 속 다형성

다형성은 맥락에 따라 여러 방식으로 정의할 수 있지만, 어떤 분야에서든 통용되는 유용한 정의는 '하나의 객체가 둘 이상의 형태를 나타낼 수 있는 능력'입니다. 단어 자체를 분해해 보면 그 의미가 더 명확해집니다. poly는 '많은'을, morph는 '형태'를 뜻합니다.

실생활의 간단한 예로 한 명의 여성이 동시에 경찰관일 수 있고, 누군가의 언니이자 딸이며, 또 다른 누군가의 어머니일 수 있는 경우를 들 수 있습니다. 각 역할은 그녀의 행동 방식을 결정하고, 그 사람이라는 존재를 만들어 가는 요소가 됩니다.

유전학에서의 다형성

컴퓨터 프로그래밍 외적으로 다형성은 생물학 및 유전학과 깊은 연관이 있는 용어입니다. 이 분야에서 다형성은 하나의 종 내에서 여러 개의 뚜렷하게 구분되는 형태나 유형이 나타나게 하는 유전적 변이로 정의됩니다.

재규어(jaguar)를 예로 들어 보겠습니다. 재규어는 여러 유전자 변이를 가질 수 있으며, 이는 털 색깔에 영향을 미칩니다. 대부분의 재규어는 검은 원무늬가 있는 황갈색 털을 가지고 있지만, 유전자 변이에 따라 무늬가 더 밝거나 어두울 수 있고, 일부는 검은 털을 지닙니다.

같은 새 종 안에서 개체별 색소 침착이 다른 것도 다형성의 좋은 예입니다. 고디언 핀치(Gouldian finch)는 개체 간 색깔 차이가 매우 뚜렷한 대표적인 종입니다.

단형성(Monomorphism) vs. 다형성(Polymorphism)

단형성 개념을 함께 살펴보면 다형성을 더 잘 이해할 수 있습니다. 생물학적 관점에서 단형성은 '단 하나의 형태만을 지닌 종', 즉 발달 과정 전반에 걸쳐 동일한 형태를 유지하는 종으로 정의할 수 있습니다.

펭귄은 단형종입니다. 전문가조차 암수를 구별하기 어려울 정도입니다. 펭귄의 유전자 차이는 미미하기 때문에 신체적 특징, 특히 크기와 흑백 털 색깔 면에서 거의 구분이 불가능합니다.

그렇기 때문에 단형종에서는 행동적 신호가 암수를 구별하는 가장 확실한 방법이 됩니다.

이제 프로그래밍, 특히 OOP에서의 다형성으로 시선을 옮겨 보겠습니다.

OOP에서의 다형성

앞서 정의한 다형성 — 객체가 둘 이상의 형태를 나타낼 수 있는 능력 — 은 OOP에 자연스럽게 적용됩니다.

OOP에서는 서로 다른 객체를 전달함으로써 동일한 메서드로 다른 결과를 얻을 수 있습니다. 조건문(if/else)으로도 이를 구현할 수 있지만, 코드가 비대해지고 DRY(Don't Repeat Yourself) 원칙에서 벗어나기 쉽습니다. 깔끔하고 논리적인 OOP 애플리케이션을 만들려면 다형성이 필수적입니다.

Ruby 같은 OOP 언어에서 다형성을 구현하는 두 가지 방식 — 상속(Inheritance)과 덕 타이핑(Duck Typing) — 을 예제와 함께 살펴보겠습니다.

Ruby에서 상속을 활용한 다형성

상속이란 자식 클래스가 부모 클래스의 속성을 물려받는 것을 말합니다.

아래는 상속을 통해 다형성을 구현하는 예제입니다.


위 코드에는 부모 클래스 Instrument를 상속받은 두 개의 자식 클래스 StringedPercussion이 있습니다. 메서드 instrument_example 하나를 호출했는데 Saxophone, Guitar, Drums라는 여러 형태의 출력이 나온다는 점에서 이 예제는 다형적입니다.

상속을 통한 다형성 구현은 본질적으로 메서드 오버라이딩(method overriding)에 해당하지만, OOP 언어에서 다형성을 이해하는 데 도움이 되는 명확한 예시입니다.

Ruby에서 덕 타이핑을 활용한 다형성

OOP에서 다형성을 더 실용적으로 구현하는 방법은 아래처럼 덕 타이핑을 활용하는 것입니다.


각 클래스의 메서드 이름이 모두 brand로 동일하지만, 다형적 상속과 달리 메서드를 오버라이딩하지 않습니다. 부모 클래스로부터 상속받는 대신, 네 개의 독립된 클래스가 각자 자신만의 메서드를 가집니다. 덕 타이핑이 유용한 이유는 각 메서드를 따로 호출할 필요 없이 클래스들을 순회(iterate)하며 각 메서드의 출력을 얻을 수 있기 때문입니다.

마찬가지로 메서드 brand 하나를 호출했는데 Gibson, Pearl, Fender, Casio라는 여러 형태의 출력이 생성되므로 이 역시 다형적입니다. 물론 이러한 결과를 만들기 위해 덕 타이핑과 다형성이 반드시 필요한 것은 아니지만, 깔끔하고 논리적인 코드를 작성하는 데 매우 유용합니다.

Ruby on Rails에서의 다형성

Ruby on Rails에서는 다형성이 Active Record 연관관계(association) 형태로 훌륭하게 작동합니다. 여러 모델이 본질적으로 같은 기능을 한다면, 이를 하나의 모델로 통합하여 다형적 관계(polymorphic relationship)를 만들 수 있습니다.

악기 주제를 이어가서, 사용자가 악기를 게시하고, 댓글을 달고, 리뷰(평점)를 남길 수 있는 애플리케이션을 가정해 보겠습니다. 아래 ERD(Entity-Relationship Diagram, 개체-관계 다이어그램)를 살펴보세요.

Ruby on Rails 다형성(Polymorphism) 완벽 가이드: 개념부터 실전 구현까지

이 예제에서 사용자는 세부 정보를 포함해 악기를 게시할 수 있고, 해당 게시물에 댓글을 달 수 있습니다.

다른 사용자들은 악기에 평점을 매기고, 댓글에도 평점을 매겨 그 유용성이나 신뢰성을 판단할 수 있습니다. 이러한 Active Record 연관관계도 우리 애플리케이션의 목적에는 충분히 부합합니다.

그런데 애플리케이션에 다른 연관관계를 추가하고 싶다면 어떻게 될까요? 중복된 연관관계를 반복해서 추가해야 합니다.

예를 들어, 사용자의 신뢰도를 평가하는 user_rating 모델을 추가하려면 별도의 테이블과 해당 테이블만의 연관관계를 새로 만들어야 합니다. 즉, user 모델과 user_rating 모델 사이에 새로운 관계를 추가해야 하며, ERD는 다음과 같은 모습이 됩니다.

Ruby on Rails 다형성(Polymorphism) 완벽 가이드: 개념부터 실전 구현까지

이제 세 개의 모델이 사실상 같은 일 — 다른 객체에 대한 평가 — 을 서로 다른 맥락에서 수행하고 있습니다. 이런 연관관계야말로 다형적 연관관계(polymorphic association)로 통합하기에 완벽한 후보입니다.

평점 모델들을 다형적으로 재구성한 ERD를 확인해 보겠습니다.

Ruby on Rails 다형성(Polymorphism) 완벽 가이드: 개념부터 실전 구현까지

rating 컬럼이 이미 존재하므로 혼동을 피하기 위해 모델 이름을 ratings 대신 reviews로 지었습니다. 여기서 리뷰 대상이 되는 모델들은 기존 모델들과의 연관관계를 그대로 유지하되, 개별 평점 모델들은 하나의 review 모델로 통합되었습니다.

reviewable_typereviewable_id 컬럼이 이제 개별 평점 모델들의 역할을 대신하며, 리뷰가 어떤 모델과 연결되는지를 나타냅니다.

reviewable_type 컬럼에는 모델 클래스 이름(user, instrument_post, 또는 comment)이 저장되고, reviewable_id에는 해당 모델의 ID가 저장됩니다.

이제 이 두 컬럼을 활용해 Active Record 쿼리나 조건문을 통해 평점 값과 특정 사용자, 게시물, 댓글을 연결할 수 있습니다. 외래 키인 user_id는 review 모델에 그대로 남아 있어, 어떤 사용자가 리뷰를 작성했는지 추적할 수 있습니다.

다형적 모델 이름에 붙은 '-able' 접미사가 지금은 낯설게 느껴질 수 있지만, 잠시 후 Rails의 마법을 경험하면 그 목적이 명확해질 것입니다.

review 모델이 다형적이라고 불리는 이유는, 하나의 모델(객체)이 사용자 리뷰, 댓글 리뷰, 악기 게시물 리뷰라는 여러 형태를 대표하고 담당할 수 있기 때문입니다.

Ruby on Rails에서 다형성 구현하기

이제 Rails 애플리케이션에 다형성을 직접 구현해 보겠습니다. 이미 user, instrument_post, comment 모델을 만들었다고 가정하고, 다형적 모델인 reviews를 추가해 보겠습니다.

먼저 터미널에서 다음과 같이 마이그레이션 파일을 생성하고 모델을 만듭니다.


이렇게 하면 마이그레이션 파일이 생성됩니다.


마이그레이션 실행 후 schema.rb 파일이 업데이트됩니다. polymorphic 옵션은 reviewable 컬럼을 reviewable_typereviewable_id 두 컬럼으로 변환해 줍니다.



앞서 '-able' 접미사를 언급했던 것이 기억나시나요? 이것은 다형적 연관관계를 지정하기 위한 Rails 네이밍 컨벤션으로, user, instrument_post, comment를 '리뷰 가능한(reviewable)' 대상으로 만들어 줍니다.

이 Rails 마법이 작동하려면 다른 모델들이 다형적 모델과 올바르게 연결되어 있어야 합니다.




이제 user, instrument_post, comment 모델 모두 리뷰를 받고 평점을 매길 수 있습니다.

사용자 두 명 이상, 댓글 하나, 악기 게시물 하나가 이미 생성되어 있다면, Active Record 쿼리를 통해 다양한 방식으로 리뷰를 생성하고 조회할 수 있습니다.





렌더링해야 할 데이터에 따라 reviews 모델과 상호작용하는 방법은 무궁무진합니다. 특히 중요한 점은 부모 모델이 리뷰를 생성하는 방식입니다. 리뷰가 사용자와 reviewable 모델에 연결되어 있기만 하면, Active Record가 자동으로 reviewable_idreviewable_type을 연관된 모델과 연결해 줍니다.

만약 Rails 예제에서 다형성을 사용하지 않았다면, 훨씬 더 많은 테이블과 불필요하게 중복된 컬럼, 그리고 수많은 belongs_tohas_many 연관관계가 필요했을 것입니다. 다형성은 테이블 조인(join)의 필요성을 줄여주어, 더 쉽고 빠른 Active Record 쿼리와 연관관계를 가능하게 합니다.

마무리: 다형성으로 깔끔하고 논리적인 Ruby 코드 작성하기

이번 글에서는 생물학과 Ruby 프로그래밍이라는 두 가지 완전히 다른 환경에서 다형성을 살펴보았습니다. 두 경우 모두 다형성의 본질은 '하나의 객체가 둘 이상의 형태를 나타내는 능력'입니다.

상속과 덕 타이핑을 통한 Ruby에서의 다형성 구현 방법을 알아보았고, 이어서 Ruby on Rails에서 다형성을 활용하는 구체적인 사례까지 다루었습니다.

다형성은 깔끔하고 논리적인 코드를 작성하는 데 큰 도움을 줍니다. 이 필수적인 OOP 개념을 여러분의 도구 상자에 꼭 추가하셔서, 현재와 미래의 프로젝트는 물론 과거에 작성한 애플리케이션 개선에도 활용하시길 바랍니다.

즐거운 코딩 되세요!

P.S. Ruby Magic 글을 가장 먼저 읽고 싶으시다면 Ruby Magic 뉴스레터를 구독하세요. 어떤 글도 놓치지 않을 수 있습니다!