What is HoBom?
HoBom이 뭐냐는 질문을 정말 많이 받습니다. HoBom이란, 제 이름의 맨 마지막 글자와, 제 사랑하는 사람의 이름 맨 마지막을 합친 것입니다. HoBom 이라는 이름과 시스템이 탄생한지 4년차가 되어 갑니다. 처음 이 이름을 딴 시스템이 탄생한 배경 또한 매우 흥미로운데요. 오늘은 그에 대한 짧은 소개와 어떠한 철학으로 해당 시스템을 설계 했는지에 대한 이야기를 해볼까 합니다.
History
HoBom의 역사는 저와 저의 사랑하는 사람이 신입 시절부터 현업에서 근무하며 겪은 문제들을 해결해 보며 공부해 보고자 하는 의지로 탄생했습니다. 그래서 처음엔 작게나마 공부한 내용들을 공유하는 취지로 작게나마 블로그로 시작을 했었습니다. 하지만 이것 만으론 아쉬움이 많아 우리만의 시스템을 구축해 보고자, 작은 사이드 프로젝트 형태로 점차 시스템을 확장해 나갔습니다. 이러한 과정을 겪으며 지금과 같은 형태의 HoBom System이 탄생하게 됐습니다. 이렇게 사이드 프로젝트 치고는 꽤나 큰 시스템이 탄생하기 까지.. 길다면 길고, 짧다면 짧은 시간이 흘러버렸습니다.
System Architecture of HoBom
HoBom의 시스템 구성은 다음과 같이 구성돼 있습니다. 사용자가 사용하는 웹뷰 + Native App과 이를 바탕으로 다양한 MSA로 구성된 서버가 존재 합니다.
FrontEnd
- React
- React Native
- Angular
BackEnd
- Nestjs
- Spring Boot
- Go
사이드 프로젝트 치고는 꽤나 규모가 있는 아키텍처를 가지고 있는데요. 실제 현업을 경험하며 겪었던 문제들과 이를 유연하게 대체할 수 있는 대안을 고민하다 보니, 위와 같은 다양한 스택과 시스템 구성을 가지게 됐습니다. 먼저 어떠한 문제를 해결 하고자 했는지 알아 보기 이전에, 간단히 시스템의 플로우를 소개해 드리겠습니다.
Flow
Client → HoBom API Gateway → Nestjs
- 여기까지 App과의 통신 부분입니다.
- 그리고 Nestjs 서버에서, 푸시, 메일 알림을 보낼 경우, Outbox에 적재합니다.
- 또한 Gateway를 거쳐온 API 로그를 Outbox에 적재합니다. Go → Polling ( gRPC ) → Outbox
- Go 서버가 Nestjs와 gRPC 통신을 통해 Outbox에 적재된 데이터를 확인합니다. Spring Boot
- Kafka 이벤트 Consume
위와 같은 흐름을 갖게 됨으로써, 사용자와 비즈니스 로직을 수행하는 서버와의 통신은 간단하게 끝나게 됩니다. HoBom의 시스템을 다음과 같이 복잡하게 구성했을까요?
Why?
HoBom 시스템을 설계하게 된 출발점은 아주 단순한 불편함이었습니다.
“왜 우리는 에러 하나를 찾기 위해 서버에 SSH로 접속해야 할까?”
이전 회사에서는 로깅 시스템이 파일 기반으로만 존재했습니다. 문제가 발생하면 각 MSA 서버에 직접 접속해 로그 파일을 뒤져야 했고, 일부 오래된 레거시 시스템에는 그마저도 존재하지 않았습니다. Gateway 서버 또한 존재하지 않았기 때문에, 뒤늦게 중앙 로깅을 도입하려 해도 모든 API 엔드포인트를 수정해야 했고, 이는 현실적으로 감당하기 어려운 비용이었습니다. 이 경험을 통해 저는 다음과 같은 질문을 하게 되었습니다.
- 로그와 에러를 중앙에서 수집할 수는 없을까?
- 서버가 늘어나도 추적 비용이 증가하지 않는 구조는 없을까? 그래서 HoBom에는 Gateway와 traceId 기반의 중앙 로깅 구조를 도입하게 되었습니다.
도대체 이러한 문제들은 어떻게 해결해야 할까요? 어떻게 해야 시스템의 가용성을 조금이라도 더 높일 수 있는 걸까요? 클린 아키텍처, 클린 코드와 같은 것은 현업에서 적용 불가능한 문제인 걸까요?
HoBom 시스템은 다음과 같은 원칙을 기준으로 설계되었습니다.
- 비즈니스 서버는 메시지를 “보내지 않는다”
- 이벤트 발행 책임은 단일 서버가 가진다
- 무거운 처리와 보상 로직은 비동기로 뒷단에서 담당한다
단순히 위임할 로직은 위임하고, 각 서버의 역할만 나누었을 뿐인데, 위에서 제가 문제라고 느꼈던 것들을 대부분 해소할 수 있었습니다. 푸시와 메일 전송에 실패할 경우, 보상은 Spring Boot에서 담당하면 되고, 이를 위한 수기처리 Internal API 역시 손쉽게 제공할 수 있는 형태가 됐습니다. 또한 로깅의 중앙화를 통해 더 이상 각각의 서버마다 파일을 탐색하지 않아도 됐습니다. Kafka 이벤트 발행의 책임 역시 Go 서버에 있기 때문에, 추가할 이벤트가 있다면 Go 서버에 손쉽게 추가할 수 있는 형태가 됐습니다. 더 이상 여러 서버를 탐색하며 비즈니스 로직을 추적하지 않아도 되겠습니다.
Review
HoBom은 거창한 서비스가 아니라, 실무에서 겪은 아쉬움을 스스로 해소해 보고자 만든 개인적인 실험에 가깝습니다. 하지만 사이드 프로젝트였기 때문에, 오히려 클린 아키텍처, 역할 분리, 책임 위임과 같은 개념들을 부담 없이 적용하고 검증해 볼 수 있었습니다. 이 시스템이 정답이라고 말할 수는 없지만, “왜 이런 구조가 필요한지”에 대해서는 스스로 충분히 납득할 수 있는 경험이었습니다. 앞으로 이 글을 시작으로, HoBom 시스템의 세부 구성과 선택의 이유들을 하나씩 정리해 보려 합니다.