diff --git a/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/07.md b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/07.md new file mode 100644 index 00000000..70aa7d8f --- /dev/null +++ b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/07.md @@ -0,0 +1,82 @@ +## Chapter 08: 재사용 패턴 + +### 개요 + +- 가급적 개발자들은 반복적인 코드를 줄이고 재사용성을 높인다 +- 모놀리식의 경우 라이브러리 혹은 모듈 임포트만으로 재사용이 쉽기 때문에 문제가 되지 않음 +- 허나 분산 아키텍처에서는 공유 코드를 작성할 때 고려해야 하는 부분이 많음 + +--- + +### 코드 복제 + +- 공유 코드 (여러 서비스에서 두루두루 쓰이는 코드) 를 단순히 서비스 레포지토리 안에 복사하는 행위 +- 정적인 코드, 일회성 코드 등 변경사항이 극히 적고, 버그의 가능성이 낮은 코드의 경우 오히려 복사 붙여넣기가 빛을 발할 때도 있다 + - 그렇지 않을 경우, 여러 군데에 단순 복사한 코드들을 다같이 관리해주어야 하기 때문에 여간 번거로운 것이 아님 + - 예를 들어, 한 곳에 복사한 코드에서 버그가 발생했다면? 나머지는? + - 가능한 한 변경될 여지가 적은 정적 유틸리티 클래스에 적합하다 + +--- + +### 공유 라이브러리 + +- 컴파일 타임에 바인딩되는 라이브러리를 통해 여러 서비스가 한 라이브러리를 바라보게 하는 기법 + - 컴파일 타임에 라이브러리 코드들이 서비스들에 공유된다 + - 공유 코드의 변경 빈도가 (코드 복사가 필요할 때처럼 변경 빈도가 아예 없는 수준은 아니지만) 꽤 낮을 경우 적합한 기법 +- 라이브러리의 트레이드오프는 라이브러리의 크기가 좌우한다 +- 단위가 큰 공유 라이브러리 한두 개만 사용할 경우, 하나의 통짜 라이브러리만 업데이트하고 관리하면 되기 때문에 각 서비스별 디펜던시 관리가 수월하다 (디펜던시 적음) + - 허나 라이브러리 안의 코드가 조금씩 변경되면, 라이브러리 전체에 영향을 미치므로, 라이브러리 업데이트와 함께 테스트 범위가 매우 커진다 (변경 관리 어려움) + - 단위가 작은 라이브러리 여러 개를 사용할 경우, 한 라이브러리에서 발생한 변경점은 다른 라이브러리나 코드에 큰 영향을 미치지 않으므로 유지보수가 수월하다 (변경 관리 쉬움) + - 단 서비스 - 라이브러리 간 의존도가 복잡해지며 (한 서비스가 하나의 라이브러리만 바라보는 것이 아니므로) 이것이 나중에는 큰 진흙덩어리 하나가 될 가능성이 있다 (디펜던시 많음) +- 서비스가 커지면 트레이드오프에서 오는 부작용도 커지므로 가급적 거대 라이브러리보단 작은 라이브러리 여러 개를 사용하여 디펜던시를 포기하고 변경 관리에서 이점을 얻는 것이 낫다 + - 정적인 성격의 기능들을 라이브러리로 분리할 경우 의외로 디펜던시에 영향도 적음 +- 라이브러리 사용 시, 무조건 버저닝을 해야 호환성에서 이점을 얻을 수 있다 + - 라이브러리의 버전을 올리거나, 구 버전을 레거시화 하여 지원하지 않게끔 하는 것도 전략이다 + - 잘 변경될 일 없는 정적인 라이브러리는 비교적 적은 범위의 버전 안에서 관리 + - 자주 변경되는 라이브러리는 여러 버전으로 관리 + - 변경점이 잦은 라이브러리를 적은 버전만으로 관리할 경우, 라이브러리 업데이트 시 영향받는 모든 서비스들을 추적하고 배포 및 테스트 해야 하므로 불필요한 공수가 늘어난다 + - 특정 범위를 벗어나는 옛 버전은 가능한 한 구식화하여 불필요한 관리를 줄여야 함 + - 덩치 큰 라이브러리 사용 지양 + +--- + +### 공유 서비스 + +- 공유 라이브러리보다 더 큰 규모의 기법 + - 공용 기능을 묶어 하나의 서비스로 두고, 다른 서비스들이 이를 바라보게 하는 기법 + - 다른 기법 (복제, 라이브러리화) 와 같은 코드 상속보다는, 여러 기능을 조합하여 큰 기능을 만드는 형태 +- 기능을 공유 서비스로 분리할 경우, 해당 기능이 변경되어도 다른 서비스까지 재배포할 일이 없어 간편하다 + - 단, 해당 기능이 '잘' 변경되었을 경우에만 한정 +- 공유 기능의 변경 빈도가 높을 경우, 그리고 다양한 프로그래밍 언어가 공존하는 환경에 적합 +- 공유 서비스로 분리한 기능에서 버그가 발생할 경우, 나머지 서비스도 무너지는 참사가 발생할 수 있음 (내고장성 이슈) + - 공유 서비스의 경우 컴파일타임 전까지는 버그 판독이 어렵다 + - 런타임에 서비스들끼리 유기적으로 엮이고 나서야 버그 여부를 알게 됨 + - 적절한 버저닝을 통해 리스크를 완화시킬 수 있음 +- 코드만 가져다 쓰는 다른 두 기법과 다르게, 공유 서비스는 결국 기능을 위해 또다른 서비스를 호출해야 하므로 성능 이슈가 생길 수밖에 없다 +- 공유 서비스를 사용하는 다른 서비스의 사이즈에 따라, 공유 서비스 크기를 적절하게 관리해야 한다 + +--- + +### 사이드카와 서비스 메시 + +- 사이드카 패턴: 육각형 아키텍처 (도메인을 중앙에, 포트와 어댑터를 통해 다른 계층과 커플링) 기반 + - 인프라 로직과 도메인 로직을 분리하는 것에 의의를 둠 + - 오토바이 옆에 붙어있는 사이드카에서 명칭을 따옴 +- 한 서비스 내에 분리가능한 파트 (도메인 관심사와 조금 떨어져있는 파트), 타 서비스와 엮이는 커플링에 해당하는 파트를 사이드카로 분리 + - 타 서비스와 사이드카 파트를 통해 기능을 연결할 수 있다 +- 사이드카끼리 맞물려 형성한 그물망과 같은 네트워크를 서비스 메시(service mesh)라고 합니다. + +``` +논의점 및 느낀점: 유독 공유 서비스 기법에 대한 단점이 길게 서술된 느낌이 든다. 반면 사이드카 기법은 구축이 복잡한 편이다~ 정도 외엔 장점 위주로 적혀 있는데, 다른 분들은 해당 두 파트를 읽으면서 비슷한 생각을 하셨을지 유독 궁금한 챕터였다. + +개인적으론 초기 구축에 들어가는 에너지 (?) 면에선 공유 서비스 분리가 훨씬 간단해 보이고, 사이드카 기법은 서비스에 대한 충분한 지식이 뒷받침되어야 한다는 추가적인 단점이 있을 듯하다 +``` + +--- + +### 코드 재사용: 어떤 경우에 가치 있는가? + +- 코드 재사용이 권장되고는 있지만, 무턱대고 재사용할 경우에도 부작용이 발생할 수 있다 + - 하나의 코드에서 너무 많은 업무를 수행하게 되면서, 복잡해질 우려가 있다 + - 코드의 일부분에 문제가 생길 경우, 그 코드를 사용하는 모든 곳이 영향을 받는다 +- 너무 자주 변경되는 코드보단 변경 빈도가 낮은 코드들이 재사용 성공률이 높다 diff --git a/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/08.md b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/08.md new file mode 100644 index 00000000..fd213340 --- /dev/null +++ b/2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon/08.md @@ -0,0 +1,124 @@ +### Chapter 09: 데이터 오너십과 분산 트랜잭션 + +### 개요 + +- 데이터 오너십 + - 한 서비스가 어떠한 데이터를 소유할 지를 정의하는 부분 + - 특정 테이블에 한 서비스만 접근가능하면 문제가 크게 없지만, 여러 서비스가 접근가능할 경우 (공동 오너십) 복잡해진다 +- 단독 오너십 + - 한 서비스만 테이블에 데이터를 쓸 수 있음 + - 서비스와 테이블 간 경계 콘텍스트와 오너십이 명확하다 +- 공통 오너십 + - 대부분의 서비스가 동일한 테이블에 데이터를 같이 쓸 수 있는 상황 + - 데이터가 공유되므로 내고장성, 확장성 등 데이터 공유 측면에서 매우 취약하다 + - 하나의 데이터 쓰기 전용 서비스를 별도로 두어 해당 서비스에 테이블에 대한 권한을 위임하는 것이 대표적인 솔루션 + - 또는 동기적으로 데이터를 쓰고 읽게끔 하는 방식도 있다 +- 공동 오너십 + - 공**동** 임에 주의 + - 공통 오너십은 '대부분의 서비스' 가 테이블에 데이터를 쓰는 경우 (almost every) + - 공동 오너십은 '대부분' 이 아닌 몇몇 서비스가 데이터를 쓰는 경우 (some) + +--- + +### 테이블 분할 기법 + +- 테이블을 각 서비스가 담당하는 데이터 파트별로 분할하는 방법 +- 이 기법을 통해 공동 오너십을 단독 오너십으로 전환할 수 있다 + - 각 테이블을 하나의 담당 서비스만이 바라보게 하는 기법이므로 +- 분할된 테이블 간의 데이터가 일관성을 갖추기 위해, 데이터 동기화 및 통신 방식을 고민해야 함 + +--- + +### 데이터 도메인 기법 + +- 공유 데이터 도메인을 추가하는 것 +- 여러 서비스가 데이터 오너십을 공유하는 게 아니고, 여러 서비스가 공유하여 사용하는 테이블들을 같은 데이터베이스 or 스키마에 넣어 컨텍스트를 넓히고 데이터 동기화가 수월하게끔 한다 +- 데이터 구조 변경이 좀 더 까다로워지는 문제가 있다 + +--- + +### 대리자 기법 + +- 한 서비스에 테이블의 독점권을 주고 대리자로 만든 다음, 다른 서비스들은 이 대리자 서비스와 통신하여 데이터를 조작하는 방식 +- 어떤 서비스를 대리자로 만들 지 고민해야 한다 + - 주 도메인 우선: 해당 데이터의 주 도메인을 가장 잘 나타내는 서비스 + - 보통 해당 메인 엔티티에 대부분의 CRUD 를 수행하는 서비스가 테이블의 주인이 됨 + - 데이터 수정이 필요할 경우, 서비스간 통신이 필요함 + - 동기 통신: 데이터 일관성이 보장되지만, 성능이 떨어짐 + - 비동기 통신: 서비스 동작속도가 빨라지나 데이터는 최종 일관성만 보장됨 + - 재고처리 등 업데이트 에러가 발생할 여지가 있음 + - 운영 특성 우선: 성능, 확장성, 가용성 등 운영 아키텍처의 특성이 더 많이 필요한 서비스 + - 데이터를 훨씬 빈번하게 건드리는 서비스가 대리자가 되므로, 데이터간 일관성 보장이 쉽다 + - 허나 도메인 책임이 뒤바뀔 위험이 있어, 가급적이면 주 도메인 우선 방법을 사용하고 캐싱 등으로 성능을 올린다 +- 서비스 커플링 (서비스간 통신) 이 필수적임 + - 성능 / 내고장성 저하 + +--- + +### 서비스 통합 기법 + +- 같은 테이블을 바라보는 여러 서비스를 아예 통합해서 단독 오너십으로 전환 +- 서비스가 커지므로 테스트 범위가 넓어지고, 배포 시 짊어져야 할 리스크가 커진다 + +--- + +### 분산 트랜잭션 + +- ACID 특성을 알아야 분산 트랜잭션을 알 수 있다 + - Atomicity (원자성) + - 한 트랜잭션에서 업데이트가 몇 번 발생하든, 하나의 덩어리로 취급되기 때문에 변경된 데이터는 한번에 커밋 or 롤백되어야 한다 + - Consistency (일관성) + - 트랜잭션 도중 데이터베이스가 일관되지 않은 상태 (무결성 제약조건 위배) 가 되지 않아야 한다 + - 트랜잭션 도중에는 외래 키 등 일관성 제약조건을 위배할 수 없다 + - Isolation (격리성) + - 개별 트랜잭션은 서로 무관해야 하며, 서로가 서로를 볼 수 없게끔 격리되어야 한다 + - Durability (내구성) + - 트랜잭션을 커밋하고 성공 응답이 반환되면, 이후 어떠한 장애가 발생해도 해당 데이터는 모두 영구 보존되어야 한다 +- 허나 분산 트랜잭션은 ACID 속성을 지원하지 않는다 (??) + - 분산 트랜잭션에서는 대신 BASE 를 지원한다 + - Basic Availability (기본 가용성) + - 모든 서비스 또는 시스템이 분산 트랜잭션에 참여할 수 있으리라 기대하는 것 + - Soft state (소프트 상태) + - 분산 트랜잭션이 진행중이고, 원자적 비즈니스 요청이 미완료된 상태 + - Eventual Consistency (최종 일관성) + - 언젠가는 분산 트랜잭션의 모든 부분이 잘 완료될 것이고 모든 데이터가 잘 동기화될 것이라는 믿음 + - 이 '언젠가' 소요 시간은 최종 일관성 패턴 유형과, 오류 처리 방법에 따라 달라진다 + +--- + +### 최종 일관성 패턴 + +- 백그라운드 동기화 패턴 + - 별도의 외부 서비스나 프로세스가 주기적으로 데이터를 체크해서 동기화하는 방법 + - 최종 일관성이 지켜지는 시간은 프로세스를 감시하는 주체를 언제에 한번씩 구동시킬지에 따라 다르지만, 대개 가장 오래 걸린다 + - 즉시 동기화할 필요 없는 데이터에 대해 사용 (예시: 고객 회원 탈퇴 후 해당 고객 정보 삭제) + - 이 외부 서비스가 결국 테이블의 소유권 (읽기, 쓰기 권한) 을 갖고 있어야 동기화가 가능하기 때문에, 데이터와 서비스 간 경계 콘텍스트가 무너진다 + - 분산 아키텍처 내부에서 엄격한 경계 콘텍스트를 만들어야 함 + - 각 서비스가 소유한 테이블의 구조가 변경될 경우, 해당 외부 서비스와도 조정이 필요하므로 테이블을 건드리는 것 또한 쉽지 않다 + - 비즈니스 로직 중복의 위험도 있음 + +- 오케스트레이티드 요청 기반 패턴 + - 비즈니스 요청 처리 도중에 데이터 소스를 동기화하는 기법 + - 동기화 주체인 오케스트레이터 (조정자) 서비스가 분산 트랜잭션을 관리한다 + - 기존 서비스를 오케스트레이터로 지정할 경우, 해당 서비스는 자기 본업도 해야 하고, 오케스트레이터 일도 해야 한다 + - 과부하가 걸리거나, 서비스 간 커플링이 고착화될 위험이 있음 + - 각 서비스 상위에 관리자 서비스가 존재해서, 이 서비스가 데이터가 동기화되게끔 조정한다고 생각하면 됨 + - 응답성보다 데이터 일관성 추구 + - 시간이 오래 걸리더라도 데이터 일관성이 더 중요 + - 에러 처리가 복잡해짐 + - 특정 하위 서비스에서 오류 발생시, 오케스트레이터는 어떻게 대처해야 할 지 복잡해짐 + +- 이벤트 기반 패턴 + - 인기가 제일 많고 믿음직스러움 (??) + - 각 서비스들이 특정 액션 (고객 탈퇴 등) 을 이벤트 형태로 발행하여 게시하면, 이 이벤트를 구독하는 다른 서비스들이 이벤트에 따른 응답을 제공하는 방식 + - 고객이 탈퇴했을 경우, 고객이 탈퇴했다는 이벤트를 이벤트 스트림 (or 메시지 토픽) 에 발행하여 알림 + - 과금 서비스와 지원 서비스는 해당 이벤트를 수신하고, 그에 따른 뒤처리 수행 + - 응답성이 우수하고, 서비스 간 커플링이 강하지 않으며, 적시에 일관성을 맞출 수 있다는 장점이 있음 + - 역시나 에러 처리가 제일 까다로움 + - 이벤트 처리 실패할 경우 + - 이벤트 처리 중에 서비스가 죽을 경우 + - 이벤트를 여러 번 전달하고, 그럼에도 실패할 경우 해당 이벤트를 데드 레터 큐에 밀어넣고, 관리자가 직접 대응할 수 있게끔 별도 처리 + +``` +느낀점: 트랜잭션에 대한 배경지식이 충분하지 않을 경우 난감하다. 예전에 잠깐 겉핥기로 봤던 얕은 지식을 토대로 읽었다.. +```