배경

  • 멀티모듈과 계층 형태의 프로젝트에서 하위 모듈인 core 모듈에 Spring Security 를 기반으로 구현된 인증 컴포넌트들을 배치해뒀고, core 모듈에 의존하여 여러 애플리케이션 모듈에서 인증 컴포넌트를 사용하는 구조였다.
  • 요구사항의 변경으로  새로운 인증 주체가 필요해졌고 애플리케이션 모듈에서 구현해야 했기 때문에 기존 구조로는 확장이 어려운 상황이 발생했다.

구조 파악 및 문제 분석

구조적 한계

  • 상위 모듈 접근 불가: 인증 관련 컴포넌트가 모두 하위 모듈(core)에 위치하고 있고 인증저장소에 접근하는 컴포넌트에서 직접 인증 주체 클래스를 참조해야만 하는 구조로 인해, 요구사항을 구현하려면 해당 컴포넌트들을 모두 상위 모듈(app)에 복사하여 수정하는 형태로 작업해야 했다.

  • 인증 주체에 대한 강한 결합: 인증 컴포넌트가 특정 인증 주체에 강하게 결합되어, 새로운 인증 주체를 추가하거나 교체할 때 컴포넌트 내부 로직 전체를 수정해야 한다. 즉, 확장성과 유연성이 고려되지 못한 설계로 인해 다른 인증 주체를 쉽게 추가하거나 인증 정책을 변경할 수 없었다.

불안전한 데이터 관리

  • 영속성 컨텍스트 생명주기 불일치: 인증 객체가 JPA Entity를 직접 필드로 참조하여 스레드로컬 기반의 인증저장소(Spring Security의 SecurityContextHolder)에 저장하여 관리하고 있었다.
    • 트랜잭션 범위를 벗어난 영역(Filter 등)에서 Lazy Loading 시 no Session 예외 가 발생하는 이슈가 남아있었다.
    • 엔티티가 다른 트랜잭션에 재진입(Merge)할 경우, 의도치 않은 Update 쿼리(Dirty Write)가 발생하여 데이터 무결성을 위반하게 될 수 있다.

해결과정

  • 아키텍처 개선:
    • 기존의 인증정보 조회 컴포넌트를 공통적으로 인증정보 저장소에 접근하는 객체와 인증주체별 인증정보를 조회하는 객체의 책임을 분리하였다.
    • 분리 과정에서 상속과 합성을 고려하였으나 테스트코드 작성과 유연한 변경성을 위해 합성을 택하여 재설계하였다.
  • JPA Entity 참조 제거:
    • 인증저장소에 저장된 인증정보의 데이터 조회 용도였기에, JPA Entity 의존성을 DTO로 전환하였다.

개선 후기

확장성을 고려한 설계, 느슨한 결합이 중요하다는 말은 어디서나 볼 수 있었지만 이번 개선 작업을 통해 제대로 체감할 수 있었다. 사실 당분간은 앞으로도 변경에 대한 인사이트와 확장성을 고려한 설계를 바로 개발시점에 적용할 수는 있을까 싶지만, 적어도 변경이 필요한 상황에 작업할 컴포넌트가 덕지덕지 얽혀있다면 쎄함을 감지하고 구조를 들여다 보는 정도는 해볼수 있게 되지 않을까 싶다.