본문 바로가기
Develop/Design

[Clean Architecture] 육각형 아키텍처 - 웹 어댑터 (컨트롤러)

by 코딩의성지 2022. 4. 16.

이전 포스팅에서 육각형 아키텍처가 무엇인지? 그리고 패키지 구조는 어떻게 잡아야하는지? 등을 포스팅했었다.

 

혹시 포스팅을 놓쳤다면 아래 링크를 통해 공부하고 오면 좋을듯하다.

1.육각형 아키텍처란 무엇인가?

https://devkingdom.tistory.com/341

 

[Clean Architecture] 계층형 아키텍처의 문제점을 해결하는 육각형 아키텍처(헥사고날 아키텍처)

얼마전에 계층형 아키텍처의 문제점에 다뤘었다. https://devkingdom.tistory.com/340 [Clean Architecture] 계층형 아키텍처의 문제점 최근에 "만들면서 배우는 클린 아키텍처"라는 책을 정독했다. 요즘 웹 아

devkingdom.tistory.com

2. 육각형 아키텍처의 패키지 구조

https://devkingdom.tistory.com/342

 

[Clean Architecture] 육각형 아키텍처 (헥사고날 아키텍처) 패키지 구조

지난번 포스팅에서 간단하게 육각형 아키텍처에 대해 포스팅을 했었다. https://devkingdom.tistory.com/341?category=838914 [Clean Architecture] 계층형 아키텍처의 문제점을 해결하는 육각형 아키텍처(헥사고..

devkingdom.tistory.com

 

오늘은 어댑터의 구현에 대해 정리를 해둘 예정이다. 어댑터는 이전에 포스팅했던 내용에서 얘기했듯이 어댑터는 코어를 호출하며 어플리케이션을 주도하는 어댑터(driving adapter)와 코어에 의해 호출이 되며 어플리케이션에 의해 주도 되는 어댑터(driven adapter)로 나뉜다. 보통 실무에서 주도하는 어댑터는 웹 어댑터, 주도되는 어댑터는 영속성 어댑터이다. 오늘은 웹 어댑터에 대해서 포스팅을 진행해 보겠다.

 

웹 어댑터란?

 

스프링이나 .NET 프레임워크 등을 활용해 MVC 프로젝트를 해본 분들이라면 웹어댑터는 쉽게 이해하실 수 있다.

웹어댑터는 보통 Controller라 불리우는 녀석을 의미한다.

 

아래 그림을 보자.

보통 처음 프로젝트를 하면 이런식으로 컨트롤러에서 서비스객체를 호출하는 형태로 개발을 하곤 한다. 하지만 설계적인 관점에서 이는 그리 좋지 못한 방식이다.

 

조금더 완성도 높은 설계를 위해서는 어댑터와 유스케이스 사이에 또다른 간접계층을 넣어주는 것이 좋다. 아래 그림처럼 말이다.

 

보통의 경우 제어의 흐름이 컨트롤러에서 서비스로 흘러가게 되는데, 이때 중간에 웹 어댑터가 통신할 수 있는 특정 포트를 제공함으로써 의존성 역전 원칙을 적용했다.

즉 서비스는 이 포트를 구현하고, 웹 어댑터는 이 포트를 호출하여 사용하는 것이다.

 

여기서 왜 포트와 같은 간접 계층을 넣어주는 것이 설계적으로 좋을까? 

어플리케이션을 개발하다보면 언젠가 그 시스템은 레거시가 되고 초기 제작자는 이미 사라져 버리고 누군가가 유지보수하게 될 가능성이 매우 높다.(지금 내가 회사에서 진행중인 프로젝트가 그렇다.)

이때 이러한 설계가 되어있다면 해당 프로젝트를 유지보수하는 엔지니어는 아주 행복할 것이다. 그 이유는 여기서 포트가 프로젝트의 명세 역할을 해주게 되기 때문이다.

 

웹어댑터의 역할

 

이번에는 웹 어댑터의 역할에 대해 알아보자. 일반적으로 웹어댑터는 HTTP 관련 처리를 할 책임이 있다.

구체적으로 말씀드리면 아래와 같은 역할을 한다.

 

1. HTTP 요청을 객체로 매핑

2. 인증 및 권한 부여

3. 입력 유효성 검증

4. 입력을 유스케이스의 입력 모델로 매핑

5. 유스케이스 호출

6. 유스케이스의 출력을 HTTP로 매핑

7. HTTP 응답을 반환

 

웹어댑터는 URL, 경로, HTTP Method, Contents Type 등의 기준을 만족하는 HTTP 요청을 수신한다. 그리고 해당 요청으로 들어온 파라미터와 콘텐츠를 객체로 역직렬화 해준다.

그리고 어댑터가 요청한 유저가 정상적인 유저인지 등의 인증을 진행하고, 비지니스를 수행할 수 있는 권한을 부여한다. 이과정이 실패하면 별도의 에러를 리턴한다.

 

다음은 입력 유효성을 검증한다. 여기서 입력 유효성은 유스케이스(서비스)에서의 입력모델과는 구조나 의미가 다를 수도 있다. 여기서는 단순하게 웹어댑터에 입력되는 (파라미터로 들어오는) 모델에 대한 유효성을 검증하는 것이며, 유스케이스의 입력모델은 별도로  유효성 검증을 해줘야한다.

여기서는 웹어댑터의 입력 모델이 유스케이스의 입력모델로 정상적으로 변환할 수 있는지를 검증해야한다.  이 과정이 실패하면 유효성 검증에러를 발생시킨다.

 

여기서 입력 모델을 유스케이스의 입력모델로 매핑하여 해당 유스케이스를 호출한다. 여기서는 유스케이스가 무엇을 하는지 전혀 알지 못한다. 단순히 호출하고나서 해당 유스케이스의 출력의 결과물을 받아온다. 그리고 그 출력을 HTTP 응답으로 직렬화해서 호출한 곳으로 전달해준다.

 

위의 각과정에서 문제가 생길때에 대한 예외처리는 필수적이다. 각 에러에 대한 메시지 정의 역시 중요하니 잘 정의해 두도록하자.

 

어려운 내용이다. 여기서 중요한 것 딱 하나만 명심하자. 

HTTP 와 관련된 작업은 어플리케이션 계층의 전적인 책임이다.

개발을 하다보면 이러한 책임을 어기고 경계를 넘나드고 싶은 유혹에 빠질 수 있다. 여기서 이러한 유혹을 피하기 위해서는 웹계층 개발보다 도메인과 어플리케이션 계층부터 개발한 후에 추후에 웹계층을 개발하고 이를 어댑터로 연결만 해주면 안정적으로 개발이 가능하다.

 

컨트롤러 잘 만들기

 

보통 잘 못만들어진 프로젝트를 보면 컨트롤러 하나가 굉장히 큰 책임을 수행하고 있는 경우가 많다. 그리고 서비스 하나당 컨트롤러 하나 이런식으로 아주 규칙적으로 프로그래밍을 해놓은 경우도 많이 보았다.(전혀 그럴필요가 없고, 오히려 그래서는 안되는데 말이다..)

 

컨트롤러(웹어댑터)는 여러개 여도 된다. 너무 적게 만들어서 많은 책임을 주는 것보다는 많이 만드는게 천만배 낫다고 생각한다.

 

만약 10만줄짜리 거대한 컨트롤러가 있다고 생각해보자. 거기서 당신이 딱 메서드 하나를 추가했다고 생각해보자. 아무리 기존 코드가 깔끔하게 잘 짜여 있어도 시간이 지나면서 관리가 어려워지고 심지어 배포하는 과정에서도 부담이 된다. 그리고 테스트를 할때도 어렵다. 컨트롤러에 코드가 많으면 당연히 해당 컨트롤러에 대한 코드도 많을 것인데 이렇게 되면 관련된 테스트코드를 찾기가 어렵게 될 것이다. (보통은 테스트 코드가 더 추상적이기에 어려움이 많아진다.)

 

그리고 컨트롤러가 거대하면 많은 연산들이 특정 모델 클래스를 공유하게 되는데 이또한 문제가 많다. 예를 들어 어떤 연산에는 특정 필드 값이 필요하지 않아 그 연산에만 필드 값을 null로 한다던지 하는 이슈가 발생할 수 있다.

 

그러므로 컨트롤러를 분리하고 그 컨트로럴에 해당하는 자체 모델을 만들거나, 아예 원시값으로 처리하는 방식을 선택해보자. 전용 모델은 컨트롤러 패키지 내부에 private로 선언함으로써 다른곳에서 재사용되게하는 실수를 방지할 수도 있다. 그리고 컨트롤러를 분리하게 되면 여러 개발자들이 동시작업을 하는데에도 유리하다.

 

한가지 더 얘기해보면 컨트롤러의 명칭도 잘생각해서 만들자. 서비스 클래스도 마찬가지다. 각각의 컨트롤러를 만들 때는 생각에 생각에 생각을 해서 신중하게 명칭을 정해주자. 잘못 만든 명칭은 나중에 유지보수할때 두고두고 후회하게 될 것이다.

 

Ref.
톰 홈버그, 『만들면서 배우는 클린 아키텍처』, 위키북스(2021), p53~61

반응형

댓글