Spring 구조를 참고했습니다
오늘은 직접 구현 중인 WAS에서 라우팅과 메서드 실행 구조를 설계했다.
핵심은 요청 -> DTO 매핑 -> 메서드 실행 -> 응답이라는 흐름을 어떻게 깔끔하고 확장 가능하게 만들 것인가였다.
내가 고려했던 대안, 참고한 Spring의 구조, Spring이 왜 그렇게 설계했는지, 그리고 내가 왜 이런 선택을 했는지를 정리해보려고 한다.
가장 직관적이지만, 엔드포인트가 늘어날수록 복잡해진다.
빠른 프로토타입이라면 적합하겠지만, 유지보수의 한계가 명확해 바로 제외했다.
(처음에 생각한 방법)
RouteKey(Path+Method) → Function<HttpRequest, HttpResponse> 형태로 매핑하는 방식
간단하고 가벼운 구조라는 장점이 있지만, 파라미터 바인딩과 메서드 추상화가 부족하다는 단점이 있다.
Spring의 HandlerAdapter처럼 다양한 핸들러 타입을 지원할 수 있다.
@RequestMapping 같은 선언적 방식은 깔끔하지만, 현재 내 목표는 구조를 직접 이해하고 제어하는 것이다.
나는 위 대안 중 Map 기반을 발전시키면서, Spring의 핵심 아이디어를 참고했다.
Spring의 HandlerMethod처럼 라우트마다 메서드 단위로 관리하도록 설계했다.
메서드 자체와 파라미터 정보를 함께 가지고 있어 실행 전 파라미터 매핑까지 책임진다.
Spring의 HandlerMethodArgumentResolver 개념을 참고해 Content-Type별 Mapper를 구현했다.
예를 들어 ArgumentMapper라는 인터페이스를 만들고 StaticResourceMapper,FormMapper, JsonMapper(구현예정)등 구현체를 만들어 요청 데이터를 DTO에 매핑하도록 했다.
Spring의 HttpMessageConverter와 ServletWebRequest 개념을 참고해 Body 인터페이스(StaticResourceBody, FormBody)를 만들었다.
요청 raw data (byte[])를 일관된 방식으로 관리하기 위함이다.
Spring이 복잡한 계층을 둔 이유는 명확하다.
범용성과 유연성
다양한 컨트롤러 타입과 처리 방식을 지원하기 위해 HandlerAdapter 계층이 필요하다.
확장성
Content-Type이나 파라미터 주입 방식이 달라질 수 있으므로 ArgumentResolver와 HttpMessageConverter를 분리했다.
테스트와 유지보수
각 계층이 독립되어 있어 교체와 테스트가 용이하다.
Spring의 설계 이유를 이해하면서도, 현재 내 WAS의 요구에 맞게 단순화했다.
단일 컨트롤러 타입만 지원
다양한 핸들러 유형이 필요 없으므로 HandlerAdapter 계층은 생략했다.
확장 가능성은 확보, 과설계는 지양
라우트마다 MethodHandler를 등록해두면 새로운 Content-Type 지원이나 ArgumentResolver 확장은 용이하다.
하지만 지금 단계에서 Spring 수준의 Converter나 복잡한 Resolver는 불필요하다고 생각했다. 추후에 적용해 볼 수 있을 거 같다.
이해하고 직접 제어하는 것이 목표
Spring은 이미 검증된 프레임워크지만, 나는 작동 원리를 직접 구현해보는 과정을 통해 구조적 이해를 쌓고 싶었다.
따라서 선언적 라우팅 대신 RouteKey와 MethodHandler를 직접 등록하는 방식을 선택했다.
오늘 설계한 구조는 Spring의 HandlerMethod와 ArgumentResolver를 단순화한 축소판이다.
Spring이 범용성과 유연성을 위해 복잡한 계층을 도입했다면, 나는 현재 필요한 범위 안에서 유연성을 확보하면서도 설계 복잡도를 줄이는 방법을 택했다.
즉, Spring이 왜 그렇게 설계했는지는 이해하되, 지금 내 WAS에 꼭 필요한 부분만 가져온 선택적 설계라고 생각한다.
물론 확장에 용이한 지는 시간이 지나면 알게 되겠지..