0Pricing
Ruby Academy · 강의

흔한 병목

Ruby 코드의 속도를 높입니다

흔한 병목은(는) CoddyKit의 무료 Ruby Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Ruby Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Ruby Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

병목 인식하기

Ruby에서 느려지는 현상은 대부분 반복해서 나타나는 몇 가지 패턴에서 비롯됩니다. 이를 알면 코드를 빠르게 고칠 수 있습니다.

  • 작업에 맞지 않는 자료 구조
  • 캐시할 수 있는데도 반복하는 작업
  • N+1 질의와 중첩 루프

이 학습에서는 가장 흔한 원인을 다룹니다.

puts 'Speeding up Ruby starts with knowing the usual suspects'

Array#include?와 Set 비교

Array#include?로 포함 여부를 확인하면 O(n)입니다. Set(또는 해시)을 사용하면 조회가 O(1)이 됩니다.

  • 포함 여부를 반복해서 확인해야 한다면 Set을 한 번 만드세요
  • 큰 컬렉션에서 속도가 크게 향상됩니다
require 'set'

allowed = Set.new(['a', 'b', 'c'])
puts allowed.include?('b')
puts allowed.include?('z')

중첩 루프(O(n의 제곱))

모든 요소 쌍을 비교하면 시간 복잡도가 제곱으로 증가하며, 규모가 커질수록 폭발적으로 느려집니다.

  • 내부 루프를 해시 조회로 바꿉니다
  • O(n의 제곱)을 O(n)으로 바꿉니다
a = [1, 2, 3, 4]
b = [3, 4, 5]
set_b = b.to_set rescue require('set') || b.to_set
common = a.select { |x| set_b.include?(x) }
puts common.inspect

N+1 패턴

루프 안에서 관련 데이터를 가져오면 N+1번 작업이 발생합니다.

  • 목록에 대해 질의 1번을 실행한 다음 항목마다 1번씩 실행합니다
  • 대신 한 번의 그룹 조회로 일괄 처리합니다
# Bad: lookup inside loop
orders = [1, 2, 3]
prices = { 1 => 10, 2 => 20, 3 => 30 }
# Good: one preloaded hash, O(1) per item
total = orders.sum { |id| prices[id] }
puts total

메모이제이션

비용이 큰 값을 매번 다시 계산하면 시간이 낭비됩니다. ||=로 메모이제이션하세요.

  • first 계산 후 결과를 캐시합니다
  • 이후 호출은 즉시 결과를 반환합니다
class Report
  def total
    @total ||= begin
      puts 'computing...'
      (1..1000).sum
    end
  end
end

r = Report.new
puts r.total
puts r.total

알맞은 메서드 선택하기

일부 Enumerable 메서드는 메서드 체인보다 훨씬 빠릅니다.

  • sum이 inject(:+)보다 빠릅니다
  • any?는 select.empty?와 달리 조건을 만족하는 항목을 찾으면 즉시 중단합니다
  • find는 first 일치 항목에서 멈춥니다
nums = (1..1_000_000)
puts nums.any? { |n| n > 5 }
puts nums.find { |n| n > 5 }

반복 정렬 피하기

루프 안에서 정렬하거나 정렬을 반복하면 비용이 큽니다. 한 번 정렬한 결과를 재사용하세요.

  • 정렬의 시간 복잡도는 O(n log n)입니다
  • 데이터가 변하지 않는다면 정렬된 결과를 캐시합니다
data = [5, 2, 8, 1, 9]
sorted = data.sort
puts sorted.first
puts sorted.last

해시로 그룹화하기

group_by와 tally는 반복해서 순회하는 대신 한 번의 순회로 집계합니다.

  • tally는 출현 횟수를 효율적으로 셉니다
  • 수동으로 세는 루프를 피할 수 있습니다
words = ['a', 'b', 'a', 'c', 'b', 'a']
puts words.tally.inspect

문자열 생성 비용

컬렉션에서는 반복해서 연결하는 것보다 join으로 결합하는 편이 빠릅니다.

  • 배열을 만든 다음 join을 한 번 호출합니다
  • 중간 문자열을 여러 개 만들지 않습니다
parts = (1..5).map { |i| "item#{i}" }
puts parts.join(', ')

정규식 컴파일

자주 실행되는 루프 안에 정규식 리터럴을 정의하면 암묵적으로 다시 컴파일됩니다. 상수로 바깥에 옮기세요.

  • 한 번 컴파일하고 여러 번 일치시킵니다
  • 부울값만 필요할 때는 match?를 사용합니다
EMAIL = /\A[^@\s]+@[^@\s]+\z/

inputs = ['a@b.com', 'nope', 'x@y.org']
valid = inputs.select { |s| EMAIL.match?(s) }
puts valid.inspect

작업 지연 실행

사용하지 않을 수도 있는 것은 계산하지 마세요. 지연 평가나 가드 조건으로 처리를 미루세요.

  • 가능하면 일찍 반환합니다
  • 요청이 있을 때만 무거운 값을 계산합니다
def describe(items)
  return 'empty' if items.empty?
  "#{items.size} items, first is #{items.first}"
end

puts describe([])
puts describe([10, 20])

빠른 확인

병목에 대한 지식을 확인해 보세요.

복습

흔한 병목을 해결하는 방법을 배웠습니다:

  • include? 대신 빠른 포함 여부 확인을 위해 Set이나 해시를 사용합니다
  • 미리 불러온 해시로 중첩 루프와 N+1 패턴을 없앱니다
  • ||=로 비용이 큰 계산을 메모이제이션합니다
  • 효율적인 메서드(sum, any?, tally)를 선택하고 정규식을 바깥으로 옮깁니다
  • 필요하지 않을 수도 있는 작업은 미루거나 건너뜁니다

효과를 확인하려면 항상 전후를 측정하세요.

자주 묻는 질문

“흔한 병목” 강의는 무료인가요?

네 — “흔한 병목” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Ruby Academy 강의 전체를 잠금 해제할 수 있습니다. Ruby Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“흔한 병목”에서 뭘 배우나요?

Ruby 코드의 속도를 높입니다 브라우저에서 직접 실행하는 실습 코드로 Ruby Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Ruby Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Ruby Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“흔한 병목” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Ruby Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Ruby Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 성능 측정
  2. 프로파일링 도구
  3. 메모리 최적화
  4. 흔한 병목
← Ruby Academy(으)로 돌아가기