JSP 기반 시스템의 구조적 문제를 해결한 아키텍처 전환기: DTO 중심 아키텍처 전환과 검증 체계 개선
🛠️ 레거시 JSP 시스템의 구조적 한계 극복기 — DTO 중심 아키텍처 전환과 검증 체계 개선
✍️ 들어가며
오랜 기간 유지되어 온 JSP 기반의 웹 시스템은 빠르게 변화하는 비즈니스 요구사항과 정제되지 않은 데이터 흐름, 그리고 복잡하게 얽힌 UI 중심 로직으로 인해 유지보수성에 큰 어려움을 겪고 있었습니다. 특히 Map 기반의 비정형 데이터 처리와 검증 및 예외 처리의 부재, 감사 추적의 어려움은 시스템 신뢰성을 크게 저해하고 있었습니다.
이 글에서는 이러한 문제들을 해결하기 위해 Swagger 기반의 API 명세 정립, DTO 중심의 정적 타입 구조 도입, 유효성 검증 체계 고도화, 도메인 중심 설계 재구성, 감사 로깅 체계 도입 등을 통해 시스템의 안정성과 유지보수성을 어떻게 개선했는지를 공유합니다.
📍 문제 정의: 무엇이 문제였는가?
1. UI 중심의 비즈니스 로직
- JSP 내에 로직이 과도하게 집중되어 있어, 프론트와 백엔드의 역할 구분이 모호했습니다.
- 화면을 구성하기 위해 직접 쿼리 호출과 조건 분기를 수행하는 등 프레젠테이션 계층이 서비스 로직을 침범하고 있었습니다.
2. Map 기반의 데이터 처리
- 컨트롤러 및 서비스 계층에서
Map<String, Object>
형태로 데이터를 주고받아 타입 안정성이 떨어졌습니다. - 정적 분석이 어렵고, 자동 완성 및 컴파일 타임 검증이 불가능해 유지보수가 매우 어려웠습니다.
3. 검증 및 예외 처리 체계 부재
- 사용자 입력 값에 대한 명확한 검증 체계가 존재하지 않아, 런타임 오류가 빈번하게 발생했습니다.
- 에러가 발생해도 일관된 예외 처리 및 사용자 친화적인 응답 구조가 없어 오류 추적이 어려운 구조였습니다.
4. 변경 이력 및 감사 추적의 부재
- 사용자 요청으로 인해 어떤 데이터가 언제, 어떻게 변경되었는지를 추적할 수 있는 로깅이나 히스토리 기능이 부재했습니다.
🔧 해결 전략
1. Swagger 기반 API 명세 수립
- OpenAPI 3.0 기반의 Swagger 문서 작성을 통해 프론트-백 간 계약을 명확히 했습니다.
- Springdoc을 활용하여 컨트롤러에 대한 명세를 자동화하고, API 문서 유지보수 비용을 줄였습니다.
“명확한 API 명세는 개발자 간의 불필요한 커뮤니케이션을 줄이고, 테스트 자동화 기반을 마련하는 첫걸음입니다.”
2. DTO 중심의 타입 안정성 확보
- 각 API 요청/응답을 위한 Request/Response DTO 클래스를 도입해, 명확한 데이터 구조를 정의했습니다.
- 도메인 모델과 API DTO 간 분리를 통해 계층 간 의존성을 제거하고, API 단 변경이 도메인 로직에 영향을 미치지 않도록 설계했습니다.
1
public record UserRegistrationRequest(String username, String email, String password) {}
Map
→DTO
구조로 전환함으로써 IDE 자동완성, 컴파일 타임 검증, 정적 분석이 가능해졌습니다.
3. Jakarta Bean Validation + 커스텀 유효성 검증
- Jakarta Bean Validation 3.0을 활용해 기본적인 필수값 및 포맷 검증을 수행했습니다.
- 복합 조건 검증 (ex. 특정 필드 조합의 유효성 등)을 위해 커스텀 ConstraintValidator를 구현하여 다단계 유효성 검증 체계를 도입했습니다.
1
2
3
4
5
6
7
8
9
10
11
@ValidPassword
public class UserRegistrationRequest {
@NotBlank
private String username;
@Email
private String email;
@PasswordComplexity
private String password;
}
- Mapstruct를 통해 매핑을 하며, 매핑 전후로 직접 작성한 Validator를 통한 검증을 수행하도록 했습니다.
- 유효성 실패 시 에러 응답을 일관되게 처리하도록
@ControllerAdvice
와@ExceptionHandler
기반의 전역 예외 처리 시스템을 구성했습니다.
4. 도메인 중심 서비스 계층 재구성
- 기존 JSP에 흩어진 비즈니스 로직을 도메인 기반의 Service 계층으로 이전하고, 역할과 책임을 명확히 분리했습니다.
- Input → Validation → Domain Logic → Persistence 흐름을 표준화하고, 로직 복잡도에 따라 Command / Query 책임도 분리했습니다.
5. 변경 이력 로깅 및 감사 가능성 확보
- 주요 도메인 데이터에 대해 변경 이력을 기록하는 AuditLogger를 구성하여 사용자 행위를 추적 가능하게 만들었습니다.
- 예를 들어, 계약 상태 변경, 승인 처리 등의 행위 로그를 JSON 구조로 Elasticsearch 및 DB에 저장하여 Kibana를 통해 검색 가능하도록 구성했습니다. 이를 통해, ISMS, ITGC 등 각종 감사에 대응하기 수월해졌습니다.
1
2
3
4
5
6
7
{
"action": "contract_status_updated",
"user": "admin01",
"before": "PENDING",
"after": "APPROVED",
"timestamp": "2025-05-22T10:15:30+09:00"
}
✅ 개선 효과
항목 | 개선 전 | 개선 후 |
---|---|---|
타입 안정성 | Map 기반, 런타임 오류 빈번 | DTO 기반, 컴파일 타임 검증 |
검증 체계 | 화면 단 JavaScript 또는 누락 | 다단계 유효성 검증 + 공통 예외 처리 |
감사 추적 | 변경 이력 없음 | 모든 주요 이벤트 로깅 |
유지보수성 | 화면 코드 복잡도 심각 | 계층 분리로 로직 집중도 개선 |
협업 생산성 | API 명세 누락, 커뮤니케이션 과잉 | Swagger 기반 계약 명확화 |
🔚 마무리하며
이번 개선 작업은 단순한 기술 스택 전환이 아니라, 소프트웨어 구조 자체를 도메인 중심으로 재정립한 과정이었습니다. 검증 가능성, 안정성, 감사 추적 가능성이라는 세 가지 키워드를 중심으로 설계를 정비하면서, 앞으로는 더 빠르고 신뢰할 수 있는 기능 개발과 유지보수가 가능해졌습니다.
레거시는 기술의 문제가 아니라 설계의 문제일 수 있습니다. 명확한 책임과 역할이 정의된 구조는 변화에 강하고, 점진적인 리팩토링을 자연스럽게 만들어 줍니다.
리팩토링은 집을 정리하는 일과 닮아 있습니다. 매일 조금씩 정리하면 늘 쾌적한 상태를 유지할 수 있지만, 미루다 보면 어느새 손대기 어려울 정도로 복잡해지곤 합니다. 특히 잘 정리된 집은 물건의 자리가 정해져 있어서 치우기 쉽듯, 소프트웨어도 구조와 책임이 잘 정의되어 있으면 리팩토링이 훨씬 수월합니다.
리팩토링은 특별한 이벤트가 아니라, 좋은 시스템을 만들기 위한 일상적인 습관입니다. 작은 개선을 반복하며 더 나은 방향으로 나아가는 것이 결국 지속 가능한 개발의 핵심입니다.