# Chapter18. 반응형 아키텍처와 어니언 아키텍처
# 1. 반응형 아키텍처와 어니언 아키텍처 개요
아키텍처 | 내용 |
---|---|
반응형 아키텍처 |
|
어니언 아키텍처 |
|
# 2. 반응형 아키텍처
- 애플리케이션을 구조화하는 방법으로 핵심 원칙은 이벤트에 대한 반응으로 일어날 일을 지정하는 것이다.
- 웹 서비스와 UI에 잘 어울리는데 웹 서비스는 웹 요청 응답에 일어날 일을 지정하고, UI는 버튼 클릭과 같은 이벤트 응답에 일어날 일을 지정하면 된다.
- 이런 것을 일반적으로 이벤트 핸들러라고 한다.
- 반응형 아키텍처는 코드에 나타난 순차적 액션의 순서를 뒤집는다.
- X를 하고 Y 하는 대신, X가 일어나면 언제나 Y를 한다.
- 이렇게 하면 코드를 읽기 쉽고 유지보수하기도 좋다.
- 반응형 아키텍처가 코드에 주는 중요한 영향
- 원인과 효과가 결합되 것을 분리한다.
- 여러 단계를 파이프라인으로 처리한다.
- 타임라인이 유연해진다.
# 3. 반응형 아키텍처 예시
# (1) ValueCell
-> 액션!
ValueCell
의update()
메서드 사용시 현재 값을 항상 올바르게 유지할 수 있는 이유update()
를 사용할 때 계산을 넘기기 때문이다.- 계산은 현재 값을 받아 새로운 값을 리턴한다.
- 현재 값이 도메인 상에서 올바른 값이 계산이 항상 올바른 값을 리턴한다면 이 메서드는 올바른 값으로 새로운 값을 계산하기 때문에 항상 올바른 값을 유지할 것이다.
ValueCell
은 다른 타임라인에서 읽거나 쓰는 순서를 보장하지 않는다.- 하지만 어떤 값이 저장되어도 그 값이 항상 올바른 값이라는 것은 보장한다.
ValueCell
을 일관되게 유지하기 위한 전제조건- 올바른 값으로 초기화한다.
update()
메서드에는 계산을 전달한다. (절대로 액션을 전달하지 말 것)- 계산은 올바른 값이 주어졌다면 올바른 값을 리턴해야한다.
function ValueCell(initialValue) {
var currentValue = initialValue; // 변경 불가능한 값(컬렉션이 될 수 있다)을 하나 담아 둔다.
var watchers = []; // 감시자 목록을 저장
return {
val: function() { // 현재의 값을 가져온다.
return currentValue;
},
update: function(f) { // 현재 값에 함수를 적용해 값을 바꾼다.(교체 패턴)
var oldValue = currentValue;
var newValue = f(oldValue);
if (oldValue !== newValue) { // 값이 바뀔 때
currentValue = newValue;
forEach(watchers, function(watcher) { // 모든 감시자를 실행
watcher(newValue);
});
}
},
addWatcher: function(f) { // 새로운 감시자를 추가
watchers.push(f);
}
}
}
# (2) FormulaCell
-> 액션!
FormulaCell
로 이미 있는 셀에서 파생한 셀을 만들 수 있다.- 다른 셀의 변화가 감지되면 값을 다시 계산한다.
function FormulaCell(upstreamCell, f) {
var myCell = ValueCell(f(upstreamCell.val())); // ValueCell을 재사용
upstreamCell.addWatcher(function(newUpstreamValue) { // 셀 값을 다시 계산하기 위해서 감시자를 추가
myCell.update(function(currentValue) {
return f(newUpstreamValue);
});
});
return { // val()과 addWatcher()를 myCell에 위임
val: myCell.val,
addWatcher: myCdell.addWatcher, // FormulaCell 값은 직접 바꿀 수 없다.
};
}
var shipping_cart = ValueCell({});
var cart_total = FormulaCell(shopping_cart, calc_total); // shopping_cart가 바뀔 때 cart_total도 바뀐다.
FormulaCell
은 값을 직접 바꿀 수 없다.- 감시하던 상위(upstream) 셀 값이 바뀌면
FormulaCell
값이 바뀐다. - 상위 셀이 바뀌면 상위 값을 가지고 셀 값을 다시 계산한다.
- 감시하던 상위(upstream) 셀 값이 바뀌면
FormulaCell
에는 값을 바꾸는 기능은 없지만FormulaCell
을 감시할 수 있다.
# 4. 반응형 아키텍처가 주는 영향
# (1) 원인과 효과가 결합한 것을 분리한다.
- 결합의 분리는 원인과 효과의 중심을 관리한다.
- 원인이나 효과가 늘어나면 관리해야 할 것도 늘어난다.
- 원인과 효과의 중심을 잘 관리해서 관리해야 할 것이 빠르게 늘어나지 않도록 해야 한다.
- 관리해야 할 것이 늘어나는 문제를 곱셈에서 덧셈으로 바꿀 수 있다.
- 5개의 원인과 4개의 결과가 있기 때문에 5 X 4 대신 5 + 4로 만들 수 있다.
- 효과를 추가해도 원인을 고치지 않아도 되고, 원인을 추가해도 효과를 고치지 않아도 되기 때문에 관리해야 할 것은 하나만 늘어난다.
- 비슷한 상황으로 DOM을 갱신하는 곳에서는 DOM을 갱신하는 것만 신경 쓰면 된다.
- 주의할 점은 문제가 없는데 이 방법으로 분리하는 것은 좋지 않다.
- 코드에 액션을 순서대로 표현하는 것이 더 명확한 경우도 있다.
- 장바구니처럼 원인과 효과의 중심이 없다면 분리하지 않는 것이 좋다.
# (2) 여러 단계를 파이프라인으로 처리한다.
- 반응형 아키텍처도 간단한 액션과 계산을 조합해 복잡한 동작을 만들 수 있다.
- 조합된 액션은 파이프라인과 같다.
- 데이터가 파이프라인으로 들어가 각 단계에서 처리된다.
- 파이프라인은 작은 액션과 계산을 조합한 하나의 액션이라고 볼 수 있다.
- 어떤 일이 발생하는 여러 단계가 있다면 파이프라인으로 처리하는 것이 좋다.
- 각 단계에서 생성된 데이터는 다음 단계의 입력값으로 사용될 것이다.
- 자바스크립트의
Promise
패턴,RxJS
등 여러 가지 구현체가 있다. - 만약 여러 단계가 있지만 데이터를 전달하지 않는다면 이 패턴을 사용하지 않는 것이 좋다.
- 데이터를 전달하지 않으면 파이프라인이라고 볼 수 없다.
- 따라서 올바른 반응형 아키텍처가 될 수 없다.
# (3) 타임라인이 유연해진다
- 순서를 정의하는 방법을 뒤집기 때문에 자연스럽게 타임라인이 작은 부분으로 분리된다.
- 기존에 짧은 타임라인이 좋은 것이라고 했지만 타임라인이 많아지는 것도 좋지 않다.
- 하지만 타임라인이 많아도 문제가 없는 경우가 있다.
- 공유하는 자원이 없으면 타임라인이 많아져도 문제가 없다.
# 5. 어니언 아키텍처
- 어니언 아키텍처는 현실 세계와 상호작용하기 위한 서비스 구조를 만드는 방법이다.
- 이름에서 알 수 있듯이 둥글게 겹겹이 쌓인 양파 모양을 하고 있다.
- 어니언 아키텍처 계층 구분
계층 | 설명 |
---|---|
인터랙션 계층 | 바깥세상에서 영향을 주거나 받는 액션으로 도메인 계층과 액션을 사용하는 것을 조율한다. |
도메인 계층 | 도메인 로직과 비즈니스 규칙을 주로 정의하고 대부분 계산으로 구성된다. |
언어 계층 | 소프트웨어를 만들 수 있는 언어 유틸리티와 라이브러리로 되어 있다. |
- 어니언 아키텍처는 특정 계층이 꼭 필요하다고 강제하지 않는다.
- 하지만 많은 경우에 위와 같이 3가지 큰 분류로 나눌 수 있다.
- 어니언 아키텍처의 중요 규칙
- 현실 세계와 상호작용은 인터랙션 계층에서 해야 한다.
- 계층에서 호출하는 방향은 중심 방향이다.
- 계층은 외부에 어떤 계층이 있는지 모른다.
# ✔️ 전통적인 계층형 아키텍처
- 전통적인 아키텍처로 웹 API를 만들 때 계층(layer)이라고 하는 개념을 사용한다.
- 하지만 어니언 아키텍처의 계층과는 다르다.
계층 | 설명 |
---|---|
웹 인터페이스 계층 | 웹 요청을 도메인으로 바꾸고 도메인을 웹 응답으로 바꾼다. |
도메인 계층 | 애플리케이션 핵심 로직으로 도메인 개념에 DB 쿼리나 명령이 들어간다. |
데이터베이스 계층 | 시간에 따라 바뀌는 정보를 저장한다. |
- 전통적인 계층형 아키텍처는 데이터베이스를 기반으로 한다.
- 도메인 계층은 데이터베이스 동작으로 만든다.
- 그리고 웹 인터페이스는 웹 요청을 도메인 동작으로 변환한다.
- 데이터베이스 계층이 가장 아래 있다면 그 위에 있는 모든 것이 액션이 되기 때문에 함수형 스타일이 아니다.
- 모든 것이 계층에 쌓여있고 계산은 따로 관리되지 않고 우연히 사용된다.
- 함수형 아키텍처는 계산과 액션에 대한 명확한 규칙이 있어야 한다.
# 6. 어니언 아키텍처 이모저모
# (1) 변경과 재사용이 쉬워야 한다
- 어니언 아키텍처는 데이터베이스나 API 호출과 같은 외부 서비스(인터랙션 계층)를 바꾸기 쉽다.
- 가장 높은 계층에서 사용하기 때문이다.
- 도메인 계층은 외부 서비스에 의존하지 않아서 테스트하기 좋고 재사용하기 좋다.
- 어니언 아키텍처는 좋은 인프라보다 좋은 도메인을 강조한다.
- 전형적인 아키텍처에서 도메인 규칙은 데이터베이스를 부르지만 어니언 아키텍처에서는 그렇게 하면 안 된다.
# (2) 도메인 규칙은 도메인 용어를 사용한다
- 도메인 규칙은 도메인 용어를 사용한다.
- 도메인 규칙에 속하는지 인터랙션 계층에 속하는지 판단하려면 코드에서 사용하는 용어를 보면 된다.
- 도메인 규칙에는 제품, 이미지, 가격, 할인과 같은 용어를 사용한다.
- 데이터베이스, AJAX 요청들은 도메인을 나타내는 용어가 아니다.
# (3) 가독성을 따져 봐야 한다
- 가독성을 결정하는 요소
- 사용하는 언어
- 사용하는 라이브러리
- 레거시 코드와 코드 스타일
- 개발자들의 습관
- 코드의 가독성
- 일반적으로 함수형 코드는 읽기 좋다.
- 하지만 함수형 코드가 아닌 코드가 더 명확한 경우도 있다.
- 이때 얼마나 더 명확해지는지 봐야 한다.
- 도메인 계층을 계산으로 만들어 인터랙션 계층과 분리하면서 읽기 좋은 코드를 만들려고 노력해야 한다.
- 개발 속도
- 비즈니스 이유로 기능을 빨리 출시해야 하는 경우도 있다.
- 일반적으로 업무가 밀려오는 것은 좋지 않지만 이런 경우 많은 것을 타협해야 하기 때문이다.
- 이런 경우에도 나중에 아키텍처에 맞춰 코드를 정리할 준비를 하는 것이 좋다.
- 시스템 성능
- 변경 가능한 데이터 구조는 불변 데이터 구조보다 빠르다.
- 성능 개선과 도메인을 계산으로 만드는 것은 따로 생각하는 것이 좋다.
- 최적화는 인터랙션 계층에서 하고 도메인 계층은 재사용 가능한 계산으로 만드는 것이다.