노션,옵시디언 같은 에디터 기능을 vanilaJS만 사용하여 간단하게 구현해보았다.
개발기간 - 일주일
사실 처음에는 생각보다 간단한 프로젝트가 될 거라고 생각했었다.
컴포넌트를 크게 왼쪽에 Sidebar / 오른쪽에 문서의 내용을 보여주는 Content 라는 컴포넌트로 나누어서, 두 컴포넌트가 상호작용할 일은 크게 없지 않을까? 라는 생각을 했다
이런식으로
Sidebar에서 이벤트 발생 -> API 요청 통해 Content 렌더
라는 일방적인? 방법만 생각하고 기능 구현에 들어갔다.
Sidebar의 state - {directory : 전체 문서 data}
Content state {id : 선택된 문서의 id , title , content ...}
Sidebar 클릭하면 -> 클릭된 문서의 id값으로 API 요청 후 Content state 변경
하지만 점점 기능을 추가하고 싶다는 생각이 들었고, 예외 케이스가 하나둘씩 생겨나게 되었다.
Ex)
노션 기능중에 문서 제목을 수정하면 -> 왼쪽의 Sidebar 문서 제목도 수정되는 기능이 있다.
또 Sidebar에서 현재 편집중인 문서를 삭제하면 -> 현재 편집중인 문서는 더 이상 편집하지 못하게 만들어야 하는데,
그러려면 Sidebar에서 이벤트가 단순히 API 요청해서 받는 게 아니라, 추가적인 기능이 더 필요하다는 것을 알게 되었다.
여기서 모든 기능의 공통적인 특징으로는
각 컴포넌트에서 어떤 이벤트가 발생해서 특정 값이 바뀌게 된다면, 두 컴포넌트 모두 변경되었다는 것을 인지하고 있어야 된다는 것이다.
Ex)
Sidebar에서 문서를 클릭했다면 - Content에서 API 요청
문서를 삭제 - 삭제된 문서랑 현재 Content에 있는 문서랑 같은지 비교
Content에서 제목 수정 - Sidebar 다시 렌더링
이를 위해 옵저버 패턴(Observer Pattern)이라는 디자인 패턴이 존재한다
옵저버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.
한마디로 특정 데이터(store)를 구독하고 있으면 , 이 데이터가 변경되었을 시 구독자들에게 알릴 수 있다는 뜻이다.
Sidebar 컴포넌트에는 존재하는 문서 전체의 data를 API 요청받아 렌더링한다
id 값으로 url을 이동시켜주고, Content에서 이를 파싱해서 API 요청을 보낸 후 그 값을 기반으로 렌더링처리 해주고 있음그래서 처음에는 각 컴포넌트에서 Store에 대한 구독을 각각 하는 방식으로 했다.
Store - {directory : '전체 파일구조에 대한 데이터', deletedDocument : 삭제된 파일에 대한 데이터}
Sidebar - Store를 구독하여 사용
Content - Store를 구독하고는 있지만, 컴포넌트의 state는 그대로 사용하여 deletedDocument
단순히 변경되었다는 걸 알리기만 하면 - 구독되어있는 함수가 실행되는 패턴이기 때문에 복잡한 기능을 생각하지 않아도 되어서 좋긴 했지만, 각 컴포넌트별로 구독을 진행할 필요가 있을까? 에 대한 의문이 들었다
{directory : 전체 문서 ,
selectedDocument : 선택된 문서(Content에 렌더링됨) , deletedDocument
, newDocument ...}
const initialState = {
number: 0
};
function reducer(state, action) {
switch (action.type) {
case 'ADD':
return {
number: state.number + 1
}
default:
return state;
}
}
간단한 예시로
1. 버튼을 클릭 시 'ADD'라는 액션이 발생하게 코드를 짜놓는다( 보통 dispatch({action : "ADD" , payload : null} 이런식으로 한다)
2. reducer 함수를 통해 'ADD' 라는 action이 발생하면 해당 state를 변경시킨다.
3. state가 변경되었음을 구독중인 컴포넌트에 알려, 렌더링이 발생한다
이를 이용해서,
Sidebar 클릭 시 해당 state의 selectedDocument 값을 현재 클릭한 문서의 id 값으로 변경시키고 싶다 라고 하면
1. 클릭 이벤트 발생 시 , dispatch를 통해 Store에 클릭 이벤트가 발생했다고 알리면서 특정 값을 전달한다.
2. 이후 Store에서는 reducer 함수를 통해 전달받은 액션과, 값을 가지고 state를 변화시킨 다음
3. 다시 Component에 state가 변화되었다는 걸 알린다(이후 다시 렌더링)
이런 식으로 문서의 삭제, 추가, 수정 이벤트마다 특정 리듀서 함수를 통해 state를 변경시키고, 다시 렌더링 시키는 방식으로 구현해 보았다