IMCAT.DEV
로그인 가입
Ruby 입문 함수형

Ruby 블록과 프로시저 (Blocks and Procs)

Ruby 블록과 프로시저 (Blocks and Procs) — 초급

블록과 프로시저 (Blocks and Procs)

1. 도입 (왜 배워야 하는가)

루비에서 블록과 프로시저는 매우 중요한 개념입니다. 이 두 가지는 코드의 재사용성과 가독성을 높여주며, 특히 콜백(callback)이나 이벤트 기반 프로그래밍을 할 때 유용합니다. 현업에서는 데이터 처리나 비동기 작업에서 이러한 기능이 자주 사용됩니다. 예를 들어, 쇼핑몰에서 사용자가 장바구니에 상품을 추가할 때마다 특정 작업(예: 총 가격 계산)을 자동으로 수행해야 할 때 블록이나 프로시저를 사용하면 코드가 더욱 효율적이고 깨끗해집니다.

블록과 프로시저를 모르면 코드를 중복해서 작성하게 되고, 이는 유지보수의 어려움으로 이어집니다. 따라서 이 개념을 이해하고 활용하는 것은 프로그래밍 실력을 높이는 데 필수적입니다.

2. 핵심 개념 설명

  • 블록 (Block): 루비에서 블록은 코드의 집합으로, 메서드에 인자로 전달되어 실행됩니다. 블록은 {} 또는 do...end로 정의됩니다.
  • 프로시저 (Proc): 프로시저는 블록을 객체로 만들어 저장할 수 있게 해주는 클래스입니다. 블록을 프로시저로 변환하면 나중에 재사용할 수 있습니다.
  • 비유하자면, 블록은 요리의 레시피와 같습니다. 요리사가 레시피를 보고 요리를 하듯이, 메서드는 블록을 보고 특정 작업을 수행합니다. 반면 프로시저는 미리 만들어 놓은 요리입니다. 언제든지 꺼내 먹을 수 있는 거죠.

    용어설명
    블록메서드에 전달되는 코드의 집합
    프로시저블록을 객체로 만들어 재사용 가능하게 한 것

    3. 기본 코드 예제

    블록을 사용하는 간단한 예제입니다.

    def greet(&block)
      puts "안녕하세요!"
      block.call if block_given?
    end
    
    greet { puts "반갑습니다!" }

    실행 결과

    안녕하세요!
    반갑습니다!
  • def greet(&block): greet라는 메서드를 정의하고, 블록을 인자로 받기 위해 &block을 사용합니다.
  • puts "안녕하세요!": 메서드가 호출되면 먼저 "안녕하세요!"를 출력합니다.
  • block.call if block_given?: 블록이 주어졌을 경우, block.call로 블록을 실행합니다.
  • greet { puts "반갑습니다!" }: greet 메서드를 호출하며 블록을 전달합니다. 블록 안의 코드는 block.call에 의해 실행됩니다.
  • 4. 실전 활용 예제

    1. 장바구니 총 가격 계산: 쇼핑몰에서 사용자가 장바구니에 상품을 추가할 때마다 총 가격을 계산할 수 있습니다.
    2. class ShoppingCart
        def initialize
          @items = []
        end
      
        def add_item(item, &block)
          @items << item
          block.call(@items) if block_given?
        end
      
        def total_price
          @items.sum
        end
      end
      
      cart = ShoppingCart.new
      cart.add_item(100) { |items| puts "현재 장바구니: #{items}, 총 가격: #{cart.total_price}" }
      cart.add_item(200) { |items| puts "현재 장바구니: #{items}, 총 가격: #{cart.total_price}" }

      실행 결과

      현재 장바구니: [100], 총 가격: 100
      현재 장바구니: [100, 200], 총 가격: 300
    3. 비동기 작업 처리: 웹 애플리케이션에서 비동기 요청 후 특정 작업을 수행할 수 있습니다.
    4. def fetch_data(&block)
        puts "데이터를 가져오는 중..."
        sleep(2) # 데이터 가져오는 시간 시뮬레이션
        block.call("데이터 수신 완료!") if block_given?
      end
      
      fetch_data { |result| puts result }

      실행 결과

      데이터를 가져오는 중...
      (2초 대기)
      데이터 수신 완료!

      5. 흔한 실수와 주의사항

    5. 블록의 유효성 검사: 블록을 호출하기 전에 block_given?으로 블록이 전달되었는지 확인해야 합니다. 그렇지 않으면 에러가 발생합니다.
    6. # 잘못된 코드
      def greet
        puts "안녕하세요!"
        block.call # 에러 발생
      end
      # 올바른 코드
      def greet(&block)
        puts "안녕하세요!"
        block.call if block_given? # 블록이 있을 때만 호출
      end
    7. 프로시저를 잊지 않기: 프로시저를 잘못 사용하면 의도한 대로 동작하지 않을 수 있습니다. 프로시저를 정의하고 사용 시에는 항상 Proc.new 혹은 proc을 사용하여 정의해야 합니다.
    8. # 잘못된 코드
      my_proc = proc { puts "Hello" }
      my_proc.call # 정상 동작
      
      # 올바른 코드
      my_proc = Proc.new { puts "Hello" }
      my_proc.call # 정상 동작

      6. 정리

    9. 블록은 메서드에 전달되는 코드의 집합이며, 프로시저는 블록을 객체로 만들어 재사용할 수 있게 해줍니다.
    10. 블록과 프로시저를 사용하면 코드의 재사용성과 가독성을 높일 수 있습니다.
    11. 블록 사용 시 block_given?으로 블록의 유효성을 검사하는 것이 중요합니다.
    12. 다음에 학습하면 좋은 주제는 "람다(lambda)"와 "고급 블록 패턴"입니다.