채용과제를 진행함, 간만에 개발해보면서 느낀것들 간단히 적기
디렉토리 구조를 어떻게 가져갈까에 대한 고민이 항상 있다.
뭔가 공식처럼 패턴화해서 나만의 노하우를 갖고 싶었다.
처음에는 Atomic Design Pattern 을 생각했다가 몇가지 고민거리들이 생겼고, 나만의 규칙을 세웠었다.
하나의 페이지에서만 일회성으로 활용되는 컴포넌트도
src/components/*
하위에 두는게 맞는 것인가? 컴포넌트의 갯수가 너무 많아지는건 아닌가?
그래서 생각했던건 mainPage를 구성한다고 했을때,components/pages/mainPage/index.tsx
형태로 컴포넌트를 구성하고, 페이지 내에서 일회성으로 사용되는 컴포넌트는 components/pages/mainPage
하위에 생성해두는 것 이었다.
그리고 추후에 공통으로 사용된다면, src/components/[atoms|molecules|organisms]
레벨로 올린다.
애플리케이션이 커지면 커질수록 구조를 개선해야되는건 당연하다.
그나마 구조를 제일 뜯어고치지 않아도 되는건 이런 구조가 아닐까 싶다..
처음에 세웠던 규칙은
하지만 side effect가 우려되는 동작을 포함하고 있는 컴포넌트들을 공통컴포넌트로 분류하는 것이 애매하다는 생각이 들었다.
그래서 결국 Organisms은 나만의 Design Pattern 에서는 제외시켰다. side effect가 우려되는 동작들은 앞서 이야기했던 src/components/pages/*
하위에서만 허용하도록 하기로했다.
처음에는 거대한 페이지를 여러 컴포넌트로 나누는 과정에서, 각 컴포넌트들이 부모와는 단절된 채, 독립적인 동작들을 수행하고있었다.
문제는 이렇게 나눠진 형제 컴포넌트들간의 연관성이 있을때다.
예를 들어 헤더 폼
와 푸터 폼
를 컴포넌트로 나눴는데, 두 폼이 모두 작성되었을 때만 다른 영역에 있는 특정 버튼을 활성화 시키거나 할 때다.
그러면 이 상태값을 root 부모에서 관리하자니, 이런 경우가 많아지면 많아질수록 root 부모의 state가 늘어난다.
이런 흐름으로 가다보면, 어플리케이션 전체에서 사용되는 state가 생기게되고 여기서 애플리케이션 상태관리
라는 이야기가 나온다.
경험상 컴포넌트에서 state는 최소화하는게 깔끔해지는 경우가 많았다. (그렇다고 state를 아애 쓰지말자는게 아니라, state, 그리고 이 state와 관련된 로직들을 별도의 hook이나 컴포넌트로 분리하는 경우에 코드가 깔끔해졌다는 의미임)
그래서 별도의 영역으로 상태를 분리하기위한 노력이 필요했고, 일명 컴포넌트들의 공통 저장소인 store를 사용하게된다. (그냥 Observable 전역 변수라고 생각해도 됨)
이 store를 구현하기위해 다양한 라이브러리들이 존재하며 입맛에 맞춰서 자신이 편한걸 쓰면 된다.
적다보니 뭔가 책을 쓰는 느낌인데;
무튼 하고싶었던 말은 컴포넌트를 쪼개는건 너무나 좋지만, 항상 형제컴포넌트들간의 연관성이 있는지를 파악할 것
그리고 연관성이 있다면, store의 사용을 고려하는 것이 코드를 깔끔하게 만들 수 있다.
그렇게되면 root component의 상태값을 줄일 수 있다.
위 기조들로 개발을 하다보면 Page Root Component에서는 하는게 없다.
그냥 어떤 컴포넌트들에 있는지에 대한 로드맵정도의 역할만 한다.
뜬금없지만 난 책을 고르거나 읽을 때 카테고리를 먼저본다.
머리가 안좋아서 처음부터 나무를 보기보다 숲을 먼저보는게 전체적인 맥락을 이해하기 좋기 때문이다.
비슷한 맥락으로 Page의 Root Component 도 이런 역할을 하는게 맞다고 생각했다.
그러나 그런 역할만 수행하기엔 Root Component가 너무 아깝다는 생각이 들었다.
애플리케이션의 상태와 별개로 Page 의 전체에 영향을 미치는 로직들을 Page Root Component에 넣으면 좋을 것 같다는 생각이 들었다.
그래서 생각한건
이 2가지의 기능이 페이지 전체에 영향을 미치는 영역이라고 생각했다.
router의 경우 페이지간의 전환을 담당하니, root component에서 어떤 자식 컴포넌트에서 발생한 이벤트가 페이지 전환을 발생시키는지를 확인할 수 있다면 좋을 것 같았다.
api call의 경우 axios와 같은 api 호출 레이어에서 에러처리를 할 수도 있지만, 에러 alert을 띄운다거나, 에러 페이지로 전환하는 처리를 root에서 하는게 좋아보였다.
이유는 api 콜을 자식컴포넌트들에서 각자 독립적으로 하다보면, 어디서 에러가 발생했는지를 디버깅하기가 쉽지 않았다.
물론 Suspense를 사용하면 try catch의 catch문처럼 다 잡아낼 순 있지만, 결국 호출한 부분의 코드를 봐야하기때문에 그냥 애초에 root에서 한꺼번에 볼 수 있다면 좋을 것 같았다.
이렇게 하다보니 단지 api call
, routing
을 root에서 처리하기 위해 별도의 event handler를 자식컴포넌트의 props으로 추가해야하는 경우가 발생했다.
그래도 아직까지는 저 방식이 나에게 잘 맞고 편하다.
물론 코드상에 들어나지않은 암묵적인 나만의 규칙이기에 누군가와 협업한다면 위 내용을 함께 공유/공감하거나 아니면 직관적으로 알 수 있는 다른 방법들을 강구해야 할 것이다.