티스토리 뷰

Spring/Spring Boot

MVC 어댑터 패턴

woo'^'chang 2022. 6. 16. 22:04

개요

정해진 틀에 의해서만 동작하게 되는 장치가 존재한다면 어떻게 될까? 결함이나 새로운 기능의 추가로 인해 장치가 변경되어 틀과 맞지 않게 된다면 틀까지 바꿔야 하는 큰 비용을 투자해야 하는 문제가 발생한다.

 

이를 간단히 해결하는 방법이 어댑터이다.

 

실생활에서 어댑터의 사전적 의미는 다른 전기나 기계 장치를 서로 연결해서 작동할 수 있도록 만들어 주는 결합 도구이다. 이는 하드웨어적인 의미로 소프트웨어적으로 생각해보면 다음과 같이 생각해볼 수 있다.

 

서로 다른 인터페이스(=장치)를 연결해서 작동할 수 있도록 한다.

 

이렇게 본다면 이해가 어려울 수 있다. 따라서 MVC 구조의 프론트 컨트롤러핸들러(컨트롤러)간의 예시를 통해 하나씩 알아보고자 한다.

MVC Adapter Pattern

프론트 컨트롤러

자바 웹 프로그래밍 기술 중 하나인 서블릿을 통해 컨트롤러는 클라이언트의 요청을 쉽게 처리할 수 있었지만, 각각 컨트롤러에서 중복되는 처리 과정이 존재한다.

클라이언트의 요청을 파싱해서 데이터를 뽑아내는 작업이나 요청의 결과를 반환하는 작업은 대표적으로 발생하는 중복 처리 과정이다. (물론 보여주는 결과에 따라 결과 반환은 컨트롤러마다 다를 수 있다.)

 

위와 같이 3개의 컨트롤러만 존재하더라도 중복되는 코드 작성은 귀찮은 작업이지만 실제 대규모 서비스에서는 컨트롤러가 3개만 존재하지 않을 것이다. 또한 해당 컨트롤러의 로직에 집중해야 하는데 공통 코드로 인해 시간을 낭비할 수 없다.

 

이를 해결하고자 나온 것이 프론트 컨트롤러 패턴 이다. 공통된 작업을 컨트롤러의 앞단 서블릿으로 뽑아내서 처리해 각 컨트롤러는 핵심 로직에 집중할 수 있도록 한다.

역할의 입장도 추가해 생각해 본다면 각 컨트롤러는 자신이 수행해야 할 로직을 처리할 역할만을 담당하게 되고 앞단으로 추출된 서블릿은 공통적인 로직을 처리해야 할 역할을 담당하게 되는 것이다.

개발 프로세스

위와 같은 그림처럼 공통 로직에 모든 컨트롤러가 연결되기 위해서는 모든 컨트롤러가 반환하는 결과의 틀이 동일 해야 한다.

하지만 핸들러(컨트롤러)마다 요구사항, 처리되는 로직 등이 다르므로 반환하는 결과는 충분히 다를 수 있고 만약 억지로 같게 맞추려고 한다면 해당 시스템은 변화에 대한 유연성을 잃어버리게 된다.

 

각기 다른 결과를 공통 로직에서 일일이 처리할 수 있게 만들 수 있으나, 이는 공통 로직의 책임이 커지게 돼서 역할이 모호해지고 반환되는 결과의 틀이 변경되거나 추가되면 공통 로직까지 수정해야 하는 비용이 발생한다.

 

어댑터처럼 중간에서 핸들러를 알맞게 조립시키자!

 

다양한 타입의 핸들러를 유연하게 다루기 위해 어댑터를 도입한다. 동일한 결과의 틀을 생산하는 핸들러(컨트롤러)들은 동일한 인터페이스를 구현하도록 하고 해당 인터페이스와 공통 로직 사이 어댑터를 두어 연결하도록 한다.

핸들러(컨트롤러)는 자신의 로직에 집중하여 알맞은 결과를 반환하고 해당하는 어댑터는 공통 로직과 핸들러를 알맞게 연결하고 공통 로직은 공통으로 처리되는 로직을 수행한다.

 

객체 지향적 관점에서도 각각의 객체는 자신이 책임져야 할 역할에 충실하다고 볼 수 있다.

 

이제는 새로운 핸들러가 추가되더라도 핸들러와 어댑터만 적절히 추가한다면 공통 로직에서의 수정사항은 발생하지 않는다. (공통 로직의 변화가 있지 않다면)

코드의 관점

전체적인 흐름은 다음과 같다.
  1. 클라이언트로부터 HTTP 요청이 들어온다.
  2. 프론트 컨트롤러에서 HTTP 요청 메시지를 파싱해서 핸들러 목록 중 알맞은 핸들러를 조회한다.
  3. 핸들러 어댑터 목록 중 조회한 핸들러를 처리할 수 있는 어댑터를 조회한다.
  4. 해당 어댑터가 존재한다면 프론트 컨트롤러가 핸들러 어댑터에게 요청을 위임한다.
  5. 핸들러 어댑터는 요청 정보를 바탕으로 핸들러에게 요청 진행을 지시한다.
  6. 핸들러는 요청에 알맞은 로직을 수행 후 결과를 반환한다.
  7. 전달받은 결과를 프론트 컨트롤러가 원하는 틀로 조립하여 반환한다.
  8. 프론트 컨트롤러는 결과를 바탕으로 View Resolver에게 요청한다.
  9. 알맞은 View를 반환하게 되고 클라이언트에게 최종 결과를 응답한다.

프론트 컨트롤러

  • 클라이언트 HTTP 요청을 알맞게 파싱하고 알맞은 응답을 클라이언트에게 전송
  • 핸들러 리스트 정보를 알고 있어야 한다.
  • 핸들러 어댑터 리스트 정보를 알고 있어야 한다.

핸들러 어댑터

  • 해당하는 핸들러가 자신이 처리할 수 있는 핸들러인지 확인할 수 있는 메서드 필요
  • 요청을 위임받았을 때 요청을 핸들러에게 수행하라고 지시하는 메서드 필요
    • 리턴 시 핸들러로부터 전달받은 결과를 알맞은 틀로 변환해서 리턴

핸들러

  • 요청에 대한 알맞은 로직을 수행하는 메서드 필요

DispatcherServlet

Spring MVC 프로젝트를 생성한다면 별도의 프론트 컨트롤러를 생성할 필요 없이 이미 DispatcherServlet이라는 프론트 컨트롤러가 존재하게 된다.

 

그렇기에 Spring 프로젝트를 시작할 때 각 컨트롤러에 대한 서블릿을 생성할 필요 없이 URL Mapping만으로 간단히 프로젝트를 진행할 수 있다. DispatcherServlet 또한 Spring에서 제공하는 프론트 컨트롤러이기에 위에서 설명한 역할과 같은 역할을 수행한다.

정리

좋은 시스템을 설계하고 개발하기 위해서는 변경 가능성확장성은 기본적으로 고려되어야 한다. 또한 변경으로 인한 사이드 이펙트는 시스템의 큰 위험을 초래할 수 있다. 이러한 상황에서 어댑터 패턴이라는 디자인 패턴을 고려해볼 수 있을 것이다.

 

하지만 디자인 패턴의 지나친 의존은 역으로 시스템 복잡도의 증가라는 문제를 일으킬 수 있기에 잘 이해하고 효율적으로 사용하는 것이 중요하다.

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday