메서드(Methods): 프로그램의 기능을 정의하는 문법

메서드(Methods): 프로그램의 기능을 정의하는 문법

메서드는 프로그램이 실제로 하는 일을 구현해요. Ruby의 메서드 정의는 def 키워드부터 시작해요. 이번에는 메서드 이름을 짓는 법, 반환 값, 그리고 다양한 인자 문법을 하나씩 짚어볼게요. 이 문서는 메서드 정의에만 집중하고, 호출하는 쪽 문법은 별도 문서에서 다룰게요.

출처: 공식문서 - Methods

본문

간단한 메서드 정의부터 볼게요.

def one_plus_one
  1 + 1
end

메서드 정의는 def 키워드, 메서드 이름, 메서드 본문, end 키워드로 이루어져요. 호출하면 메서드 본문이 실행되고, 이 메서드는 2를 반환해요. Ruby 3.0부터는 표현식이 정확히 하나뿐인 메서드를 위한 축약 문법도 있어요.

def one_plus_one = 1 + 1

메서드 이름

메서드 이름은 연산자 중 하나이거나, 문자나 8번째 비트가 설정된 문자로 시작해야 해요. 그 뒤에는 문자, 숫자, _(밑줄), 또는 8번째 비트가 설정된 문자가 올 수 있어요. 관례적으로 여러 단어로 된 메서드 이름은 밑줄로 단어를 구분해요.

단항 연산자 메서드 -+를 정의할 때는 연산자 뒤에 @를 붙여요. 이 @는 단항 연산자를 이항 연산자와 구분하기 위해 필요해요.

class C
  def -@
    puts "you inverted this object"
  end
end

obj = C.new
-obj # prints "you inverted this object"

요소 참조와 할당 메서드 [][]=도 정의할 수 있어요. 둘 다 인자를 하나 이상 받을 수 있고, 요소 참조([])는 인자를 안 받을 수도 있어요.

class C
  def [](a, b)
    puts a + b
  end

  def []=(a, b, c)
    puts a * b + c
  end
end

obj = C.new

obj[2, 3]     # prints "5"
obj[2, 3] = 4 # prints "10"

반환 값

기본적으로 메서드는 본문에서 마지막으로 평가된 표현식을 반환해요. 위 예시에서 마지막(그리고 유일한) 표현식은 1 + 1이었어요. return 키워드를 쓰면 메서드가 값을 반환한다는 걸 명시적으로 드러낼 수 있어요.

def one_plus_one
  return 1 + 1
end

return은 마지막 표현식이 평가되기 전에 메서드를 일찍 끝낼 때도 써요.

def two_plus_two
  return 2 + 2
  1 + 1  # this expression is never evaluated
end

할당 메서드의 경우, = 할당 문법을 쓸 때는 반환 값이 무시돼요. 대신 인자가 반환돼요.

def a=(value)
  return 1 + value
end

p(self.a = 5) # prints 5

메서드를 직접 호출하면 실제 반환 값을 돌려받아요.

p(send(:a=, 5)) # prints 6

스코프

메서드를 정의하는 기본 문법은 클래스에 메서드를 추가해요. class 키워드로 특정 클래스에 인스턴스 메서드를 정의할 수도 있어요.

class C
  def my_method
    # ...
  end
end

단일 객체에만 메서드를 추가할 수도 있어요. def greeting.broaden처럼 객체를 지정해서 정의하는 메서드는 싱글턴 메서드(singleton method) 라고 불러요.

def String.hello
  "Hello, world!"
end

조금 더 확실한 예로, 아래처럼 특정 문자열 인스턴스에만 메서드를 붙일 수도 있어요. broadengreeting 문자열 인스턴스에만 존재하고, 다른 문자열에는 없어요.

greeting = "Hello"
def greeting.broaden
  self + ", world!"
end

greeting.broaden # returns "Hello, world!"

인자

메서드는 인자를 받을 수 있어요. 인자 목록은 메서드 이름 뒤에 와요.

def add_one(value)
  value + 1
end

인자는 메서드 본문 안의 지역 변수예요. 호출 쪽에서 add_one에 1을 주면 이 메서드는 2를 반환해요. 인자 주변의 괄호는 선택 사항이에요.

def add_one value
  value + 1
end

축약 메서드 정의에서는 괄호가 필수예요.

# OK
def add_one(value) = value + 1
# SyntaxError
def add_one value = value + 1

여러 인자는 쉼표로 구분해요.

def add_values(a, b)
  a + b
end

기본 값

인자에는 기본 값을 줄 수 있어요.

def add_values(a, b = 1)
  a + b
end

기본 값이 있는 인자가 반드시 처음에 올 필요는 없지만, 기본 값이 있는 인자들은 함께 모여 있어야 해요. 아래는 괜찮아요.

def add_values(a = 1, b = 2, c)
  a + b + c
end

아래는 SyntaxError가 나요.

def add_values(a = 1, b, c = 1)
  a + b + c
end

기본 값은 이미 평가된 인자를 지역 변수로 참조할 수 있고, 인자 값은 항상 왼쪽에서 오른쪽으로 평가돼요.

def add_values(a = 1, b = a)
  a + b
end
add_values
# => 2

Array 분해(Decomposition)

인자에 추가 괄호를 쓰면 Array를 분해(언팩)할 수 있어요.

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2])

이 코드는 아래처럼 출력해요.

{:a => 1, :b => 2}

Array에 쓰지 않는 요소가 더 있으면 무시돼요. 분해는 얼마든지 중첩할 수 있어요.

def my_method(((a, b), c))
  # ...
end

배열/해시 인자

인자 앞에 *를 붙이면 남는 인자가 모두 Array로 변환돼요.

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, 2, 3 # prints [1, 2, 3]

배열 인자는 키워드 인자보다 먼저 와야 해요.

키워드 인자

키워드 인자는 기본 값이 있는 위치 인자와 비슷해요.

def add_values(first: 1, second: 2)
  first + second
end

**를 쓰면 임의의 키워드 인자를 받을 수 있어요.

def gather_arguments(first: nil, **rest)
  p first, rest
end

gather_arguments first: 1, second: 2, third: 3
# prints 1 then {:second=>2, :third=>3}

**nil은 메서드가 키워드를 받는다고 표시하면서 실제로는 키워드를 받지 않는다는 뜻이에요.

def no_keywords(**nil)
end

블록 인자

블록 인자는 &로 표시하고 반드시 마지막에 와야 해요.

def my_method(&my_block)
  my_block.call(self)
end

블록 인자는 블록을 다른 메서드로 전달할 때 가장 자주 써요.

def each_item(&block)
  @items.each(&block)
end

인자 전달(Argument Forwarding)

... 문법으로 인자를 그대로 다른 메서드에 전달할 수 있어요.

def concrete_method(*positional_args, **keyword_args, &block)
  [positional_args, keyword_args, block]
end

def forwarding_method(...)
  concrete_method(...)
end

forwarding_method(1, b: 2) { puts 3 }
#=> [[1], {:b=>2}, #<Proc:...skip...>]

...로 호출하는 방법은 ...로 정의된 메서드에서만 사용할 수 있어요.

def regular_method(arg, **kwarg)
  concrete_method(...) # Syntax error
end

더 알아보기