설계의 기본적인 규칙은 벡엔드나 프론트엔드나 대동소이하다.(사실 프로그래밍 외적인 분야로 봐도 비슷한 거 같다) 하나의 컨셉(관심사)에 집중한다. 하나의 행동(목적)을 여러 단계로 추상화하고 그 단계에 적합한 관심사에 맞는 부분에 집중해서 처리한다. 그런 작은 단위의 작업을 목적을 완성할 때까지 반복한다.
그리고 해당 목적의 디테일이 달라졌을때 또는 그런 추상화한 구조를 다른 목적을 수행하는데 재사용 가능하게 한다.
내가 철학적인 성향이 강해서 그런지 개인적인 생각을 말하자면 프로그래밍 자체가 철학과 유사하다는 생각이 많이 든다. 어떤 관심(목적)을 위해서 지속적으로 추상화된 보편율(코드)를 구하고 그를 검증하고 다듬는다.
물론 그렇게 개발을 하지 않는 사람도 많겠지만... 적어도 나는 그렇게 생각하고 개발한다. 요즘 보니 프로그래밍 패러다임과 수학적 개념뿐 아니라 철학적 개념을 연결하는 포스팅도 보이는거 보니 비슷한 생각을 한 사람들이 꽤 많아 보이긴 한다만 그저 이것 또한 하나의 시각일 뿐이다.
프로그래밍에서 중요한 것은 참으로 여러가지가 있지만 좋은 코드를 작성하는 데 있어서 고려해야 할 부분이 있고 그것을 설계적인 측면에서 고려한다고 보면 재사용성과 도메인 최적화(?)를 생각해 볼 수 있다. 즉 추상화되어서 최대한 많은 부분에서 재사용 가능한 로직 그리고 도메인에 특화해서 해당 로직들을 별문제 없이 확장할 수 있는 부분이 중요하다고 볼 수 있다.
프런트엔드 개발에서는 그런 점으로 바라볼 때 크게 프레젠테이션(UI)과 컨테이너(data & state) 영역으로 구분된다.
기존의 모놀리스식의 MVC 패턴(또는 파생 패턴)에서 컨트롤러와 모델단이 모두 벡엔드에서 처리되고 프론트라고하면 기껏 해봐야 UI 측면을 다룰 때와는 다르게 나날이 프론트엔드 영역의 개발 파트가 넓어지고 있는 상황이다 보니 이런 분류는 한동안 계속될 것 같다.
UI의 경우 재사용성이 매우 높은 편이다. HTML tag나 CSS나 최대한 재사용을 염두에 두고 만들었기 때문인지 기존의 부트스트랩 등의 UI 프레임워크(또는 각 회사의 디자인 시스템)와 컴포넌트 개념으로 재사용에 많은 신경을 쓰고 있다. 사실 그럴 수밖에 없는 게 브라우저에서 보여주는 View영역의 성능은 렌더링이나 프로그래밍 로직 실행을 제외하고 UI를 그리는데 필요한 리소스들을 다운로드하는 네트워크 성능이 여러모로 영향을 주기 때문이다.
그렇다 보니 최대한 코드나 이미지 등 리소스를 재활용하는 기법을 많이 신경 쓴다. 물론 리소스에 대한 최적화 역시 마찬가지다.
데이터의 경우는 여러 가지 상황이 있지만 UI와 거의 반대의 성질을 가진다. UI가 기존의 리소스를 재활용하고 그것을 도메인에 최적화하는 것에 가깝다면, 데이터의 경우 그 원본이 되는 상태는 불변성을 유지하고 매번 새롭게 사용하지만 그 데이터 구조나 가공하는 로직 등을 재사용한다. 이는 사실 벡엔드와 비슷하다. DB에서 끌어오는 데이터는 매번 최신화되어 있고 그 자체로 불변성을 가진 상태로 쓰이지만 그를 가공하는 비즈니스 로직 등은 미리 설계된 구조를 재활용하듯이 말이다.
그러다 보니 프론트엔드에서의 개발의 경우 공통 UI를 디자인 시스템이나 아토믹 패턴등으로 정리하는 경우가 많다. 이를 폴더 구조로 나누면 components폴더안의 세부구조로 나눌 수 있다. 또 그를 사용자에게 유의미한 서비스로 제공하기 위해서는 각 도메인에 적합한 UI를 만들어야 하는데 프론트엔드 영역에서 제공하는 서비스라는 것이 애초에 사용자에게 특정 state(data)를 가공해서 적합한 UI를 만들어 보여주는 것과 사용자 Action을 받아서 특정 응답을 내놓는 것을 모두 포함하다 보니 단순히 UI라고 보기 힘들다.
그래서인지 클래스 101 같은 회사에서는 그래서인지 해당 부분을 features라는 개념으로 명명하던데 나도 딱히 좋은 생각이 떠오르진 않으니 features라는 이름으로 명명하기로 했다.(깃 플로우에서 쓰이는 명명이기도 하다)
features는 결국 components에 있는 공통 UI와 해당 도메인에 특화된 재사용 가능성이 없는 UI, data(state)를 바인딩한 해당 도메인에 적합한 여러 컴포넌트(UI와 data 포함)를 가진다. 하나의 도메인은 거의 대부분 규모에서 하나의 feature를 가진다. 하나의 feature는 여러 컴포넌트를 가진다.라고 정의 내릴 수 있다. data를 어떻게 바인딩할지는 프로젝트 구조에 따라서 달라지지만 보통 view models(리액트 함수 컴포넌트에서는 커스텀 훅) 로직을 호출하는 방식이 가장 기본적이고 추가로 provider나 controller 등의 레이어를 상황에 따라서 추가하면 될 것 같다. 그렇게 구성된 컴포넌트들을 조합한 root가 해당 feature를 통합하는 역할을 가진다.
또 그렇게 만들어진 feature는 route에 따라서 적절하게 제공된다.
프론트엔드에서의 data는 보통 state로 명명하므로 state에 관해서 논해보겠다. 보통 state는 전역 state와 지역 state가 나뉜다. (전역 변수와 지역 변수처럼) 예외 상황이 없다고 볼 순 없겠지만 대부분의 경우 지역 state의 경우 해당 컴포넌트 또는 컴포넌트 자손 관계에 국소적으로 관계된 렌더링에 관련된 즉 UI에 관한 state가 대부분이다.
이것을 UI state라고 임의로 명명하겠다. UI state의 경우 해당 컴포넌트 내부에서 렌더링에 영향을 주는 선에서 그치거나 또는 해당 컴포넌트의 몇몇 자손에 국소적으로 영향을 주는데. 이를 굳이 전역 변수로 삼을 이유는 없다. 물론 전역으로 영향을 주는 UI state의 경우 전역 변수로 관리하는 게 합리적이지만 사실 그런 경우는 일단 설계 구조가 잘못된 경우일 가능성이 높다. (콘셉트에 적합하지 않음)
리액트 등에서는 해당 자손의 props가 길어지면 props drilling이라는 용어로 지칭하고 지양해야 한다고 보는 관점도 있지만 해당 관점은 공통 UI 즉 제네릭 한 UI끼리의 결합도에나 유의미하다. 그 또한 추상화 정도에 따라서 달라지므로 특별한 이유가 없는 한 props drilling을 처리하고자 UI state를 전역으로 만드는 것은 stateless 한 View의 방향성과 그다지 적절하지 않다고 볼 수 있다. (react의 경우 context 등을 활용해서 state를 props 방식이 아닌 방법으로 공유하는데 나는 아직도 그게 정답인지 모르겠다.)
UI state 즉 지역 state를 다루었으니 이제 전역 state에 대해서 생각해보자.
전역 state의 경우 보통 store라는 구조를 따른다. 이것을 프론트엔드 영역에서의 Model이라고 볼 수 있다. store 자체를 어떤 방식으로 구축하는지는 여러가지 방법이 있으니 자세히 다루지는 않겠지만 결국은 store구조가 어떻든 전역 store의 경우 1 store 구조를 가진다.(내부 구조는 여러가지가 있을 수 있지만) 이를 보통 플럭스 패턴을 통해서 처리하는게 모던 프론트엔드 개발의 상태관리에 가깝다.
프론트엔드 개발이 현재 고도화된 서비스에 의해서 점점 MSA 방향성을(마이크로 또 엔터프라이즈 프론트엔드) 향해서 발전하고 있는데 이를 통해서 예상해보자면 전역 store를 일종의 ssr처럼 따로 분리해주고 (서비스 워커를 이에 활용할 수 있을지는 아직 깊이 파보지 않아서 모르겠다.) 각 domain별로 컨테이너를 분리해서 제공할 것으로 예상이 가능하다. 결국 프론트엔드를 최적화하다 보면 코드 스플릿 방식의 리소스 최적화와 분리를 할 것이고 서비스의 안정성을 위해서 라면 굳이 이를 모놀리스식의 방식으로 제공할 이유가 없기 때문이다.(비용 문제가 걸리긴 한다.)
어쨌든 전역 store의 구조를 프로젝트에 적합하게 잡았다면 store(Model)의 데이터를 그대로 가져다 쓰거나 재가공할 View model 레이어를 만드는 것이 적합하다. 리액트 기준으로 보면 이를 custom hook 방식으로 만들어서 재가공한다. 보통 간단한 CRUD 로직 중 필요한 부분들을 구현하고 각 feature에서 가져다 쓴다.
만약 이것에 대한 규모가 더 복잡해진다면 custom hook을 feature내부에서 도메인에 최적화해서 사용하는 방향으로 진행한다. view model의 경우 임의의 명명을 해도 상관없지만 리액트에서는 hooks라는 명칭을 많이 쓰는 것으로 보인다.
정리하자면
도메인에 최적화되지 않은 공통 UI는 components라는 구조(디자인 시스템 등).
도메인에 최적화된 컴포넌트는 feature(임의)라는 구조(UI와 state).
store에서 state를 가져오고 가공해서 처리하는 view model은
store에 독립적으로 보통 hooks.
'프론트엔드' 카테고리의 다른 글
[React] React-dom에서의 innerHTML (0) | 2022.05.31 |
---|---|
프론트엔드 설계 고민 -4- (0) | 2022.05.21 |
[React]React와 JSX. (0) | 2022.04.14 |
리액트 포탈과 렌더링 고민 (0) | 2022.04.09 |
프론트엔드 설계 고민 -2- (0) | 2022.04.08 |