-
Notifications
You must be signed in to change notification settings - Fork 5
소프트웨어 아키텍처 The Hard Parts 4번째 - 최지윤 #614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||||||||||||||||||
| ## Chapter 08: 재사용 패턴 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 개요 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 가급적 개발자들은 반복적인 코드를 줄이고 재사용성을 높인다 | ||||||||||||||||||||||
| - 모놀리식의 경우 라이브러리 혹은 모듈 임포트만으로 재사용이 쉽기 때문에 문제가 되지 않음 | ||||||||||||||||||||||
| - 허나 분산 아키텍처에서는 공유 코드를 작성할 때 고려해야 하는 부분이 많음 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 코드 복제 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 공유 코드 (여러 서비스에서 두루두루 쓰이는 코드) 를 단순히 서비스 레포지토리 안에 복사하는 행위 | ||||||||||||||||||||||
| - 정적인 코드, 일회성 코드 등 변경사항이 극히 적고, 버그의 가능성이 낮은 코드의 경우 오히려 복사 붙여넣기가 빛을 발할 때도 있다 | ||||||||||||||||||||||
| - 그렇지 않을 경우, 여러 군데에 단순 복사한 코드들을 다같이 관리해주어야 하기 때문에 여간 번거로운 것이 아님 | ||||||||||||||||||||||
| - 예를 들어, 한 곳에 복사한 코드에서 버그가 발생했다면? 나머지는? | ||||||||||||||||||||||
| - 가능한 한 변경될 여지가 적은 정적 유틸리티 클래스에 적합하다 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 공유 라이브러리 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 컴파일 타임에 바인딩되는 라이브러리를 통해 여러 서비스가 한 라이브러리를 바라보게 하는 기법 | ||||||||||||||||||||||
| - 컴파일 타임에 라이브러리 코드들이 서비스들에 공유된다 | ||||||||||||||||||||||
| - 공유 코드의 변경 빈도가 (코드 복사가 필요할 때처럼 변경 빈도가 아예 없는 수준은 아니지만) 꽤 낮을 경우 적합한 기법 | ||||||||||||||||||||||
| - 라이브러리의 트레이드오프는 라이브러리의 크기가 좌우한다 | ||||||||||||||||||||||
| - 단위가 큰 공유 라이브러리 한두 개만 사용할 경우, 하나의 통짜 라이브러리만 업데이트하고 관리하면 되기 때문에 각 서비스별 디펜던시 관리가 수월하다 (디펜던시 적음) | ||||||||||||||||||||||
| - 허나 라이브러리 안의 코드가 조금씩 변경되면, 라이브러리 전체에 영향을 미치므로, 라이브러리 업데이트와 함께 테스트 범위가 매우 커진다 (변경 관리 어려움) | ||||||||||||||||||||||
| - 단위가 작은 라이브러리 여러 개를 사용할 경우, 한 라이브러리에서 발생한 변경점은 다른 라이브러리나 코드에 큰 영향을 미치지 않으므로 유지보수가 수월하다 (변경 관리 쉬움) | ||||||||||||||||||||||
| - 단 서비스 - 라이브러리 간 의존도가 복잡해지며 (한 서비스가 하나의 라이브러리만 바라보는 것이 아니므로) 이것이 나중에는 큰 진흙덩어리 하나가 될 가능성이 있다 (디펜던시 많음) | ||||||||||||||||||||||
| - 서비스가 커지면 트레이드오프에서 오는 부작용도 커지므로 가급적 거대 라이브러리보단 작은 라이브러리 여러 개를 사용하여 디펜던시를 포기하고 변경 관리에서 이점을 얻는 것이 낫다 | ||||||||||||||||||||||
| - 정적인 성격의 기능들을 라이브러리로 분리할 경우 의외로 디펜던시에 영향도 적음 | ||||||||||||||||||||||
| - 라이브러리 사용 시, 무조건 버저닝을 해야 호환성에서 이점을 얻을 수 있다 | ||||||||||||||||||||||
| - 라이브러리의 버전을 올리거나, 구 버전을 레거시화 하여 지원하지 않게끔 하는 것도 전략이다 | ||||||||||||||||||||||
| - 잘 변경될 일 없는 정적인 라이브러리는 비교적 적은 범위의 버전 안에서 관리 | ||||||||||||||||||||||
| - 자주 변경되는 라이브러리는 여러 버전으로 관리 | ||||||||||||||||||||||
| - 변경점이 잦은 라이브러리를 적은 버전만으로 관리할 경우, 라이브러리 업데이트 시 영향받는 모든 서비스들을 추적하고 배포 및 테스트 해야 하므로 불필요한 공수가 늘어난다 | ||||||||||||||||||||||
| - 특정 범위를 벗어나는 옛 버전은 가능한 한 구식화하여 불필요한 관리를 줄여야 함 | ||||||||||||||||||||||
| - 덩치 큰 라이브러리 사용 지양 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 공유 서비스 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 공유 라이브러리보다 더 큰 규모의 기법 | ||||||||||||||||||||||
| - 공용 기능을 묶어 하나의 서비스로 두고, 다른 서비스들이 이를 바라보게 하는 기법 | ||||||||||||||||||||||
| - 다른 기법 (복제, 라이브러리화) 와 같은 코드 상속보다는, 여러 기능을 조합하여 큰 기능을 만드는 형태 | ||||||||||||||||||||||
| - 기능을 공유 서비스로 분리할 경우, 해당 기능이 변경되어도 다른 서비스까지 재배포할 일이 없어 간편하다 | ||||||||||||||||||||||
| - 단, 해당 기능이 '잘' 변경되었을 경우에만 한정 | ||||||||||||||||||||||
| - 공유 기능의 변경 빈도가 높을 경우, 그리고 다양한 프로그래밍 언어가 공존하는 환경에 적합 | ||||||||||||||||||||||
| - 공유 서비스로 분리한 기능에서 버그가 발생할 경우, 나머지 서비스도 무너지는 참사가 발생할 수 있음 (내고장성 이슈) | ||||||||||||||||||||||
| - 공유 서비스의 경우 컴파일타임 전까지는 버그 판독이 어렵다 | ||||||||||||||||||||||
| - 런타임에 서비스들끼리 유기적으로 엮이고 나서야 버그 여부를 알게 됨 | ||||||||||||||||||||||
| - 적절한 버저닝을 통해 리스크를 완화시킬 수 있음 | ||||||||||||||||||||||
| - 코드만 가져다 쓰는 다른 두 기법과 다르게, 공유 서비스는 결국 기능을 위해 또다른 서비스를 호출해야 하므로 성능 이슈가 생길 수밖에 없다 | ||||||||||||||||||||||
| - 공유 서비스를 사용하는 다른 서비스의 사이즈에 따라, 공유 서비스 크기를 적절하게 관리해야 한다 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 사이드카와 서비스 메시 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 사이드카 패턴: 육각형 아키텍처 (도메인을 중앙에, 포트와 어댑터를 통해 다른 계층과 커플링) 기반 | ||||||||||||||||||||||
| - 인프라 로직과 도메인 로직을 분리하는 것에 의의를 둠 | ||||||||||||||||||||||
| - 오토바이 옆에 붙어있는 사이드카에서 명칭을 따옴 | ||||||||||||||||||||||
| - 한 서비스 내에 분리가능한 파트 (도메인 관심사와 조금 떨어져있는 파트), 타 서비스와 엮이는 커플링에 해당하는 파트를 사이드카로 분리 | ||||||||||||||||||||||
| - 타 서비스와 사이드카 파트를 통해 기능을 연결할 수 있다 | ||||||||||||||||||||||
| - 사이드카끼리 맞물려 형성한 그물망과 같은 네트워크를 서비스 메시(service mesh)라고 합니다. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| 논의점 및 느낀점: 유독 공유 서비스 기법에 대한 단점이 길게 서술된 느낌이 든다. 반면 사이드카 기법은 구축이 복잡한 편이다~ 정도 외엔 장점 위주로 적혀 있는데, 다른 분들은 해당 두 파트를 읽으면서 비슷한 생각을 하셨을지 유독 궁금한 챕터였다. | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 어느정도 비슷한 생각이 들긴 했습니다. |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 개인적으론 초기 구축에 들어가는 에너지 (?) 면에선 공유 서비스 분리가 훨씬 간단해 보이고, 사이드카 기법은 서비스에 대한 충분한 지식이 뒷받침되어야 한다는 추가적인 단점이 있을 듯하다 | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
Comment on lines
+69
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. '논의점 및 느낀점' 부분은 코드가 아니므로, 코드 블록(```) 대신 인용문 블록(
Suggested change
Comment on lines
+69
to
+73
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 공유서비스의 단점이 많이 서술된 것 같다고 느끼긴 했는데, 다 맞는 말이라서 별 생각은 없이 넘어 갔습니다 사이드카패턴의 경우, 개발팀이 횡단관심사 보다 비즈니스로직에만 더 집중하게 할 목적이고, 컨테이너 환경으로 되어있다면 적극 검토해볼만 하다고 생각합니다 |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 코드 재사용: 어떤 경우에 가치 있는가? | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - 코드 재사용이 권장되고는 있지만, 무턱대고 재사용할 경우에도 부작용이 발생할 수 있다 | ||||||||||||||||||||||
| - 하나의 코드에서 너무 많은 업무를 수행하게 되면서, 복잡해질 우려가 있다 | ||||||||||||||||||||||
| - 코드의 일부분에 문제가 생길 경우, 그 코드를 사용하는 모든 곳이 영향을 받는다 | ||||||||||||||||||||||
| - 너무 자주 변경되는 코드보단 변경 빈도가 낮은 코드들이 재사용 성공률이 높다 | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 메시지 토픽) 에 발행하여 알림 | ||
| - 과금 서비스와 지원 서비스는 해당 이벤트를 수신하고, 그에 따른 뒤처리 수행 | ||
| - 응답성이 우수하고, 서비스 간 커플링이 강하지 않으며, 적시에 일관성을 맞출 수 있다는 장점이 있음 | ||
| - 역시나 에러 처리가 제일 까다로움 | ||
| - 이벤트 처리 실패할 경우 | ||
| - 이벤트 처리 중에 서비스가 죽을 경우 | ||
| - 이벤트를 여러 번 전달하고, 그럼에도 실패할 경우 해당 이벤트를 데드 레터 큐에 밀어넣고, 관리자가 직접 대응할 수 있게끔 별도 처리 | ||
|
|
||
| ``` | ||
| 느낀점: 트랜잭션에 대한 배경지식이 충분하지 않을 경우 난감하다. 예전에 잠깐 겉핥기로 봤던 얕은 지식을 토대로 읽었다.. | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마지막 한빛가이버 사가에서 최종 ADR은 공유 라이브러리로 결정했으니까 어떻게든 끝판왕이 공유 서비스가 아님을 강조하고 싶었던 저자의 치밀한 빌드업...은 아닌 것 같고
공유 서비스의 단점을 잘 알아둬야지 공유 라이브러리도 나쁘지 않은 선택지라는 걸 인식하지 않을까 싶어서 강조하지 않았나하는 관계자 같은 소리를 해봤습니다. ㅎㅎ
(유튜브 흑백리뷰에서 흑돈이 "관계자 같은 소리하지마!" 가 자동 재생되네요.)