External Store
리액트의 외부에서 상태를 관리하는 것
관심사의 분리(Separation of Concerns)
하나의 시스템은 작은 부품이 모여서 만들어진다. 우리는 이미 작은 컴포넌트를 합쳐서 더 큰 컴포넌트를 만드는 방식으로 개발하고 있다.
어떤 기준을 사용할 수 있을까? 흔히 사용되는 Layered Architecture에선 사용자에게 가까운 것과 사용자에게서 먼 것으로 구분한다. 가장 가까운 건 UI를 다루는 부분, 그 다음엔 Business Logic을 다루는 부분, 그 너머에는 데이터에 접근하고 저장하는 부분으로 나눌 수 있게 된다. 각 부분은 하나의 역할, 하나의 관심사로 격리됨으로써 복잡도를 낮추게 된다.
거대한 프로그램이 아니라고 해도 흔히 Input → Process → Output이란 3단계로 코드를 적절히 구분만 해도 코드를 이해하고 유지보수하는데 크게 도움이 된다. 하나의 Output은 다시 사용자에게 Input을 요청하게 되고, 일반적인 프로그램은 다음과 같이 계속 순환하는 구조가 된다.
Input: 프로그램 시작
Process: 프로그램 초기화
Output: 사용자에게 초기 UI 보여주기
Input: 사용자의 입력
Process: 사용자의 입력에 따라 처리
Output: 처리 결과 보여주기
Input: 사용자의 또 다른 입력
…반복…
널리 알려진 MVC로 거칠게 매핑하면 다음과 같다.
Model → Process
View → Output
Controller → Input
Layered Architecture(계층화 아키텍쳐)
Layered Architecture는 소프트웨어 개발에서 가장 일반적으로 널리 사용되는 아키텍처이다. 구성되는 계층의 숫자에 따라 N 계층 아키텍처 (N-tier Architecture) 라고도 한다.
각 계층은 어플리케이션 내에서의 특정 역할과 관심사(화면 표시, 비즈니스 로직 수행, DB 작업 등)별로 구분된다. 이는 Layered Architecture 의 강력한 기능인 '관심사의 분리 (Separation of Concern)' 를 의미한다. 특정 계층의 구성요소는 해당 계층에 관련된 기능만 수행한다. 이런 특징은 높은 유지보수성과 쉬운 테스트라는 장점이 존재한다.
4-Tier Layered Architecture
Presentation Layer
사용자가 데이터를 전달하기 위해 화면에 정보를 표시하는 것을 주 관심사로 둔다. Presentation Layer 는 비즈니스 로직이 어떻게 수행되는지 알 필요가 없다. 대표적인 구성요소는 View와 Controller가 있다.
Business Layer
비즈니스 로직을 수행하는 것을 주 관심사로 둔다. 마찬가지로 화면에 데이터를 출력하는 방법이나 혹은 데이터를 어디서, 어떻게 가져오는지에 대한 내용은 알고있지 않다. 그저 Persistence Layer에서 데이터를 가져와 비즈니스 로직을 수행하고 그 결과를 Presentation Layer 로 전달하면 된다. 대표적인 구성요소는 Service와 Domain Model 등이 있다.
경우에 따라 아래처럼 Service와 Domain Model을 별개의 계층으로 나누거나, 아예 Domain Model을 Layered Architecture 와 별개의 것으로 분리하는 경우도 더러 있는 것 같다
Persistence Layer
어플리케이션의 영속성을 구현하기 위해, 데이터 출처와 그 데이터를 가져오고 다루는 것을 주 관심사로 둔다. 대표적인 구성요소는 Repository, DAO 등이 있다.
Database Layer
MySQL, MariaDB, PostgreSQL, MongoDB 등 데이터베이스가 위치한 계층을 의미한다
수직적으로 구성된 격리된 레이어(Layers of isolation)
그런데 그냥 Presentation Layer 에서 그냥 Database Layer 에 연결해서 정보를 가져오는게 더 편하지 않을까? Presenstation Layer 에서 직접 데이터베이스에 접속하여 데이터를 가져오게 되면, SQL에 대한 변경사항이 Presentation Layer에 직접 영향을 미친다. 즉, 과도한 의존성이 발생하게 된다. 이는 어플리케이션의 변경을 매우 어렵게 만든다.
Layered Architecture 에서 각 레이어는 격리되어 있다. 각 레이어가 다른 레이어와 독립적이므로 특정 레이어는 다른 레이어의 내부 동작을 모르게 된다. 즉 각 계층은 캡슐화되어 있고, 단일 책임을 갖는다. 따라서 특정 레이어는 다른 레이어에 영향을 주지 않고 변경될 수 있다.
Layered Architecture 시나리오
사용자가 특정 고객 정보를 요청한 상황을 가정하여, Layered Architecture 가 이 요청을 수행하는 시나리오를 정리해보자.
사용자가 보고있는 화면(Customer Screen, 흔히 말하는 View 라고 할 수 있을 것 같다)에서 사용자는 고객 정보를 요청한다. 이 요청은 그 요청을 처리할 수 있는 모듈이 무엇인지 알고있는 Customer Delegate (흔히 말하는 Controller 라고 할 수 있을 것 같다) 로 전달된다. Customer Delegate 는 해당 요청을 처리하기 위해 Business Layer 의 Customer Object 로 요청을 다시 전달한다. Customer Object는 요청을 받고 비즈니스 로직을 수행하기 위한 데이터를 얻기 위해, Persistence Layer의 Customer dao 와 Order dao 에 요청을 보낸다. Persistence Layer 의 DAO들은 요청을 수행하기 위해 Database Layer 에 접근하여 데이터를 가져온다. 이 요청은 다시 반대로 Persistence Layer → Business Layer → Presentation Layer 로 전달되고 최종적으로 사용자에게 전달된다.
출처 : https://hudi.blog/layered-architecture/
Flux Architecture
Facebook(현 Meta)에서 MVC의 대안으로 내세운 아키텍처.
2-way binding을 썼을 때 생길 수 있는 Model-View의 복잡한 관계(전통적인 MVC에선 이런 상황을 지양한다)를 겨냥해 명확히 “unidirectional data flow”를 강조한다.
Action → 이벤트/메시지 같은 객체.
Dispatcher → (여러) Store로 Action을 전달. 메시지 브로커와 유사하다.
Store (여러 개) → 받은 Action에 따라 상태를 변경. 상태 변경을 알림.
View → Store의 상태를 반영.
Redux는 단일 Store를 사용함으로써 좀 더 단순한 그림을 제안한다.
Action
Store → dispatch를 통해 Action을 받고, 사용자가 정의한 reducer를 통해 State를 변경한다.
View → State를 반영.
Action을 어떻게 표현하느냐가 사용성에 큰 차이를 만든다. 하지만 상태를 UI에 반영하는 방법은 모두 동일하다.
3단계 프로세스와 거칠게 매핑하면 다음과 같다.
Input → Action + dispatch
Process → reducer
Output → View(React)
External Store
특별히 쓰이지 않는 상태라고 해도(React는 이걸 판단하기 어려움), “상태가 바뀌면” 해당 컴포넌트와 하위 컴포넌트를 다시 렌더링한다.
setState는 내부적으로 useReducer를 사용함.
거의 같은 거라고 보면 됨.
위와 같은 방식이 기본적인 External State의 아이디어이다.
중요한것은 상태의 변경이다.
위와 같은 접근을 잘 하면, React가 UI를 담당하고, 순수한 TypeScript(또는 JavaScript)가 비즈니스 로직을 담당하는, 관심사의 분리(Separation of Concerns)를 명확히 할 수 있다. 자주 바뀌는 UI 요소에 대한 테스트 대신, 오래 유지되는(바뀌면 치명적인) 비즈니스 로직에 대한 테스트 코드를 작성해 유지보수에 도움이 되는 테스트 코드를 치밀하게 작성할 수 있다.
useCallback
useCallback을 사용하면, 함수가 매번 바뀌는 것이 아니라, 항상 같다.
useEffect와 비슷함
Last updated