Framework Selection: 도구가 아니라, 책임의 문제
HoBom을 설계하며 가장 많이 받은 질문은 이것이었습니다.
- 왜 NestJS, Spring Boot, Go를 같이 쓰나요?
- 왜 하나로 통일하지 않았나요?
제 대답은 항상 같습니다.
나는 프레임워크를 선호로 고르지 않는다. 책임과 시스템 요구사항으로 고른다.
프레임워크 선택은 ‘생산성’이 아니라 ‘운영 비용’의 문제가 아닐까?
프레임워크 선택은 빠르게 만드는 문제처럼 보이지만, 실제로는 운영 비용과 장애 복잡도의 문제입니다. 그래서 저는 항상 이렇게 묻습니다.
“이 선택이 3년 뒤 우리 팀의 운영 비용에 어떤 영향을 줄까?”
HoBom에서 서버는 다음처럼 책임이 나뉩니다.
- NestJS: 사용자 요청 처리, Outbox 기록
- Go: 이벤트 발행 책임 단일화
- Spring Boot: 무거운 비즈니스 로직과 보상 처리
중요한 건 통일이 아니라 각 서버가 맡는 책임과 런타임 특성이 맞는가 였습니다.
왜 NestJS인가
NestJS는 다음 요구에 적합했습니다.
- 빠른 기능 확장
- 모듈 단위 설계
- 낮은 진입 장벽
- 도메인 구조 실험 DI 기반 구조와 TypeScript 생산성은 장점이지만, CPU 바운드 작업이나 고부하 처리에는 한계가 있습니다. 그래서 NestJS는 API 처리와 Outbox 기록까지만 맡깁니다. 그리고 무거운 책임은 의도적으로 주지 않았습니다.
왜 Go인가
이벤트 발행 책임은 분산시키지 않았습니다. 이유는 단순합니다.
이벤트 발행 책임이 분산되면, 장애 복잡도는 기하급수적으로 증가한다.
Go의 특징은 다음과 같습니다.
- 높은 처리량
- 단순한 동시성 모델
- 작은 배포 단위 그렇기에 단순히 이벤트 발행을 담당하기에 적합하다 판단했고, Kafka Produce 책임을 단일 서버로 집중시켰습니다. 이 선택은 코드 취향이 아니라 운영 복잡도를 줄이기 위한 결정이었습니다.
왜 Spring Boot인가
복잡한 트랜잭션, 정교한 쿼리, 재시도·보상 로직은 JVM 생태계가 가장 안정적입니다.
- 무거운 책임은 JVM에
- 빠른 I/O와 이벤트 발행은 Go에 각 런타임의 강점을 극단적으로 분리했습니다.
왜 하나로 통일하지 않았는가
프레임워크 통일은 편할 수 있습니다. 하지만 그 대가로
- 책임이 한 서버에 몰리고
- 런타임 최적화가 어려워지고
- 장애 격리가 약해진다 통일은 전략이기보다 종종 심리적 안정감에 가깝다고 느낍니다.
Node.js를 둘러싼 문화적 편향
“Node로 백엔드를 한다고?” “결국 Spring으로 돌아올 거야.” 이 반응은 기술적 토론이 아니라, 제 경험상 문화적 관성에 가까웠습니다. Node.js는
- I/O 바운드 작업에 강하고
- 빠른 API 서버에 적합하지만
- CPU 바운드 작업에는 불리하다 문제는 기술이 아니라 적절한 맥락에 사용했는가 라고 생각합니다. 선호는 존재하지만, 그것이 아키텍처의 기준이 될 수 없다고 생각합니다.
내가 프레임워크를 고를 때 던지는 질문
- 이 서버의 책임은 무엇인가?
- CPU 바운드인가, I/O 바운드인가?
- 장애 격리가 가능한가?
- 운영 복잡도는 증가하는가?
- 3년 뒤 유지보수 비용은?
- 팀의 역량과 맞는가? 기술은 유행을 타지만, 책임 분리와 런타임 특성은 유행을 타지 않습니다.
결론
프레임워크 선택은 취향의 문제가 아닙니다.
구조의 문제입니다.
중요한 질문은
“이 선택이 책임을 명확히 하고, 장애 복잡도를 줄이는가?”
그 질문에 답하지 못한다면 기술은 도구가 아니라 장식이 된다고 생각합니다.