1. 어니언, 헥사고날 아키텍처
헥사고날, 어니언 아키텍처는 port, adaptor 패턴의 별칭일 뿐입니다.
https://blog.ploeh.dk/2016/03/18/functional-architecture-is-ports-and-adapters/
포트는 인터페이스, 어댑터는 실제 구현체일 뿐입니다.
즉 포트(인터페이스)를 통해 어댑터(부수효과)를 격리하는 것이 핵심입니다.
https://engineering.linecorp.com/ko/blog/port-and-adapter-architecture/
프론트엔드는 포트, 어댑터 패턴을 어떻게 구현하죠?
컨테이너 컴포넌트와 컨텍스트 API를 IOC 처럼 사용하여 구현합니다.
참고 : 리액트는 UI 런타임입니다. https://overreacted.io/react-as-a-ui-runtime/
그런데 포트 / 어댑터 패턴을 사용하는 이유는 뭘까요?
2. AOP, 레이어드 아키텍처
백엔드 애플리케이션 프레임워크를 배울때 가장 어려웠던 것은,
각 계층(레이어)의 라이프사이클과 책임, 코드 실행 순서였던것 같습니다.
이는 요청 / 응답이라는 백엔드 애플리케이션의 핵심 기능 / 라이프사이클 관점에서 바라봐야 합니다.
앱이 구현하는 feature, domain은 그 다음 문제입니다.
다음은 nestjs의 요청, 응답 생명 주기입니다.
(출처 : https://wikidocs.net/158658)
(Todo : 각 레이어의 관심사에 대해서도 나중에 정리하여 추가 예정)
위 그림은 사실 아래 그림과 같습니다.
즉 인터페이스를 통해 아래는 위만 알며, 세부 구현은 모릅니다.
각 레이어는 파라미터와 리턴값을 통해 소통할까...요?
사실 숨은 경로가 있는데요, 바로 예외 처리입니다.
이 경우 에러 처리 로직을 최상단 레이어에 위임할 수 있습니다.
즉, 파리미터 기반으로 레이어가 소통하는 것은 맞지만,
리턴값 기반으로 소통은 아닐 수도 있습니다.
(데코레이터의 시그니처를 봐도 알 수 있죠)
해당 레이어가 하나의 책임을 담당하는 것,
이것이 레이어드 아키텍처고 진정한 AOP의 힘입니다.
각 레이어 별로 변화를 캡슐화하고, 자신의 관심사를 담당하는 것 (단일 책임 원칙, 횡단관심사)이죠.
다른 점이 있다면, 데코레이터는 하나만 런타임에 바꾸는 것이 불가능하고,
파라미터, 생성자 주입을 이용하면 런타임에 의존성을 쉽게 바꿀 수 있다는 점입니다.
3. 대수적 효과와 AOP
이전 문단에서 오류 필터를 볼 수 있었는데요,
마치 IOC를 담당하는 스프링, nestJS 런타임이 존재하는 것처럼,
리액트는 최상단에 리액트 런타임이 존재하는 것처럼 생각할 수 있습니다.
https://overreacted.io/ko/algebraic-effects-for-the-rest-of-us/
이는 일반적인 개발자들이 평소와 같이 코딩하면서, 매우 프로그레시브한 프로그래밍 패턴을 사용할 수 있게 해줍니다.
- 서스펜스를 사용하여 비동기 성공 / 실패 상태관리를 리액트에 위임합니다.
- 개발자는 실패 / 성공 시 UI만 개발하면 됩니다.
- 에러바운더리를 사용하여 오류 처리를 리액트에 위임합니다.
- 개발자는 에러 발생 시 UI만 개발하면 됩니다.
위는 AOP의 사례입니다.
다른 예도 들어보죠
도메인 서비스 로직에
- 트랜잭션 관리 로직이 섞이는게 맞나요?
- 에러처리 로직이 섞이는게 맞나요? (에러는 비즈니스 적으로 발생할 수 있으니 throw 하는건 괜찮죠)
- 서비스가 레포지토리에서 데이터를 가져오는 방법을 알아야 하는게 맞나요?
- (물론 "저장, 기록"은 비즈니스의 일부일 수 있으니 해당 로직이 들어가는건 적절합니다.)
위에 있는 모든 로직이 섞이지 않게 하려면 AOP를 사용해야 합니다.
이는 내 관심 아니야~ 하고 상단 레이어로 throw 하는 것처럼 생각할 수 있습니다.
혹은 이후 실행될 함수가 중간과정을 모르게 데이터를 트랜스폼 하거나 실행을 차단합니다.
대신 최상단에서 의존성을 제공하는 것처럼, 해당 관심사를 처리하는 방법을 알아야겠죠.
아쉬운 점은 throw한 위치에서 재개는 불가능하단 점입니다.
대신 함수를 잘게 쪼개서 이 아쉬움을 달랠 수는 있습니다. (ex 리덕스)
또 비슷한 사례로 chain of responsibility 패턴(미들웨어)을 들 수 있습니다.
각 미들웨어는 자신의 관심사만 처리하고, 아닌 경우는 다른 미들웨어에 처리를 위임하죠.
4. DI, DIP
헥사고날 아키텍처란
- 인터페이스와 구현체의 분리
- 포트와 어댑터
- 레이어 별 관심사의 분리
- 오른손이 하는 일을 왼손이 모르게 하기
- 대수적 효과
- ex 미들웨어, 에러필터, 서스펜스,
이 3가지 요소가 핵심이라는 건데요 (제 생각에)
AOP와 포트/어댑터 패턴을 구현하려면 DIP 개념과 이를 실현하기 위한 DI가 필요합니다.
자세한 설명은 아래 글에서 확인하세요
https://itchallenger.tistory.com/766?category=1057505
'FrontEnd' 카테고리의 다른 글
Iframe 완벽 가이드 (0) | 2022.10.26 |
---|---|
[1일 1 알고리즘] 프론트엔드 JS 알고리즘 문제풀이 : 배열 평탄화(flatten) (0) | 2022.10.25 |
Nest JS와 CQRS [CQRS Explained With Nest JS] (0) | 2022.10.23 |
이벤트 루프에 대한 이해도를 파악할 수 있는 면접질문 (0) | 2022.10.23 |
리액트의 의존성 주입 [NestJs의 모듈로 살펴보는] (0) | 2022.10.23 |