해당 id를 parent로 하는 새로운 document 생성 API 요청 후 fetchDocuments()를 통해 Sidebar를 리렌더링 한다.
onRemoveItem(id)
(id) ⇒ request(.../{id}) ⇒ fetchDocuments()
해당 id를 path로 하는 DELETE API 요청 후 fetchDocuments()를 통해 Sidebar를 리렌더링 한다
featEditor 내용 수정 시 server데이터에 디바운스 처리하여 반영<Editor>
커밋 설명
Editor의 state 및 setState 작동 확인
input event발생할 때마다 setState() 발생하고 새로 render() 함수를 호출 하지 않고,
첫 실행에만 template을 렌더링하고, 이후에는 element의 값만 변경하도록 처리
onEditing() -basic
title과 content가 변경되면, setState로 반영한다.
onEditing() - 실제 API 연동
title과 content가 변경되면, server에 반영할 수 있도록 API 수정 요청을 보낸다.
onEditiing() - debounce
연속된 요청이 있을 경우 매번 API요청을 하는 것이 아니라,
가장 마지막 요청만 실제 API request를 보내도록 한다.
feat 라우터 처리 - HistoryAPI를 통한 SPA 구성 <EditPage>
커밋 설명
라우터
💡
1. initRouter(onRoute)를 통해 ‘route-change’ 이벤트를 등록한다.
- route-change이벤트 발생 시, nextUrl을 받아 history에 push 후, onRoute() 작업실행
2. push(nextUrl) 메소드를 통해, nextUrl을 받은 후 ‘route-change’ 이벤트를 디스패치하여 실행시킨다.
3.onRoute() 함수로 this.route()를 등록한다.
- 현재 pathname을 가져와 해당 값에 따라 현재 state의 currentDocumentId를 적절히 바꾸어주는 역할
1. server State와 clientState를 하나로 통합시킬 것인가 VS 따로 관리할 것인가 ✅
(3/13) state는 렌더링에 적합한 자료형태로 담아야한다.
즉 isToggled와 같은 clientState를 위한 속성이 있어야하고, documentsState를 순회하면서 심플하게 렌더링할 수 있어야한다.
따라서 serverState를 fetch 한 직후에 이를 format하는 유틸함수를 통해 필요한 state형식에 맞게 가공하고 state에 저장한다.
flatDocuments() 함수는 재귀를 통해 serverData의 자식 document를 루트 도큐먼트를 기준으로 한 배열에 담는다.
Before
After
2. flex CSS에 대한 처리 부족
3. initialState 처리 필요한 것인가?
💡
roto의 경우 루트에서 state가 흐르는 것이 아니라, 필요로 하는 state를 해당 컴포넌트에 선언하고, 그 컴포넌트만을 직접 setState 해주는 방식을 사용한다.
나는 그러한 방식이 state를 쉽게 알아보기 어렵고, 흐름제어가 어렵다고 판단하여, 루트인 App에서 state를 흘러내려주는 구조를 취하고 있다.
이 때, roto가 사용하는 initialState를 props로 넘겨주는 방식은 내부 컴포넌트를 짐작할 수 있게 해주는 효과를 가지는데, 내가 사용하는 방식에는 적합하다고 생각하지 않는다.
initialState를 컴포넌트 내부에 선언하는 것 처럼 구현할 수 있는데, 적합한지 의문
4. EditPage 의 필요성
Editor에서 데이터 보관,저장 처리, 업데이트 등 모든 로직이 동작하고 있는데, 상태관리를 해주는 EditPage 컴포넌트를 만들어 Editor의 역할을 좀 더 분명히 하는 것이 바람직하다는 생각이 든다.
5. Router의 역할은 무엇인가 ✅ (3/19)
💡
Router 모듈은
1. 브라우저의 History관리 역할을 맡는다.
- 페이지 이동 시, 페이지 정보 담기
- 페이지 삭제 시, 접근 불가하도록 페이지 정보 지우기
2. path의 값에 따라 state.currentDocumentId 를 setState하여 리렌더링을 일으킨다.
라우터의 구현
💡
1. initRouter(onRoute)를 통해 ‘route-change’ 이벤트를 등록한다.
- route-change이벤트 발생 시, nextUrl을 받아 history에 push 후, onRoute() 작업실행
2. push(nextUrl) 메소드를 통해, nextUrl을 받은 후 ‘route-change’ 이벤트를 디스패치하여 실행시킨다.
3.onRoute() 함수로 this.route()를 등록한다.
- 현재 pathname을 가져와 해당 값에 따라 현재 state의 currentDocumentId를 적절히 바꾸어주는 역할
this.route()
현재 pathname을 가져온 후 currentDocumentId를 얻어와서, App의 state인 currentDocumentId를 update 해준다.
path가 ‘/’ 일 경우 예외 처리
path의 documentId로 setState() 해주어 App 전체 리렌더링
6. DocumentList에서 Document를 하나의 컴포넌트로 만들어 넣는 방식으로 변경하는 방법
현재는 template에서 document state를 순회하며, 템플릿에 직접넣어주고 있는데, Document를 하나의 컴포넌트로 만들어준다면, 렌더링 작업을 어디서해주어야할까..?
상위컴포넌트인 Sidebar의 mount 부분에서?
7. 리렌더링을 줄위기 위한 방법 ( router First, stateFirst) (3/19)✅
💡
현재 발생하는 리렌더링 문제는 path를 통한 리렌더링과 setState를 통한 리렌더링 방식은 혼돈해서 왔다.
”Event함수 → url path 변경 → path에 따른 setState → 리렌더링” 의 흐름제어를 통해 해결
해결방법
기존방식
onClickItem, onInserItem, onRemoveItem 등의 이벤트가 발생했을 때, setState로 currentDocumentId를 직접 바꾸어주는 방식
변경 방식
router의 push메소드를 통해 페이지 주소의 변경을 먼저 발생하게 하고, 이후 route() 함수가 실행되며, 현재 pathname을 통해 state의 currentDocumentId를 setState해주는 방식
변경을 통한 이점
기존 방식에서 setState로 리렌더링 된 이후, path변화를 읽어 또 다시 리렌더링 발생하는 현상 해결
그외 리렌더링 방지를 위해 사용 가능한 방법
proxy를 통해 Model을 만들어 변경된 state를 가지는 Model만을 리렌더링하는 방법
url path로 없는 documentId를 직접 접근 할 때, 이에 대한 처리를 어떻게 해주어야 하나에 대한 고민
방법1
path에서 documentId를 가져와 현재 documentList의 state를 순회하며, 존재 여부를 검사한 후 API요청을 진행한다.
방법2
입력한 path의 documentId로 GET API 요청을 진행하고, 404에러를 캐치하여, 이에 대한 후속처리를 진행한다.
해결방법
처음에는 API 콜을 줄이는 것이 좋지 않을까하여, 방법1 처럼 구현을 시도하였다.
하지만, 현재 documentList의 state 구조가 루트 도큐먼트를 기준으로 중첩배열의 형식으로 담겨 있어서, 빠른 탐색에 적합하지 않은 구조 라고 판단하였고,
코드흐름에 있어서도, API 콜 후 후속 에러처리하는 방식이 적합하다고 생각하였다.
try - catch
9. 404에러 처리를, 모든 api를 처리하고 있는 request.js에서 catch 하여 처리하는 것이 맞나?
💡
404에 대한 후속 처리( 404pag로 pushState)는 API 요청이 발생한 곳에서 해주는 것이 코드의 흐름을 파악하기에 더 용이하다고 생각했다.
따라서 response.status === 404 를 통해 404에러를 특정해내고, err.name을 지정하여 throw해준다.
이를 API 요청하는 곳 (Editor)에서 catch로 받아 후속처리를 해주도록 하였다.
function setEndOfContenteditable(contentEditableElement) {
var range, selection;
range = document.createRange(); //Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement); //Select the entire contents of the element with the range
range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection(); //get the selection object (allows you to change selection)
selection.removeAllRanges(); //remove any selections already made
selection.addRange(range); //make the range you have just created the visible selection
}