🐛 버그: 백스페이스를 누르면 IRB가 크래시됩니다
문제는 재현하기 매우 쉬웠습니다. IRB를 열고, 여러 유니코드 코드포인트가 결합된 “가족” 이모티콘을 붙여넣은 다음, 백스페이스를 누르면 IRB가 종료되는 현상이 발생했습니다. 이는 이모티콘이 단일 문자가 아닌 Zero-Width Joiner(ZWJ)로 연결된 여러 코드포인트로 구성된 그래핀 클러스터이기 때문입니다. IRB가 클러스터의 한 부분만 삭제하려 하면서 내부 불일치가 발생하여 크래시로 이어졌습니다.
🔍 1단계: 크래시 재현 및 문서화
모든 오픈 소스 기여에서 가장 중요한 부분은 코딩이 아니라 실제로 발생하는 일을 문서화하는 것입니다. 깊은 유니코드 지식 없이도 관찰하고, 문서화하고, 문제를 분리하는 것이 핵심입니다.
ruby
$ irb
> "👨👩👧👦"
# 백스페이스를 반복적으로 누름
# => 크래시
🧠 2단계: 근본 원인 이해 (힌트: 유니코드는 어렵습니다)
IRB 자체는 편집 로직을 처리하지 않습니다. 이 작업은 Ruby의 라인 편집기인 Reline에 속합니다. Reline은 커서 이동, 문자 너비, 백스페이스 동작, 멀티바이트 너비 계산 및 그래핀 경계를 처리합니다. Reline이 그래핀이 아닌 코드포인트를 삭제하고 있었기 때문에 커서가 이모티콘 클러스터 내부에 위치할 수 있었고, 이는 복구할 수 없는 상태를 초래했습니다.
🛠 3단계: 수정 — 그래핀 인식 편집으로 전환
수정의 핵심은 “1 문자 = 1 코드포인트”(유니코드에서는 잘못됨)에서 “1 문자 = 1 그래핀 클러스터”로 변경하는 것이었습니다. Ruby는 이미 이를 위한 도구를 제공합니다.
ruby
require "unicode/grapheme_break"
clusters = buffer.grapheme_clusters
패치는 Reline이 백스페이스를 누를 때 다음을 수행하도록 업데이트했습니다.
-
전체 그래핀을 제거합니다.
-
클러스터를 중간에 분할하지 않습니다.
-
커서와 버퍼 상태를 안전하게 조정합니다.
-
라인을 올바르게 다시 그립니다.
예시 로직(간소화):
ruby
clusters = buffer.grapheme_clusters
index = cursor_cluster_index
clusters.delete_at(index)
buffer = clusters.join
🧪 4단계: 수정 사항 테스트
패치는 광범위한 유니코드 시나리오에서 작동해야 했습니다. 단일 이모티콘(😄), ZWJ로 결합된 이모티콘(👨👩👧👦), 피부 톤 수정자(👍🏽), 악센트 결합 문자(é), 넓은 문자(漢字), 반복적인 백스페이스, 다양한 터미널 및 로케일 환경에서 테스트하여 Reline이 모든 엣지 케이스에서 올바르게 작동하도록 보장했습니다.
🚀 이것이 중요한 이유: 실제 기여는 “작은” 버그에서 시작됩니다
이 수정은 모든 개발자가 들어야 할 중요한 메시지를 전달합니다. Ruby를 개선하기 위해 전문가가 될 필요도, 전체 코드베이스를 이해할 필요도 없습니다. 그저 뭔가 이상한 것을 발견하고, 문서화하고, 첫 걸음을 내딛으면 됩니다. 나머지는 커뮤니티가 도와줄 것입니다. 이모티콘 삭제로 인한 작은 크래시가 IRB와 Reline에 실제적이고 의미 있는 개선으로 이어졌으며, Ruby 내부 코드에 익숙하지 않은 사람도 이 수정을 주도할 수 있었습니다.