RUBY

루비 골드 완벽 가이드: 고급 객체 모델, 메타프로그래밍, 예외/스레드까지 실전 해설

Ruby Gold는 문법 암기 시험이 아니라, Ruby의 고급 객체 모델과 실행 원리를 깊이 이해했는지를 묻는 시험입니다. 이 글은 Ruby Gold 빈출 주제를 중심으로 고난도 개념, 함정 포인트, 실전 해설을 한 번에 정리한 완성형 가이드입니다.

1) Ruby Gold 시험의 핵심 평가 포인트

  • 객체 모델 심화: singleton class, method lookup, self 동작
  • 메타프로그래밍: define_method, class_eval, method_missing
  • 모듈/상속 고급: include/extend/prepend 우선순위
  • 예외/리소스 관리: custom exception, ensure, retry 개념
  • 동시성 기초: Thread, Mutex, Queue
  • 표준 라이브러리 이해: Enumerator, Fiber 개념, GC 기본

2) Silver와 Gold의 차이

Silver가 "코드 결과를 맞히는 문법 중심"이라면, Gold는 "왜 그렇게 동작하는지"를 설명할 수 있어야 합니다.

  • Silver: 변수, 조건문, Enumerable, 기본 OOP
  • Gold: 객체 모델 내부 동작, 메서드 탐색 체인, 메타프로그래밍

3) 메서드 탐색 순서(Method Lookup Chain)

module A
  def hello
    "A"
  end
end

module B
  def hello
    "B"
  end
end

class Base
  def hello
    "Base"
  end
end

class User < Base
  include A
  prepend B
end

u = User.new
puts u.hello
p User.ancestors

해설: prepend 모듈이 클래스보다 앞에 오므로 결과는 B입니다. 시험에서는 ancestors 순서를 직접 묻는 문제가 자주 나옵니다.

4) Singleton Class(고유 클래스) 이해

obj = "ruby"

def obj.shout
  upcase + "!"
end

puts obj.shout

another = "ruby"
puts another.respond_to?(:shout)

해설: 메서드는 해당 객체의 singleton class에만 정의됩니다. 따라서 objshout를 갖고, another는 갖지 않습니다.

5) class << self 와 클래스 메서드

class Config
  class << self
    attr_accessor :env

    def production?
      env == "production"
    end
  end
end

Config.env = "production"
puts Config.production?

시험 포인트: class << self 블록은 현재 객체(여기서는 클래스 객체)의 singleton class 컨텍스트입니다.

6) define_method vs def

class Report
  [:daily, :weekly].each do |name|
    define_method(name) do
      "#{name} report"
    end
  end
end

r = Report.new
puts r.daily
puts r.weekly

해설: define_method는 클로저를 캡처합니다. 동적으로 메서드를 생성할 때 유용하며 Gold에서 자주 다루는 주제입니다.

7) method_missing과 respond_to_missing?

class DynamicFinder
  def method_missing(name, *args)
    if name.to_s.start_with?("find_by_")
      field = name.to_s.sub("find_by_", "")
      "search by #{field}: #{args.first}"
    else
      super
    end
  end

  def respond_to_missing?(name, include_private = false)
    name.to_s.start_with?("find_by_") || super
  end
end

f = DynamicFinder.new
puts f.find_by_email("a@test.com")
puts f.respond_to?(:find_by_email)

시험 포인트: method_missing만 구현하면 반쪽짜리입니다. respond_to_missing?도 함께 구현해야 일관성이 맞습니다.

8) Refinements, reopen, monkey patch 주의점

Ruby는 클래스 재오픈이 가능해 강력하지만, 전역 monkey patch는 부작용이 큽니다. 시험에서는 개념 차이를 묻는 형태가 나옵니다.

9) Proc/lambda 심화: arity와 return

p1 = Proc.new { |x, y| [x, y] }
l1 = lambda  { |x, y| [x, y] }

p p1.call(1)      # [1, nil]
# p l1.call(1)    # ArgumentError

def test_proc
  p_obj = Proc.new { return "proc escaped" }
  p_obj.call
  "after"
end

def test_lambda
  l_obj = -> { return "lambda only" }
  l_obj.call
  "after"
end

puts test_proc
puts test_lambda

해설: Proc은 인자 체크가 느슨하고 return이 바깥 메서드를 탈출합니다. lambda는 함수처럼 엄격하게 동작합니다.

10) Enumerator와 Lazy 평가

enum = (1..Float::INFINITY).lazy
result = enum.select { |n| n.odd? }
             .map { |n| n * n }
             .first(5)
p result

시험 포인트: lazy는 즉시 계산하지 않고 필요 시점에 평가합니다. 대용량/무한 시퀀스 문제에 연결됩니다.

11) Module.nesting, 상수 탐색, lexical scope

VALUE = "TOP"

module App
  VALUE = "APP"

  class Service
    VALUE = "SERVICE"

    def self.show
      puts VALUE
      puts ::VALUE
      p Module.nesting
    end
  end
end

App::Service.show

해설: 상수는 lexical scope를 기준으로 탐색됩니다. ::VALUE는 최상위 상수를 참조합니다.

12) 예외 계층과 커스텀 예외

class PaymentError < StandardError; end
class CardDeclinedError < PaymentError; end

def charge!(amount)
  raise CardDeclinedError, "card declined" if amount > 100
  "ok"
end

begin
  puts charge!(120)
rescue CardDeclinedError => e
  puts "declined: #{e.message}"
rescue PaymentError => e
  puts "payment error: #{e.message}"
ensure
  puts "audit log written"
end

시험 포인트: 구체적인 예외를 상위 예외보다 먼저 rescue해야 합니다.

13) Thread와 Mutex 기초

counter = 0
mutex = Mutex.new

threads = 10.times.map do
  Thread.new do
    1000.times do
      mutex.synchronize { counter += 1 }
    end
  end
end

threads.each(&:join)
puts counter

해설: Mutex 없이 공유 자원을 갱신하면 race condition이 발생할 수 있습니다.

14) freeze, dup, clone 차이

s = "ruby"
s.freeze

d = s.dup
c = s.clone

puts d.frozen?  # false
puts c.frozen?  # true

시험 포인트: clone은 frozen 상태 등 객체의 상태를 더 충실히 복제합니다.

15) GC 기본 개념

Gold에서는 GC 튜닝 깊이보다 개념 이해가 중요합니다. "객체 참조가 끊긴 대상은 가비지 컬렉션 대상"이라는 원리를 기반으로 메모리 누수 패턴을 판단하는 문제가 출제됩니다.

16) 블록 기반 리소스 관리 패턴

File.open("app.log", "w") do |f|
  f.puts "start"
end

# 블록이 끝나면 자동 close

시험 포인트: Ruby는 블록 기반 API로 리소스 해제를 안전하게 처리합니다.

17) 빈출 함정 12선

  1. prepend 우선순위 오해
  2. singleton class 개념 혼동
  3. class << self 컨텍스트 해석 오류
  4. define_method 클로저 캡처 특성 누락
  5. method_missing만 구현하고 respond_to_missing? 누락
  6. Proc/lambda arity 차이 실수
  7. Proc return 탈출 범위 오해
  8. 상수 탐색 lexical scope 오해
  9. 예외 rescue 순서 오류
  10. Thread 공유 데이터 동기화 누락
  11. dup vs clone 차이 혼동
  12. Enumerable lazy 평가 시점 오해

18) 실전 문제 해설

문제 A: 메서드 탐색

module M1
  def x; "M1"; end
end

module M2
  def x; "M2"; end
end

class C
  include M1
  prepend M2
end

puts C.new.x

정답: M2. prepend가 클래스 앞에 삽입되기 때문입니다.

문제 B: 클래스 메서드 정의 위치

class A
  def self.a1
    "a1"
  end

  class << self
    def a2
      "a2"
    end
  end
end

puts A.a1
puts A.a2

정답: 둘 다 클래스 메서드이며 호출 가능합니다.

문제 C: lambda 인자 체크

l = ->(x, y) { x + y }
puts l.call(1, 2)
# puts l.call(1)

정답: 첫 줄은 3, 주석 해제 시 ArgumentError.

19) 6주 Ruby Gold 학습 로드맵

1주차: 객체 모델 복습(self, 클래스 객체, singleton class)
2주차: 모듈/상속/include-extend-prepend + ancestors
3주차: 메타프로그래밍(define_method, method_missing, eval 계열)
4주차: 예외 계층, 상수 탐색, Proc/lambda 심화
5주차: Thread/Mutex, Enumerator/lazy, 파일/리소스 패턴
6주차: 실전 문제 반복 + 오답 노트 압축 정리

20) 시험 직전 요약 카드

  • prepend > class > include > superclass
  • Proc: 느슨한 인자, 바깥 return 탈출 / lambda: 엄격 인자, 내부 return
  • method_missing 구현 시 respond_to_missing? 함께
  • 상수는 lexical scope 우선, ::는 최상위
  • Thread 공유 데이터는 Mutex로 보호

마무리

Ruby Gold 합격은 "Ruby를 Ruby답게 이해"하는 데서 시작합니다. 이 글의 핵심 코드를 직접 실행하고, 실행 결과를 근거와 함께 설명할 수 있게 훈련하면 고난도 문항에서도 흔들리지 않습니다.

F

Fit System

10년 이상의 소프트웨어 엔지니어링 경험을 가진 개발자입니다. 고성능 시스템 설계와 클라우드 네이티브 아키텍처를 전문으로 합니다.