Skip to content

7‐5. 계약을 통한 MSA 환경에서 서비스 연결 안정성 얻기

이건창 edited this page Jun 4, 2024 · 2 revisions

결과

개요

1️⃣ 멀티 모듈로 변경

변경한 이유는 다음과 같다.

  1. 쿠폰 외 타도메인을 관리하면서 집중 대상이 불명확해졌다.
    • 한 곳에만 집중한다면 새로운 기회를 잡을 수 있다고 생각한다.
  2. 쿠폰은 이커머스에서 사용자와 일대일 소통할 수 있는 수단이다.
    • 큐폰으로 사용자와 친밀감을 형성할 수 있는 기회라 생각한다.
  3. 헥사고날을 깊게 고민하고 싶었다.
    • MSA 환경에서 헥사고날은 어떤 장점이 있을지 궁금하다. 글해서 모듈을 분리하고 API로 통신해 고민의 범위를 넓히고자 한다.

2️⃣ spring cloud contract 채택

msa 환경에서 api 스펙 변경으로 발생하는 사이드 이벡트는 상상을 초월한다. 이러한 통신 프로토콜에 계약을 맺는다면 api 스펙 변경에 대응 할 수 있다. 계약을 맺는다는 건 합의한 결과가 기록되는 일이다. 기록을 생성하고 관리하는 건 정말 많은 비용이 소모된다. 심지어 계약서 관리 작업은 단순 작업이라 의욕이 떨어져 몰입을 깨버린다.

단순 작업은 자동화 할 수 있지 않을까해서 spring cloud contract 라이브러리를 채택했다. 계약서를 버전에 맞게 쉽게 생성하고 관리할 수 있는 환경을 만들어보려 한다.

구현

1️⃣ spring contract 적용

spring contract는 계약을 관리 방법이며 API 제공자API 사용자 양측 검증 환경을 제공한다. 환경을 구성하는 방법은 다음과 같다.

  1. API 제공자에서 계약서를 생성한다.
  2. API 제공자에서 계약서를 검증할 기반 베이스를 작성하고 테스트 후 계약서를 저장소에 업로드한다.
  3. API 제공자는 업로드 된 계약서를 지속적으로 검증하도록 설정한다.
  4. API 사용자는 업로드 된 계약서를 받는다.
  5. API 사용자는 받은 계약서로 테스트를 진행한다.

소비자 중심 계약을 만족하기 위해 contract stub runnercontract verifier를 사용한다. 각 라이브러리는 다음과 같이 수행한다.

  • API 사용자는 contract stub runner 적용해 검증 완료된 contract로 테스트를 수행한다.
  • API 제공자는 contract verifier 적용해 contract를 관리한다.

gradle에 선언한 contractTest 작업이 계약서 검증을 위해 테스트 코드를 생성하기 때문에 많은 제약 조건이 존재한다. 적용하면서 찾은 제약 조건은 다음과 같다.

  • gradle 파일에 contractTest 와 contracts 를 선언한다.
  • contracts 에는 contract 파일 위치 base package 정보를 선언해야 한다.
  • 계약서는 contractsDslDir/{artifact}/ 폴더에 추가하고 {artifact}Base 클래스로 생성한다.
  • kotlin 을 사용한다면 {artifact}Base 클래스는 open 클래스로 생성한다.

계약으로 여러 테스트를 작성하면서 느낀 노하우는 다음과 같다.

  • 발생할 수 있는 엣지 케이스에 대한 계약서도 작성하자. API 사용자는 계약서를 이용해 다양한 테스트를 수행하고 싶어한다.
  • 계약서를 작성하기 전 API 생성자 끼리 리소스을 일관성있게 가져가야 한다. 단골 식별자가 1, 2, 3이 있는데 회원 정보는 1, 2 만 조회 할 수 있다면 테스트 의미가 없어진다.
  • 엣지 케이스를 쉽게 분별할 수 있어야 한다. 1, 2, 4, 6 사용자는 있고 5, 7 사용자는 없다면 API 사용자는 테스트할 대마다 머리 아프다.

계약서 명세는 사용법 기준으로 고려해야 해서 인수 테스트를 기준으로 작업했다. 추가될 계약서 명세는 다음과 같다.

  • 존재하는 회원은 200 과 함께 정보를 반환한다.
  • 가게 단골홀수 식별자를 가진 회원인 1, 3, 5 만 존재한다.
  • 이벤트에 참가한 회원홀수 식별자를 가진 회원인 1, 3, 5 만 존재한다.
  • 식별자 1000인 회원은 존재하지 않습니다. 조회한다면 400 ERROR가 발생한다.
  • 식별자 1100을 조회한다면 500 ERROR가 발생한다.
  • 특수 예외가 존재한다면 1000 이상의 숫자를 지정합니다. 각 예외 사항은 모듈마다 표로 관리해야 한다.
  • 식별자의 최대 자리수는 8자리다.

특수 예외는 다음과 같다.

식별자 상태 코드 설명
1000 400 존재하지 않는 회원입니다.
1001 429 주어진 시간 동안 너무 많은 요청을 보냈습니다.
1100 500 서버에 문제가 발생했습니다.
1101 503 서버가 요청을 처리할 준비가 되어있지 않습니다.

현실처럼 계약서는 명확할 수록 커뮤니케이션 비용을 줄일 수 있었다. 처음 계약을 위해 의도적으로 예외를 발생시켜 제공자 입장에서 검증하는게 맞을까 의문이 들었고, 의도적으로 예외를 발생시켜야하는 인프라 예외를 핸들링할 필요성을 경험했다.


2️⃣ 도메인 주도 설계

모듈을 분리로 어디까지가 관리할지 고민이 필요했고 행동하는 주체도메인에 집중했다.

쿠폰 서비스에서 행동하는 주체는 회원가게주인 그리고 쿠폰이 있다. 회원은 쿠폰을 받고 사용하는 책임이 있고 가게주인은 쿠폰을 게시하고 나눠주며 사용하는 쿠폰을 반환받는 책임이 있다. 마지막으로 쿠폰은 할인 방식으로 할인을 진행하는 책임을 가진다.

스크린샷 2024-05-26 오후 10 52 54

회원을 예시로 고민하면 회원 정보는 회원 도메인에서 회원이 가진 쿠폰은 쿠폰 도메인에서 관리하게 된다. 데이터 모델링도 어디에 집중해야 할지 명확해졌지만 쿠폰 도메인에서 어떻게 데이터를 조회할지 결정해야 했다.

연관 관계 시퀀스 다이어 그램
스크린샷 2024-05-26 오후 10 52 54 스크린샷 2024-05-26 오후 10 52 54

회원 엔티티 조회를 쉽게 하기 위해 회원 식별자만 저장한 테이블을 만들어야 할지 고민했다. 이유는 JPA에서 제공하는 엔티티 특성을 이용해 사용되지 않는 쿠폰의 상태 관리를 맡기고 싶었기 때문이다. 그러나 이런 생각은 잘못됐다.

연관 관계 엔티티
스크린샷 2024-05-26 오후 10 49 42 스크린샷 2024-05-26 오후 10 50 19

잘못됨을 인지한 이유는 다음과 같다.

  • 회원이 등록될 때마다 쿠폰 도메인을 갱신해야 하면서 결합력이 강해지기 때문이다.
  • 범용적인 update를 활용하지 않고 쿠폰 사용, 쿠폰 획득으로 명명해 명확한 의미 부여가 가능하기 때문이다.

요즘 업무를 진행하면서 지름길을 택해 발생할 문제를 생각하지 않는 경향이 강해졌다. 난 나름 최선을 다하고 있다고 위안하며 스트레스를 받지 않기 위해 귀와 눈을 닫은게 문제가 아니었을까싶다.

업무 외 활동으로 역량을 성장시켜 타협하는 순간을 최대한으로 늦출 수 있도록 노력해야겠다.

마지막으로

1️⃣ 자동화 쉽지 않다...

계약서를 자동화하기 위해 spring cloud contract를 적용했는데 진행하는 과정이 꽤나 번거롭다. (1)계약서를 생성하면 (2)검증하게 되고, (3)검증이 끝나면 저장소에 직접 저장해야 한다. (4)저장소에서 계약서를 받고, (5) 계약서 사용을 위한 환경을 세팅한다.

저장소에 따라 사용하는 방법이 다르고 어떤 계약서를 사용하는지에 따라 선언 방법이 다르며 디렉토리 구조에 따라 구조 선언 방법이 다르다. 문서 자체도 복잡하다. 초기에는 기본적인 세팅을 사용자가 해야해서 꺼리는 라이브러리였지만 지금은 꽤나 편리했다. 선언할 내용도 매우 적다.

장점은 다음과 같다고 생각한다.

  1. 높은 신뢰도를 가진 계약서
  2. 자동화된 API 스펙 버저닝
  3. 버전에 맞게 테스트가 가능한 환경
  4. 인프라가 포함되어도 가능한 단위 테스트 환경

정리하자면 계약하는 행위는 소비자의 자율성을 높이기 위한 생산자의 행동을 제한하는 행위다. 계약서 작성하기 전 (1) 비즈니스 검증(2) 인프라 검증을 정리하기 위한 적극적인 소통이 필요하고 도입 후 소비자의 자율성을 보장해야 한다.

2️⃣ spring contract 후기

직접 사용해보면서 느낀 결론은 기능 단위로 팀이 꾸려지는 경우에만 효용이 높겠다는 생각이 들었다. 팀에 적용하면 어떨까 고민했을 때는 기능 단위가 아닌 제품 단위 팀이 나눠지기 때문에 관리 영역만 늘어날 수 있어보였다. 또한 spring contract를 워크플로우 일부로 사용하는 팀이 드물기 때문에 채택하지 않은 이유도 있으리라 생각했다.