본문 바로가기

카테고리 없음

[TIL] 프로그래머스 데브코스 - 바닐라JS/ SPA구조로 노션만들기.

 

시작.

이번 클론 코딩에서 가장 핵심이라 생각했던 부분은 노션의 왼쪽 배너의 여러 문서들의 트리구조를 어떻게 구현할 것인가 였다고 생각했다.

그래서 이부분만 해결하면 나머지는 쉽게 쉽게 풀릴 줄 알고 사전에 상태 관리를 어떻게 해야 할지, 어떻게 해면 좀 더 확장성이 좋은 코드가 될 수 있는지를 고려하지 않았다. 그 결과 재귀 함수를 활용하여 구현은 했지만(이외에도 무수한 에러를 많이 만났다..), 이후 추가와 삭제 버튼을 만드는 과정에서 아래와 같이 새로 만들어진 문서에 대해서 추가와 삭제 이벤트를 직접 달아줘야 한다라는 문제점을 보고 상태 기반으로 렌더링 하는 방식을 선택해야겠다는 생각을 하게 되었다.

 

1. 새로운 문서를 생성했을 때, 이에 대한 상태를 관리하고 있는 곳이 있는가?

위의 첫 번째 스크린숏을 보면 문서 생성 버튼을 눌렀을 때 $ul의 존재 유무로  자식 문서가 최초로 만들어지는지, 아니면 이미 다른 자식 문서들이 존재하고 그 뒤로 생성이 되는지를 판단한다. 또한 새로 만들어진 문서엔 이벤트가 등록되어있지 않음으로, 두 번째 스크린숏과 같이 이벤트 핸들러를 등록시켜준다. 이렇게 작성하면 화면상에 보이는 부분은 해결이 되긴 하지만, 만약 다른 컴포넌트에서 배너의 문서들을 참조하는 상황이 발생한다면? 새로운 api호출을 하지 않는 이상, 새로 추가된 문서엔  접근이 불가능하다. 

 

2. 상태 변경 -> 렌더링 함수 호출로 화면 그리기.

물론 위의 방식이 하나의 돔 요소에만 접근 후 변경하기 때문에 성능적인 면에서 좋다고 볼 수 있지만, 확장성과(다른 컴포넌트에서 배너 요소에 접근할 경우), 일관성(렌더링 방식) 측면에서 불리하다고 판단했다. 그래서 확장성을 고려하여 App컴포넌트에서 상태를 관리하고, 일관성을 위해 상태 변경 -> 렌더링 함수 호출 방식을 선택했다. 

 

3. 결합도는 낮게, 응집도는 높게.

이전 TODO리스트를 구현했을 때 멘토님의 코드 리뷰를 통해 알게 된 지식인데, 좀 더 나아가서 상태 변경을 어떤 식으로 인지시켜 렌더링 함수를 호출시킬까에 대한 고민이다. 위의 TodoForm은 이름 그대로 새로 등록할 Todo의 입력 폼에해당하는 컴포넌트다. 입력폼에 입력하여 Todo등록을 시도하면, TodoList컴포넌트에게 상태가 변경되었음을 알려주고, TodoList에 등록된 todo의 총개수를 보여주는 TodoCount컴포넌트에게도 상태가 변경되었음을 인지시켜줘야 한다. (그래야 렌더 함수를 호출하여 새로운 데이터가 반영된 화면이 보이니까) 문제는 빨간색 밑줄 부분에 있다. todoList, TodoCount컴포넌트의 상태 변경이 TodoForm에 의존적이다. 즉, TodoForm컴포넌트가 선언된 파일 내에서 TodoList,TodoCount컴포넌트가 존재하지않으면, 각컴포넌트의 setState함수를 불러올 수 없게된다. 이는 같은 파일내에 존재하지 않았을 때 호출하는 것에 비해 상대적으로 결합도가 높다고 판단할 수 있다. 또한 ToDoList, TodoCount 컴포넌트의 setState함수가 외부로 노출되기 때문에 응집도가 낮다고 판단할 수 있다. 

 

4. pub-sub구조 적용.

위와 같은 문제를 해결하여 결합도를 낮추고 응집도를 높이기 위해 고려된 패턴이다.  상태 변경을 변경시킬 컴포넌트의 렌더 함수를 직접적으로 호출하는 방식이 아닌, 렌더 함수는 컴포넌트 내에서 존재하고 이벤트를 발생시켜 렌더 함수 호출 시점을 결정하는 방식이다. 이렇게 되면 동일한 파일 내에 컴포넌트가 존재하지 않아도 이벤트를 통해 렌더링 시점을 컨트롤할 수 있게 되어 컴포넌트 간의 결합도를 낮출 수 있게 되며, 내부 렌더링 함수를 외부로 내보낼 필요도 없게 됨으로써 컴포넌트의 응집도 또한 높일 수 있게 된다.

pub-sub 구조.

4.1 적용 사례.

 

아래의 첫 번째 스크린숏이 publisher부분으로 App컴포넌트에서 상태를 업데이트하고 Event Channel을 통해 변경된 상태를 전달해주는 부분, 두번째가 Event Channel, 새번째가 Subscriber부분으로 이벤트를감지하고 Event Channel을통해 받은 상태를 기반으로 setState함수를 호출하여 상태를 변경시키고 렌더링을 하는 코드다.

Publisher
Event Channel
Subscriber

이렇게 되면 Banner컴포넌트가 "banner"라는 이벤트로 묶여있기 때문에 어떤 파일 내에 존재해도 상태 변경을 감지하여 렌더링을 시킬 수 있게 된다.(컴포넌트 간 결합도가 낮아졌다) 또한 setState함수를 내부에서 관리하기 때문에 컴포넌트 자체의 응집력이 높아졌다.

 

 

 

5. 결론

지금은 데이터를 App컴포넌트에서 관리하고 있지만($target이 <div id = "app"></div>을 가리킴으로), 필요에 의해선 좀 더 작은 규모로 store를 줄일 수 있게된다. 그렇게되면 굳이 전역으로 관리할 필요가 없는 데이터를 관리하지않을 수 있게됨으로 데이터의 사용 용도를 좀더 쉽게 파악할 수 있지 않을까 라는 생각이 든다.