이번 글에서는 최근 진행한 프로젝트에서 도메인 타입에 따라 데이터를 조회하는 서비스 로직을 전면적으로 리팩터링한 경험을 공유합니다.
특히 도메인이 늘어날수록 반복적으로 등장하는 if문과 switch-case문으로 인해 발생한 문제점을 어떻게 해결했는지 예시와 함께 살펴보겠습니다.
배경 및 문제점
기존 금융 데이터 처리 시스템은 도메인(일반, 은행, 카드, 증권 등)별로 서로 다른 데이터 조회 및 변환 로직을 갖고 있었습니다.
각 도메인마다 데이터 변환 로직을 개별적으로 구현이 되어 있는데, 이로 인해 시간이 지날수록 다음과 같은 문제들이 발생했습니다.
기존 코드에서는 여러 도메인 타입(general, bank, card, insurance 등등)의 데이터를 조회하기 위해 서비스 레이어마다 유사한 switch-case 분기가 반복적으로 존재했습니다. 예를 들어, A 서비스와 B 서비스에서 모두 다음과 같은 형태의 switch-case 구조가 중복 사용되고 있었습니다.
예시 코드
switch(companyType) {
case BANK:
return bankMapper.getData(companyCode);
case CARD:
return cardMapper.getData(companyCode);
// 나머지 케이스 생략...
}
문제점
이러한 구조는 다음과 같은 문제를 야기했습니다.
- 중복 코드 발생: 동일한 switch문이 여러 서비스 클래스에 분산되어 있었습니다.
- 확장성 문제: 새로운 도메인 타입이 추가될 때마다 해당 데이터를 조회하는 모든 로직에서 switch문을 추가해야 했습니다. (= 코드 가독성 저하)
- 유지보수 및 확장 비용 증가: 동일한 로직이 여러 곳에 분산되어 있어, 한 부분의 변경이 다른 부분에 미치는 영향을 예측하기 어려웠습니다. 이로 인해 버그 발생 가능성이 높아지고, 신규 기능 추가나 요구사항 변경 시 개발 생산성이 저하되었습니다.
접근 및 고민 과정
저는 코드 중복을 최소화하고 로직을 하나로 모아 유지보수성을 높이는 것이 중요하다고 판단했습니다. 그래서 다음 두 가지를 리팩터링의 목표로 세웠습니다.
- 중복 코드 제거 및 로직 단일화
- 기업 유형별 조회 로직을 하나의 클래스로 묶어서 관리한다.
- 도메인 데이터를 조회하고 변환하는 책임을 각 개별 Mapper에서 분리하여 별도 접근자 클래스에서 관리한다.
- 확장성을 위한 설계 개선
- 공통 인터페이스로 표준화하고, 도메인 타입별 데이터를 조회/변환하는 방법을 통일한다.
해결 방법
제가 도입한 해결책은 바로 'Mapper 접근자(Mapper Accessor)' 클래스의 도입과 인터페이스 통합입니다
Mapper Accessor 도입으로 중복 제거
기존의 switch-case 구조를 제거하기 위해 데이터 접근자 클래스를 만들고, 공통된 인터페이스로 데이터를 조회하도록 구현했습니다.
예시 코드
@Component
@RequiredArgsConstructor
public class FinancialDataMapperAccessor {
private final FinancialDataMapper financialDataMapper;
public FinancialData getFinancialData(CompanyType companyType, String companyCode) {
return switch (companyType) {
case BANK -> new BankData(financialDataMapper.getBankData(companyCode));
case CARD -> new CardData(financialDataMapper.getCardData(companyCode));
// 나머지 기업 유형들...
default -> EmptyFinancialData.INSTANCE;
};
}
}
이 방식으로 기업 유형이 추가되거나 변경될 때 단 한 곳만 수정하면 되도록 하여 유지보수의 효율을 크게 높였습니다.
변환 로직 통합 및 표준화
기존에는 각 도메인별로 데이터 변환 로직이 흩어져 있었습니다. 이를 인터페이스로 추상화하고, 각각의 도메인 클래스가 이 인터페이스를 구현하도록 변경하여 구조를 통일했습니다.
예시 코드
public interface FinancialDataConverter<T> {
FinancialDto convert(List<T> rawData);
}
// 구현 예시
public class BankDataConverter implements FinancialDataConverter<BankDomain> {
@Override
public FinancialDto convert(List<BankDomain> rawData) {
// 변환 로직...
}
}
이러한 구조 덕분에 각 도메인의 데이터 변환 로직이 일관된 방식으로 관리되어 확장성이 높아졌습니다.
성과
- 조회 로직 단일화 및 공통 인터페이스 도입으로 중복 코드 약 83% 감소
- 기업 유형별 조회 및 변환 로직이 중앙 집중화되어 유지보수 용이성 향상
- 코드 가독성 및 확장성이 대폭 개선되어 팀의 생산성 향상
이번 리팩터링을 통해, 단순히 코드의 중복을 제거하는 것을 넘어서 코드의 구조와 설계를 개선하여, 유지보수의 편의성과 확장성까지 고려한 리팩터링 결과를 얻을 수 있었습니다.
'트러블슈팅' 카테고리의 다른 글
Confluent Schema Reference 관련 문제 (0) | 2024.07.24 |
---|---|
[E-commerce] 동시성 문제 해결하기 (비관적 락, 네임드 락, 분산 락) (0) | 2024.05.01 |
[E-commerce] 주문 결제를 이벤트 기반 아키텍처로 구축하기 (0) | 2024.04.16 |
[E-commerce] Facade Pattern을 사용하여 시스템 응집도와 재사용성을 어떻게 개선할 수 있을까? (0) | 2024.04.11 |
EKS ERROR couldn't get current server API group list: the server has asked for the client to provide credentials (0) | 2024.03.11 |