Red Hat 오픈소스에 기여한 이야기

Red Hat 오픈소스에 기여한 이야기

얼마전 Debezium(디비지움)이라는 오픈소스 프로젝트에 처음으로 코드로 기여를 하였다. 이전까지는 여러 오픈소스 프로젝트의 README 나 문서에 대한 컨트리뷰션 밖에 해보지 못했는데 드디어 코드로써의 기여를 하게 되었다.

비록 특별한 내용은 없지만 나의 경험이 처음으로 오픈소스 컨트리뷰션을 하고자 하는 사람들에게 조금이나마 도움이 되었으면 하는 마음에서 글을 작성하게 되었다.

머지된 PR 스샷

Debezium(디비지움)은 Red Hat 에서 관리되는 프로젝트이며, CDC(Change Data Capture) 를 위한 도구이다. CDC는 간단히 말해서 데이터의 변화를 포착하여 적절한 처리를 할 수 있도록 하는 행위를 말하는데, 데이터 파이프라인(ETL) 이나 마이크로서비스 아키텍처(Outbox Pattern) 등 다양한 곳에서 활용된다.

동기

Debezium 에 기여하게된 동기에 대한 이야기를 하기 전에 아주 간단하게 Debezium 에 대한 설명을 하는 것이 좋을것 같다.

Debezium 의 특징

Debezium 은 기본적으로 DB의 replication 메커니즘에서 사용되는 transaction log(e.g. MySQL 의 binlog 와 MongoDB 의 oplog)를 tailing 하여 실시간으로 데이터의 변경을 포착한다. 현재 지원하는 DB는 8가지(MySQL, MongoDB, PostgreSQL, Oracle, SQL Server, Db2, Cassandra, Vitess)이다.

Debezium 를 사용하는 방법은 크게 3가지가 있다. 우선 Kafka Connect 의 connector 로 실행시키는 것이 가장 일반적인 사용 방법이다. 디비지움은 각 DB 별 connector 를 제공한다.

이 방식을 사용할 경우 Debezium connector 는 데이터 변경 이벤트를 Kafka 에 실시간으로 전송하게 된다.

Kafka Connect 를 통한 Debezium 의 가장 기본적인 사용 방식 (출처: https://debezium.io/documentation/reference/1.8/architecture.html)

두번째는 Debezium Engine 라는 것을 사용하는 것인데, 애플리케이션에서 API를 통해 디비지움의 CDC 기능을 사용할 수 있다.

마지막으로는 Debezium Server 라고, 디비지움에서 자체적으로 만든 별도의 서버를 띄우는 방법이다. 참고로 이 방식은 아직 인큐베이팅 상태라서 향후 기능이 변경 될 가능성이 있다.

두번째와 세번째 방식은 첫번째 방식과는 다르게 Kafka 를 거치지 않고도 다른 곳으로 데이터를 바로 보낼 수 있다.

Kafka Connect 는 SMT(Single Message Transforms) 라는 기능을 제공하는데, source connector 가 카프카에 레코드를 쏘기 전에, 혹은 sink connector 가 카프카에서 레코드를 가져온 후에 Key, Value, Header 등 레코드의 정보를 변경할 수 있도록 한다.

Debezium 도 자체 connector 와 함께 사용할 수 있는 SMT 를 몇 가지 제공한다.

그래서 어떤 기여를, 왜 했나

Event-driven MSA로 서버 아키텍처를 구성하던 도중 트랜잭셔널 아웃박스 패턴을 위해서 CDC 기능이 필요했다. 그래서 어떤 도구를 사용할까 살펴보던 도중 Debezium 이 눈에 들어왔다. 어느정도 훑어보니 괜찮아 보여서 한 번 사용해보고 싶었는데, SQL DB 의 경우 즉시 사용할 수 있는 SMT 가 존재했지만, MongoDB 는 직접 커스텀 SMT 를 개발해야 했다. 기존의 Outbox SMT 가 MongoDB 를 지원하지 않는 이유는 MongoDB 의 connector 가 만들어내는 Record 에서 변경된 이후의 데이터를 담고있는 after 필드의 구조가 다르기 때문이다. Debezium 에서 제공하는 다른 SMT의 경우도 SQL DB 용과 MongoDB 용이 별도로 존재하는 경우가 있다.

아무튼 그래서 이참에 그냥 내가 직접 개발해서 컨트리뷰션을 하면 어떨까 생각을 하게 되었다. 그래서 디비지움 IRC 에서 메인테이너에게 이 기능에 관심이 있다고 했고, 메인터이너는 기존에 있던 이슈의 링크를 주었다. 참고로 디비지움은 원래 Gitter 를 통해 IRC 채널을 운영했는데, 2021년 하반기부터는 Zulip을 사용하고 있다.

디비지움의 IRC 에서 다양한 소통을 할 수 있다

디비지움의 모든 이슈는 Jira 에서 관리된다

이슈를 살펴보니 이슈의 작성자가 설계에 대한 고민을 적어두었는데, 내가 했던 고민과 정확히 같았다. 그래서 메인테이너에게 내가 생각하는 구현 방식을 제안해봤더니 좋은것 같다고 해서 개발을 시작하게 되었다.

과정

우선 CONTRIBUTE.md 의 내용을 정독하는 것이 굉장히 중요했다. 프로젝트마다 차이는 있을 수 있겠지만 디비지움의 경우는 컨트리뷰션 가이드가 꽤 상세하게 기술되어 있다. 그래서 초반에 드는 궁금증 중에 상당 부분은 이곳에서 답을 찾을 수 있다.

PR 과 커밋이 따라야하는 규칙들, 커밋 메시지의 형식, 테스트 방법, code formatting 방식 등의 대한 정보가 적혀있는데, 예를 들자면 새로운 기능을 추가하는 PR은 항상 그 기능에 대한 단위 테스트/통합 테스트, 그리고 문서도 함께 작성돼야 한다는 규칙이 존재한다.

위와 같은 규칙에 따라 문서도 함께 작성하게 되었다. 물론 기존의 SQL DB 용 SMT 와 동일한 기능을 제공하는것이 목표였기 때문에 기존의 문서와 전체적인 짜임새는 비슷하게 갈 수 있었다.

공식 사이트에 반영된 직접 작성한 문서

개발

이렇게 큰 프로젝트에 기여를 막상 하려고하면 처음엔 굉장히 막막하다. 그래서 우선 전체적인 동작 원리와 사용 방식에 대한 감을 잡기위해 최대한 많은 정보를 활용하려고 했다.

우선 프로젝트의 전체적인 그림과 방향성을 파악하기 위해 깃헙 레포를 Watch 하고 구글 그룹에 가입하여 실시간으로 개발자들간의 토론이나 새로운 이슈에 대한 알림을 받아보았다.

공식 문서와 블로그의 글을 많은 부분 정독했고, 가장 관련이 있는 기능들의 비즈니스 로직과 단위/통합 테스트 코드를 참고했다. 참고했던 주요한 부분은 기존의 SQL DB 용 Outbox SMT 인 Outbox Event Router 와 MongoDB connector 에서 JSON string 형태의 envelope 을 flattening 할 때 사용되는 SMT 인 MongoDB New Document State Extraction 이었다.

잘 작성된 테스트 코드는 클래스의 기능을 잘 나타내기 때문에 테스트를 하나씩 실행시키면서 테스트 코드를 분석했다. 그러면서 어떤 기능들이 필요한지 둘러보았고, 테스트는 어떤 식으로 작성해야하는지 파악했다. 전체적인 그림이 잡혀나가기 시작할 무렵, 우선 필요한 기능들에 대한 단위 테스트를 작성했고, 이것들을 모두 통과하기 위한 실제 코드를 작성하였다.

개발 중간마다 설계에 대한 고민과 이 SMT 를 유저가 어떻게 사용하게 할 것인지에 대한 여러가지 고민을 하기도 했는데, 이건 IRC 에서 메인테이너들과 이야기를 나누거나 코드 리뷰를 통해 개발 방향을 명확히 하였다.

PR 을 올리고 나서 받은 다양한 피드백 중에 동의하는 부분은 수정했고, 일부 의견에 대해서는 대화가 필요하기도 했다. 나는 내가 고민한 부분과 그것에 대한 솔직한 나의 의견에 대해 가능한 자세하게 말했다. 물론 전체 프로젝트에 대해서는 메인테이너가 훨씬 잘 알것이고 실력도 나보다 뛰어나겠지만 큰 규모의 프로젝트를 관리하다보면 디테일한 부분을 모두 기억하지 못 할 수도 있고, 적어도 내가 작업한 부분에 대해서는 나도 어느정도 잘 안다고 생각했기 때문에 자신감과 오너십을 가져야한다고 생각했다.

메인테이너의 피드백에 대한 나의 의견을 상세하게 적기도 했다

후속 작업

애그리거트 ID 기본 타입 변경

Outbox 콜렉션에서 aggregateid 의 기본 타입을 String 으로 개발을 진행했는데, 메인테이너가 ObjectId 로 변경하는 것이 어떻겠냐고 했다. 만약 같은 생각이면 후속 PR로 이 부분을 수정하면 좋을것 같다고 했는데 나도 같은 생각이라 이 부분에 대한 작업을 하면 좋을것 같다.

+ 추가(21.11.21): 후속 PR을 통해 변경을 완료했다.

샘플 프로젝트

디비지움은 debezium-example이라는 레포를 통해 Debezium 을 활용한 다양한 예제를 제공한다. 기존 SQL 용 Outbox SMT 에 대한 예제도 존재한다.

Outbox Event Router 샘플 프로젝트의 README

이곳에 MongoDB Outbox Event Router 에 대한 예제 프로젝트도 만들어야하는데 이 부분도 개발할 생각이 있는지 제안을 받았다. 그래서 시간이 날 때 추가 기여를 하려고 생각 중이다.

+ 추가(21.11.27): 후속 PR을 통해 예제 추가를 완료했다. 메인테이너가 로컬에서 머지를 해서 원래 PR 은 closed 가 됐다. 하지만 main 브랜치에는 커밋이 잘 들어가 있는 것을 확인할 수 있다.

Embedded Document 형태를 지원하는 옵션

MongoDB 같은 Document DB 의 경우 일반적인 관계형 DB 와 데이터 모델링을 하는 방식이 조금 다르다. 보통 높은 정규화를 하지 않고 여러 데이터를 하나의 Document 에 저장하도록 데이터를 모델링하는 경우가 많기 때문에 multi-document 트랜잭션을 사용하지 않고 Outbox Pattern 을 사용할 수 있도록 하는 기능을 제공하면 좋겠다고 생각했다. 그래서 처음에 Jira 이슈에서도 우선은 별도의 Collection 으로 Outbox 데이터를 저장하여 multi-document 트랜잭션을 사용하도록 하고, 추후 업데이트를 통해서 이런 기능을 사용할 수 있는 설정을 추가 개발하면 어떻겠냐고 제안했었다.

그 외

MySQL 과 MongoDB Connector 의 코어 로직에 대한 기여도 해보고 싶어서 소스 코드를 분석하고 있다. 또, Jira 에 올라와있는 다양한 이슈들을 보면서 현재 어떤 작업들이 필요한지 둘러보고 이 중에 관심있는 이슈는 watch 기능으로 진행 상황을 지켜보려고 하고있다.

느낀점

개발자가 새로운 팀에 합류하게 되면 기존에 개발되어 있는 방대한 양의 코드와 아키텍처를 빠르게 파악하고 기존의 규칙들을 정확하게 준수하여 코드를 기여해야 하는 경우가 많을 것이고, 기존 기능을 변경하거나 새로운 기능을 추가 하기 위해 다른 개발자와의 커뮤니케이션이 중요할 것이다. 이러한 점에서 다양한 오픈소스에 기여를 하는 경험이 실제 업무를 하는데도 큰 도움이 될 것 같다는 생각이 들었다.

또, 영어로 전세계의 다양한 개발자들과 소통을 하며 오픈소스에 기여함으로써 외국 기업에서의 협업을 간접적으로 경험할 수 있는것 같다. 그래서 만약 나중에 실제 업무에서 영어로 협업을 해야하는 상황이 온다면 이런 경험이 조금이나마 도움이 되지 않을까 생각이 든다.

추가로, 잘 관리되는 오픈소스 프로젝트의 소스코드를 분석하면 좋은 설계와 코드를 경험할 수 있어 좋은것 같다. 평소에 오픈소스를 '사용할 때'에는 직접 작성할 일이 많지 않을 수 있는 정교한 최적화나 로우 레벨 코드도 볼 수 있는 경우도 있어서 공부가 되기도 한다. 이러한 점에서 앞으로도 Debezium 뿐 아니라 다양한 오픈소스의 코드를 분석하여 기여해보려고 한다. 비록 이번에 기여한 기능은 복잡한 기능은 아니었기 때문에 상대적으로 어렵지 않았지만, 다른 오픈소스 프로젝트의 코드에도 기여할 수 있다는 자신감이 생겨서 시간이 날 때 내부 동작을 자세하게 알고 싶은 다양한 오픈소스들의 코드를 분석해 보려고 한다.

Comments