콘텐츠 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11

공식 Ruby FAQ

If you wish to report errors or suggest improvements for this FAQ, please go to our GitHub repository and open an issue or pull request.

메서드

Ruby는 어떤 메서드를 호출할지 어떻게 선택하나요?

Ruby는 모든 메시지를 메서드에 동적으로 바인딩합니다. 먼저 수신자의 싱글톤 메서드를 검색한 다음, 수신자의 자체 클래스에 정의된 메서드를 검색하고 마지막으로 수신자의 수퍼클래스(혼합되었을 수 있는 모듈 포함)에 정의된 메서드를 검색합니다. ClassName의 조상 클래스와 모듈을 표시하는 ClassName.ancestors로 검색 순서를 확인할 수 있습니다.

대체 메서드를 검색한 후에도 일치하는 메서드를 찾을 수 없는 경우, Ruby는 동일한 검색 절차를 반복하여 method_missing이라는 메서드를 호출하여 메서드를 찾으려고 시도합니다. 이를 통해 알 수 없는 메서드에 대한 메시지를 처리할 수 있으며 클래스에 동적 인터페이스를 제공하는 데 자주 사용됩니다.

module Emphasizable
  def emphasize
    "**#{self}**"
  end
end

class String
  include Emphasizable
end

String.ancestors
  # => [String, Emphasizable, Comparable, Object, Kernel, BasicObject]

"Wow!".emphasize  # => "**Wow!**"

emphasize 메서드를 찾을 때 String 클래스에서 찾을 수 없으므로 Ruby는 다음에 Emphasizable 모듈에서 찾습니다.

수신자의 클래스에 이미 존재하는 메서드(예: String#capitalize)를 재정의하려면 prepend를 사용하여 해당 클래스 앞의 조상 체인에 모듈을 삽입해야 합니다.

module PrettyCapitalize
  def capitalize
    "**#{super}**"
  end
end

class String
  prepend PrettyCapitalize
end

String.ancestors
  # => [PrettyCapitalize, String, Comparable, Object, Kernel, BasicObject]

"hello".capitalize  # => "**Hello**"

’, -, *, …등은 연산자인가요?

+, - 등은 연산자가 아니라 메서드 호출입니다. 따라서 새로운 정의로 인해 과부하가 걸릴 수 있습니다.

class MyString < String
  def -(other)
    self[0...other.size]  # self를 other의 크기로 자름
  end
end

그러나 다음은 메서드가 아닌 기본 제공 제어 구조로, 재정의할 수 없습니다.

=, .., ..., not, ||, &&, and, or, ::

단항(unary) +, - 연산자를 오버로드하거나 정의하려면 메서드 이름으로 +@, -@을 사용해야 합니다.

=는 객체의 속성을 설정하는 메서드를 정의하는 데 사용됩니다.

class Test
  def attribute=(val)
    @attribute = val
  end
end

t = Test.new
t.attribute = 1

+, -와 같은 연산자가 정의되어 있으면 Ruby는 자체 할당 형식(+=, -= 등)을 자동으로 처리합니다.

++--는 없나요?

Ruby에는 자동 증가 및 자동 감소 연산자가 없습니다. 대신 += 1-= 1을 사용할 수 있습니다.

싱글톤 메서드란 무엇인가요?

싱글톤 메서드는 하나의 특정 객체와 연관된 인스턴스 메서드입니다.

정의에 객체를 포함시켜 싱글톤 메서드를 생성합니다.

class Foo; end

foo = Foo.new
bar = Foo.new

def foo.hello
  puts "Hello"
end

foo.hello
bar.hello

결과:

Hello
prog.rb:11:in `<main>': undefined method `hello' for #<Foo:0x000000010f5a40> (NoMethodError)

싱글톤 메서드는 객체에 메서드를 추가하고 싶지만 새 서브클래스를 만드는 것이 적절하지 않을 때 유용합니다.

객체들은 됐고 Ruby에 간단한 함수는 없나요?

예인 동시에 아니요입니다. Ruby에는 C나 Perl과 같은 언어의 함수처럼 보이는 메서드가 있습니다.

def hello(name)
  puts "Hello, #{name}!"
end

hello("World")

결과:

Hello, World!

그러나 실제로는 수신자가 생략된 메서드 호출입니다. 이 경우 Ruby는 수신자가 자신(self)이라고 가정합니다.

따라서 hello는 함수처럼 보이지만 실제로는 Object 클래스에 속하는 메서드이며 숨겨진 수신자 자신에게 메시지로 전송됩니다. Ruby는 순수한 객체 지향 언어입니다.

물론 이러한 메서드를 함수처럼 사용할 수 있습니다.

그렇다면 이러한 함수 비슷한 메서드들은 모두 어디에서 온 것일까요?

Ruby의 거의 모든 클래스는 Object 클래스에서 파생됩니다. Object 클래스의 정의는 Kernel 모듈에 정의된 메서드에 섞여 있습니다. 따라서 이러한 메서드는 시스템의 모든 객체에서 사용할 수 있습니다.

클래스가 없는 간단한 Ruby 프로그램을 작성한다 하더라도 실제로는 Object 클래스 안에서 작업하는 것이 됩니다.

객체의 인스턴스 변수에 액세스할 수 있나요?

객체의 인스턴스 변수(@로 시작하는 변수)는 객체 외부에서 직접 액세스할 수 없습니다. 이는 좋은 캡슐화를 촉진합니다. 하지만 Ruby에서는 클래스 사용자가 인스턴스 변수를 어트리뷰트처럼 취급할 수 있도록 이러한 인스턴스 변수에 대한 접근자를 쉽게 정의할 수 있습니다. attr_reader, attr_writer, attr_accessor 중에 사용하기만 하면 됩니다.

class Person
  attr_reader   :name           # 쓰기 전용
  attr_accessor :wearing_a_hat  # 읽기/쓰기

  def initialize(name)
    @name = name
  end
end

p = Person.new("Dave")
p.name           # => "Dave"
p.wearing_a_hat  # => nil
p.wearing_a_hat = true
p.wearing_a_hat  # => true

유효성 검사를 수행하거나 파생된 속성을 처리하기 위해 자신만의 접근자 함수를 정의할 수도 있습니다. 읽기 접근자는 매개변수를 받지 않는 메서드이고, 할당 접근자는 단일 매개변수를 받는 =로 끝나는 메서드 이름입니다. 메서드 정의에서 메서드 이름과 = 사이에 공백이 없어도 되지만, 메서드를 호출할 때 공백을 삽입하여 다른 할당처럼 보이게 만들 수 있습니다. + 또는 - 메서드가 정의되어 있는 한 +=-=와 같은 자체 대입을 사용할 수도 있습니다.

privateprotected의 차이점은 무엇인가요?

가시성 키워드 ‘private’은 명시적 수신자 없이 함수 형식으로만 메서드를 호출할 수 있게 하므로 수신자로 ‘self’만 가질 수 있습니다. private 메서드는 메서드가 정의된 클래스 또는 그 서브클래스 내에서만 호출할 수 있습니다.

class Test
  def foo
    99
  end

  def test(other)
    p foo
    p other.foo
  end
end

t1 = Test.new
t2 = Test.new

t1.test(t2)

# Now make `foo' private

class Test
  private :foo
end

t1.test(t2)

결과:

99
99
99
prog.rb:8:in `test': private method `foo' called for #<Test:0x00000000b57a48> (NoMethodError)
        from prog.rb:23:in `<main>'

protected 메서드는 자체 클래스 또는 그 하위 클래스 내에서만 호출할 수 있지만 함수 형식과 수신자를 사용하여 모두 호출할 수 있습니다. 다음 예를 보시죠.

def <=>(other)
  age <=> other.age
end

age가 protected 메서드인 경우 컴파일되지만 private이면 컴파일되지 않습니다.

이러한 기능을 통해 클래스 내부에 대한 접근을 제어할 수 있습니다.

메서드의 가시성을 변경하려면 어떻게 해야 하나요?

private, protected, public를 사용하여 메서드의 표시 여부를 변경합니다. 클래스 정의 중에 매개변수 없이 사용하면 후속 메서드의 표시 여부에 영향을 줍니다. 매개변수와 함께 사용하면 명명된 메서드의 가시성을 변경합니다.

class Foo
  def test
    puts "hello"
  end
  private :test
end

foo = Foo.new
foo.test

결과:

prog.rb:9:in `<main>': private method `test' called for #<Foo:0x0000000284dda0> (NoMethodError)

private_class_method를 사용하여 클래스 메서드를 private로 설정할 수 있습니다.

class Foo
  def self.test
    puts "hello"
  end
  private_class_method :test
end

Foo.test

결과:

prog.rb:8:in `<main>': private method `test' called for Foo:Class (NoMethodError)

클래스에 정의된 메서드의 기본 가시성은 public입니다만 인스턴스 초기화 메서드인 initialize는 예외입니다.

최상위 수준에서 정의된 메서드도 기본적으로 public이 됩니다.

대문자로 시작하는 식별자가 메서드 이름이 될 수 있나요?

예, 할 수 있지만 쉽게 하지는 않습니다! 대문자로 된 이름 뒤에 공백이 있는 경우 Ruby는 (문맥에 따라) 메서드 이름이 아니라 상수라고 간주할 수 있습니다. 따라서 대문자로 된 메서드 이름을 사용하는 경우 항상 매개변수 목록을 괄호 안에 넣고 공백 없이 메서드 이름 옆에 괄호를 넣는 것을 잊지 마세요. (이 마지막 제안은 어쨌든 좋은 생각입니다!)

super를 호출하면 ArgumentError가 발생합니다.

메서드에 매개변수 없이 super를 호출하면 해당 메서드의 모든 인수가 수퍼클래스에 있는 같은 이름의 메서드로 전달됩니다. 원래 메서드의 인자 수가 상위 메서드의 인자 수와 일치하지 않으면 ArgumentError가 발생합니다. 이 문제를 해결하려면 super를 호출하고 적절한 수의 인수를 전달하면 됩니다.

같은 이름의 메서드를 두 단계 위로 호출하려면 어떻게 해야 하나요?

super는 같은 이름의 메서드를 한 단계 위로 호출합니다. 더 먼 조상에 있는 메서드를 오버로드하는 경우 메서드 정의로 마스킹하기 전에 alias를 사용하여 새 이름을 지정합니다. 그런 다음 해당 별칭을 사용하여 호출할 수 있습니다.

내장 메서드를 재정의한 후 원래 메서드를 호출하려면 어떻게 해야 하나요?

메서드 정의 내에서 super를 사용할 수 있습니다. alias을 사용하여 다른 이름을 지정할 수도 있습니다. 마지막으로, 원래 메서드를 Kernel의 싱글톤 메서드로 호출할 수 있습니다.

파괴적 메서드란 무엇인가요?

파괴적인 메서드는 객체의 상태를 변경하는 메서드입니다. String, Array, Hash 등이 이러한 메서드를 가지고 있습니다. 메서드에는 보통 두 가지 버전이 있는데, 하나는 일반 이름이고 다른 하나는 이름은 같지만 뒤에 !가 붙는 버전입니다. 일반 버전은 수신자의 복사본을 생성하고 이를 변경한 후 복사본을 반환합니다. “뱅” 버전(! 포함)은 수신자를 제자리에서 수정합니다.

그러나 할당 메서드(name=), 배열 할당([]=), Array.delete와 같은 메서드를 포함하여 !가 없는 파괴적인 메서드가 상당히 많다는 점에 유의하세요.

파괴적인 메서드가 위험할 수 있는 이유는 무엇인가요?

대부분의 경우 할당은 객체 참조를 복사할 뿐이며, 매개변수 전달은 할당과 동일하다는 점을 기억하세요. 즉, 동일한 객체를 참조하는 변수가 여러 개 생길 수 있습니다. 이러한 변수 중 하나가 파괴적인 메서드를 호출하는 데 사용되면 모든 변수가 참조하는 객체가 변경됩니다.

def foo(str)
  str.sub!(/foo/, "baz")
end

obj = "foo"
foo(obj)     # => "baz"
obj          # => "baz"

이 경우 실제 인수가 변경됩니다.

메서드에서 여러 값을 반환할 수 있나요?

예인 동시에 아니요입니다.

def m1
  return 1, 2, 3
end

def m2
  [1, 2, 3]
end

m1  # => [1, 2, 3]
m2  # => [1, 2, 3]

따라서 하나의 값만 반환되지만 그 값은 임의로 복잡한 객체가 될 수 있습니다. 배열의 경우 다중 할당을 사용하여 여러 반환 값의 효과를 얻을 수 있습니다. 다음의 예를 보세요.

def foo
  [20, 4, 17]
end

a, b, c = foo
a              # => 20
b              # => 4
c              # => 17