로그 관리 서비스의 리팩토링: 공통 인터페이스 도입을 통한 코드 품질 향상

YuJun Oh·2024년 8월 7일
0

1. 리팩토링의 필요성

소프트웨어 개발에서 리팩토링은 단순히 코드를 정리하는 것 이상의 의미를 갖습니다. 그것은 시스템의 구조를 개선하고, 미래의 변화에 대비하며, 팀의 생산성을 높이는 중요한 과정입니다. 최근 여러 엔티티의 로그를 관리하는 서비스들을 개발하면서 코드 중복과 유지보수의 어려움이라는 도전에 직면했습니다. 이 글에서는 이러한 문제를 해결하기 위해 선택한 공통 인터페이스 도입 전략과 그 과정에서 얻은 인사이트를 공유하고자 합니다.

2. 문제 인식: 코드 중복과 유지보수의 난제

2.1 초기 상황

프로젝트 초기, 각 엔티티별로 독립적인 로그 관리 서비스를 구현했습니다. 예를 들어:

@Service
public class ContractLogService {
    public void logCreation(Contract contract, String userId) {
        // 계약 생성 로그 기록 로직
    }
    // 기타 메서드...
}

@Service
public class BusinessLogService {
    public void logCreation(Business business, String userId) {
        // 비즈니스 생성 로그 기록 로직
    }
    // 기타 메서드...
}

2.2 발생한 문제점

  1. 코드 중복: 각 서비스 클래스에서 유사한 로그 기록 및 조회 로직이 반복되었습니다.
  2. 확장성 부족: 새로운 엔티티 추가 시 마다 새로운 로그 서비스 클래스를 작성해야 했습니다.
  3. 유지보수의 어려움: 공통 로직 변경 시 모든 관련 클래스를 수정해야 했습니다.

3. 해결 전략: 공통 인터페이스 설계

3.1 LogService 인터페이스 정의

문제 해결의 핵심은 모든 로그 서비스가 공유할 수 있는 인터페이스를 설계하는 것이었습니다.

public interface LogService<T, D> {
    List<D> getLogsById(Long id);
    void logOperation(T oldEntity, T newEntity, String userId, String operationType);
    void logCreation(T newEntity, String userId);
    void logUpdate(T oldEntity, T updatedEntity, String userId);
    void logDeletion(T deletedEntity, String userId);
}

이 인터페이스는 제네릭을 활용하여 다양한 엔티티 타입(T)과 DTO 타입(D)에 대응할 수 있도록 설계되었습니다.

3.2 추상 클래스를 통한 공통 로직 구현

인터페이스만으로는 중복 코드를 완전히 제거하기 어려웠기 때문에, 공통 로직을 담은 추상 클래스를 추가로 도입했습니다.

public abstract class AbstractLogService<T, D> implements LogService<T, D> {
    @Override
    public void logOperation(T oldEntity, T newEntity, String userId, String operationType) {
        // 공통 로그 기록 로직
    }

    protected abstract String getEntityType();
    protected abstract Long getEntityId(T entity);
    // 기타 공통 메서드...
}

4. 구현: 구체적인 로그 서비스 클래스

각 엔티티별 로그 서비스는 AbstractLogService를 상속받아 구현했습니다.

@Service
@RequiredArgsConstructor
public class ContractLogServiceImpl extends AbstractLogService<Contract, ContractLogDTO> {
    private final ContractLogRepository logRepository;

    @Override
    protected String getEntityType() {
        return "Contract";
    }

    @Override
    protected Long getEntityId(Contract contract) {
        return contract.getId();
    }

    // 엔티티 특화 로직 구현...
}

5. 리팩토링의 효과

5.1 코드 품질 향상

  • 중복 제거: 공통 로직이 추상 클래스로 이동하여 코드 중복이 크게 감소했습니다.
  • 일관성 확보: 모든 로그 서비스가 동일한 인터페이스를 따르게 되어 코드의 일관성이 향상되었습니다.

5.2 개발 생산성 증가

  • 신규 엔티티 로그 서비스 추가 용이: 새로운 엔티티에 대한 로그 서비스를 추가할 때, 기존 구조를 쉽게 활용할 수 있게 되었습니다.
  • 테스트 용이성: 공통 인터페이스를 활용한 목(mock) 객체 생성이 쉬워져, 단위 테스트 작성이 간편해졌습니다.

5.3 유지보수성 개선

  • 변경의 국소화: 공통 로직 변경 시 추상 클래스만 수정하면 되므로, 변경의 영향 범위가 줄었습니다.
  • 코드 이해도 향상: 표준화된 구조로 인해 다른 개발자들이 코드를 이해하고 수정하기 쉬워졌습니다.

6. 도전과 학습

리팩토링 과정에서 몇 가지 도전에 직면했습니다:

  1. 기존 코드와의 호환성: 리팩토링된 구조를 기존 시스템에 통합하는 과정에서 일시적인 호환성 문제가 발생했습니다. 이를 해결하기 위해 점진적인 마이그레이션 전략을 수립했습니다.

  2. 성능 최적화: 공통 인터페이스 도입 초기에는 약간의 성능 저하가 관찰되었습니다. 이는 추상화 레이어로 인한 것이었으며, 프로파일링을 통해 핫스팟을 식별하고 최적화하여 해결했습니다.

7. 결론 및 향후 계획

이번 리팩토링을 통해 단순히 코드를 개선하는 것을 넘어, 더 유연하고 확장 가능한 시스템 아키텍처를 구축했습니다. 이는 미래의 요구사항 변화에 더 빠르게 대응할 수 있는 기반이 되었습니다.

향후 계획:
1. 다른 도메인으로의 확장: 이번에 얻은 인사이트를 바탕으로 다른 도메인의 서비스들도 유사한 방식으로 리팩토링할 예정입니다.
2. 지속적인 개선: 정기적인 코드 리뷰와 리팩토링 세션을 통해 시스템의 품질을 계속해서 향상시켜 나갈 것입니다.
3. 문서화 강화: 이번 경험을 바탕으로 아키텍처 결정 사항과 설계 원칙을 문서화하여 팀의 지식 자산으로 활용할 계획입니다.

0개의 댓글

관련 채용 정보