Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby on Rails 패턴 및 안티 패턴 소개

Ruby on Rails 패턴 및 안티 패턴에 대한 시리즈의 첫 번째 게시물에 오신 것을 환영합니다. 각 게시물에서 우리는 Rails 앱으로 작업하는 동안 접할 수 있는 모든 종류의 패턴에 대해 자세히 알아볼 것입니다.

오늘은 (디자인) 패턴이 무엇인지 보여주고 안티 패턴이 무엇인지도 함께 설명드리겠습니다. 설명을 더 잘 설명하기 위해 꽤 오랫동안 사용된 Ruby on Rails 프레임워크를 사용합니다. 어떤 이유로 Rails가 마음에 들지 않는 경우 잠시만 기다려 주십시오. 여기에 설명된 아이디어(또는 패턴)는 귀하가 사용하게 될 기술에 반향을 일으킬 수 있습니다.

그러나 패턴과 안티 패턴이 무엇인지 설명하기 전에 어떻게 필요한 지점에 도달했습니까? 우리 소프트웨어에 이 모든 것이 필요한 이유는 무엇입니까? 디자인해야 하는 이유 우리의 솔루션은 무엇입니까?

네, 당신은 디자이너입니다

초기 컴퓨터 프로그래밍 시대부터 사람들은 자신이 작성하는 프로그램의 설계를 처리해야 했습니다. 프로그램(또는 소프트웨어)을 작성하는 것은 문제에 대한 솔루션을 설계하는 것입니다. 소프트웨어를 작성할 때 당신은 디자이너입니다. 당신의 직책에 그것을 자유롭게 덧붙일 수 있습니다. 우리가 작성한 소프트웨어는 다른 사람들이 읽고/편집할 것이기 때문에 좋은 솔루션을 설계하는 것이 중요합니다. 또한 우리가 생각해낸 솔루션은 미래에 다른 사람들이 구축할 것입니다.

이 모든 것을 염두에 두고 여러 세대의 엔지니어가 경력 전반에 걸쳐 코드와 아키텍처에서 유사한 디자인을 보기 시작했습니다. 사람들은 문제에 대한 표준 솔루션을 추출하고 문서화하기 시작했습니다. 어떤 사람들은 그것이 우리 인간이 기능하는 방식의 자연스러운 방식이라고 말할 것입니다. 우리는 모든 것에서 패턴을 분류하고 찾는 것을 좋아하며 소프트웨어도 예외는 아닙니다.

소프트웨어 엔지니어링이 더 복잡해짐에 따라 인간으로서 패턴이 점점 더 나타나기 시작했습니다. 소프트웨어 디자인 패턴은 전 세계의 엔지니어들과 함께 발전하기 시작했습니다. 책, 에세이, 강연 등을 통해 잘 생각하고 검증된 솔루션에 대한 아이디어를 더욱 널리 퍼뜨렸습니다. 이러한 솔루션은 많은 사람들의 시간과 비용을 절약해 주었으므로 디자인 패턴이라는 용어를 살펴보고 진정한 의미를 살펴보겠습니다.

디자인 패턴이란 무엇입니까?

소프트웨어 엔지니어링에서 패턴은 일반적인 문제를 해결하는 데 재사용할 수 있는 솔루션으로 설명됩니다. 패턴은 소프트웨어 엔지니어들 사이에서 모범 사례로 간주되는 것입니다. 소프트웨어 엔지니어가 설정하기 때문에 패턴에서 반대 패턴(반패턴)으로 빠르게 이동할 수 있지만 나중에 설명하겠습니다.

디자인 패턴은 솔루션으로 가는 길을 보여주지만 나머지 소프트웨어에 연결할 준비가 된 코드를 제공하지는 않습니다. 패턴을 잘 디자인된 코드를 작성하기 위한 지침으로 생각하고 구현해야 합니다. 일상적인 코딩에 패턴을 사용하는 것은 80년대 후반에 나타났으며, 여기서 Kent Beck과 Ward Cunningham은 '패턴 언어'를 사용하는 아이디어를 생각해 냈습니다.

패턴 언어에 대한 아이디어는 70년대 후반 크리스토퍼 알렉산더(Christopher Alexander)의 책 패턴 언어(A Pattern Language)에서 나왔습니다. 놀라실 수도 있지만 이 책은 소프트웨어 엔지니어링이 아니라 건물 아키텍처에 관한 것입니다. 패턴 언어는 여러 가지 방법으로 사용할 수 있는 문제와 솔루션의 핵심을 설명하는 조직적이고 일관된 패턴 집합입니다. 익숙한 소리? (힌트:프레임워크, 다른 힌트:Rails)

나중에 소프트웨어 공학의 디자인 패턴은 1994년 Gang Of Four의 전설적인 책 Design Patterns에 의해 출판된 이후 많은 청중들에게 유명해졌습니다. 이 책에는 오늘날 사용되는 패턴에 대한 설명과 정의가 있습니다. 바로 Factory, Singleton, Decorator입니다. 몇 개.

좋습니다. 이제 디자인 및 패턴에 대한 지식을 익히거나 새로 고쳤으니 안티 패턴이 무엇인지 알아보겠습니다.

디자인 안티 패턴이란 무엇입니까?

패턴을 좋은 사람이라고 생각하면 안티 패턴이 나쁜 것입니다. 더 정확하게 말하면 소프트웨어 안티 패턴은 일반적으로 사용되지만 비효율적이거나 비생산적인 것으로 간주되는 패턴입니다. 안티 패턴의 전형적인 예는 다양한 객체로 추출 및 분리될 수 있는 많은 기능과 종속성을 포함하는 God 객체입니다.

코드에서 안티 패턴의 일반적인 원인은 많습니다. 예를 들어, 좋은 사람은 좋은 사람(패턴)이 나쁜 사람(반패턴)이 될 때입니다. 이전 회사에서 특정 기술을 사용하는 데 익숙해지고 해당 기술에서 높은 수준의 능력을 얻었다고 가정해 보겠습니다. 예제를 위해 Docker를 사용하겠습니다. 애플리케이션을 Dockercontainer에 효율적으로 패키징하고, 클라우드에서 오케스트레이션하고, 클라우드에서 로그를 가져오는 방법을 알고 있습니다. 갑자기 프론트 엔드 애플리케이션을 배송해야 하는 새로운 직업을 얻게 됩니다. Docker 및 Docker와 함께 앱을 제공하는 방법에 대해 많이 알고 있으므로 첫 번째 결정은 모든 것을 패키징하여 클라우드에 배포하는 것입니다.

그러나 프론트 엔드 앱은 현재 작업에서 그렇게 복잡하지 않으며 컨테이너에 넣는 것이 가장 효과적인 솔루션이 아닐 수도 있습니다. 처음에는 좋은 생각처럼 들리지만 나중에는 역효과를 낳습니다. 이 안티 패턴을 "골든 해머"라고 합니다.

"망치가 있으면 모든 것이 못처럼 보인다"는 말로 요약할 수 있습니다. Docker와 서비스 오케스트레이션에 정말 능숙하다면 모든 것이 클라우드에서 오케스트레이션되도록 만들어진 Docker 서비스입니다.

이러한 일들이 일어나고 일어날 것입니다. 좋은 사람들은 나쁜 구매로 바뀌고 그 반대도 마찬가지입니다. 그러나 Ruby와 Rails는 이 그림에서 어디에 해당합니까?

루비 먼저, 그 다음 레일즈

대부분의 사람들은 웹사이트를 빠르게 구축하기 위한 인기 있는 프레임워크인 Ruby on Rails를 사용하여 Ruby를 소개받았습니다. 나는 루비와 같은 방식으로 알게 되었고, 그 점은 잘못되었다. Rails는 Model-View-Controller 또는 줄여서 MVC라고 하는 잘 정립된 소프트웨어 패턴을 기반으로 합니다. 그러나 Rails의 MVC 패턴에 대해 자세히 알아보기 전에 자주 발생하는 큰 오류 중 하나는 Ruby를 제대로 배우지 않고 Rails를 사용하는 것입니다.

Rails 프레임워크는 아이디어가 있고 빠르게 구축하고 싶을 때 가장 많이 찾는 프레임워크 중 하나였습니다. 오늘날에는 완전히 다른 이야기입니다. Rails는 여전히 사용되지만 전성기만큼은 아닙니다. 사용과 실행이 매우 쉽기 때문에 많은 초보자가 rails new 명령을 사용하여 웹 앱을 구축하기 시작했습니다. 그런데 길을 따라 문제가 발생하기 시작했습니다. 초보자는 Rails를 사용한 개발의 속도와 단순성에 매료되며 처음에는 모든 것이 마술처럼 매끄럽게 작동합니다. 그러면 당신은 많은 '마법'을 당연하게 여겼고 커튼 뒤에서 무슨 일이 일어나고 있는지 이해하지 못한다는 것을 알게 됩니다.

나는이 문제가 있었고 많은 초보자와 고급 초보자가 어려움을 겪고 있다고 확신합니다. 손에 든 프레임워크로 시작하여 그 위에 빌드하고 고도로 사용자 정의된 것을 추가하려고 하면 해당 프레임워크의 모든 마법 포인트를 사용했기 때문에 할 수 없습니다. 그 시점에서, 당신은 처음으로 돌아가서 기본을 배워야 합니다. 되돌아가는 것은 큰 문제가 아닙니다. 그러나 Ruby에서와 같이 필수 사항을 배우지 않고 계속 진행하면 문제가 더 심각해집니다. 이와 관련하여 도움이 될 수 있는 좋은 책은 Well-Grounded Rubyist입니다.

초보자는 처음부터 끝까지 읽을 필요가 없습니다. 그러나 빨리 상담할 수 있도록 곁에 두십시오. 하던 일을 갑자기 멈추고 책 전체를 읽어야 한다는 말은 아닙니다. 하지만 때때로 멈추고 Ruby 기본 지식을 새로고침하면 새로운 지평이 열릴 수도 있습니다.

MVC:Rails의 빵과 버터

하지만 MVC는 어떻습니까? Model-View-Controller 패턴은 마초에 관한 것이었습니다. Ruby(Rails), Python(Django), Java(Play, Spring MVC)와 같은 수많은 언어의 많은 프레임워크에서 채택되었습니다. 아이디어는 각각의 작업을 수행하는 별도의 구성 요소를 갖는 것입니다.

  • 모델은 데이터와 비즈니스 로직을 처리합니다.
  • 보기는 데이터 및 사용자 인터페이스를 표시하기 위한 것입니다.
  • 컨트롤러는 모델에서 데이터를 가져오고 사용자에게 보기를 표시하여 둘을 연결합니다.

이론상으로는 훌륭하고 논리가 최소화되고 웹사이트가 복잡한 논리를 포함하지 않을 때 탁월합니다. 여기에서 문제가 복잡해 지지만 잠시 후에 알아보도록 하겠습니다.

MVC는 웹 개발 커뮤니티 전체에 들불처럼 퍼졌습니다. 요즘 엄청나게 인기 있는 React와 같은 Evenlibraries는 웹 앱의 보기 계층으로 설명됩니다. 흔들릴 수 없을 정도로 대중화된 패턴은 없습니다. Rails는 채널 개념이 MVC 패턴의 컨트롤러로 설명되는 ActionCable로 Publish-Subscribe를 추가했습니다.

그러나 널리 사용되는 패턴에서 안티 패턴은 무엇입니까? MVC 패턴의 각 부분에 대해 가장 일반적인 안티 패턴을 살펴보겠습니다.

모델 문제

애플리케이션이 성장하고 비즈니스 로직이 확장됨에 따라 사람들은 모델을 너무 많이 사용하는 경향이 있습니다. 지속적인 성장은 뚱뚱한 모델이라는 안티 패턴으로 이어질 수 있습니다.

유명한 '뚱뚱한 모델, 스키니 컨트롤러' 패턴은 나쁜 사람으로, 때로는 좋은 사람으로 식별합니다. 우리는 지방이 있는 것이 안티 패턴이라고 말할 것입니다. 더 잘 이해하기 위해 예를 들어 보겠습니다. Spotify 또는 Deezer와 같은 스트리밍 서비스가 있다고 상상해보십시오. 그 안에 다음과 같은 노래에 대한 모델이 있습니다.

class Song < ApplicationRecord
  belongs_to :album
  belongs_to :artist
  belongs_to :publisher
 
  has_one :text
  has_many :downloads
 
  validates :artist_id, presence: true
  validates :publisher_id, presence: true
 
  after_update :alert_artist_followers
  after_update :alert_publisher
 
  def alert_artist_followers
    return if unreleased?
 
    artist.followers.each { |follower| follower.notify(self) }
  end
 
  def alert_publisher
    PublisherMailer.song_email(publisher, self).deliver_now
  end
 
  def includes_profanities?
    text.scan_for_profanities.any?
  end
 
  def user_downloaded?(user)
    user.library.has_song?(self)
  end
 
  def find_published_from_artist_with_albums
    ...
  end
 
  def find_published_with_albums
    ...
  end
 
  def to_wav
    ...
  end
 
  def to_mp3
    ...
  end
 
  def to_flac
    ...
  end
end

이러한 모델의 문제는 노래와 관련될 수 있는 다른 논리의 쓰레기장이 된다는 것입니다. 이것은 메서드가 시간이 지남에 따라 하나씩 천천히 추가되면서 발생합니다. 그러면 전체 모델이 크고 복잡해 보이며 논리를 다른 두 곳으로 나누는 것이 나중에 유용할 수 있습니다.

박쥐에서 바로 이 모델이 깨는 몇 가지 권장 사례가 있음을 알 수 있습니다. SRP(Single Responsibility Principle)를 위반하는 것입니다. 팔로어 및 게시자에게 알리는 것을 처리합니다. 욕설이 있는지 텍스트를 확인하고 노래를 다른 오디오 형식으로 내보내는 방법 등이 있습니다. 이 모든 것이 모델의 복잡성을 더하고 이 모델의 테스트 파일을 상상조차 할 수 없습니다.

이 모델을 리팩토링하는 방법은 주로 메서드가 다른 곳에서 어떻게 호출되고 사용되는지에 달려 있습니다. 이러한 문제를 처리하는 방법에 대한 몇 가지 일반적인 아이디어를 제시할 것이며 귀하의 경우에 가장 적합한 것을 선택할 수 있습니다.

팔로워와 게시자에게 알리는 콜백은 작업으로 추출될 수 있습니다. 다음과 같이 작업이 대기열에 추가되고 논리가 모델에서 제외됩니다.

class NotifyFollowers < ApplicationJob
  def perform(followers)
    followers.each { |follower| follower.notify }
  end
end
 
class NotifyPublisher < ApplicationJob
  def perform(publisher, song)
    PublisherMailer.song_email(publisher, self).deliver_now
  end
end

작업은 모델과 다른 별도의 프로세스에서 자체적으로 실행됩니다. 이제 작업 로직을 개별적으로 테스트하고 적절한 작업이 모델에서 대기열에 추가되었는지 확인할 수 있습니다.

욕설과 사용자가 노래를 다운로드했는지 여부를 확인하는 모든 작업이 앱의 보기 부분에서 발생한다고 가정해 보겠습니다. 이 경우 Decorator 패턴을 사용할 수 있습니다. 빠르게 시작할 수 있는 인기 있는 솔루션 중 하나는 Draper gem입니다. 그것으로 다음과 유사한 데코레이터를 작성할 수 있습니다.

class SongDecorator < Draper::Decorator
  delegate_all
 
  def includes_profanities?
    object.text.scan_for_profanities.any?
  end
 
  def user_downloaded?(user)
    object.user.library.has_song?(self)
  end
end

그런 다음 decorate를 호출합니다. 예:

def show
  @song = Song.find(params[:id]).decorate
end

다음과 같이 보기에서 사용하십시오.

<%= @song.includes_profanities? %>
<%= @song.user_downloaded?(user) %>

종속성을 사용하는 것이 마음에 들지 않으면 데코레이터를 사용할 수 있지만 다른 블로그 게시물에서 이에 대해 이야기하겠습니다. 이제 대부분의 모델 문제를 분리했으므로 노래를 찾고 노래를 변환하는 방법을 처리해 보겠습니다. 모듈을 사용하여 분리할 수 있습니다.

module SongFinders
  def find_published_from_artist_with_albums
    ...
  end
 
  def find_published_with_albums
    ...
  end
end
 
module SongConverter
  def to_wav
    ...
  end
 
  def to_mp3
    ...
  end
 
  def to_flac
    ...
  end
end

노래 모델은 SongFinders를 확장합니다. 모듈이므로 해당 메서드를 클래스 메서드로 사용할 수 있습니다. 노래 모델에는 SongConverter가 포함됩니다. 모듈이므로 해당 메서드를 모델 인스턴스에서 사용할 수 있습니다.

이 모든 것이 우리의 Song 모델을 매우 슬림하고 적절하게 만들 것입니다.

class Song < ApplicationRecord
  extend SongFinders
  include SongConverter
 
  belongs_to :album
  belongs_to :artist
  belongs_to :publisher
 
  has_one :text
  has_many :downloads
 
  validates :artist_id, presence: true
  validates :publisher_id, presence: true
 
  after_update :alert_artist_followers, if: :published?
  after_update :alert_publisher
 
  def alert_artist_followers
    NotifyFollowers.perform_later(self)
  end
 
  def alert_publisher
    NotifyPublisher.perform_later(publisher, self)
  end
end

더 많은 모델 안티 패턴이 있으며 이것은 모델과 함께 남쪽으로 갈 수 있는 한 가지 예일 뿐입니다. 이 시리즈의 다른 블로그 게시물에서 더 많은 모델 안티 패턴에 대해 자세히 알아볼 것입니다. 지금은 보기에 문제가 발생할 수 있는 부분을 살펴보겠습니다.

문제 보기

모델 문제 외에도 Rails 사람들은 때때로 보기의 복잡성으로 인해 어려움을 겪을 수 있습니다. 예전에는 HTML과 CSS가 웹 애플리케이션의 보기 부분의 왕이었습니다. 시간이 지남에 따라 천천히 JavaScript가 지배하게 되었고 프론트 엔드의 거의 모든 부분이 JavaScript로 작성되었습니다. Rails는 이와 관련하여 약간 다른 패러다임을 따릅니다. JavaScript에서 모든 것을 보는 대신 JS를 "뿌려야" 합니다.

어쨌든 HTML, CSS, JS, Ruby를 같은 곳에서 처리해야 하는 것은 번거로울 수 있습니다. Rails 뷰를 빌드할 때 까다로운 점은 도메인 논리가 뷰 내부에서 가끔 발견될 수 있다는 것입니다. 이것은 처음부터 MVC 패턴을 깨뜨리기 때문에 절대 금물입니다.

또 다른 경우는 뷰와 부분에 너무 많은 임베디드 루비를 사용하는 것입니다. 어쩌면 로직의 일부가 도우미 또는 데코레이터(뷰 모델 또는 프리젠터라고도 함) 내부에 들어갈 수 있습니다. 이 시리즈의 다음 게시물 중 일부에서 예제를 다룰 예정이므로 계속 지켜봐 주시기 바랍니다.

컨트롤러 문제

Rails 컨트롤러는 또한 다양한 문제를 겪을 수 있습니다. 그 중 하나는 Fat Controller 안티패턴입니다.

이전에는 우리 모델이 뚱뚱했지만 약간의 무게가 줄었습니다. 이제 컨트롤러가 프로세스에서 추가 무게를 추가한 것을 알 수 있습니다. 일반적으로 이것은 비즈니스 로직이 컨트롤러 내부에 있지만 실제 위치는 모델 또는 다른 곳에 있을 때 발생합니다. 큰 모델 섹션에서 공유된 아이디어 중 일부는 여전히 컨트롤러에 적용할 수 있습니다. 즉, 발표자에게 코드를 추출하고, ActiveRecord 콜백을 사용하고, toService 개체에 의존합니다.

어떤 사람들은 Trailblazer ordry-transaction과 같은 gem을 사용하기도 합니다. 여기서 아이디어는 특정 트랜잭션을 처리하는 클래스를 만드는 것입니다. 컨트롤러에서 모든 것을 이동하고 모델을 마른 상태로 유지하면서 일부는 서비스, 트랜잭션, 작업 등을 호출하는 이러한 별도의 클래스 내에 논리를 저장하고 테스트합니다.

결론

더 많은 안티 패턴과 더 많은 솔루션이 있습니다. 이 게시물의 모든 내용을 다루려면 너무 많은 공간과 시간이 소요되며 게시물이 뚱뚱해 보일 것입니다(예:우리가 이야기한 모델 및 컨트롤러). Rails에서 MVC 패턴의 모든 측면에 대해 자세히 설명하는 시리즈를 따라야 합니다. 거기에서 가장 유명한 안티 패턴을 처리하는 방법을 찾을 수 있습니다. 그때까지 Ruby on Railsframework에서 가장 일반적인 패턴과 안티 패턴에 대한 개요를 즐겼기를 바랍니다.

다음 편까지, 건배!

추신 Ruby Magic 게시물이 언론에 공개되는 즉시 읽고 싶다면 Ruby Magic 뉴스레터를 구독하고 게시물을 놓치지 마세요!