Honeybadger는 처리 파이프라인 전반에서 Sidekiq에 크게 의존하고 있습니다. 우리가 하는 거의 모든 작업은 어느 시점에는 큐를 거치게 됩니다. 그만큼 Sidekiq를 안정적으로 운영하는 일이 중요했습니다. 최근 EC2로 인프라를 이전하면서, 안정적으로 오래 운영되던 고정 서버 세트에서 끊임없이 생성되고 교체되는 인스턴스 세트로 환경이 바뀌었습니다. 이에 따라 부팅 시점에 Sidekiq를 어떻게 시작할지 다시 검토하게 되었는데, 이제는 예전보다 훨씬 중요한 문제가 되었습니다. 과거에는 god를 사용해 모든 워커 프로세스를
Ruby 3x3 성능 개선 프로젝트를 주목해 왔다면 Optcarrot이라는 이름을 들어보셨을 겁니다. 순수 Ruby만으로 작성된 NES 에뮬레이터입니다. 최근 Optcarrot의 소스 코드를 살펴보다가 흥미로운 부분 하나가 눈에 띄었습니다. 이 프로젝트는 Ruby 해시(Hash)의 자주 간과되지만 매우 유용한 기능을 폭넓게 활용하고 있는데, 바로 어떤 객체든 해시 키로 사용할 수 있다는 점입니다. 배경: NES 메모리 매핑 고수준 언어에 익숙한 개발자라면 메모리를 단순히 RAM으로 생각하는 경향이 있습니다. 하지만 더 낮은 수준에서
요약: 핵심 변경 사항만 빠르게 확인하고 싶다면 CHANGELOG를 참고하세요. 최근 Ruby 애플리케이션의 예외를 보고하는 저희 젬(gem)의 최신 메이저 업데이트를 출시했습니다. v2 릴리스가 새로운 설정 전략을 도입한 완전한 재작성이었다면, v3는 기존 코드를 깨는 변경 사항은 적으면서 기능과 사용성 면에서 큰 개선이 이루어진 버전입니다. 다룰 내용이 많으니 바로 시작하겠습니다. 순수 Ruby(Plain ol Ruby) 모드 Rails 생태계에서는 젬을 설치하면 애플리케이션에 자동으로 통합되는 것이 당연하게 여겨집니다.
최근 저는 대부분의 Ruby 문자열 메서드를 특정 유니코드 문자로 테스트해 예상치 못한 동작이 있는지 확인하는 글을 발행했습니다. 결과적으로 많은 메서드들이 예상과 다르게 동작했습니다. 그 글에 대한 비판 중 하나는 테스트에 정규화되지 않은(normalized) 문자열을 사용했다는 것이었습니다. 솔직히 말하면 저 역시 유니코드 정규화에 대해 잘 알지 못했습니다. 아마 많은 Ruby 개발자들도 마찬가지일 겁니다. 정규화를 활용하면 제 테스트에서 예상치 못하게 동작했던 많은 유니코드 문자열을 Ruby의 문자열 메서드와 잘 호환되는 형태
오늘은 성능 최적화 기법 중에서도 특히 즐겨 사용하는 방법 하나를 소개하려고 합니다. 이 기법은 작지만 확실한 성능 향상을 누적시켜 주는 데다가, 아주 드물게—정말 아주 드물게만—애플리케이션을 잿더미로 만들곤 합니다. 이 기술의 이름은 메모이제이션(memoization)입니다. 어려워 보이는 컴퓨터 공학 용어와 달리, 그 의미는 아주 단순합니다. 메서드를 호출할 때마다 똑같은 작업을 반복하는 대신, 반환 값을 변수에 저장해 두고 이후에는 저장된 값을 재사용하는 것입니다. 메모이제이션이란? 의사코드(pseudocode)로 표현하
모듈(그리고 클래스)은 본래 중첩되어 사용하도록 설계되었습니다. ActiveRecord::RecordNotFound 같은 코드 조각은 너무나 익숙해서 우리는 두 번 생각하지 않고도 사용합니다. 하지만 Ruby의 중첩(nesting) 구현 내부와 Rails의 autoload 시스템에는 몇 가지 함정이 숨어 있어, 코드가 이상하고 기묘한 방식으로 실패할 수 있습니다. 이 글에서는 이러한 함정의 원인과 이를 피하는 방법을 살펴보겠습니다. 상수란 무엇인가? 이 글은 모듈에 관한 이야기지만, 모듈을 제대로 이해하려면 먼저 상수(const
개발자들은 프로그램이 실행되도록 만드는 데 많은 시간을 쓰다 보니, 프로그램이 어떻게 종료되는지는 쉽게 간과하곤 합니다. 하지만 이것은 매우 중요합니다. 프로그램이 올바르게 종료되면 관리가 훨씬 수월해지고, 표준 DevOps 도구들과의 연동도 원활해집니다. Ruby 프로그램을 종료하는 방법은 다양합니다. 아래에서 몇 가지를 소개하고, 각 방법의 세부 사항과 이를 활용해 애플리케이션을 유닉스 환경에서 예의 바른 프로그램으로 만드는 방법까지 살펴보겠습니다. # 메서드 호출로 종료하기 exit exit! abort(She cannot
요즘 루비(Ruby) 코드에서 #freeze 메서드가 사용되는 모습을 어렵지 않게 찾아볼 수 있습니다. 하지만 정작 왜 freeze를 사용하는지 그 이유가 명확하게 드러나지 않는 경우가 많습니다. 이 글에서는 개발자들이 변수나 객체를 freeze하는 가장 대표적인 이유들을 살펴보겠습니다. 각각의 이유를 설명하기 위해 Rails 코드베이스와 여러 인기 오픈소스 프로젝트에서 발췌한 실제 예제 코드를 함께 소개합니다. 1. 불변(Immutable) 상수 만들기 루비에서 상수는 사실 변경 가능(mutable)합니다. 다소 혼란스러울 수
컬렉션을 열거자(enumerator)로 다루면 #map이나 #reduce 같은 익숙한 메서드를 추가 코드 없이 그대로 활용할 수 있습니다. 정말 강력한 기능이죠. 예전에는 열거자를 정의하는 일이 꽤 번거로웠습니다. 새 클래스를 만들고, Enumerable 모듈을 포함시키고, #each 메서드까지 직접 정의해야 했으니까요. 하지만 Ruby 1.9부터는 훨씬 가벼운 방식으로 즉석에서 열거자를 정의할 수 있게 되었습니다. 지금부터 그 방법을 살펴보겠습니다. Enumerator 클래스 소개 Enumerator 클래스를 사용하면 블록 문법만
Rails 개발자들에게 Turbolinks는 아마 가장 미움받는 단어 중 하나일 겁니다. 한 번쯤 시도해 보셨을지도 모릅니다. 새 프로젝트나 기존 애플리케이션에 turbolinks를 추가했는데, 곧 앱이 이상하고 기묘한 방식으로 오작동하기 시작하죠. 다행히 해결책은 간단했습니다. 바로 turbolinks를 끄는 것. ...하지만 잘 활용하는 회사들도 있습니다. Honeybadger에서도 성공적으로 사용하고 있는데, 우리가 천재여서 그런 건 아닙니다. 답은 너무나 단순해서 소개하기가 망설여질 정도입니다. 하지만 Ruby Nati
Jekyll로 문서 사이트를 다시 구축하면서 한 가지 고민에 부딪혔습니다. 문서 페이지가 워낙 방대하다 보니, 최상위 내비게이션만으로는 부족하고 별도의 하위 내비게이션(subnavigation)이 반드시 필요했습니다. 이 글에서는 포스트나 페이지의 헤딩(heading)을 기반으로 하위 내비게이션 링크를 자동 생성해 주는 간단한 Jekyll 플러그인을 만드는 방법을 단계별로 살펴보겠습니다. 개요 이 프로젝트는 다음과 같은 작업들로 나누어 진행합니다. 사이트의 모든 페이지에 대해 실행되는 Jekyll 제너레이터(Generator)를
지난 포스트에서는 Jekyll 페이지의 각 H2 제목에 대한 하위 내비게이션 링크를 생성하는 방법을 살펴보았습니다. 이번 포스트에서는 그 기반을 한 단계 더 발전시켜, H3, H4 등 원하는 만큼 깊은 수준의 하위 내비게이션을 추가하는 방법을 알아보겠습니다. 개요 이 프로젝트는 크게 두 단계로 나누어 진행합니다. 첫 번째 단계: Nokogiri를 사용하여 H2 태그 내부에 속한 H3 태그로 정의된 섹션들을 추출합니다. 두 번째 단계: 재귀적 템플릿(recursive template)이라는 멋진 기법을 활용해 임의의 깊이를 가
Ruby의 리파인먼트(Refinement)를 사용해 본 적이 없다면, 처음 접했을 때 꽤 놀랄 수 있습니다. 리파인먼트는 원래 몽키 패칭(monkey patching)을 대체하기 위해 도입된 기능입니다. 그래서 ActiveSupport의 hours 메서드처럼 기존 클래스에 메서드를 얹는 코드를 아래와 같이 작성할 수 있으리라고 자연스럽게 기대하게 됩니다. module TimeExtension refine Fixnum do def hours self * 60 end end end class MyFra
배경 지식 없이 구글에서 루비 리파인먼트(Ruby refinements)를 검색해 보면, 리파인먼트가 느리다는 인상을 받기 쉽습니다. 사실 처음 제안되었던 설계대로라면 리파인먼트는 실제로 느렸을 겁니다. 메서드 탐색(method lookup) 같은 작업을 인터프리터가 최적화하는 것 자체가 불가능해졌을 테니까요. 그러나 실제로 구현된 리파인먼트는 초기 제안보다 훨씬 제한적인 형태입니다. 그래서 오늘날 루비에 존재하는 리파인먼트를 대상으로 일련의 벤치마크를 직접 돌려 보면 재미있겠다고 생각했습니다. TL;DR — 결론부터 말하면 리파인
2013년 Ruby 2.0이 출시하며 선보인 수많은 새로운 기능 중에서, 제가 가장 주목하지 않았던 것은 바로 새로운 정규 표현식 엔진인 Onigmo였습니다. 어차피 정규 표현식은 정규 표현식이니까요 — 루비가 이를 어떻게 구현했는지까지 신경 쓸 필요가 있을까요? 그런데 알고 보니 Onigmo 정규식 엔진에는 몇 가지 멋진 기술이 숨겨져 있었고, 그중 하나가 바로 정규 표현식 안에서 조건문을 사용할 수 있는 기능입니다. 이 글에서는 정규식 조건문에 대해 자세히 알아보고, 루비 구현체만의 특수한 동작 방식을 살펴본 뒤, 루비의 한계를
어제 정규표현식 조건문에 관한 글을 소개해 드렸는데요, 오늘은 루비에서 정규표현식으로 활용할 수 있는 또 다른 재미있고 실용적인 트릭들을 살펴보려 합니다. 정규표현식으로 문자열 나누기 문자열을 구분자(delimiter)로 분리하는 것은 누구나 익숙하실 겁니다: one,two.split(,) # => [one, two] 그런데 split 메서드에 정규표현식도 그대로 넘길 수 있다는 사실을 알고 계셨나요? # `,`와 `-`를 모두 구분자로 사용 one,two-three.split(/,|-/) => [one, two,
오늘은 Ruby의 self에 대해 이야기해 보려고 합니다. Ruby로 프로그래밍을 한 지 어느 정도 된 개발자라면 이미 self의 개념이 몸에 배어 있을 것입니다. 코드를 읽거나 작성할 때마다 self는 늘 머릿속 어딘가에 존재하죠. 하지만 경험이 적은 Rubyist에게 self는 다소 혼란스러운 존재입니다. 값은 끊임없이 바뀌는데도 코드에는 명시적으로 드러나지 않으며, 그저 알고 있으라고 기대하기 때문입니다. 사실 초보자가 겪는 많은 문제는 self에 대한 이해 부족에서 비롯됩니다. 인스턴스 변수가 어디선가 사라졌다고 느낀 적
자신이 사용하는 도구에 대해 더 깊이 이해할수록 개발자로서 더 나은 결정을 내릴 수 있습니다. 특히 성능 문제를 디버깅할 때는 Ruby가 여러분의 프로그램을 실행하는 동안 실제로 무엇을 하고 있는지 이해하는 것이 큰 도움이 됩니다. 이 글에서는 간단한 프로그램 하나가 렉싱(lexing)되고, 파싱(parsing)되고, 바이트코드로 컴파일되기까지의 여정을 따라가 보겠습니다. Ruby가 제공하는 도구들을 활용해 인터프리터의 동작을 단계별로 직접 들여다볼 것입니다. 걱정하지 마세요. 전문가가 아니더라도 충분히 따라올 수 있도록 쉽게
루비가 주는 매력 중 하나는 바로 기능의 깊이입니다. 평소에 익숙하게 쓰던 연산자도 조금만 파고들면 아직 표면적인 부분만 사용하고 있었다는 사실을 깨닫게 됩니다. 겸손해 보이는 스플랫 연산자(*와 **)가 그 좋은 예입니다. 아마 여러분도 스플랫을 나머지 전부(catch-all) 인수를 받는 용도로 사용해 보셨을 겁니다. 그리고 대부분의 사람들이 스플랫을 이 용도로만 사용합니다. def go(x, *args) puts args.inspect end go(a, b, c) 새로운 키워드 인수 문법을 사용한다면 다음과 같이
오늘은 slice_before, slice_when, slice_after 세 가지 메서드에 대해 알아보겠습니다. 이 메서드들은 배열이나 기타 열거형(enumerable)의 요소들을 임의의 기준에 따라 그룹으로 묶어야 할 때 매우 유용합니다. Array#slice는 이미 익숙하실 겁니다. 인덱스 범위를 지정해 배열의 일부분을 추출할 수 있는 메서드죠: a = [a, b, c] a.slice(1, 2) # => [b, c] 유용한 메서드이지만, 열거형에는 사용할 수 없습니다. 열거형에는 인덱스가 없기 때문입니다. 반면