루비(Ruby)에서 private 메서드란 무엇일까요?
private 메서드는 오직 해당 메서드가 정의된 클래스 내부에서만 호출할 수 있는 메서드입니다. 이를 통해 개발자는 메서드에 대한 외부 접근을 효과적으로 제어할 수 있습니다.
루비 메서드의 가시성은 세 가지로 나눌 수 있습니다:
privatepublic(기본값)protected
기본적으로 작성하는 모든 메서드는 public입니다. 즉, 누구든 어디서든 호출할 수 있죠.
하지만 메서드를 private 또는 protected로 지정하면 이 기본 동작을 바꿀 수 있습니다.
왜 이것이 유용할까요?
메서드를 더 쉽게 수정할 수 있기 때문입니다.
회사에서 여러 프로젝트에 사용될 코드 라이브러리를 작성한다고 상상해 보세요. 각 프로젝트는 여러분이 만든 클래스의 메서드를 호출하며 라이브러리를 활용할 겁니다.
그런데 새 버전을 출시하면서 public 메서드의 이름을 변경했다면 어떻게 될까요?
이 메서드를 사용 중인 모든 프로젝트에서 오류가 발생합니다!
꼭 기억하세요: public 메서드의 수를 줄일수록 클래스 내부 구조를 자유롭게 변경할 수 있는 여지가 커집니다.
그럼 코드 예제를 통해 자세히 살펴보겠습니다!
루비 private 메서드 이해하기
"private method called"라는 오류 메시지를 본 적이 있으신가요?
바로 이 오류입니다:
self.puts 123 # NoMethodError: private method `puts' called
이 오류는 private 메서드를 잘못된 방식으로 호출하려 했을 때 발생합니다.
private 메서드는 반드시 단독으로(수신자 없이) 호출해야 합니다.
예제:
puts 123
같은 메서드지만 위와 같이 호출해야 합니다.
private 메서드는 항상 self 컨텍스트 안에서 호출되기 때문입니다.
다시 말해, private 메서드는 다음 경우에만 사용할 수 있습니다:
- 같은 클래스에 정의된 다른 메서드
- 부모 클래스로부터 상속받은 메서드
- 모듈을 통해 포함(include)한 메서드
즉, private 메서드를 정의한 클래스의 외부에서는 호출할 수 없습니다. 외부에서 호출하려면 명시적 수신자(explicit receiver)가 필요하기 때문입니다.
물론 예외는 있습니다. send 메서드를 사용하면 이 규칙을 우회할 수 있죠.
send(:puts, "apple")
그렇다면 private 메서드는 어떻게 정의할까요?
다음과 같이 정의합니다:
def bacon "private bacon" end private :bacon
여기서 private이라는 단어는 키워드가 아니라 Kernel 모듈에 정의된 하나의 메서드라는 점이 흥미롭습니다.
private 메서드는 어디에 배치할까?
클래스에 private 메서드가 여러 개 존재하는 것은 매우 자연스러운 일입니다. 그렇다면 이 메서드들을 어디에 배치해야 할까요?
권장 방식은 다음과 같습니다:
class Food def public_method end private def bacon end def orange end def coconut end end
private 선언 이후에 등장하는 모든 인스턴스 메서드는 자동으로 private 메서드가 됩니다.
클래스 메서드를 private으로 만들고 싶다면
private_class_method :method_name을 사용하세요.
실무에서는 public 메서드를 먼저 모두 정의한 뒤, 클래스 마지막 부분에 private 메서드를 한데 모아 정의하는 패턴이 널리 사용됩니다.
public 메서드
public은 루비의 기본 메서드 가시성입니다.
예제:
def orange "Vitamin C" end
orange 메서드를 가진 객체 food가 있다면 다음과 같이 호출할 수 있습니다:
food.orange
만약 어떤 메서드가 private 또는 protected로 지정되었다 해도, 다시 public으로 되돌릴 수 있습니다.
방법은 다음과 같습니다:
public :orange
protected 메서드란?
protected 메서드는 private 메서드만큼 흔하지 않습니다.
동작 방식은 private과 비슷하지만, 결정적인 차이가 하나 있습니다. 직접 호출뿐 아니라 객체를 통해서도 호출할 수 있다는 점입니다.
아래 예제를 private으로 작성하면 오류가 발생합니다:
class Food
def initialize(name)
@name = name
end
def ==(other)
name == other.name
end
protected
attr_reader :name
end
food = Food.new("chocolate")
puts food == food
오류가 발생하는 이유는 name이 private이 되어 other.name처럼 객체를 통해 접근할 수 없기 때문입니다.
하지만 protected를 사용하면 이 코드가 정상적으로 작동합니다!
private vs protected 메서드
정리하자면, protected는 메서드를 private처럼 숨기면서도 객체를 통해 메서드를 호출할 수 있게 허용한다는 점이 핵심 차이입니다.
- private:
name형태로만 호출 가능 - protected:
object.name형태로도 호출 가능
그렇다면 protected는 언제 사용해야 할까요?
동등 비교(==) 메서드처럼 아주 특수한 경우에만 사용하는 것이 좋습니다. 루비 공식 문서 역시 가능한 한 protected 대신 private을 사용할 것을 권장합니다.
또한 다음과 같은 주의 사항도 있습니다:
"protected 메서드는 인라인 캐시(inline cache)를 사용할 수 없어 속도가 느립니다."
실제로 궁금해서 간단한 벤치마크를 돌려 보았습니다:
public: 2813891.9 i/s private: 2699273.8 i/s protected: 2572122.0 i/s
결과적으로 protected는 public 대비 약 8.5%의 성능 차이를 보였습니다.
요약
지금까지 루비의 메서드 가시성인 public, private, protected 메서드에 대해 알아보았습니다. 이 세 가지는 루비의 키워드가 아니라 Module 클래스에 정의된 메서드 그 자체라는 점도 기억해 두세요.
이 글이 도움이 되셨다면 주변에 공유해 더 많은 분들이 이 주제를 이해할 수 있기를 바랍니다!
읽어주셔서 감사합니다 🙂