Backend/Ruby

[λ²ˆμ—­] Ruby Modules(루비 λͺ¨λ“ˆ)

Seyun(Marco) 2024. 9. 20. 18:09
728x90

πŸ’‘μ›λ³ΈκΈ€ : https://alchemists.io/articles/ruby_modules

 

Ruby Modules | Alchemists

While this is by no means exhaustive of what modules can do for you, I hope it’s been empowering. As you architect your own code, take time to give careful thought to naming, organization, objects versus methods, inheritance, composition, etc. in your ow

alchemists.io

 

 

루비 λͺ¨λ“ˆμ€ 높은 μˆ˜μ€€μ—μ„œ λ‹€μŒκ³Ό 같은 μ£Όμš” 이점을 μ œκ³΅ν•©λ‹ˆλ‹€.

  • Namespaces(λ„€μž„μŠ€νŽ˜μ΄μŠ€): λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό μ§€μ •ν•˜κ³ , 객체λ₯Ό κ΅¬μ„±ν•˜κ³ , 객체λ₯Ό 쀑첩할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • Multiple Inheritance(닀쀑 상속): 닀쀑 상속을 μ œκ³΅ν•˜μ—¬ 클래슀 및 μΈμŠ€ν„΄μŠ€ μˆ˜μ€€μ—μ„œ κΈ°μ‘΄ 객체에 μΆ”κ°€ λ©”μ„œλ“œ ν˜•νƒœλ‘œ ν–‰μœ„λ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • Functions(ν•¨μˆ˜): λ‹€λ₯Έ 루비 객체처럼 직접 λ©”μ‹œμ§€λ₯Ό 전달할 수 μžˆλŠ” 순수 ν•¨μˆ˜ν˜• λ©”μ„œλ“œλ₯Ό μΊ‘μŠν™” ν•©λ‹ˆλ‹€.

이 κΈ€μ—μ„œλŠ” 이 κΈ°λŠ₯을 μ‚΄νŽ΄λ³΄λŠ”λ° μ‹œκ°„μ„ ν• μ• ν•  μ˜ˆμ •μ΄λ‹ˆ, μ§€κΈˆ λ°”λ‘œ μ‹œμž‘ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

Object Hierarchy(객체 계측 ꡬ쑰)

μœ„λŠ” μ•„λž˜ μ½˜μ†”μ—μ„œ 볼수 μžˆλŠ” λ‚΄μš©μ„ κ·Έλ¦° κ·Έλ¦Όμž…λ‹ˆλ‹€.

Module.ancestors.reverse
# [
#   BasicObject,
#   Kernel,
#   Object < BasicObject,
#   Module < Object
# ]

Class.ancestors.reverse
# [
#   BasicObject,
#   Kernel,
#   Module < Object,
#   Class < Module
# ]

λͺ¨λ“ˆκ³Ό 클래슀λ₯Ό λ‹€μŒκ³Ό 같이 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.

  • Functional(module) - ν•¨μˆ˜ν˜•(λͺ¨λ“ˆ)
  • Object Oriented(classes) - 객체 지ν–₯(클래슀)

λ¬Όλ‘  λ£¨λΉ„μ˜ 거의 λͺ¨λ“  것이 λͺ¨λ“ˆμ„ ν¬ν•¨ν•œ κ°μ²΄μ΄μ§€λ§Œ, μ΄λŠ” μœ μš©ν•œ ν”„λ ˆμž„μ›Œν¬κ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.

Namespaces(λ„€μž„μŠ€νŽ˜μ΄μŠ€)

λͺ¨λ“ˆμ€ μ•„ν‚€ν…μ²˜ λ‚΄μ—μ„œ 객체의 λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό μ§€μ •ν•˜κ³  κ΅¬μ„±ν•˜λŠ” κΈ°λ³Έ λ°©λ²•μž…λ‹ˆλ‹€.

module Models
  Location = Data.define :latitude, :longitude
end

λ¬Όλ‘  클래슀λ₯Ό μ„œλ‘œ 쀑첩할 μˆ˜λŠ” μžˆμ§€λ§Œ μ€‘μ²©λœ 클래슀 μ•ˆν‹°νŒ¨ν„΄μ΄λ―€λ‘œ λ“œλ¬Έ 경우처럼 λΉ„κ³΅κ°œ λ‚΄λΆ€ ν΄λž˜μŠ€κ°€ ν•„μš”ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ ꢌμž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μΆ”κ°€ ꡬ성을 μœ„ν•΄ λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό 쀑첩할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

module One
  module Two
    module Models
      Location = Data.define :latitude, :longitude
    end
  end
end

μœ„μ˜ 방법은 λͺ¨λ“ˆμ„ λ“€μ—¬μ“°κΈ°ν•˜μ—¬ μ€‘μ²©ν•˜λŠ” ꢌμž₯ λ°©λ²•μž…λ‹ˆλ‹€. λ˜λŠ” Flat ꡬ쑰λ₯Ό μ‚¬μš©ν• μˆ˜λ„ μžˆμ§€λ§Œ μ΄λŠ” Flat λͺ¨λ“ˆ μ•ˆν‹°νŒ¨ν„΄μœΌλ‘œ κ°•λ ₯히 ꢌμž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

module One::Two::Models
  Location = Data.define :latitude, :longitude
end

λ„€μž„μŠ€νŽ˜μ΄μŠ€λ‘œμ„œμ˜ λͺ¨λ“ˆμ˜ μœ μ—°μ„± 덕뢄에 쀑첩을 μ½˜μ†”μ— 좜λ ₯ν•˜μ—¬ 계측 ꡬ쑰λ₯Ό λΉ λ₯΄κ²Œ 검사 및 디버깅할 수 μžˆμŠ΅λ‹ˆλ‹€.

module One
  module Two
    module Models
      puts Module.nesting

      Location = Data.define :latitude, :longitude
    end
  end
end

# One::Two::Models
# One::Two
# One

μœ„μ™€ 같이 λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜μ—¬ μ½”λ“œλ₯Ό κ΅¬μ„±ν•˜κ³  ꡬ쑰적 의미λ₯Ό λΆ€μ—¬ν•˜λŠ” 방법(putsλ¬Έ μ œμ™Έ)이 μ˜¬λ°”λ₯Έ λ°©λ²•μž…λ‹ˆλ‹€.

Hooks

Hook을 μ‚¬μš©ν•˜λ©΄ 객체 λ‚΄μ—μ„œ λͺ¨λ“ˆμ΄ μ •μ˜λ  λ•Œ μΆ”κ°€ λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°κ³Ό 같은 κ³ κΈ‰ κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 4가지 κΈ°μ‘΄ Hook이 μžˆμŠ΅λ‹ˆλ‹€.

  • .extended : 클래슀 λ©”μ„œλ“œλ₯Ό ν™•μž₯ν•©λ‹ˆλ‹€. descendant(μžμ†)λΌλŠ” 단일 인자λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
  • .included : μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€. descendant(μžμ†)λΌλŠ” 단일 인자λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
  • .prepended : 객체λ₯Ό μ„œλΈŒν΄λž˜μŠ€ λŒ€μ‹  슈퍼클래슀둜 λ§Œλ“€μ–΄ μ•žμ— μ˜€λŠ” 객체 μ•žμ— λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. ancestor(쑰상)λΌλŠ” 단일 인자λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
  • .using : κΈ°μ‘΄ λ©”μ„œλ“œλ₯Ό κ΅¬μ²΄ν™”ν•˜κ±°λ‚˜ μƒˆ λ©”μ„œλ“œλ₯Ό μ‚½μž…ν•©λ‹ˆλ‹€. target μ΄λΌλŠ” 단일 인자λ₯Ό λ°›μŠ΅λ‹ˆλ‹€. target은 μ„ΈλΆ„ν™”ν•  λŒ€μƒμž…λ‹ˆλ‹€. μ„ΈλΆ„ν™”λŠ” λ¬Έμ„œμ˜ λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜μ§€λ§Œ 더 μžμ„Ένžˆ μ•Œμ•„λ³΄λ €λ©΄ μ„ΈλΆ„ν™” λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

μ‹œκ°μ μœΌλ‘œλŠ” μœ„μ™€ κ°™μŠ΅λ‹ˆλ‹€.

module Demo
  def self.extended(descendant) = super

  def self.included(descendant) = super

  def self.prepended(ancestor) = super
end

Class.new.extend(Demo).singleton_class.ancestors
# [
#   #<Class:#<Class:0x0000000130c101e8>>,
#   Demo,
#   #<Class:Object>,
#   #<Class:BasicObject>,
#   Class,
#   Module,
#   Object,
#   Kernel,
#   BasicObject
# ]

Class.new.include(Demo).ancestors
# [
#   #<Class:0x0000000130c58038>,
#   Demo,
#   Object,
#   Kernel,
#   BasicObject
# ]

Class.new.prepend(Demo).ancestors
# [
#   Demo,
#   #<Class:0x00000001222123c0>,
#   Object,
#   Kernel,
#   BasicObject
# ]

이λ₯Ό μ’€ 더 μ„ΈλΆ„ν™”ν•˜κΈ° μœ„ν•΄ λͺ‡ 가지 참고사항을 μ„€λͺ…ν•©λ‹ˆλ‹€.

  • 읡λͺ… 클래슀(Class.newλ₯Ό ν†΅ν•œ)λŠ” #<Class:0x00*> 둜 ν‘œμ‹œλ˜λŠ” 데λͺ¨μš©μž…λ‹ˆλ‹€.
  • descendant(μžμ†) κ³Ό ancestor(쑰상) 을 μ‚¬μš©ν•˜λ©΄ 가독성이 ν–₯μƒλ˜λ―€λ‘œ extending(ν™•μž₯), including(포함), prependingν•˜λŠ” 객체의 μ’…λ₯˜λ₯Ό λΉ λ₯΄κ²Œ 이해할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • extending(ν™•μž₯), including(포함) μ‹œ DemoλŠ” 항상 μƒμœ„ ν΄λž˜μŠ€μž…λ‹ˆλ‹€. μ•žμ— 뢙일 λ•Œλ§Œ Demoκ°€ ν•˜μœ„ν΄λž˜μŠ€κ°€ λ©λ‹ˆλ‹€. 이 계측 ꡬ쑰λ₯Ό μƒκΈ°μ‹œν‚€κΈ° μœ„ν•΄ including, extending ν• λ•ŒλŠ” descendant(μžμ†) λ₯Ό μ‚¬μš©ν•˜κ³  μ•žμ— 뢙일 λ–„λŠ” ancestor(쑰상) λ₯Ό μ‚¬μš©ν•˜λŠ” μ΄μœ λ„ μ—¬κΈ° μžˆμŠ΅λ‹ˆλ‹€. 이 μš©μ–΄λ₯Ό μ‚¬μš©ν•˜μ—¬ μžμ‹ μ˜ μ½”λ“œλ₯Ό 가독성 있게 μœ μ§€ν•˜μ„Έμš”.

이와 κ΄€λ ¨ν•˜μ—¬ ν•˜μœ„ ν΄λž˜μŠ€κ°€ λΆ€λͺ¨ ν΄λž˜μŠ€μ—μ„œ 상속할 λ•Œ ν•˜μœ„ ν΄λž˜μŠ€κ°€ μžμ†μœΌλ‘œ μ „λ‹¬λ˜κΈ° λ•Œλ¬Έμ— Class.inherited ν˜Ήμ€ 인자둜 descendant λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

class Parent
  def self.inherited(descendant) = super
end

λͺ¨λ“ˆμ„ ν¬ν•¨ν•˜κ³  이와 λ™μΌν•œ κ΅¬ν˜„μ„ μ‚¬μš©ν•˜λŠ” 경우 λ‹€μŒμ„ 톡해 ν΄λž˜μŠ€μ— κΈ°λŠ₯이 ν¬ν•¨λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” μ—¬λŸ¬ 가지 방법이 μžˆμŠ΅λ‹ˆλ‹€.

Example.include? Demo            # => true
Example < Demo                   # => true
Example.ancestors.include? Demo  # => true

κΈ°λŠ₯ μΆ”κ°€

λ‹€μŒλ‹¨κ³„λ‘œ λ‚΄λ €κ°€λ©΄, Module#include 에 μ˜ν•΄ ν˜ΈμΆœλ˜λŠ” Module#append_features κ°€ μžˆμŠ΅λ‹ˆλ‹€. 이 λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•˜λ©΄, μ°©ν•˜κ²Œ μ‚¬μš©ν•  μˆ˜λ„ 있고, μ•…ν•˜κ²Œ μ‚¬μš©ν• μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ° #append_featrues κ°€ μ•…μš©λ˜λŠ” 상황을 λ³΄μ—¬λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.

module Demo
  def self.append_features descendant
    descendant.module_eval do
      def label = "😈"
    end
  end
end

Example = Class.new { include Demo }

Example < Demo     # nil
Example.new.label  # "😈"

μœ„μ—μ„œ μš°λ¦¬λŠ” #include 에 μ˜ν•΄ ν˜ΈμΆœλ˜λŠ” #append_features λ₯Ό μ‚¬μš©ν•˜μ—¬ 후속 객체에 #label λ©”μ„œλ“œλ₯Ό λ™μ μœΌλ‘œ μƒμ„±ν•˜μ—¬ λ™μž‘μ„ λ³€κ²½ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이것은 #include λ₯Ό 직접 ν˜ΈμΆœν•  λ•Œ λ°œμƒν•˜λŠ” κΈ°λ³Έ κΈ°λŠ₯을 μ μš©ν•˜μ§€ μ•Šκ²Œ λ§Œλ“€μ–΄, 즉 superλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•ŠμŒμœΌλ‘œμ¨ #include 의 λ™μž‘μ„ λ³€κ²½ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이런 방식은 μ‚¬μš©ν•˜μ§€ λ§ˆμ„Έμš”! #label λ©”μ„œλ“œλ₯Ό λˆ„κ°€ μ •μ˜ν–ˆλŠ”μ§€ 숨기면 디버깅 μ‹œ 큰 고톡을 μ΄ˆλž˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

쒋은 μΈ‘λ©΄μ—μ„œλŠ”, #append_features λ₯Ό ν™œμš©ν•˜μ—¬ 좔가적인 κ΅¬ν˜„, 디버깅, μ œμ–΄ 검사 등을 μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œλŠ” 디버깅 λͺ©μ μœΌλ‘œ μ–΄λ–€ 것이 ν¬ν•¨λ˜μ—ˆλŠ”μ§€ 둜그λ₯Ό λ‚¨κΈ°λŠ” λ°©μ‹μœΌλ‘œ #append_features λ₯Ό 쒋은 μ‚¬λ‘€λ‘œ μ‚¬μš©ν•œ μ˜ˆμ‹œμž…λ‹ˆλ‹€.

module Demo
  def self.append_features descendant
    puts "Including #{name.inspect} in #{descendant.name.inspect}."
    super
  end
end

class Example
  include Demo
end

# Including "Demo" in "Example".

큰 νž˜μ—λŠ” 큰 μ±…μž„μ΄ λ”°λ¦…λ‹ˆλ‹€. - Uncle Ben, Spiderman

악이 μ•„λ‹Œ 선이 λ˜μ„Έμš”.

Multiple Inheritance(닀쀑 상속)

닀쀑 상속, ν”νžˆ mixin(믹슀인)이라고 λΆˆλ¦¬λŠ” 방식은 μ—¬λŸ¬ 객체에 곡톡 κΈ°λŠ₯을 포함(include), ν™•μž₯(exclude) λ„λŠ” μ „μ²˜λ¦¬(prepend)ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. (μœ„μ˜ Hook μ„Ήμ…˜μ—μ„œ 닀룬것 처럼). 특히, λ©”μ„œλ“œλ₯Ό 곡톡 λͺ¨λ“ˆλ‘œ μΆ”μΆœν•˜λ©΄ 이 곡톡 λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜λŠ” λͺ¨λ“  객체가 λ™μΌν•œ 데이터 및 μžμ‹ λ©”μ„œλ“œμ— μ˜μ‘΄ν•  수 μžˆλ‹€λŠ” 암묡적인 가정을 ν•˜κ²Œ λ©λ‹ˆλ‹€.

module Nameable
  def full_name = [first_name, last_name].compact.join " "
end

User = Data.define :first_name, :last_name do
  include Nameable
end

User[first_name: "Jayne", last_name: "Doe"].full_name
# Jayne Doe

Nameable λͺ¨λ“ˆμ΄ #first_name κ³Ό #last_name λ©”μ„œλ“œκ°€ μžˆλ‹€κ³  κ°€μ •ν•˜λŠ” 점에 μ£Όλͺ©ν•˜μ„Έμš”. 이 암묡적인 계약은 Nameable 을 μ΄λŸ¬ν•œ λ©”μ„œλ“œλ₯Ό 가진 κ°μ²΄μ—μ„œ μ‚¬μš©ν•˜κ²Œ 될 것이라고 κ°€μ •ν•©λ‹ˆλ‹€. 이런 방식이 κ·Ήλ‹¨μœΌλ‘œ 치우치면, 단일 λͺ¨λ“ˆμ—μ„œ μ—¬λŸ¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  λ•Œ, 특히 μƒˆλ‘œμš΄ λ™μž‘μ΄ μΆ”κ°€λ˜λ©΄μ„œ 객체듀이 μ„œλ‘œ λ‹€λ₯΄κ²Œ λ³€ν™”ν•˜κΈ° μ‹œμž‘ν•˜λ©΄, 이λ₯Ό λ¦¬νŒ©ν„°λ§ ν•˜κ±°λ‚˜ μ •λ¦¬ν•˜λŠ” 것이 맀우 μ–΄λ €μ›Œμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

이것이 λ°”λ‘œ μ—¬λŸ¬ 상속을 λ¬΄λΆ„λ³„ν•˜κ²Œ μ‚¬μš©ν•˜μ—¬ λ³΅μž‘ν•œ κ΅¬ν˜„μ„ λ§Œλ“€μ–΄λ‚΄λŠ” λ§Žμ€ νŒ€λ“€μ΄ 곀경에 λΉ μ§€λŠ” μ΄μœ μž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μ„― 개 μ΄μƒμ˜ λͺ¨λ“ˆλ‘œ λ™μž‘μ„ λ‚˜λˆ„κ³ , λ‹€μŒκ³Ό 같은 객체가 λ§Œλ“€μ–΄μ§„ 상황을 μƒκ°ν•΄λ΄…μ‹œλ‹€.

class Demo
  include Comparable
  include Geolocatable
  include Indexable
  include Nameable
  include Observable
  include Searchable
end

μœ„μ˜ 상황은 λΆ„λͺ… κ·Ήλ‹¨μ μ΄μ§€λ§Œ, μ—¬λŸ¬ 번 이런 경우λ₯Ό λͺ©κ²©ν•œ 적이 μžˆμŠ΅λ‹ˆλ‹€. μœ„μ™€ 같은 κ΅¬ν˜„μ—λŠ” λͺ‡κ°€μ§€ 문제점이 μžˆμŠ΅λ‹ˆλ‹€.

  • ν˜Όλž€μŠ€λŸ¬μš΄ λ™μž‘: Demo ν΄λž˜μŠ€λŠ” λ„ˆλ¬΄ λ§Žμ€ λ™μž‘μ„ 가지고 μžˆμŠ΅λ‹ˆλ‹€. λΉ„κ΅ν• μˆ˜λ„ 있고, 지리적 μœ„μΉ˜λ₯Ό μΆ”μ ν• μˆ˜λ„ 있고, 인덱싱이 κ°€λŠ₯ν•œ λ“± λ‹€μ–‘ν•œ κΈ°λŠ₯이 μΆ”κ°€λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 즉, λˆ„κ΅°κ°€ νŽΈλ¦¬ν•¨κ³Ό κ²ŒμœΌλ¦„μœΌλ‘œ 인해 객체의 본래 λͺ©μ μ„ 깊이 μƒκ°ν•˜μ§€ μ•Šκ³  μ—¬λŸ¬ κΈ°λŠ₯을 덕지덕지 뢙인 κ²ƒμž…λ‹ˆλ‹€. 객체의 단일 λͺ©μ μΈ μ΄ν•΄ν•œ ν›„μ—λŠ”, κΈ°λŠ₯을 κ°œλ³„ 객체둜 λ‚˜λˆ„μ–΄ SOLID μ›μΉ™μ˜ S 인 단일 μ±…μž„ 원칙(Single Responsibility Principle)을 λ”°λ₯΄λŠ” 것이 더 μ μ ˆν•©λ‹ˆλ‹€.
  • μˆœμ„œμ˜ μ€‘μš”μ„±: μ—¬κΈ°μ„œ 각 λͺ¨λ“ˆμ„ μ•ŒνŒŒλ²³ μˆœμ„œλ‘œ ν¬ν•¨μ‹œμΌ°μ§€λ§Œ, κ·Έ μˆœμ„œκ°€ 항상 μ˜³μ§€λŠ” μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 특히 일뢀 λͺ¨λ“ˆμ΄ 이전 λͺ¨λ“ˆκ³Ό λ™μΌν•œ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•˜κ³  μžˆλ‹€λ©΄ λ¬Έμ œκ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ 말해, λ‚˜μ€‘μ— ν¬ν•¨λœ λͺ¨λ“ˆμ΄ 이전 λͺ¨λ“ˆλ³΄λ‹€ μš°μ„ ν•˜κ²Œ 되며, 이둜 인해 μΆ©λŒν•˜λŠ” λ™μž‘μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ£Όμ˜ν•˜μ§€ μ•ŠμœΌλ©΄ μ΄λŸ¬ν•œ μΆ©λŒμ€ λΆˆν•„μš”ν•˜κ²Œ λ³΅μž‘ν•œ 디버깅 상황을 μ΄ˆλž˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ°€μž₯ μ‹¬κ°ν•œ λ¬Έμ œλŠ” Active Support Concernsμž…λ‹ˆλ‹€. 이것은 기본적으둜 닀쀑 상속을 μž₯λ €ν•©λ‹ˆλ‹€. 더 λ‚˜μœ 점은 Concernsκ°€ νŠΉμ •ν•œ Rails 방언이기 λ•Œλ¬Έμ— 더 이상 μˆœμˆ˜ν•œ Ruby κΈ°λŠ₯을 μ‚¬μš©ν•  수 μ—†λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 즉, κΈ°λŠ₯을 λΌμ΄λΈŒλŸ¬λ¦¬λ‚˜ gem으둜 μΆ”μΆœν•˜μ—¬ μ—¬λŸ¬ ν”„λ‘œμ νŠΈμ—μ„œ μž¬μ‚¬μš©ν•˜λ € ν•  λ•Œ 더 λ§Žμ€ λ…Έλ ₯이 ν•„μš”ν•©λ‹ˆλ‹€. μˆœμˆ˜ν•œ Rubyλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. κ·Έλ ‡κ²Œ ν•˜λ©΄ ν•„μš”ν•  λ•Œ 더 자유둭게 λ¦¬νŒ©ν„°λ§ν•  수 μžˆλŠ” μœ μ—°μ„±μ„ κ°€μ§ˆ 수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

닀쀑 상속을 μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ κ°•λ ₯ν•  수 μžˆμ§€λ§Œ, κΈ°μ‘΄ 객체의 μ½”λ“œλ₯Ό 쀄이기 μœ„ν•΄ μ—¬λŸ¬ λ©”μ„œλ“œλ₯Ό 곡톡 λͺ¨λ“ˆλ‘œ μΆ”μΆœν•˜λŠ” 것은 κ²°μ½” ν˜„λͺ…ν•œ 선택이 μ•„λ‹™λ‹ˆλ‹€. λŒ€μ‹ , Command Pattern을 톡해 κΈ°λŠ₯을 μΆ”μΆœν•œ ν›„, ν•„μš”ν•œ μ‹œμ μ— μ˜μ‘΄μ„± μ£Όμž…μ„ 톡해 ν•„μš”ν•œ 것을 μ£Όμž…ν•˜λŠ” 방법을 κ³ λ €ν•˜μ„Έμš”. 상속보단 μ‘°ν•©(Composition)을 μ‚¬μš©ν•˜λŠ” 것이 μž₯기적으둜 μœ μ§€κ°€λŠ₯ν•œ μ½”λ“œμ— 훨씬 μœ λ¦¬ν•©λ‹ˆλ‹€. λ˜ν•œ, 더 κ³ κΈ‰μŠ€λŸ¬μš΄ Module Builder Pattern도 또 λ‹€λ₯Έ 해결책이 될수 μžˆμŠ΅λ‹ˆλ‹€.

Functions(ν•¨μˆ˜)

λ˜λ‹€λ₯Έ μž₯점은 κΈ°λŠ₯적 λ©”μ„œλ“œμ˜ 논리적 그룹을 λ§Œλ“œλŠ” κ²ƒμž…λ‹ˆλ‹€. 예λ₯Όλ“€μ–΄, μƒνƒœλ‚˜ 좔가적인 λ™μž‘μ΄ ν•„μš”ν•˜μ§€ μ•Šμ€ ν•¨μˆ˜λ“€μ˜ λͺ¨μŒμ„ μœ„ν•΄ MyModule.my_function κ³Ό 같은 λ°©μ‹μœΌλ‘œ μˆœμ „νžˆ κΈ°λŠ₯적인 이유둜 λͺ¨λ“ˆ λ‚΄μ˜ ν•¨μˆ˜λ₯Ό κ·Έλ£Ήν™”ν•˜λŠ” 것은 λ…Όλ¦¬μ μœΌλ‘œ νƒ€λ‹Ήν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ μž‘μ—…μ€ extend self 와 #module_function 을 μ‚¬μš©ν•˜μ—¬ μˆ˜ν–‰ν•  수 있으며, μ—¬μ „νžˆ λͺ¨λ“ˆμ„ 포함(include), ν™•μž₯(extend), λ˜λŠ” μ „μ²˜λ¦¬(prepend)ν•  수 μžˆλŠ” μ˜΅μ…˜λ„ μ œκ³΅ν•©λ‹ˆλ‹€. 각각의 방법은 μ•„λž˜μ— μž‘μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

Extending Self

Extending Selfλ₯Ό μ‚¬μš©ν•˜λ©΄ 일반적으둜 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλ‘œ λ™μž‘ν•˜λŠ” λ©”μ„œλ“œλ₯Ό 클래슀 λ©”μ„œλ“œλ‘œ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

module Demo
  extend self

  def label = "Demo"
end

Demo.label                              # "Demo"
Demo.singleton_methods.include? :label  # true

κ°€μ‹œμ„±μ€ ν΄λž˜μŠ€μ— 포함될 λ•Œλ„ λ³€ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

MyClass = Class.new { include Demo }
my_class = MyClass.new

my_class.label                             # "Demo"
my_class.public_methods.include? :label    # true
MyClass.singleton_methods.include? :label  # false

μœ„ μ˜ˆμ‹œμ—μ„œ λ³Ό 수 μžˆλ“―μ΄, Demo λͺ¨λ“ˆμ΄ MyClass에 ν¬ν•¨λ˜λ©΄ #label λ©”μ„œλ“œλŠ” μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œκ°€ 되고, μ—¬μ „νžˆ 곡개 μƒνƒœλ‘œ μœ μ§€λ©λ‹ˆλ‹€.

λ˜ν•œ μ΄λŸ¬ν•œ λ©”μ„œλ“œλ“€μ€ λͺ¨λ‘ μ˜€λ²„λΌμ΄λ“œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

module Demo
  extend self

  def label = "Demo"
end

Demo.label # "Demo"

module Demo
  def label = "Override"
end

Demo.label # "Override"

μœ„ μ½”λ“œμ—μ„œ Demo λͺ¨λ“ˆμ˜ label λ©”μ„œλ“œκ°€ μ²˜μŒμ— “Demo” 값을 λ°˜ν™˜ν•˜μ§€λ§Œ, λ™μΌν•œ λͺ¨λ“ˆ λ‚΄μ—μ„œ λ‹€μ‹œ μ •μ˜λ˜λ©΄μ„œ “Override”둜 μ˜€λ²„λΌμ΄λ“œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” Rubyμ—μ„œ λͺ¨λ“ˆμ΄λ‚˜ 클래슀 λ‚΄ λ©”μ„œλ“œλ₯Ό λ‹€μ‹œ μ •μ˜ν•˜λŠ” 것이 κ°€λŠ₯함을 λ³΄μ—¬μ€λ‹ˆλ‹€.

Module Functions

λͺ¨λ“ˆμ˜ λͺ¨λ“  λ©”μ„œλ“œλ₯Ό ν•¨μˆ˜λ‘œ μ§€μ •ν•˜λ©΄ “self”둜 ν™•μž₯ν•  λ•Œ μœ μ‚¬ν•œ λ™μž‘μ„ κ°€μ§ˆ 수 μžˆμ§€λ§Œ, 더 λ‚˜μ€ μΊ‘μŠν™”λ₯Ό μœ„ν•΄ κ°€μ‹œμ„±μ„ λ³€κ²½ν•˜λŠ” 좔가적인 이점을 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

module Demo
  module_function

  def label = "Demo"
end

Demo.label                              # "Demo"
Demo.singleton_methods.include? :label  # true

ν΄λž˜μŠ€μ— 포함될 λ•Œ κ°€μ‹œμ„±μ΄ λ³€κ²½λ˜μ§€λ§Œ, 이전에 selfλ₯Ό ν™•μž₯ν•  λ•Œμ™€ λ™μΌν•œ λ™μž‘μ„ λ³΄μ΄λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

MyClass = Class.new { include Demo }
my_class = MyClass.new

my_class.label                             # `NoMethodError`
my_class.private_methods.include? :label   # true
my_class.public_methods.include? :label    # false
MyClass.singleton_methods.include? :label  # false

#label λ©”μ„œλ“œκ°€ 이제 privateλ©”μ„œλ“œκ°€ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” selfλ₯Ό ν™•μž₯ν•˜λŠ” 것보닀 μ μ ˆν•œ μΊ‘μŠν™”λ₯Ό 보μž₯ν•˜λŠ” 좔가적인 이점이 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“ˆ ν•¨μˆ˜μ˜ 또 λ‹€λ₯Έ 츑면은, ν•œ 번 ν¬ν•¨λ˜λ©΄ μ›λž˜ λ™μž‘μ΄ 볡사본이 λœλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

module Demo
  module_function

  def label = "Demo"
end

Demo.label # "Demo"

module Demo
  def label = "Override"
end

Demo.label # "Demo"

module Demo
  module_function

  def label = "Override"
end

Demo.label # "Override"

μ›λž˜ κΈ°λŠ₯을 μž¬μ •μ˜ν•˜λ €κ³  μ‹œλ„ν•  λ•Œ μ›λž˜ λ™μž‘λ§Œ λ‚˜νƒ€λ‚΄λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λͺ¨λ“ˆ ν•¨μˆ˜λ₯Ό ν¬ν•¨ν•˜λ©΄ 그것듀이 μ›λ³Έμ˜ 볡사본이 되기 λ•Œλ¬Έμž…λ‹ˆλ‹€. λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜κ³  λ§ˆμ§€λ§‰ μ˜ˆμ‹œμ—μ„œ module_function을 μ‚¬μš©ν•  λ•Œλ§Œ μ˜€λ²„λΌμ΄λ“œκ°€ μž‘λ™ν•˜λŠ” 것을 볼수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” extend self λ₯Ό μ‚¬μš©ν•΄ μ˜€λ²„λΌμ΄λ“œν•  경우 예기치 μ•Šκ²Œ λ™μž‘μ΄ λ³€κ²½λ˜λŠ” λΆ€μž‘μš©μ„ μΌμœΌν‚¬ 수 μžˆλ‹€λŠ” μ μ—μ„œ 이점이 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“ˆμ„ ν¬ν•¨ν•˜κ±°λ‚˜ ν™•μž₯ 및 μ „μ²˜λ¦¬ν•  λ•ŒλŠ” extend self λŒ€μ‹  module_function 을 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” 객체 API의 λ²”μœ„λ₯Ό μž‘κ²Œ(private) μœ μ§€ν•˜λŠ” 것이 크게(public)μœ μ§€ν•˜λŠ” 것보닀 λ¦¬νŒ©ν† λ§μ„ 더 μ‰½κ²Œ ν•  수 있고, λ‹€λ₯Έ 객체가 μ•Œκ±°λ‚˜ μ ‘κ·Όν•΄μ„œλŠ” μ•ˆλ˜λŠ” 데이터에 λŒ€ν•œ 접근을 μ΅œμ†Œν™”ν•  수 있기 λ–„λ¬Έμž…λ‹ˆλ‹€. λ©”μ„œλ“œλ₯Ό public으둜 μ‚¬μš©ν•  ν•„μš”κ°€ μžˆμ„λ•Œλ§Œ ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό public으둜 λ§Œλ“œλŠ”κ²ƒμ΄ λ°”λžŒμ§ ν•©λ‹ˆλ‹€.

module_function μ‚¬μš©μ€ protected λ‚˜ private 와 μœ μ‚¬ν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€. ν•œλ²ˆ μ •μ˜ν•˜λ©΄ κ·Έ μ•„λž˜μ˜ λͺ¨λ“  λ©”μ„œλ“œκ°€ λͺ¨λ“ˆ ν•¨μˆ˜κ°€ λ˜κ±°λ‚˜, 기호λ₯Ό μ œκ³΅ν•˜μ—¬ νŠΉμ • λ©”μ„œλ“œλ₯Ό λͺ¨λ“ˆ ν•¨μˆ˜λ‘œ μ§€μ •ν• μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ˜ˆμ‹œλ‘œλŠ” module_duction :label μž…λ‹ˆλ‹€.

Module Builder Pattern

기본적으둜 λͺ¨λ“ˆμ€ μƒνƒœλ₯Ό μ €μž₯ν•˜κΈ° μ•ŠκΈ° λ•Œλ¬Έμ— Namespace 정리, 닀쀑 상속, λ˜λŠ” 순수 ν•¨μˆ˜ λ™μž‘μ˜ 집합을 λ§Œλ“€κΈ°μ— μ ν•©ν•©λ‹ˆλ‹€. κ·Έλ ‡κΈ° 떄문에 λͺ¨λ“ˆμ€ Namespace에 맀우 μœ μš©ν•©λ‹ˆλ‹€. κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³ , Module Builder Pattern은 ν΄λž˜μŠ€μ™€ λͺ¨λ“ˆμ„ λ™μ‹œμ— μ‚¬μš©ν•  수 μžˆλŠ” 방법을 μ œκ³΅ν•˜μ—¬ μ΅œλŒ€ν•œ κΈ°λŠ₯μ„±κ³Ό μž¬μ‚¬μš©μ„±μ„ 얻을 수 있게 ν•©λ‹ˆλ‹€. Module Builder Pattern에 λŒ€ν•œ 심측적인 μ„€λͺ…은 이 κΈ€μ˜ λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜λ―€λ‘œ 더 μžμ„Ένžˆ μ•Œκ³  μ‹Άλ‹€λ©΄ 링크λ₯Ό 톡해 ν•™μŠ΅ν•˜μ„Έμš”.

넀이밍

넀이밍은 μ–΄λ ΅μ§€λ§Œ 맀우 μ€‘μš”ν•œ μž‘μ—…μž…λ‹ˆλ‹€. μœ„μ—μ„œ λ…Όμ˜ν•œ 바와 같이, λͺ¨λ“ˆμ˜ 이름을 지정할 λ–„ κ³ λ €ν•΄μ•Ό ν•  λ‘κ°€μ§€λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • λͺ…사: 쑰직화λ₯Ό μœ„ν•œ Namespace
  • ν˜•μš©μ‚¬: μ‹œμŠ€ν…œμ˜ μ—¬λŸ¬ λΆ€λΆ„ κ°„μ˜ 계약 역할을 ν•˜μ—¬ μ–΄λ–€ μž‘μ—…μ΄ μˆ˜ν–‰λ  수 있으며, μ–΄λ–»κ²Œ ν˜ΈμΆœλ˜μ–΄μ•Ό ν•˜λŠ”μ§€ μ§€μ •ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€. 이 μΈν„°νŽ˜μ΄μŠ€λŠ” Hook을 톡해 닀쀑 상속을 μ‚¬μš©ν•˜μ—¬ μΈν„°νŽ˜μ΄μŠ€μ— λ§žλŠ” λ™μž‘μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

λͺ…μ‚¬μ˜ 경우 Parsers, Processors, Handlers, Clients 처럼 λ³΅μˆ˜ν˜• λͺ…사λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” λ™μΌν•œ λ„€μž„μŠ€νŽ˜μ΄μŠ€ λ‚΄μ—μ„œ μœ μ‚¬ν•œ λ™μž‘μ„ ν•˜λŠ” μ—¬λŸ¬ 객체λ₯Ό μ‘°μ§ν™”ν•˜κΈ° λ•Œλ¬Έμ— λ³΅μˆ˜ν˜•μ΄ λ…Όλ¦¬μ μœΌλ‘œ 맞기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

ν˜•μš©μ‚¬μ˜ 경우, λͺ¨λ“ˆ 이름에 -able 접미사λ₯Ό λΆ™μ΄λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. μ΄λŠ” Ruby μ½”μ–΄ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλ„ μΆ©λΆ„νžˆ μ˜ˆμ‹œκ°€ μžˆμŠ΅λ‹ˆλ‹€.

μ–΄λ–€ μ‚¬λžŒμ΄ -able λŒ€μ‹  -ing 을 μ œμ•ˆν•˜λŠ” 것을 λ³Έ 적이 μžˆλŠ”λ°, κ·Έ 주유 μ£Όμž₯은 -ing κ°€ -able 보닀 μžμ—°μŠ€λŸ½κ²Œ λ“€λ¦°λ‹€λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€.

  • Accessable → Accessing
  • Containable → Containing
  • Infusible → Infusing
  • Searchable → Searching

ν₯미둜운 μƒκ°μ΄μ§€λ§Œ, ν˜•μš©μ‚¬ λŒ€μ‹  동사λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 μ§€μ–‘ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” 행동을 κ°€λŠ₯ν•˜κ²Œ ν•œλ‹€λŠ” μ˜λ―Έμ—μ„œ -able 이 κ°•μ‘°λ˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

즉, 행동을 μˆ˜ν–‰ν•˜λŠ” 것이 μ•„λ‹ˆλΌ κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. Rubyκ°€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ„€μ΄λ°ν•˜λŠ” λ°©μ‹μ—μ„œ λ²—μ–΄λ‚˜λ©΄ λͺ¨λ“ˆμ„ μƒν˜Έμž‘μš©ν•  λ•Œ 더 ν˜Όλž€μŠ€λŸ½κ²Œ λ§Œλ“€μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. ꢁ극적으둜 일관성이 μ€‘μš”ν•©λ‹ˆλ‹€. μΆ”κ°€μ μœΌλ‘œ μ„€λͺ…ν•˜μžλ©΄, μ € μ—­μ‹œ μ΄λŸ¬ν•œ κ·œμΉ™μ„ μ œκ°€ μž‘μ„±ν•œ gemμ—μ„œλ„ λ”°λ₯΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

κ²°λ‘ 

이 글이 λͺ¨λ“ˆμ΄ μ œκ³΅ν•  수 μžˆλŠ” λͺ¨λ“  κΈ°λŠ₯을 닀룬 것은 μ•„λ‹ˆμ§€λ§Œ, μ—¬λŸ¬λΆ„μ—κ²Œ 도움이 λ˜μ—ˆκΈ°λ₯Ό λ°”λžλ‹ˆλ‹€. μžμ‹ μ˜ μ½”λ“œλ₯Ό 섀계할 λ•Œ, 넀이밍, ꡬ쑰화, 객체와 λ©”μ„œλ“œμ˜ ꡬ뢄, 상속, μ‘°ν•© λ“±μ˜ μš”μ†Œλ₯Ό μ‹ μ€‘ν•˜κ²Œ κ³ λ €ν•˜μ—¬ μ½”λ“œλ₯Ό μ‰½κ²Œ λ°œκ²¬ν•˜κ³ , μ΄ν•΄ν•˜λ©°, μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•˜μ„Έμš”. 쒋은 μ„€κ³„λŠ” μ½”λ“œμ˜ 가독성, μœ μ§€ λ³΄μˆ˜μ„±, 그리고 μž¬μ‚¬μš©μ„±μ„ 크게 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

728x90
728x90