diff --git a/2026/Fundamentals_of_Software_Architecture_2nd_Edition/geunju-lee/Chapter_1_to_3.md b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/geunju-lee/Chapter_1_to_3.md new file mode 100644 index 00000000..a6defb46 --- /dev/null +++ b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/geunju-lee/Chapter_1_to_3.md @@ -0,0 +1,235 @@ +# 트레이드오프 위에서의 아키텍처 선택 + +## 1 ~ 3장 +--- + +## chapter 1 - '베스트 프랙티스'가 없다면? + +> 정답은 없다. 다만 트레이드오프만 있을 뿐이다. + +우리는 늘 정답을 찾는다. 나 또한 그렇다. +하지만 인생이 그렇듯, 소프트웨어 설계에도 절대적인 정답은 없다. +존재하는 것은 언제나 **장점과 단점**, 그리고 그 사이에서의 **선택**뿐이다. + +아키텍처 설계 역시 마찬가지다. +"어떤 구조가 가장 좋은가?"가 아니라, +**"지금 이 시점의 제약과 상황에서 무엇이 가장 합리적인 선택인가?"** 를 고민하는 문제다. + +그래서 이 책은 정답을 주기보다는, +그 선택을 하기 위해 필요한 **사고의 도구들**을 설명한다. + +### 이 책에서 반복적으로 등장하는 키워드들 + +- coupling / cohesion +- component 경계 +- sync / async +- orchestration / choreography +- atomicity +- contract + +이 단어들은 각각 따로 보면 어려울 수 있지만, +결국은 **"무엇을 함께 묶고, 무엇을 분리할 것인가"** 라는 질문으로 수렴한다. + +책에서 다루는 *한빛가이버* 사례를 읽으면서, +나는 지난 5년간 내가 겪어온 MSA 전환 과정과 굉장히 유사하다는 느낌을 받았다. +정답을 찾아 적용하기보다는, +트래픽·조직·장애·운영이라는 현실 속에서 +계속해서 **트레이드오프를 선택해 온 과정**에 더 가까웠기 때문이다. + +이 챕터는 +"베스트 프랙티스를 알려주는 장"이 아니라, +**이후 챕터들을 어떤 관점으로 읽어야 하는지를 정리해 주는 장**이라고 느꼈다. + +## chapter 2 - 아키텍처 퀀텀 + +> MSA는 '유행'이 아니라, 현재 시스템의 제약과 이해를 바탕으로 선택해야 하는 결과다. + +나의 경험을 예로 들면, 처음에는 다들 그렇듯이 레이어드 모놀리식으로 시작했다. +그 이후 MSA를 목표로 점진적인 전환을 시도하고 있다. + +아직은 DB를 완전히 분리하지 못해, 엄밀한 의미의 MSA를 적용했다고 보기는 어렵다. +다만 향후 DB 분리와 서비스 분리를 가능하게 만들기 위해, 미리 헥사고날 아키텍처와 이벤트 기반 아키텍처를 적용하는 방향으로 설계를 진행 중이다. +(이벤트 기반 아키텍처의 경우, 늘어나는 트래픽 대응이 주된 목적이다.) + +또한 비동기 통신을 위한 보상 처리로 Saga 패턴도 함께 적용하고 있다. + +이처럼 각자의 상황과 제약에 맞게 아키텍처를 선택하는 것이 중요하다고 생각한다. +MSA가 무조건 좋은 것도 아니고, 모놀리식이 무조건 나쁜 것도 아니다. + +## chapter 3 - 아키텍처 모듈성 + +> 아키텍처 별 성격 비교 + +유지 보수성, 시험성, 확장성, 가용성/내고장성 관점에서 보면 +모놀리식 < 서비스 기반 < MSA 순으로 점수가 매겨지는 흐름이 나온다. + +### 정리 + +아키텍처 모듈성을 +유지 보수성, 시험성, 확장성, 가용성/내고장성 관점에서 비교하면 +일반적으로 **모놀리식 < 서비스 기반 < MSA** 순으로 평가가 높아진다. + +이 결과는 "아키텍처의 우열"이라기보다는, +**결합을 얼마나 잘 분리했는가**에 대한 평가에 가깝다고 느꼈다. + +- 모놀리식은 변경·장애·확장의 범위가 하나로 묶여 있어 + 규모가 커질수록 리스크가 증폭되기 쉽다. +- 서비스 기반 아키텍처는 일정 수준의 분리를 제공하지만, + 공통 로직과 동기 호출 체인이 늘어나면 다시 결합도가 높아진다. +- MSA는 서비스별 DB 분리와 비동기 통신을 전제로 할 때, + 변경 영향 최소화, 독립 확장, 장애 격리 측면에서 가장 큰 이점을 가진다. + +다만 이 점수표는 +**MSA를 '제대로 운영할 수 있다는 전제'가 붙은 결과**라는 점이 중요하다. +운영 성숙도, 관측성, 재처리, 계약 관리가 뒷받침되지 않으면 +높은 점수는 오히려 높은 복잡도로 돌아올 수 있다. + +결국 이 비교는 +"어떤 아키텍처가 더 낫다"를 말하기보다는, +**어떤 트레이드오프를 선택할 준비가 되어 있는가**를 묻는 기준이라고 생각한다. + +### 논의사항 + +1. **서비스 기반 아키텍처는 ‘과도기’인가, ‘최종 형태’인가?** + 서비스 기반은 MSA로 가기 위한 단계인가? + 아니면 **조직·트래픽·운영 역량이 맞으면 충분히 ‘최종 형태’**가 될 수 있는가? + +2. **MSA의 장점을 얻기 위해 반드시 필요한 전제는 무엇인가?** + DB 분리 없이도 MSA를 했다고 말할 수 있는가? + + +> 아래 내용은 정리나 결론을 위한 글은 아니다. +> 혹시 비슷한 규모의 시스템을 운영하면서 +> 다른 팀은 어떻게 하고 있는지 궁금한 사람이 있다면, +> 참고가 될 수 있을 것 같아 남겨둔 개인적인 경험 기록이다. + +### 경험 사례 – 내가 겪은 아키텍처 선택의 흐름 + +#### 여러 아키텍처를 거치며 느낀 점 + +**레이어드 모놀리스**는 전통적이고 빠르게 시작하기에 좋았다. +하지만 규모가 커질수록 도메인 경계가 흐려지고, +작은 변경이 예상보다 넓은 영역에 영향을 주는 경험을 자주 하게 됐다. + +**모듈러 모놀리스**는 도메인 단위로 모듈을 나누면서 +변경 영향 범위를 줄이는 데는 분명 도움이 됐다. +다만 시간이 지나면서 +"이건 어느 모듈 책임이지?" 같은 애매한 지점이 생기기 시작했고, +그 순간부터 경계를 지키는 일이 생각보다 어렵다는 걸 체감했다. + +**마이크로커널 아키텍처**는 +확장 축(기능/전략)이 반복적으로 추가될 때는 꽤 매력적이다. +예를 들어 PG 연동처럼 +결제/취소/중단(Abort) 흐름이 유사한 기능이 계속 늘어나는 경우에는 +플러그인이나 전략 패턴으로 분리할 가치가 있다. +다만 실제로 운영해보니, +생각보다 이렇게 반복적으로 확장되는 영역은 많지 않았고 +그 외의 영역에서는 과설계가 되기 쉽다고 느꼈다. + +--- + +#### 결국 선택한 방향: 서비스 기반 + 헥사고날 + +서비스 기반 아키텍처는 +팀을 나누고 서비스를 분리하는 데 분명한 장점이 있다. +하지만 실제로 운영해보면 +공통 로직이 생기는 순간부터 선택이 어려워진다. + +- 그냥 복붙할지 +- 공용 라이브러리로 묶을지 +- 아니면 공통 서비스를 하나 더 만들지 + +이 선택을 매번 고민하게 된다. + +특히 서비스 간 동기 호출이 늘어날수록, +장애가 전파되고 배포가 서로 엮이는 경험을 여러 번 했다. +이 지점이 서비스 기반 아키텍처에서 +가장 크게 체감한 한계였다. + +그럼에도 불구하고, +현재의 조직 규모와 트래픽을 고려하면 +서비스 단위로 나누는 선택 자체는 피하기 어렵다고 판단했다. +그래서 내 기준에서의 질문은 +**"나눌 것인가?"가 아니라 "어디까지 나눌 것인가?"** 였다. + +그 결과 나는 +서비스 기반 구조를 유지하되, +헥사고날 아키텍처를 적용해 +서비스 내부의 도메인 경계를 최대한 명확히 하려고 했다. +그리고 공통 로직은 정말 불가피한 경우에만 +내부 모듈이나 라이브러리로 제한하고, +도메인 로직이 공통으로 새어나가지 않도록 의식적으로 막고 있다. + +--- + +### 그리고 EDA + +EDA를 도입한 지점은 **결제 이후**다. +결제 자체는 짧고 확실하게 끝내고, +결제 이후에 따라오는 후속 작업들을 +동기 호출로 묶지 않기 위해 Kafka 기반 이벤트로 분리했다. + +#### 이벤트 발행 (결제 서비스) + +결제가 완료되면 Kafka로 이벤트를 발행한다. +이 이벤트에는 대략 다음 정보가 담긴다. + +- paymentSeq (결제 식별자) +- amount (금액) +- item 정보 (상품 / 구성 / 수량 등) + +중요한 점은 +"무엇을 해라"가 아니라 +**"결제가 완료되었다"라는 사실을 발행**한다는 것이다. + +#### 팀1 컨슘: Timeline 적재 + +팀1은 이 이벤트를 컨슘해서 +결제 건을 Timeline에 적재한다. +이 작업은 결제의 성공/실패와 운명을 같이할 필요가 없고, +지연되더라도 나중에 따라잡을 수 있기 때문에 +비동기 방식이 잘 맞았다. + +#### 팀2 컨슘: 적립 계산 및 반영 + +팀2는 동일 이벤트를 컨슘한 뒤, +자기 정책에 따라 적립 금액을 계산하고 적립을 반영한다. + +적립은 정책·이벤트·프로모션에 따라 변수가 많고, +결제 서비스가 직접 알고 있어야 할 이유가 없다고 판단했다. +오히려 결제 플로우에 섞이면 +변경 영향 범위만 커진다. +그래서 계산과 반영의 책임을 아예 분리했다. + +--- + +#### EDA를 통해 얻은 효과 + +- 결제 서비스는 결제 자체에만 집중할 수 있었다 +- 동기 호출 체인을 끊으면서 더 빠른 결제 및 더 많은 처리량을 달성할 수 있었다 +- 후속 작업은 컨슈머 확장으로 처리량을 맞추고, + 피크 트래픽은 큐가 흡수하도록 만들 수 있었다 + +#### 미래 수정할 점 + +현재는 **결제 완료 이벤트**를 타 팀에서 직접 컨슘해 +내역 적재나 포인트 작업까지 처리하고 있다. + +하지만 앞으로는 다음 형태로 바꿀 예정이다. + +- 결제 서비스는 **결제 완료(Fact)** 이벤트만 발행한다. +- 해당 토픽을 컨슘하는 서비스가 + - **내역 발행 Command** + - **포인트 적립 Command** + 를 각각 발행한다. + +이렇게 하면 내역은 쌓였는데 포인트 적립이 누락된 경우에도 +Admin을 통해 **포인트 적립 Command만 재발행**하는 형태로 보상이 가능해진다. + +#### 운영하면서 중요했던 포인트 + +- 이벤트 중복/재처리는 전제로 두고 멱등하게 설계한다 +- 재처리를 항상 가능하게 만든다 +- 추적을 위해 requestUuid 같은 식별자를 이벤트에 포함한다 +- 사내에 FDC(financial Data Center)가 존재하고, 매일 bulk로 사내 전체 DB를 조회하여 적재한다. 그리고 매일 배치를 돌려서 누락된 적립이 있는지 확인하고, 존재한다면 노티 이메일을 보내준다. 따라서 + 재처리가 가능하다. 이것이 운영에서 가장 중요한 포인트였다. diff --git a/2026/Fundamentals_of_Software_Architecture_2nd_Edition/tttghost/chapter 1.md b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/tttghost/chapter 1.md new file mode 100644 index 00000000..dd3b8f29 --- /dev/null +++ b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/tttghost/chapter 1.md @@ -0,0 +1,65 @@ +# 1장 '베스트 프랙티스'가 없다면? + +## 주요 내용 +하드파트: 어렵다와 단단하다의 뜻 +아키텍처 설계는 한번 정해놓으면 쉽사리 바꿀 수 없게 되기에 그만큼 근본적이다. +마이크로서비스: 나는 해당하는 부분이 없지만 서버팀에서 서버 설계 시 각 파트별로 나눠서 작업했던 걸 본 기억이 있다 + +오케스트레이션 +여러 서비스 호출의 순서, 성공/실패 판단, 보상 동작까지 하나의 흐름으로 책임지는 중앙 제어 로직 + +데이터는 모든 것: 데이터 설계가 가장 중요하다. +운영데이터와 분석데이터가 있다. +운영데이터는 회사 시스템이 돌아가는 데 필요한 데이터다. 정합성, 트랜잭션이 중요 +분석데이터는 서비스를 이해, 개선, 판단을 위한 데이터로 트랜잭션과 무관하며 과거 누적 축적형 데이터로 현재 운영에 필요하진 않지만 장기적인 전략 수립 및 의사결정에 중요하게 활용되는 데이터다. + +아키텍처 거버넌스 +시스템이 커져도 방향을 잃지 않게 하는 '기술 헌법'이다. +잘못 이해한 경우 = 통제 지옥 +잘 이해한 경우 = 자유를 유지하기 위한 최소 규칙 + +아키텍처 피트니스 함수 +관리해주는 함수. 피트니스 클럽은 몸을 관리해주지만 피트니스 함수는 아키텍처를 관리해준다. +필연적으로 트레이드오프가 발생할 수밖에 없다. 더 빠르게-더 정확하게, 더 강하게-더 가볍게 등 +목표가 하나라면 상관없지만 둘 이상이면 바로 트레이드오프가 발생한다. + +피트니스 함수와 단위테스트는 비슷하지만 다르다. +비슷한 부분은 자동 검증 + 실패 시 즉시 차단이라는 것인데 +피트니스 함수는 아키텍처의 특성을 검증하는 것이고 +단위테스트는 로직의 정확성을 보는 것이다. 즉 도메인 로직을 본다. + +코드 위생 도구인 소나큐브는 피트니스 함수의 재료를 턴키 방식으로 제공한다. +자바 진영의 아크유닛이라는 도구는 소나큐브와 다르게 경고 수준이 아닌 +"이 구조는 금지"라는 규칙을 테스트로 정의해 차단할 수 있다. +닷넷 진영에도 넷아크테스트(NetArchTest)라는 도구가 있다. + +피트니스 함수는 대부분 반복/자동화해 사용하지만 간혹 수동으로 실행해야 하는 경우도 있다. +민감한 법률 정보가 담긴 시스템은 합법성을 준수해야 하기에 변호사가 직접 검토해야 하므로 자동화할 수가 없다. + +이런 아키텍처 피트니스 함수가 중요한 이유는 엔터프라이즈 수준의 거버넌스 사례를 볼 때 지속적으로 체크해야 하는 영역이기 때문이다. +제로데이 익스플로잇이 발견될 수도 있지만, 지속적인 피트니스 함수로 구조, 의존성, 권한 경계 등을 체크한다면 그 확산과 피해를 최소화할 수 있다. + +피트니스 함수는 아키텍처 구조를 설계하는 사람에게 단비 같은 존재이다. +모든 코드를 직접 많이 볼 수는 없지만(코드의 디테일이 아니라 구조의 방향성을 보기 위함), 피트니스 함수를 통해 프로젝트의 구조와 상태를 검증할 수 있기 때문에 해당 프로젝트의 전반적인 방향성을 잡을 수 있다. + +하지만 피트니스 함수의 과용은 금물이다. +너무 복잡한 피트니스 함수는 오히려 설계 의도를 흐리고, 개발자에게 '무엇을 지켜야 하는지'를 모호하게 만든다. + +아키텍처와 설계는 구분되어야 한다. +아키텍처 결정 - 구조 설계 + +아키텍처의 기본 원칙을 이해할 때는 How보다 Why가 더 중요하다. +아키텍처 개념에 집중하면 구현부에 대한 상세한 이야기는 과감히 건너뛸 수 있다. +구현 과정을 일일이 따라가기 시작하면 논의 범위가 지나치게 방대해지기 때문이다. + +아키텍처의 개념들은 다음과 같이 볼 수 있다. +서비스, 커플링, 컴포넌트, 동기통신, 비동기통신, 오케스트레이션, 코레오그래피(오케스트레이션과 반대), 원자성, 컨트랙트(계약) 등이 있다. + +사가(Saga)는 영웅적인 업적을 기리는 긴 이야기라는 의미에서 유래했다. +결제 서비스와 배송 서비스가 있을 때, 결제 서비스는 성공했지만 배송 서비스가 실패한 경우, 오케스트레이션 사가를 통해 결제 취소라는 보상 트랜잭션을 실행하여 사가를 실패 상태로 종료한다. + +## 논의 주제: 아키텍처 검증의 자동화와 수동 검토 +피트니스 함수에서 자동화한 부분(소나큐브, 아크유닛 등)과 수동으로 검토해야 하는 부분이 +있다고 나오는데 실무에서 이 두 부분에 대한 경험이 있으신가요? + +어떤 것을 자동화하였고 어떤 것을 왜 사람이 직접 해야 했는지 궁금합니다. \ No newline at end of file