2-1. EventEmitter 기여 (PR #67) 25.11.15 ~ 25.11.23
문제 인식
개발 배경에서 언급했듯이 Craby의 Signal에는 네이티브에서 자바스크립트 쪽으로 이벤트를 던져줄 수는 있었지만, 이벤트와 함께 데이터를 전달할 수 있는 방법이 없었다.
예를 들어 파일 다운로드 같은 긴 작업을 네이티브에서 처리할 때, 진행률이나 에러 정보를 자바스크립트 쪽으로 넘겨주고 싶을 수 있는데, 기존에는 그냥 "이벤트가 발생했다"는 신호만 보낼 수 있었던 것이다.
이게 이슈 #57에 등록되어 있었고, 나는 이걸 구현해보겠다고 마음을 먹었다.
코드 분석의 시작
Craby 레포를 클론 받고 코드를 분석하기 시작했다.
솔직히 말하면, Craby의 코드 구조를 이해하는 게 쉽지 않았다. 러스트 코드야 그렇다 쳐도 C++ 브릿징 코드와 코드 제너레이터까지 이해해야 했기 때문이다.
Craby의 코드 생성 파이프라인은 대략 이렇다:
- TypeScript에서
NativeSpec을 정의한다 - 파서가 이 스펙을 분석한다
- 코드 제너레이터가 Rust, C++ 코드를 자동 생성한다
나는 이 세 단계를 모두 건드려야 했다.
파서 분석
먼저 native_spec_parser.rs를 살펴봤다. 여기서 TypeScript 스펙 파일을 파싱해서 내부 타입으로 변환하는데, Signal 타입에 제네릭 페이로드가 없었다. Signal은 있는데 Signal<T>는 없는 거다.
그래서 파서에 제네릭 타입 파라미터를 읽는 로직을 추가했다. 이 부분은 비교적 간단했다.
Rust 코드 제너레이터
rs_generator.rs에서는 Rust 쪽 코드를 생성하는데, Signal에 페이로드를 실어서 emit하는 코드를 생성하도록 수정했다.
기존에는 그냥 빈 이벤트만 보냈다면, 이제는 구조체를 직렬화해서 보내는 코드가 생성되어야 했다. 러스트 코드 생성 쪽은 매크로나 트레이트를 잘 활용하면 되니까 비교적 수월하게 할 수 있었다.
C++ 코드 제너레이터 (진짜 고비)
문제는 cxx_generator.rs였다.
C++ 브릿징 코드를 자동 생성해야 하는데, 네이티브에서 자바스크립트로 데이터를 넘기려면 C++ 단에서 데이터를 적절히 변환해줘야 한다. TurboModule의 이벤트 에미터를 통해 자바스크립트 쪽으로 오브젝트를 전달하는 코드를 생성해야 했다.
C++은 사실 잘 모르는 영역이라 좀 쫄렸다. 하지만 기존 코드를 충분히 읽어보고, React Native의 TurboModule 코드도 참고하면서 패턴을 파악했다.
결과적으로 C++ 쪽에서 CrabySignals.h 헤더 파일을 수정하고, CxxModule.cpp에서 이벤트 에미터를 통해 페이로드를 전달하는 코드 생성 로직을 구현했다.
변경된 파일이 30개가 넘었다. 코드 제너레이터 쪽 변경, 예제 코드 변경, 스냅샷 테스트 갱신까지 하니까 상당한 양이 되었다.
PR 제출
onProgress와 onError라는 두 개의 시그널을 예제로 구현해서 PR을 올렸다.
기본적인 기능은 확인했지만 다양한 엣지 케이스를 테스트하진 못한 상태였기에, 솔직하게 그 부분도 PR 설명에 적었다. 괜히 완벽한 척하다가 리뷰에서 들키면 민망하니까.
리뷰 과정
PR을 올리고 나서 메인테이너인 leegeunhyeok님이 "이번 주 내로 리뷰하겠다"고 코멘트를 남겨줬다.
그리고 약 일주일 뒤인 11월 23일에 리뷰가 왔다.
"The implementation looks great—I don't have any major feedback."
구현 자체는 좋다는 평이었고, 몇 가지 사소한 수정 요청이 있었다. 큰 방향의 변경은 아니었기에 바로 같은 날 피드백을 반영했다.
피드백 반영 후 두 번째 리뷰에서:
"LGTM! There's an issue with the macOS runner causing some CI failures, but I'll follow up on this before the package release!"
CI에서 macOS 러너 관련 이슈가 있었는데, 이건 GitHub Actions의 macOS 러너 자체 문제여서 내 코드 문제는 아니었다. 그렇게 최종 승인을 받았다.
머지 2025-11-23
11월 15일에 PR을 올리고, 11월 23일에 머지가 되었다. 약 8일 만이다.
처음으로 의미 있는 오픈소스 코드 기여를 완료한 순간이었다. 문서 수정이 아니라 실제 기능을 구현해서 머지가 된 거니까, 나름 뿌듯했다.
그런데 PR이 머지되고 나서 코드를 다시 보다가 문제를 하나 발견했다. 그게 다음 PR의 시작이 된다.
번외: Promise void 지원
이 PR에서 Promise<void> 타입 지원도 함께 추가했다. 기존에는 Promise가 반드시 값을 반환해야 했는데, void 타입도 지원하도록 파서와 제너레이터를 수정했다. 작은 변경이지만 사용성에 도움이 되는 부분이라 같이 넣었다.
Signal에 데이터를 실어 보내는 EventEmitter 기능을 구현해서 Craby에 기여했고, 8일 만에 머지됐다.