다른 언어에서 루비로

처음 루비 코드를 볼 때, 그것은 당신이 사용했던 다른 프로그래밍 언어를 연상하게 할 수 있습니다. 이것은 의도적입니다. 대부분의 문법은 펄, 파이썬, 자바나 다른 언어들과 유사합니다. 다른 언어를 사용한 적이 있다면 루비를 배우기는 아주 쉬울 것입니다.

이 문서에는 두 가지 주요 단원이 있습니다. 첫번째는 어떤 언어에서 루비로 넘어올 때의 빠른 요약입니다. 두번째는 이미 잘 알고있는 것과 비교할 수 있도록 언어의 주요 기능을 다룹니다.

차이점들: 어떤 언어에서 루비로

언어의 중요한 기능과 알아둘 점

루비를 배우는 동안 보게될 루비의 주요 기능들의 요점과 힌트들을 알아봅시다.

이터레이션

익숙해져야 하지만 전에 본것과는 다를 수 있는 루비의 두가지 기능은 “블록”과 이터레이터입니다. (C, C++, or pre-1.5 자바처럼) 인덱스로 반복하거나 (펄의 for (@a) {...}나 파이썬의 for i in aList: ...처럼) 리스트로 반복하는 대신, 루비에서는 매우 자주 다음과 같은 구문을 사용하는 것을 보게 됩니다.

some_list.each do |this_item|
  # 블록 안
  # this_item으로 연산 수행
end

each (나 collect, find, inject, sort 등등)에 관한 더 자세한 정보는 ri Enumerable (혹은 ri Enumerable#some_method)로 확인하세요.

모든 것은 값을 가지고 있다

표현식과 구문에 차이가 없습니다. 값이 nil 일지언정 모든 것은 값을 가지고 있습니다. 이런 것이 가능합니다.

x = 10
y = 11
z = if x < y
      true
    else
      false
    end
z # => true

심볼은 가벼운 문자열이 아니다

많은 루비 뉴비들은 심볼을 이해하는데 어려워하고, 어디에 사용하는 것인지 궁금해 합니다.

심볼은 최선의 아이덴티티라고 할 수 있습니다. 심볼은 무엇인지에 대한 것이 아니라 누구인지에 대한 것입니다. irb에서 차이점을 확인해 보세요.

irb(main):001:0> :george.object_id == :george.object_id
=> true
irb(main):002:0> "george".object_id == "george".object_id
=> false
irb(main):003:0>

object_id메서드는 객체의 아이덴티티를 반환합니다. 만약 두 객체가 같은 object_id를 가진다면 두 객체는 같습니다(메모리안의 같은 객체를 가리킵니다).

보시다시피, 심볼을 한번 사용하면, 같은 문자열을 사용하는 모든 심볼은 메모리 안의 같은 객체를 참조합니다. 어떤 두 심볼이 같은 문자들을 가지고 있다면, object_id는 일치하게 됩니다.

이제 문자열 (“george”)를 살펴봅시다. object_id가 일치하지 않습니다. 이 말은 메모리안의 다른 객체를 참조하고 있다는 뜻입니다. 새로운 문자열을 사용할 때마다, 루비는 매번 메모리에 할당 합니다.

심볼을 사용해야 할 지 문자열을 사용해야 할 지 결정해야 한다면, 무엇이 더 중요한지 생각해보세요. 객체의 아이덴티티가 중요한 경우(예를 들어 해시 키로 사용할 때)나 내용이 중요할 때(위에 있는 예제처럼 “george”)로 나눌 수 있습니다.

모든 것은 객체입니다.

“모든 것이 객체”라는 말은 과장이 아닙니다. 클래스나 정수조차도 객체입니다. 다른 객체에서 하는 일을 클래스나 정수에서도 할 수 있습니다.

MyClass = Class.new do
  attr_accessor :instance_var
end
# 위 구문은 밑의 구문과 같습니다.
# class MyClass
#   attr_accessor :instance_var
# end

변경가능한 상수

상수는 실제로 상수는 아닙니다. 이미 초기화된 상수를 수정한다면, 경고를 발생하긴 하지만, 프로그램을 종료시키진 않습니다. 이 이야기는 상수의 재 정의를 권장 한다는 이야기는 아닙니다.

이름 규칙

루비는 몇몇 이름 규칙을 강제합니다. 대문자로 시작하는 식별자는 상수입니다. 달러 기호($)로 시작하면 전역 변수입니다. @로 시작하면 인스턴스 변수입니다. @@로 시작하면 클래스 변수입니다.

하지만 메서드 이름은 대문자로 시작할 수 있습니다. 이는 밑에 예에서 볼 수 있는것 처럼 혼동을 야기할 수 있습니다.

Constant = 10
def Constant
  11
end

이제 Constant는 10이지만, Constant()는 11입니다.

키워드 인자

루비 2.0부터 메서드는 파이썬처럼 키워드 인자로 선언할 수 있습니다.

def deliver(from: "A", to: nil, via: "mail")
  "Sending from #{from} to #{to} via #{via}."
end

deliver(to: "B")
# => "Sending from A to B via mail."
deliver(via: "Pony Express", from: "B", to: "A")
# => "Sending from B to A via Pony Express."

범용적인 참

루비에서는, nilfalse 를 재외한 모든 것이 참으로 간주됩니다. C나 파이썬이나 많은 다른 언어들에서 0과 빈 리스트같은 값들이 거짓으로 간주됩니다. 다음 파이썬 코드를 보시죠. (이 예제는 다른 언어에도 적용됩니다)

# in Python
if 0:
  print "0 is true"
else:
  print "0 is false"

이 구문은 “0 is false”을 출력합니다. 같은 구문은 루비에서:

# in Ruby
if 0
  puts "0 is true"
else
  puts "0 is false"
end

“0 is true”를 출력합니다.

액세스 한정자는 스코프의 끝까지 적용

다음 루비코드에서,

class MyClass
  private
  def a_method; true; end
  def another_method; false; end
end

아마 another_methodpublic이기를 기대할 수도 있지만, private 액세스 한정자가 스코프의 끝이나 다른 액세스 한정자가 나올 때까지 지속 됩니다. 기본적으로 메서드는 public입니다.

class MyClass
  # 이제 a_method는 public입니다.
  def a_method; true; end

  private

  # another_method는 private입니다.
  def another_method; false; end
end

public, private, protected 는 사실 메서드이므로 매개 변수를 받을 수 있습니다. 한정자에 심볼을 넘겨주면, 메서드의 액세스 범위가 변경됩니다.

메서드 액세스

자바에서, public은 누구라도 액세스 할 수 있음을 의미합니다. protected는 클래스의 인스턴스, 하위 클래스의 인스턴스, 같은 패키지의 클래스의 인트턴스 는 액세스할 수 있지만, 그 밖의 장소에서는 액세스 할 수 없음을 의미합니다. private은 클래스의 인스턴스를 재외한 장소에서는 액세스 할 수 없음을 의미합니다.

루비는 약간 다릅니다. public은 말그대로 공개입니다. private은 메서드에서만 명시적인 수신기 없이 메서드를 호출할 수 있다는 말입니다. self만이 private 메서드 호출의 리시버로 허용됩니다.

protected 밖에서 부터 호출했을때 주의해야한다는 뜻입니다. protected 메서드는 클래스나 하위 클래스 인스턴스에서 호출할 수 있고, 다른 인스턴스를 리시버로 사용할 수 있습니다. 루비 FAQ에서 가져온 예제입니다.

$ irb
irb(main):001:0> class Test
irb(main):002:1>   # 기본값은 public입니다.
irb(main):003:1*   def func
irb(main):004:2>     99
irb(main):005:2>   end
irb(main):006:1>
irb(main):007:1*   def ==(other)
irb(main):008:2>     func == other.func
irb(main):009:2>   end
irb(main):010:1> end
=> nil
irb(main):011:0>
irb(main):012:0* t1 = Test.new
=> #<Test:0x34ab50>
irb(main):013:0> t2 = Test.new
=> #<Test:0x342784>
irb(main):014:0> t1 == t2
=> true
irb(main):015:0> # 이제 `func`를 protected로 만들지만 protected가 다른
irb(main):016:0* # 객체에서의 참조를 허용하기 때문에 여전히 동작합니다.
irb(main):017:0* class Test
irb(main):018:1>   protected :func
irb(main):019:1> end
=> Test
irb(main):020:0> t1 == t2
=> true
irb(main):021:0> # 이제 `func`을 private으로 만듭니다.
irb(main):022:0* class Test
irb(main):023:1>   private :func
irb(main):024:1> end
=> Test
irb(main):025:0> t1 == t2
NoMethodError: private method `func' called for #<Test:0x342784>
        from (irb):8:in `=='
        from (irb):25
        from :0
irb(main):026:0>

열린 클래스

루비의 클래스는 열려있습니다. 언제든 클래스를 열어서, 추가하고, 변경할 수 있습니다. Fixnum이나 심지어 모든 객체의 부모인 Object같은 코어 클래스도 예외는 아닙니다. 루비 온 레일즈에서는 시간을 제어하기 위해 Fixnum에 많은 메서드를 추가해서 사용합니다. 밑을 보세요.

class Fixnum
  def hours
    self * 3600 # 한 시간을 초로 환산한 수
  end
  alias hour hours
end

# 1월 1일 00:00부터 14시간 후
# (aka when you finally wake up ;)
Time.mktime(2006, 01, 01) + 14.hours # => Sun Jan 01 14:00:00

웃긴 메서드 이름

루비에서는, 메서드 이름이 물음표나 느낌표로 끝날 수 있습니다. 관례에 의하면, 질문에 답하는 메서드는 물음표로 끝납니다. (예를들어 리시버가 비었을때 true를 반환하는 Array#empty?) 관례에서 “위험할” 수 있는 메서드는 느낌표로 끝납니다. (예를들어 exit!같은 self 나 인자를 변경하는 메서드) 인자를 변경하는 모든 메서드가 느낌표로 끝나지는 않습니다. Array#replace는 다른 배열의 내용으로 배열의 내용을 치환합니다. 느낌표를 self를 수정하지 않는 메서드에 사용하는 것은 말이 되지 않습니다.

싱글턴 메서드

싱글턴 메서드는 객체 별 메서드입니다. 싱글턴 메서드는 정의된 객체에서만 사용 가능합니다.

class Car
  def inspect
    "Cheap car"
  end
end

porsche = Car.new
porsche.inspect # => Cheap car
def porsche.inspect
  "Expensive car"
end

porsche.inspect # => Expensive car

# 다른 객체는 영향 없음
other_car = Car.new
other_car.inspect # => Cheap car

없는 메서드

루비는 특정 메시지에 응답할 메서드를 찾을 수 없을 때에도 포기하지 않습니다. 찾을 수 없었던 메서드의 이름과 인자를 가지고 method_missing 메서드를 호출합니다. 기본적으로 method_missing는 NameError 예외를 일으키지만, 애플리케이션에 맞게 재정의 할 수 있고 많은 라이브러리들이 그렇게 사용합니다. 예제를 보세요.

# id는 메서드 호출의 이름이고 , * 문법은 모든 인자를
# 'arguments' 라는 이름의 배열에 넣어줍니다.
def method_missing(id, *arguments)
  puts "Method #{id} was called, but not found. It has " +
       "these arguments: #{arguments.join(", ")}"
end

__ :a, :b, 10
# => Method __ was called, but not found. It has these
# arguments: a, b, 10

위의 코드는 단지 호출의 상세 정보를 출력합니다만, 적합한 어떤 방식으로든 자유롭게 메시지를 처리할 수 있습니다.

함수 호출이 아닌, 메시지 넘기기

메서드 호출은 사실 다른 객체로 던지는 메시지입니다.

# 이 구문들은
1 + 2
# 전부
1.+(2)
# 같습니다.
1.send "+", 2

블록은 객체, 다만 아직 모를뿐

블록(사실은 클로저)는 표준 라이브러리에서 매우 많이 사용되고 있습니다. 블록을 호출하려면, yield를 사용하거나 특별한 인자를 붙여 Proc으로 만들수 있습니다.

def block(&the_block)
  # Inside here, the_block is the block passed to the method
  the_block # return the block
end
adder = block { |a, b| a + b }
# adder 는 이제 Proc 객체입니다.
adder.class # => Proc

메서드 호출 밖에서도 블록과 함께 Proc.new를 호출하거나 lambda메서드를 호출해 블록을 만들 수 있습니다.

비슷하게, 메서드도 만들때에는 객체입니다.

method(:puts).call "puts is an object!"
# => puts is an object!

연산자는 신택스 슈거

루비의 대부분의 연산자는 메서드 호출의 (몇가지 우선순위 규칙을 포함한) 신택스 슈거입니다. 예를 들어, Fixnum의 + 메서드를 오버라이드 할 수 있습니다.

class Fixnum
  # 할 수는 있지만, 하지 마세요.
  def +(other)
    self - other
  end
end

C++의 operator+ 같은 건 필요없습니다.

[][]= 메서드를 정의한다면 배열 스타일 액세스도 가능합니다. (+1 나 -2같은) 단항 +와 -를 정의하기 위해서는 각기 +@-@메서드를 정의하시면 됩니다. 밑의 연산자는 신택스 슈거가 아닙니다. 메서드가 아니며 재정의할 수 없습니다.

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

덧붙여, +=, *= 등등은 var = var + other_var, var = var * other_var 등등의 단축이며 재정의할 수 없습니다.

더 읽을 거리

더 많은 루비에 관한 정보를 보고 싶으시면 문서 항목을 보세요.