전체 아키텍처
아키텍처란 시스템의 구조, 동작 등을 정의하는 개념적인 모형으로 시스템의 목적을 달성하기 위해 시스템의 각 컴포넌트가 무엇이며 어떻게 상호작용 하는지, 정보가 어떻게 교환되는 지를 설명한다.
쉽게 말해 아키텍처란 전체 시스템을 어떻게 구성할 건지를 정합니다.
저희 팀의 아키텍처입니다.
저희 팀은 기술적인 도전으로 MSA 아키텍처를 도입하기로 결정했습니다.
디스코드 기능을 분석하면서 어떤 구성요소가 있는지 파악하고
구성이 비슷한 거 끼리 묶어서 서비스들을 구성했습니다.
처음부터 서비스들을 작게 작게 나누기보다는 우선은 크게 크게 서비스들을 나누고
거기서 세분화하는 방식으로 서비스들을 구성했습니다.
MSA를 도입하면 얻는 여러 장점들이 있다고 합니다.
loosely coupled를 기반으로 빠른 배포주기, 폴리글랏 프로그래밍, 관심사의 집중 등등
사실 위 장점들 중에서 프로젝트를 하면서 직접 느낀 거는 빠른 배포주기와 관심사의 집중 정도였습니다.
각 서비스별 브랜치들이 존재하고 특정 기능 범위마다 배포할 브랜치에 pr을 날려 작업했습니다.
모놀리식 같은 경우는 배포 시 모든 서비스들이 포함된 상태로 배포가 되지만 MSA 같은 경우 특정 서비스만 바로바로 배포를 할 수 있어 배포주기가 빠르다는 게 이런 의미이구나를 느꼈습니다. 그리고 팀원들끼리 맡을 서비스들을 나눠서 개발을 하니 본인이 맡은 서비스에 좀 더 집중할 수 있었습니다.
반대로 문제점들도 존재합니다.
사실 장점들보다는 단점들이 더 많아 보였고
이러한 단점들을 보고 배보다 배꼽이 더 크다고 느껴졌습니다. 😂
MSA를 도입 시 발생하는 새로운 문제점들은 아래와 같습니다.
Database Per Service라는 새로운 요구사항은 잘 지켜지나요?
rest 통신(synchronized)으로 인한 제약사항은 없나요?
분산된 서비스 간 트랜잭션 처리 / 반정규화 된 데이터의 동기 처리는 잘 이루어지고 있나요?
네... 벌써부터 머리가 아파오네요
이러한 문제점들은 어떻게 해결하는지 찾아보기 위해 자료들을 서치 했고
주로 EDA(Event Driven Architecture)을 적용해 문제들을 해결한다고 합니다.
그럼 EDA에 대해 알아보겠습니다.
Event Driven Architecture 란?

Event Driven Architecture(EDA)는 MSA가 적용된 시스템에서 이벤트 발생 시 해당 이벤트 로그를 보관하고 이를 기반으로 동작하며, 비동기 통신을 통해 시스템 내 통합(integration)을 수행하는 Architecture입니다.
이벤트
IT 영역에서 이벤트는 다양한 정의를 갖지만, 이곳에서 언급하는 이벤트는 상태의 변경
즉, 데이터의 변경, 생성, 삭제(CUD)를 통해 발생하는 서비스의 의미 있는 변화를 뜻합니다.
이벤트는 EDA에서 중요해서 권용근 님의 회원시스템 이벤트기반 아키텍처 구축하기 글에서 예시를 하나 가져오겠습니다.
메시징 시스템을 이용하여 메시지를 전송합니다. 메시징 시스템을 사용하면서 느슨한 결합을 가져갈 수 있을 것이라고 기대하겠지만 메시징시스템을 사용하는 아키텍처가 항상 느슨한 결합을 보장하지는 않습니다.
회원의 본인인증 해제가 발생할 때 가족계정 탈퇴 메시지를 발송하였습니다.
메시지를 발송하는 것으로 물리적인 의존이 제거되었습니다. 그러나 결합은 느슨해지지 않습니다.
가족계정 탈퇴를 기대하는 메시지를 발행했기 때문에 가족계정 시스템의 정책이 변경될 때 회원 시스템의 메시지도 함께 변경되어야 합니다. 어떤 일을 해야 하는 지를 메시지 발행자가 알려주는 경우(Command), 해야 하는 일이 변경될 때 메시지 발행자와 수신자 양쪽 모두의 코드가 변경돼야 하기 때문에 높은 결합도가 존재하게 됩니다.
또한 회원시스템은 여전히 가족계정의 비즈니스를 알고 있는 논리적인 의존관계가 남아있기 때문에 결합이 느슨해졌다고 볼 수 없습니다. 물리적으로는 결합도가 높지 않지만 개념적으로는 결합도가 높은 상태인 것입니다.
메시지를 발행하였음에도 의존관계가 남아있는 이유는 대상 도메인에게 기대하는 목적을 담은 메시지를 발행하였기 때문입니다. 메시징 시스템으로 보낸 메시지가 대상 도메인에게 기대하는 목적을 담았다면, 이것은 이벤트라 부르지 않습니다. 이것은 메시징 시스템을 이용한 비동기 요청일 뿐입니다.
메시징 시스템을 사용해 물리적인 의존을 제거할 수 있었지만, 메시지가 담는 의도에 따라 전혀 다른 결과를 얻는다는 것을 알 수 있습니다.
우리가 발행해야 할 이벤트는 도메인 이벤트로 인해 달성하려는 목적이 아닌 도메인 이벤트 그 자체입니다.
도메인이란 해결하고자 하는 문제 영역이며, 도메인 이벤트는 문제 영역에서 발생할 수 있는 핵심 가치나 행위입니다.
위 예시를 보고 저는 이벤트에 의도가 담기면 안 된다고 이해를 했습니다.
예를 들어 게시글을 작성하면 알림이 간다고 하겠습니다.
게시글 서비스에서 게시글이 작성 됐다는 이벤트가 발생하면 알림 서비스로
게시글이 작성 됐으니 알림을 보내!라는 의미를 가진 이벤트가 전송되는 게 아닙니다.
게시글이 작성 됐다는 사실 혹은 행위 자체가 이벤트로 알림 서비스에 전송된다고 생각합니다.
이벤트 로그를 보관
현재의 데이터는 상태 변경의 누적이라는 생각에서 시작합니다. 이때 상태 변경은 이벤트를 뜻하고 이를 누적하는 행위는 이벤트 로그를 보관하는 것입니다. EDA에서 생성된 이벤트는 반드시 보관되어야 합니다. 보관된 이벤트는 데이터의 현재 상태를 구성하는 근간이 됩니다. 또한, 보관된 이벤트를 바탕으로 장애 발생 또는 특정 요구사항에 따라 지정된 시점으로 복원을 수행합니다. 이벤트 로그를 보관하는 장소를 이벤트 스토어라 칭합니다.
비동기 통신
amqp, mqtt, jms 등 메세징 프로토콜을 통한 메시지 큐 방식이 자주 사용됩니다. 서비스에서 데이터의 생성,변경,삭제(CUD)을 통해 이벤트가 발생하면 발행 서비스는 메시지의 형태로 이벤트를 발행하고, 해당 이벤트에 관심이 있는 서비스에서 구독을 수행합니다. 메시지 큐를 사용함으로 requeue/dlq 등의 기능을 활용할 수 있습니다.
시스템 내 통합(integration)
이상적으로 구현된 MSA는 서비스 간 데이터 참조를 위한 내부 통신이 필요 없지만, 현실적으로 서비스 간 내부 통신이 전혀 없는 시스템을 구현하기란 불가능에 가깝습니다. 다양한 사유로 여러 서비스 간 통신을 통해 연동이 발생합니다.
Event Driven Architecture를 적용하는 이유

EDA는 MSA가 적용된 시스템에서 이벤트 발생 시 해당 이벤트 로그를 보관하고 이를 기반으로 동작하며 비동기 통신을 통해 시스템 내 통합을 수행하는 아키텍처입니다.
1) Database Per Service
Database Per Service는 MSA의 느슨한 결합, 관심사의 집중, 폴리글랏 프로그래밍, 독립적인 배포 주기 등을 달성하기 위한 핵심 키워드입니다. 하지만 MSA를 적용할 때 Database Per Service는 가장 어려운 부분 중 하나입니다.
기존의 Shared Database 구성, 그 중 관계형 DB를 사용하는 경우 ACID 원칙에 따른 트랜잭션 처리, 데이터 무결성, 모든 테이블에 접근해 Join을 통한 통합 뷰 제공, 관계를 활용한 효율적인 데이터 저장·조회 등 DBMS 레벨에서 여러 유용한 기능을 제공했습니다. MSA에서 Database Per Service를 적용하면 Shared Database 구성의 DBMS 레벨에서 제공하던 기능을 애플리케이션 레벨에서 해결해야 합니다. EDA은 DBMS 레벨이 아닌 애플리케이션 레벨에서 최종 일관성을 지원해 트랜잭션 처리를 수행하는 것에 적합하며 서비스의 목적에 최적화된 DB를 선택하는 것에 도움을 줍니다.
2) 트랜잭션 처리
비즈니스 흐름에 따라 기능을 수행하다가 중간에 문제가 발생할 수 있습니다.
문제가 발생한 시점에 롤백(Rollback) 또는 재시도(Retry)를 수행합니다.
기존 레거시 시스템에서 문제 발생 시 일관된 commit 또는 롤백 처리나 이전에 발생한 상태 변경에 직접 접근해서 데이터 수정이 가능합니다. 하지만 MSA가 적용된 시스템에서 서로 다른 서비스에 걸쳐진 기능을 수행하는 도중 일관된 commit 또는 롤백을 수행할 수 없습니다. 이때 EDA을 적용해 롤백 또는 재시도하여 처리할 수 있습니다. 롤백이 필요한 경우 Failed 이벤트를 발생시키고 이를 이전 스텝을 수행한 서비스에서 구독하여 보관되어 있던 이벤트 로그 기반으로 롤백을 수행합니다. 재시도가 필요한 경우 메시지 큐의 Requeue 또는 Dead-Letter Queue 기능을 사용해 재시도 처리를 수행할 수 있습니다.
3) 반정규화 데이터의 동기 처리
MSA가 적용된 시스템에서는 비즈니스 로직과 이를 수행하는데 필요한 데이터가 서로 다른 서비스에 나누어져 있을 수 있습니다. 처음에는 여러 서비스 간 REST 통신을 통해 데이터 참조를 수행할 수 있으나 서비스가 커지면서 복잡성·성능 등의 이슈로 서비스 간 데이터 반정규화가 발생할 수 있습니다. 이때 EDA을 적용해 서비스 간 데이터 동기 처리를 수행할 수 있습니다.
4) 최종 일관성(Eventually Consistency)
기존의 DBMS를 활용한 ACID 트랜잭션에 따른 데이터 무결성 보장은 MSA에서 서비스, DB가 나누어 가짐으로써 더는 달성할 수 없습니다. 대신 EDA을 사용해 데이터의 최종적인 일관성 유지로 변경됩니다.
( all commit or rollback → eventually consistency )
Event Driven Architecture의 요구사항 및 달성 효과는 전혀 새로운 것이 아닙니다. DBMS 내 트랜잭션 로그를 활용한 redo·undo 로직, 이기종 DB 간 분산 트랜잭션 처리, 외부 시스템과 I/F를 통해 업무를 처리할 때 상이한 시스템 간 정합성 처리 등 기존에 다루던 문제들과 요구사항 및 구현 방식이 비슷합니다.
EDA을 적용해 반정규화 데이터의 동기 처리하기
이렇게 EDA에 대해서도 알아보고 왜 EDA을 적용하는지에 대해서도 알아봤습니다.
그렇다면 저희 프로젝트는 EDA을 적용하여 MSA의 문제점들을 해결했을까요!?
네 아닙니다....
EDA을 적용했지만 MSA 적용 시 발생하는 많은 문제점들을 해결하지 못했습니다....
그래도 EDA을 적용하여 해결한 부분이 있는데 이제 해결했던 부분에 대해서 설명하겠습니다.
MSA(마이크로서비스 아키텍처)에서의 반정규화 데이터는, 각 서비스가 독립적으로 운영되면서 데이터베이스도 독립적으로 유지해야 하는 구조에서 발생하는 데이터 중복을 의미합니다.
MSA에서의 반정규화
MSA에서는 각 마이크로서비스가 독립적인 데이터베이스를 사용하여, 서비스 간의 데이터 의존성을 최소화하고 각 서비스가 자율적으로 운영 되도록 설계하는 것이 원칙입니다. 그러나 이는 서비스 간에 동일한 데이터가 필요할 때 문제를 일으킵니다.
예를 들어: 주문 서비스와 결제 서비스가 있다고 가정해 봅시다. 두 서비스 모두 사용자 정보를 필요로 합니다.
하지만 MSA에서는 각 서비스가 독립적인 데이터베이스를 가지기 때문에, 사용자 정보를 두 서비스 모두에서 사용할 수 있도록 데이터를 중복해서 저장하게 됩니다.즉, 주문 서비스 데이터베이스와 결제 서비스 데이터베이스에 사용자 정보가 각각 저장되는 경우가 발생합니다.
이처럼 동일한 데이터를 여러 서비스에 중복 저장하는 것이 MSA에서의 반정규화입니다.
MSA에서 반정규화의 필요성
MSA에서는 다음과 같은 이유로 반정규화를 사용하는 경우가 많습니다:
- 서비스 독립성 유지: 각 서비스가 독립적으로 동작해야 하므로, 필요한 데이터를 자체적으로 보유해야 합니다. 다른 서비스의 데이터에 직접 의존하지 않기 위해 데이터를 중복 저장하는 것이 필요할 수 있습니다.
- 성능 최적화: 각 서비스가 자신의 데이터만을 참조하고 빠르게 응답하기 위해, 필요한 데이터를 중복해서 저장하는 것이 성능적으로 더 유리할 수 있습니다. 데이터를 다른 서비스에서 가져오기 위해 네트워크 호출을 자주 하게 되면 성능이 저하될 수 있기 때문입니다.
- 데이터 참조를 최소화: 서비스 간의 강한 결합을 피하기 위해, 데이터를 반정규화하여 각 서비스에서 자체적으로 처리할 수 있게 설계합니다. 서비스 간에 데이터를 직접 참조하면, 하나의 서비스가 실패할 경우 다른 서비스에도 영향을 줄 수 있습니다.
반정규화의 문제점
반정규화는 데이터의 중복을 허용하므로, 다음과 같은 문제점이 발생할 수 있습니다:
- 데이터 일관성 문제: 한 서비스에서 데이터가 변경되었을 때, 그 데이터가 다른 서비스에도 즉시 또는 동기적으로 반영되어야 하는데, 이는 MSA에서 어렵습니다. 각 서비스가 독립적으로 운영되기 때문에, 데이터가 서로 일관되게 관리되지 않을 수 있습니다.
- 데이터 업데이트의 복잡성: 반정규화된 데이터는 여러 곳에 분산되어 있으므로, 하나의 서비스에서 데이터를 수정하면 그 수정 사항을 모든 서비스에 반영해야 하는 추가 작업이 필요합니다. 이를 제대로 처리하지 않으면, 데이터 불일치가 발생할 수 있습니다.
데이터 동기화와 이벤트 기반 아키텍처
MSA에서 반정규화된 데이터를 동기화하는 방법으로 이벤트 기반 아키텍처를 사용합니다.
이 방식에서는 각 서비스가 데이터 변경을 이벤트로 브로드캐스트하고, 다른 서비스가 그 이벤트를 받아 데이터를 업데이트합니다.
채팅 서비스, 커뮤니티 서비스, 시그널링 서비스에서 이벤트들이 발생했고 커뮤니티 서비스에서 발생한 이벤트를 채팅 서비스에서도 사용하고 채팅 서비스에서 발생한 이벤트를 커뮤니티 서비스에서 사용하는 등 서비스 간 동일한 데이터가 필요한 문제가 발생했습니다. 동일한 데이터가 필요해 MSA에서 반정규화를 적용할 경우 앞서 얘기 했던것처럼 문제점이 존재합니다.
그래서 문제를 해결하기위해 EDA을 적용했고 이벤트 브로커는 카프카를 사용했습니다.
여러 서비스에서 발생하는 이벤트들을 카프카에 보내고 해당 이벤트가 필요한 서비스는 카프카로부터 가져와 처리하는 방식으로 서비스 간의 데이터 동기화를 비동기적으로 수행했습니다.
정리
MSA에서의 반정규화 데이터는 각 서비스의 독립성을 유지하면서도 성능을 높이고 서비스 간 의존성을 줄이기 위해 데이터를 중복 저장하는 방식입니다. 하지만 데이터 일관성 문제나 복잡한 동기화 작업이 필요해지는 단점이 있습니다. 이러한 단점을 해결하기 위해 이벤트 기반 아키텍처를 도입하여 데이터 동기화를 실시간으로 처리하는 방식이 자주 사용됩니다.
참고
https://techblog.woowahan.com/7835/
https://medium.com/dtevangelist/event-driven-microservice-%EB%9E%80-54b4eaf7cc4a
https://www.samsungsds.com/kr/insights/msa_architecture_edm.html
https://www.msaschool.io/operation/integration/integration-three/
'프로젝트 > FitTrip' 카테고리의 다른 글
트러블 슈팅 - 채팅 서비스 scalue out 문제 (0) | 2024.06.27 |
---|---|
개발 기록 - 개발 언어 및 기반 기술 조사 (0) | 2024.06.27 |
개발 기록 - 프로젝트 공통 작업 설정 (0) | 2024.06.27 |
개발 기록 - 그라운드 룰 (0) | 2024.06.25 |
캡스톤 프로젝트 FitTrip 회고 (0) | 2024.06.25 |