작년 12월부터 next v14+로 스펙을 이전하려는 시도를 해보았지만, jwt 갱신을 위한 딱히 뽀족한 방법을 찾지 못해 이전은 보류하되, pages router 아래에서의 architecture를 app router를 염두하고 구상했었다.
page router 대비 app router의 장점은
점에 있었다.
그래서 pages router를 구성할 때 하나의 폴더에 하나의 index.tsx를 구성하고, 그 index.tsx에서 페칭을 오롯이 가져가기 위해 권한을 몰아주었다.
마침 MVVM 패턴도 도입하는 시기라 각 라우트를 기준으로,
페칭하려는 데이터의 타입을 규격화하는 Model,
데이터를 수신하고 가공, 정제하는 Viewmodel,
이후 state로 전환하거나 데이터에 더해서 입히는 store, hooks,
그리고 최종적으로 이를 받는 View와 그 하위의 components로 architecture를 꾸렸다.
FSD, MVVM, app router의 패턴들을 들여와서 조금씩 혹은 약간 뒤틀어놓은 구조였지만, 라우트 - 또는 도메인 - 을 기준으로 간단하게 만들면서 계층으로 격리, 확장할 수 있는 형태라 보았고, 훗날 jwt 갱신 문제를 해결하여 스펙을 이전할 때에도 최소 비용으로 옮길 수 있는 구조라 보았다.
그런데, 이 architecture를 사용하다보니 몇가지 문제가 나타났다.
먼저 페칭을 몰아주었던 부분에서 특이점이 찾아왔다.
가령 검색 필터를 구성하는 부분이 이러했다.
관리자에서 설정할 수 있는 몇가지 유형 값이 있는데 이는 해당 정보의 상위 도메인에서는 선택의 옵션값으로 사용해야 한다.
이런 것이 n개가 있으면, n개 항목의 리스트 데이터를 다 수신받아야 필터를 렌더링할 수 있었다. 화면 관점에서 이건 맞는 선택이라 보였고 그렇다보니 이처럼 무언가 n회를 반복하는 코드가 양산되었다.
(useQueries가 제공하는 혜택 - 병렬화 -을 1도 못 누리기에 사실 가독성 말고는 얻는 게 없다.)
데이터 관점에서는 컴포넌트가 투머치하기 때문이라 생각할 수 있다.
그런데 Filter는 충분히 격리하기에 좋은 view의 item이었고, 반복적인, 그래서 재사용하기 좋은, 나열만 하면 되는 구조다.
대응책 - 개선이라기 보다는 마뜩찮은 형태를 바꾸기 위한 목적의 - 으로 생각한 것은,
요청을 다 분할하여 개별 필드에 페칭 권한을 나눠주는 방식이었다.
한발짝 더 나아가면, Filter의 구조와 표현은 분리시켜서 구성 컴포넌트로 바꾸고, 하위의 개별 필드는 기능 - 데이터 페칭 -만 들고 가는 것이었다. 수용을 하위에서 직접하는 방식은 app router로의 스펙 이전에도 영향이 없었다. 다만 페칭 상태에 따른 view case 처리를 개별 필드가 가져야 하니 provider를 만들고 그 안에서 suspence와 error boundary를 재사용할 수 있도록 단속했다.
이렇게 하면서 얻는 이익은,
에러와 로딩이 각 필드에 격리되었기에 view case의 처리가 전체가 아닌 국지적인 렌더링에 대한 대응으로 인사이트가 바뀐다.
동시에 손해도 발생한다.
form submit으로 조회하는 방식이던 Filter의 제어는 커스텀 hook에 의존하고 있었다. 그런데 Filter의 성격이 간략한 래핑 요소로 바뀌면서 본래 여기서 만들어 내려주던 state가 갈 곳이 없어진다. view에 두면 관심사 분리에 어긋나는 점도 있어 결국 Filter를 store 체계로 바꾸어야 하는 다른 문제가 솟아나는 것이다. (망할 트레이드 오프...)
BFF를 쓰던 아니던 api는 공급자가 composite - 조합 - 하지 않으면 사용자가 나뉘어진 api를 조합해야 한다. 후자의 장점은 view에서 요구하는 데이터가 바뀌는 것에 api가 덜 흔들린다는 것이다. 대신 view가 필요로 하는 데이터의 요청을 처리하는 비즈니스 로직이 프론트로 밀려내려온다.
꾸준히 이 방식에 의문을 제기하고 로직을 안으로 거둬들이자고 했지만 데이터 항목 변동에 따른 백엔드의 작업 부하가 있어서 관철은 되지 않았고, 결국 이 로직을 프론트가 받아들었다.
여기서 문제는 viewmodel과 hook이 데이터 가공에 같이 참여하게 되는 점이다.
viewmodel은 수신받은 데이터를 그 즉시 view가 필요로 하는 형태로 가공한다. 그리고 그 타입을 export하여 참조할 수 있도록 한다.
그런데 서로 다른 객체의 정보를 얻기 위해서 api를 다중으로 사용해야 할 경우, A의 요청에서 얻은 정보를 가지고 B를 요청하거나, A와 B를 일부씩 가져다 A-1의 모델로 가정하고 가공한다.
viewmodel에서 복수의 api를 사용하면 viewmodel이 다루는 view의 도메인과 api의 uri가 1:1로 매칭하지 않고, 1:1로 매칭시키려 하면 데이터 처리가 hook까지 내려가는 일이 쉽게 발생했다. hook은 최소한의 개입 - parameter의 생성 - 으로 상정하고 있었는데, 이 생각을 여지없이 부쉈다.