MSA에서 메서드 분리에서 유스케이스 분리를 고민하는 시점
MSA 아키텍처로 여러 서비스를 운영하면서, 저는 도메인 단위로 유스케이스를 묶는 구조를 사용해 왔습니다.
예를 들어, User 서비스에서는
- 인증 관련 로직은 AuthUsecase
- 회원 정보 관련 로직은 UserUsecase
이렇게 기능별로 Usecase를 구분해 구성했습니다.
AuthUsecase에는 이메일 인증, 휴대폰 인증, 본인 인증 등을, UserUsecase에는 회원가입, 로그인, 유저 정보 조회, 비밀번호 재설정 등 다양한 메서드를 구현해 관리했습니다.
처음에는 이 구조가 충분히 단순하고 효과적이었습니다. 하지만 서비스가 점차 확장되고 비즈니스 요구사항이 복잡해지면서 한계가 드러났습니다.
문제의 시작: 사용자 유형에 따른 분기 증가
시간이 지나면서 User 서비스는 B2B와 B2C 사용자를 동시에 지원하게 되었고, 다음과 같은 변화가 나타났습니다:
- 회원가입 시 적용되는 검증 로직이나 조건이 사용자 유형별로 달라지기 시작
- 유저 정보 조회 시 노출되는 필드가 유형에 따라 달라짐
- 비밀번호 재설정 정책도 기업용(B2B)과 개인용(B2C)에서 달라짐
결과적으로, 기존 UserUsecase 안의 메서드들은
- 조건문이 많아졌고
- 내부 흐름이 복잡해졌으며
- 여러 의존성을 공유하게 되어 테스트 작성조차 어려워지는 상황이 되었습니다.
해법: 유스케이스 단위의 분리
이러한 복잡성은 단순히 메서드 분리로는 감당할 수 없는 수준이 되었고, 결국 세부 유스케이스를 독립된 구조로 분리하는 방향으로 전환했습니다.
예를 들어:
- UserUsecase.Register() →
- → B2BUserRegisterUsecase, B2CUserRegisterUsecase
- UserUsecase.ResetPassword() →
- → B2BPasswordResetUsecase, B2CPasswordResetUsecase
이처럼 유스케이스를 명확히 분리하자:
- 로직이 더 단순하고 명확하게 표현될 수 있었고
- 각 유스케이스마다 정확히 필요한 의존성만 주입하게 되어 테스트 작성도 간결해졌으며
- 새로운 정책이나 케이스가 생겨도 기존 코드에 영향을 주지 않고 독립적으로 확장할 수 있게 되었습니다.
결론: 메서드 분리의 한계를 넘어서
처음에는 단순한 메서드 분리로 충분했던 구조도,
비즈니스 도메인이 확장되고 유스케이스마다 복잡한 조건과 정책이 개입되면,
자연스럽게 "유스케이스 중심 설계"로 전환해야 할 필요성이 생깁니다.
유스케이스 분리의 기준점은 다음과 같다고 생각합니다.
- 하나의 메서드에 다수의 if/switch문이 존재하고,
- 정책 변경 시 서로 영향을 주는 로직이 얽히기 시작하고,
- 테스트를 위해 복수의 불필요한 의존 객체를 설정해야 할 때
이 시점이 되면, 명확하고 테스트 가능한 유스케이스 단위로 구조를 바꾸는걸 고려해봐도 좋겠다고 생각합니다.
추가
유스케이스를 나누다 보면, 비슷한 흐름이나 로직이 반복되면서 중복처럼 보이는 코드가 생기기 쉽습니다. 이때 많은 개발자가 중복을 제거하고 싶은 유혹을 느끼지만, 이 부분에서 신중함이 필요합니다.
왜냐하면 시간이 지나며 각 유스케이스는 서로 다른 정책과 조건들을 수용하게 되기 때문입니다.
- 고객의 유형이나 등급에 따른 정책 변화
- 서비스 채널에 따른 예외 처리
- 보안 요구사항 추가 등
이런 변화들은 결국 유사해 보였던 로직에 서로 다른 요구사항을 주입하게 되고, 한 번 통합한 로직을 다시 분리하려 할 때 더 큰 비용이 듭니다.
따라서 초기에 다소 중복이 있어도, "확장 가능한 구조"를 유지하는 것이 훨씬 더 안정적이고 유지보수하기 쉬운 선택이라고 생각합니다.