POOOLING FOREST
Lua 대신 mruby? 무거운 시스템에 가벼운 루비 심어보기 - C/C++ 등 무거운 시스템에 유연성을 더하기 위해 Lua 대신 mruby를 도입한 경험을 공유합니다. 루비
Engineering & Tech

Lua 대신 mruby? 무거운 시스템에 가벼운 루비 심어보기

C/C++ 등 무거운 시스템에 유연성을 더하기 위해 Lua 대신 mruby를 도입한 경험을 공유합니다. 루비의 생산성을 시스템 프로그래밍에 활용하는 방법을 알아보세요.

김테크

8년차 개발자

안녕하세요. 풀링포레스트 풀스택 개발자 김테크입니다.

백엔드 개발을 하다 보면 '유연성'과 '성능' 사이에서 외줄 타기를 하는 기분이 들 때가 많습니다. 특히 C나 C++, 혹은 Go로 작성된 코어 시스템을 다룰 때 더욱 그렇습니다. 비즈니스 로직 하나를 바꾸기 위해 컴파일하고, 이미지를 빌드하고, 전체 배포를 다시 하는 과정은 솔직히 말해 꽤 고통스럽습니다.

"이 부분만 스크립트로 빼서 런타임에 동적으로 바꾸면 안 될까?"

이런 고민을 할 때 보통 가장 먼저 떠오르는 건 Lua입니다. Nginx나 Redis가 그러하듯, 임베디드 스크립팅의 왕좌는 오랫동안 Lua가 지키고 있었죠. 하지만 저희 팀원 대부분은 루비(Ruby) 문법에 훨씬 익숙합니다. Lua의 인덱스가 1부터 시작한다는 사실에 혼란스러워하다가 버그를 낸 적도 있었으니까요. 그러다 문득 "루비를 C 안에 심을 순 없을까?"라는 생각이 들었고, 그렇게 mruby를 다시 보게 되었습니다.

오늘은 단순히 '가벼운 루비'를 넘어, 실제 시스템에 스크립팅 엔진을 도입하려 할 때 mruby가 왜 매력적인 선택지인지, 그리고 주의할 점은 무엇인지 이야기해 보려 합니다.

왜 하필 mruby인가?

처음 mruby를 검토할 때 가장 우려했던 건 "그거 옛날 루비 버전 아니야?"라는 의구심이었습니다. 하지만 문서를 파헤쳐보고 직접 테스트해보니 그건 기우였습니다.

mruby는 ISO 표준을 준수하면서 Ruby 3.x의 최신 문법을 대부분 지원합니다. (단, 패턴 매칭 같은 일부 기능은 제외됩니다.) 즉, 우리가 익숙하게 사용하는 Safe Navigation Operator(&.) 같은 문법을 그대로 쓸 수 있다는 뜻입니다. CRuby(MRI)가 무거운 운영체제 위에서 돌아가는 거함이라면, mruby는 필요한 것만 딱 챙겨서 떠나는 배낭여행객 같습니다.

제가 느낀 mruby의 가장 큰 매력은 철저한 모듈화입니다. mrbgems라는 패키지 관리자가 핵심인데, 이걸 통해 내가 필요한 기능만 골라서 빌드할 수 있습니다. 예를 들어 소켓 통신이 필요 없다면 빼버리면 그만입니다. 덕분에 바이너리 크기를 극단적으로 줄일 수 있죠.

컴파일러가 주는 안도감

인터프리터 언어를 임베딩한다고 하면 성능 걱정부터 앞섭니다. mruby는 이 지점에서 mrbc라는 컴파일러를 제공합니다.

루비 코드를 .rb 파일 그대로 배포하는 게 아니라, mrbc를 통해 바이트코드로 컴파일하거나 아예 C 소스 파일로 변환해서 본체 애플리케이션에 링크해버릴 수 있습니다. 이렇게 하면 파싱 오버헤드를 줄일 수 있고, 배포된 바이너리 안에서 코드를 숨기는(Obfuscation) 효과도 덤으로 얻습니다.

실제로 저희 팀 내부 툴 중 하나에 mruby를 적용했을 때, Rake를 통해 빌드 프로세스를 구성했습니다. rake all test 한 번이면 코어 라이브러리와 mrbgems가 싹 빌드되는 과정이 꽤나 쾌적했습니다.

막막했던 순간: 메모리와의 싸움

하지만 장점만 있는 건 아니었습니다. 도입 초기에 저를 가장 괴롭혔던 건 역시 메모리 관리였습니다.

일반적인 웹 개발을 할 때는 가비지 컬렉터(GC)가 다 알아서 해주지만, mruby를 임베디드 환경이나 호스트 애플리케이션 내부에서 돌릴 때는 이야기가 다릅니다. C 영역과 mruby 영역 사이에서 객체를 주고받을 때, 자칫하면 GC가 돌면서 필요한 객체를 회수해버리거나(Use-after-free), 반대로 영원히 해제되지 않는 메모리 누수가 발생할 수 있습니다.

특히 GC Arena라는 개념을 이해하는 데 시간이 좀 걸렸습니다. C 함수 내에서 생성된 mruby 객체들이 GC의 대상이 되지 않도록 보호해주는 영역인데, 이걸 적절히 관리하지 않으면 "왜 내 객체가 사라졌지?" 하며 밤을 새우게 됩니다. 결국 문서를 정독하고, mruby의 메모리 할당자를 커스터마이징할 수 있다는 점을 활용해 저희 시스템에 맞는 방식으로 튜닝하고 나서야 안정화할 수 있었습니다.

실무 적용을 위한 조언

만약 여러분이 사내 레거시 시스템이나 고성능이 필요한 데몬에 "유연한 설정 기능"이나 "규칙 엔진"을 붙이고 싶다면, mruby는 훌륭한 대안입니다.

  1. 적재적소에 쓰세요: mruby는 Rails를 대체하는 게 아닙니다. 설정 파일 파싱, 간단한 데이터 가공 필터, 게임 스크립트 등 '로직의 유연함'이 필요한 곳에 작게 심으세요.

  2. mrbgems를 적극 활용하세요: 기본 코어는 정말 작습니다. math, time 같은 기본 라이브러리조차 젬(gem) 형태로 추가해야 할 수도 있습니다. build_config.rb 파일을 잘 관리하는 것이 핵심입니다.

  3. 라이선스 걱정은 덜어도 됩니다: mruby는 MIT 라이선스를 따릅니다. 기업 환경에서 사용하고, 수정하고, 배포하는 데 있어 법적인 제약이 매우 적습니다.

마치며

개발자로서 익숙한 도구를 낯선 환경에서도 사용할 수 있다는 건 큰 축복입니다. mruby는 루비라는 언어의 생산성과 즐거움을 임베디드나 시스템 프로그래밍 영역까지 확장해 줍니다.

처음엔 C 코드와 루비 코드가 섞이는 게 어색할 수도 있습니다. 하지만 딱딱하게 굳어있던 시스템이 루비 코드 한 줄로 유연하게 동작하는 걸 보는 순간, 그 매력에 빠지게 될 겁니다. 혹시 지금 "이 로직 바꾸려면 또 배포해야 해?"라고 한숨 쉬고 계신다면, 시스템 한구석에 작은 mruby 인터프리터를 심어보는 건 어떨까요?

오늘도 쾌적한 개발 환경을 위해 고군분투하는 여러분을 응원합니다. 풀링포레스트 김테크였습니다.

지금 읽으신 내용, 귀사에 적용해보고 싶으신가요?

상황과 목표를 알려주시면 가능한 옵션과 현실적인 도입 경로를 제안해드립니다.