Skip to content

Commit bd16efb

Browse files
authored
Merge pull request #614 from ThinkAboutSoftware/chichoon/2026-03
소프트웨어 아키텍처 The Hard Parts 4번째 - 최지윤
2 parents 0a47869 + 356d721 commit bd16efb

2 files changed

Lines changed: 206 additions & 0 deletions

File tree

  • 2026/Fundamentals_of_Software_Architecture_2nd_Edition/chichoon
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
## Chapter 08: 재사용 패턴
2+
3+
### 개요
4+
5+
- 가급적 개발자들은 반복적인 코드를 줄이고 재사용성을 높인다
6+
- 모놀리식의 경우 라이브러리 혹은 모듈 임포트만으로 재사용이 쉽기 때문에 문제가 되지 않음
7+
- 허나 분산 아키텍처에서는 공유 코드를 작성할 때 고려해야 하는 부분이 많음
8+
9+
---
10+
11+
### 코드 복제
12+
13+
- 공유 코드 (여러 서비스에서 두루두루 쓰이는 코드) 를 단순히 서비스 레포지토리 안에 복사하는 행위
14+
- 정적인 코드, 일회성 코드 등 변경사항이 극히 적고, 버그의 가능성이 낮은 코드의 경우 오히려 복사 붙여넣기가 빛을 발할 때도 있다
15+
- 그렇지 않을 경우, 여러 군데에 단순 복사한 코드들을 다같이 관리해주어야 하기 때문에 여간 번거로운 것이 아님
16+
- 예를 들어, 한 곳에 복사한 코드에서 버그가 발생했다면? 나머지는?
17+
- 가능한 한 변경될 여지가 적은 정적 유틸리티 클래스에 적합하다
18+
19+
---
20+
21+
### 공유 라이브러리
22+
23+
- 컴파일 타임에 바인딩되는 라이브러리를 통해 여러 서비스가 한 라이브러리를 바라보게 하는 기법
24+
- 컴파일 타임에 라이브러리 코드들이 서비스들에 공유된다
25+
- 공유 코드의 변경 빈도가 (코드 복사가 필요할 때처럼 변경 빈도가 아예 없는 수준은 아니지만) 꽤 낮을 경우 적합한 기법
26+
- 라이브러리의 트레이드오프는 라이브러리의 크기가 좌우한다
27+
- 단위가 큰 공유 라이브러리 한두 개만 사용할 경우, 하나의 통짜 라이브러리만 업데이트하고 관리하면 되기 때문에 각 서비스별 디펜던시 관리가 수월하다 (디펜던시 적음)
28+
- 허나 라이브러리 안의 코드가 조금씩 변경되면, 라이브러리 전체에 영향을 미치므로, 라이브러리 업데이트와 함께 테스트 범위가 매우 커진다 (변경 관리 어려움)
29+
- 단위가 작은 라이브러리 여러 개를 사용할 경우, 한 라이브러리에서 발생한 변경점은 다른 라이브러리나 코드에 큰 영향을 미치지 않으므로 유지보수가 수월하다 (변경 관리 쉬움)
30+
- 단 서비스 - 라이브러리 간 의존도가 복잡해지며 (한 서비스가 하나의 라이브러리만 바라보는 것이 아니므로) 이것이 나중에는 큰 진흙덩어리 하나가 될 가능성이 있다 (디펜던시 많음)
31+
- 서비스가 커지면 트레이드오프에서 오는 부작용도 커지므로 가급적 거대 라이브러리보단 작은 라이브러리 여러 개를 사용하여 디펜던시를 포기하고 변경 관리에서 이점을 얻는 것이 낫다
32+
- 정적인 성격의 기능들을 라이브러리로 분리할 경우 의외로 디펜던시에 영향도 적음
33+
- 라이브러리 사용 시, 무조건 버저닝을 해야 호환성에서 이점을 얻을 수 있다
34+
- 라이브러리의 버전을 올리거나, 구 버전을 레거시화 하여 지원하지 않게끔 하는 것도 전략이다
35+
- 잘 변경될 일 없는 정적인 라이브러리는 비교적 적은 범위의 버전 안에서 관리
36+
- 자주 변경되는 라이브러리는 여러 버전으로 관리
37+
- 변경점이 잦은 라이브러리를 적은 버전만으로 관리할 경우, 라이브러리 업데이트 시 영향받는 모든 서비스들을 추적하고 배포 및 테스트 해야 하므로 불필요한 공수가 늘어난다
38+
- 특정 범위를 벗어나는 옛 버전은 가능한 한 구식화하여 불필요한 관리를 줄여야 함
39+
- 덩치 큰 라이브러리 사용 지양
40+
41+
---
42+
43+
### 공유 서비스
44+
45+
- 공유 라이브러리보다 더 큰 규모의 기법
46+
- 공용 기능을 묶어 하나의 서비스로 두고, 다른 서비스들이 이를 바라보게 하는 기법
47+
- 다른 기법 (복제, 라이브러리화) 와 같은 코드 상속보다는, 여러 기능을 조합하여 큰 기능을 만드는 형태
48+
- 기능을 공유 서비스로 분리할 경우, 해당 기능이 변경되어도 다른 서비스까지 재배포할 일이 없어 간편하다
49+
- 단, 해당 기능이 '잘' 변경되었을 경우에만 한정
50+
- 공유 기능의 변경 빈도가 높을 경우, 그리고 다양한 프로그래밍 언어가 공존하는 환경에 적합
51+
- 공유 서비스로 분리한 기능에서 버그가 발생할 경우, 나머지 서비스도 무너지는 참사가 발생할 수 있음 (내고장성 이슈)
52+
- 공유 서비스의 경우 컴파일타임 전까지는 버그 판독이 어렵다
53+
- 런타임에 서비스들끼리 유기적으로 엮이고 나서야 버그 여부를 알게 됨
54+
- 적절한 버저닝을 통해 리스크를 완화시킬 수 있음
55+
- 코드만 가져다 쓰는 다른 두 기법과 다르게, 공유 서비스는 결국 기능을 위해 또다른 서비스를 호출해야 하므로 성능 이슈가 생길 수밖에 없다
56+
- 공유 서비스를 사용하는 다른 서비스의 사이즈에 따라, 공유 서비스 크기를 적절하게 관리해야 한다
57+
58+
---
59+
60+
### 사이드카와 서비스 메시
61+
62+
- 사이드카 패턴: 육각형 아키텍처 (도메인을 중앙에, 포트와 어댑터를 통해 다른 계층과 커플링) 기반
63+
- 인프라 로직과 도메인 로직을 분리하는 것에 의의를 둠
64+
- 오토바이 옆에 붙어있는 사이드카에서 명칭을 따옴
65+
- 한 서비스 내에 분리가능한 파트 (도메인 관심사와 조금 떨어져있는 파트), 타 서비스와 엮이는 커플링에 해당하는 파트를 사이드카로 분리
66+
- 타 서비스와 사이드카 파트를 통해 기능을 연결할 수 있다
67+
- 사이드카끼리 맞물려 형성한 그물망과 같은 네트워크를 서비스 메시(service mesh)라고 합니다.
68+
69+
> **논의점 및 느낀점**
70+
>
71+
> 유독 공유 서비스 기법에 대한 단점이 길게 서술된 느낌이 든다. 반면 사이드카 기법은 구축이 복잡한 편이다~ 정도 외엔 장점 위주로 적혀 있는데, 다른 분들은 해당 두 파트를 읽으면서 비슷한 생각을 하셨을지 유독 궁금한 챕터였다.
72+
>
73+
> 개인적으론 초기 구축에 들어가는 에너지 (?) 면에선 공유 서비스 분리가 훨씬 간단해 보이고, 사이드카 기법은 서비스에 대한 충분한 지식이 뒷받침되어야 한다는 추가적인 단점이 있을 듯하다
74+
75+
---
76+
77+
### 코드 재사용: 어떤 경우에 가치 있는가?
78+
79+
- 코드 재사용이 권장되고는 있지만, 무턱대고 재사용할 경우에도 부작용이 발생할 수 있다
80+
- 하나의 코드에서 너무 많은 업무를 수행하게 되면서, 복잡해질 우려가 있다
81+
- 코드의 일부분에 문제가 생길 경우, 그 코드를 사용하는 모든 곳이 영향을 받는다
82+
- 너무 자주 변경되는 코드보단 변경 빈도가 낮은 코드들이 재사용 성공률이 높다
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
### Chapter 09: 데이터 오너십과 분산 트랜잭션
2+
3+
### 개요
4+
5+
- 데이터 오너십
6+
- 한 서비스가 어떠한 데이터를 소유할 지를 정의하는 부분
7+
- 특정 테이블에 한 서비스만 접근가능하면 문제가 크게 없지만, 여러 서비스가 접근가능할 경우 (공동 오너십) 복잡해진다
8+
- 단독 오너십
9+
- 한 서비스만 테이블에 데이터를 쓸 수 있음
10+
- 서비스와 테이블 간 경계 콘텍스트와 오너십이 명확하다
11+
- 공통 오너십
12+
- 대부분의 서비스가 동일한 테이블에 데이터를 같이 쓸 수 있는 상황
13+
- 데이터가 공유되므로 내고장성, 확장성 등 데이터 공유 측면에서 매우 취약하다
14+
- 하나의 데이터 쓰기 전용 서비스를 별도로 두어 해당 서비스에 테이블에 대한 권한을 위임하는 것이 대표적인 솔루션
15+
- 또는 동기적으로 데이터를 쓰고 읽게끔 하는 방식도 있다
16+
- 공동 오너십
17+
-**** 임에 주의
18+
- 공통 오너십은 '대부분의 서비스' 가 테이블에 데이터를 쓰는 경우 (almost every)
19+
- 공동 오너십은 '대부분' 이 아닌 몇몇 서비스가 데이터를 쓰는 경우 (some)
20+
21+
---
22+
23+
### 테이블 분할 기법
24+
25+
- 테이블을 각 서비스가 담당하는 데이터 파트별로 분할하는 방법
26+
- 이 기법을 통해 공동 오너십을 단독 오너십으로 전환할 수 있다
27+
- 각 테이블을 하나의 담당 서비스만이 바라보게 하는 기법이므로
28+
- 분할된 테이블 간의 데이터가 일관성을 갖추기 위해, 데이터 동기화 및 통신 방식을 고민해야 함
29+
30+
---
31+
32+
### 데이터 도메인 기법
33+
34+
- 공유 데이터 도메인을 추가하는 것
35+
- 여러 서비스가 데이터 오너십을 공유하는 게 아니고, 여러 서비스가 공유하여 사용하는 테이블들을 같은 데이터베이스 or 스키마에 넣어 컨텍스트를 넓히고 데이터 동기화가 수월하게끔 한다
36+
- 데이터 구조 변경이 좀 더 까다로워지는 문제가 있다
37+
38+
---
39+
40+
### 대리자 기법
41+
42+
- 한 서비스에 테이블의 독점권을 주고 대리자로 만든 다음, 다른 서비스들은 이 대리자 서비스와 통신하여 데이터를 조작하는 방식
43+
- 어떤 서비스를 대리자로 만들 지 고민해야 한다
44+
- 주 도메인 우선: 해당 데이터의 주 도메인을 가장 잘 나타내는 서비스
45+
- 보통 해당 메인 엔티티에 대부분의 CRUD 를 수행하는 서비스가 테이블의 주인이 됨
46+
- 데이터 수정이 필요할 경우, 서비스간 통신이 필요함
47+
- 동기 통신: 데이터 일관성이 보장되지만, 성능이 떨어짐
48+
- 비동기 통신: 서비스 동작속도가 빨라지나 데이터는 최종 일관성만 보장됨
49+
- 재고처리 등 업데이트 에러가 발생할 여지가 있음
50+
- 운영 특성 우선: 성능, 확장성, 가용성 등 운영 아키텍처의 특성이 더 많이 필요한 서비스
51+
- 데이터를 훨씬 빈번하게 건드리는 서비스가 대리자가 되므로, 데이터간 일관성 보장이 쉽다
52+
- 허나 도메인 책임이 뒤바뀔 위험이 있어, 가급적이면 주 도메인 우선 방법을 사용하고 캐싱 등으로 성능을 올린다
53+
- 서비스 커플링 (서비스간 통신) 이 필수적임
54+
- 성능 / 내고장성 저하
55+
56+
---
57+
58+
### 서비스 통합 기법
59+
60+
- 같은 테이블을 바라보는 여러 서비스를 아예 통합해서 단독 오너십으로 전환
61+
- 서비스가 커지므로 테스트 범위가 넓어지고, 배포 시 짊어져야 할 리스크가 커진다
62+
63+
---
64+
65+
### 분산 트랜잭션
66+
67+
- ACID 특성을 알아야 분산 트랜잭션을 알 수 있다
68+
- Atomicity (원자성)
69+
- 한 트랜잭션에서 업데이트가 몇 번 발생하든, 하나의 덩어리로 취급되기 때문에 변경된 데이터는 한번에 커밋 or 롤백되어야 한다
70+
- Consistency (일관성)
71+
- 트랜잭션 도중 데이터베이스가 일관되지 않은 상태 (무결성 제약조건 위배) 가 되지 않아야 한다
72+
- 트랜잭션 도중에는 외래 키 등 일관성 제약조건을 위배할 수 없다
73+
- Isolation (격리성)
74+
- 개별 트랜잭션은 서로 무관해야 하며, 서로가 서로를 볼 수 없게끔 격리되어야 한다
75+
- Durability (내구성)
76+
- 트랜잭션을 커밋하고 성공 응답이 반환되면, 이후 어떠한 장애가 발생해도 해당 데이터는 모두 영구 보존되어야 한다
77+
- 허나 분산 트랜잭션은 ACID 속성을 지원하지 않는다 (??)
78+
- 분산 트랜잭션에서는 대신 BASE 를 지원한다
79+
- Basic Availability (기본 가용성)
80+
- 모든 서비스 또는 시스템이 분산 트랜잭션에 참여할 수 있으리라 기대하는 것
81+
- Soft state (소프트 상태)
82+
- 분산 트랜잭션이 진행중이고, 원자적 비즈니스 요청이 미완료된 상태
83+
- Eventual Consistency (최종 일관성)
84+
- 언젠가는 분산 트랜잭션의 모든 부분이 잘 완료될 것이고 모든 데이터가 잘 동기화될 것이라는 믿음
85+
- 이 '언젠가' 소요 시간은 최종 일관성 패턴 유형과, 오류 처리 방법에 따라 달라진다
86+
87+
---
88+
89+
### 최종 일관성 패턴
90+
91+
- 백그라운드 동기화 패턴
92+
- 별도의 외부 서비스나 프로세스가 주기적으로 데이터를 체크해서 동기화하는 방법
93+
- 최종 일관성이 지켜지는 시간은 프로세스를 감시하는 주체를 언제에 한번씩 구동시킬지에 따라 다르지만, 대개 가장 오래 걸린다
94+
- 즉시 동기화할 필요 없는 데이터에 대해 사용 (예시: 고객 회원 탈퇴 후 해당 고객 정보 삭제)
95+
- 이 외부 서비스가 결국 테이블의 소유권 (읽기, 쓰기 권한) 을 갖고 있어야 동기화가 가능하기 때문에, 데이터와 서비스 간 경계 콘텍스트가 무너진다
96+
- 분산 아키텍처 내부에서 엄격한 경계 콘텍스트를 만들어야 함
97+
- 각 서비스가 소유한 테이블의 구조가 변경될 경우, 해당 외부 서비스와도 조정이 필요하므로 테이블을 건드리는 것 또한 쉽지 않다
98+
- 비즈니스 로직 중복의 위험도 있음
99+
100+
- 오케스트레이티드 요청 기반 패턴
101+
- 비즈니스 요청 처리 도중에 데이터 소스를 동기화하는 기법
102+
- 동기화 주체인 오케스트레이터 (조정자) 서비스가 분산 트랜잭션을 관리한다
103+
- 기존 서비스를 오케스트레이터로 지정할 경우, 해당 서비스는 자기 본업도 해야 하고, 오케스트레이터 일도 해야 한다
104+
- 과부하가 걸리거나, 서비스 간 커플링이 고착화될 위험이 있음
105+
- 각 서비스 상위에 관리자 서비스가 존재해서, 이 서비스가 데이터가 동기화되게끔 조정한다고 생각하면 됨
106+
- 응답성보다 데이터 일관성 추구
107+
- 시간이 오래 걸리더라도 데이터 일관성이 더 중요
108+
- 에러 처리가 복잡해짐
109+
- 특정 하위 서비스에서 오류 발생시, 오케스트레이터는 어떻게 대처해야 할 지 복잡해짐
110+
111+
- 이벤트 기반 패턴
112+
- 인기가 제일 많고 믿음직스러움 (??)
113+
- 각 서비스들이 특정 액션 (고객 탈퇴 등) 을 이벤트 형태로 발행하여 게시하면, 이 이벤트를 구독하는 다른 서비스들이 이벤트에 따른 응답을 제공하는 방식
114+
- 고객이 탈퇴했을 경우, 고객이 탈퇴했다는 이벤트를 이벤트 스트림 (or 메시지 토픽) 에 발행하여 알림
115+
- 과금 서비스와 지원 서비스는 해당 이벤트를 수신하고, 그에 따른 뒤처리 수행
116+
- 응답성이 우수하고, 서비스 간 커플링이 강하지 않으며, 적시에 일관성을 맞출 수 있다는 장점이 있음
117+
- 역시나 에러 처리가 제일 까다로움
118+
- 이벤트 처리 실패할 경우
119+
- 이벤트 처리 중에 서비스가 죽을 경우
120+
- 이벤트를 여러 번 전달하고, 그럼에도 실패할 경우 해당 이벤트를 데드 레터 큐에 밀어넣고, 관리자가 직접 대응할 수 있게끔 별도 처리
121+
122+
```
123+
느낀점: 트랜잭션에 대한 배경지식이 충분하지 않을 경우 난감하다. 예전에 잠깐 겉핥기로 봤던 얕은 지식을 토대로 읽었다..
124+
```

0 commit comments

Comments
 (0)