
🚩 목표

🔎 기본 동작 원리

- App.js에서 모든 컴포넌트를 관리함.
- nav.js는 좌측 카테고리를 담당함.
- Editor.js는 text editor를 그림.
- 그 외 landingPage는 페이지의 시작을, utils는 API 연동 및 라우터 기능에 필요함.
📥 API 리스트 목록 가져오기
- API를 직접 연동하기 전
DUMMY_DATA를 만들어 반영이 되는지 확인 후 연동하기.
- API 구조 : 루트 document에 하위 document가 연속되는 형태.
- 단순
map을 활용하면 하위 document가 보이지 않는 현상 발생.
- 하위 document를 계속 호출하는 재귀 함수 사용하기!
function renderPosts(parentPost, currentPost) { if (!currentPost) return; currentPost.map(post => { const $postElement = document.createElement('div') const { id, title, documents: nextPost } = post $postElement.dataset.id = id $postElement.textContent = title parentPost.appendChild($postElement) renderPosts($postElement, nextPost) }) }
API 연동
const fetchPosts = async () => { const posts = await request('/documents') this.setState(posts) } fetchPosts()
🔗 라우터 구현
SPA 환경에서 어떻게 화면 전환을 이룰까?
- 라우터는 특정한 URL에 대해 특정한 뷰로 연결함.
- 랜딩 페이지에서 새로운 포스트를 만드는 버튼 클릭시 SPA환경에서 URL과 페이지 전환이 이루어져야 함.
라우터 원리
initRouter는CustomEvent를 등록함.
push를 통해 이동할 URL을 파라미터로 받아CustomEvent를 실행시킴.
CustomEvent가 실행될 때, initRouter로부터 전달받은 onRoute가 실행됨.
onRoute가 실행될 수 있는 이유?
initRouter가 호출될 때, 전달받은 onRoute를 closer로 기억하고 있기 때문.
const ROUTE_CHANGE_EVENT_NAME = 'route-change' export const initRouter = (onRoute) => { window.addEventListener(ROUTE_CHANGE_EVENT_NAME, e => { const { nextUrl } = e.detail if (nextUrl) { history.pushState(null, null, nextUrl) onRoute() } }) } export const push = nextUrl => { window.dispatchEvent(new CustomEvent(ROUTE_CHANGE_EVENT_NAME, { detail: { nextUrl } })) }
CustomEvent()생성자는 새로운 CustomEvent 를 생성함.

typeArg: DOMString 은 이벤트의 이름을 나타냄.
customEventInit (Optional): detail로 표현하며 기본 값은 null임. 이벤트에 관련된 이벤트 의존 값.
적용하기
- 페이지 전환이 필요한 page 돔 객체 생성.
initRouter로 this.route()를 실행시킴. 이때 this에 대한 실행 컨텍스트가 실행되지 않도록() => this.route()를 파라미터로 전달함.
- this.route는 pathname으로 그릴 컴포넌트를 결정함.
- LandingPage.js에서 이벤트를 발생시킬 버튼을 선택 후
push에 이동할 URL 이름을 전달함. ex)push('new-post')


뒤로가기
popstate 는 사용자의 세션 기록 탐색으로 인해 현재 활성화된 기록 항목이 바뀔 때 발생함.window.addEventListener('popstate', () => { this.route() })
💾 자동 저장 기능
- 텍스트를 입력하면 자동 저장이 되어야 함.
onEditing함수를 만들어 keyup 이벤트가 일어날 때setItem이 이뤄지도록 함.
- 포인트 1. 자동저장 이벤트가 일어날 때마다 render가 호출되어 input에 글을 쓸 수 없는 현상 발생 →
editor-container가 존재할 때만 render를 그릴 수 있도록 함.
- 포인트 2. keyup 이벤트 동작 원리 →
this.state[name]이 있을 때, target.value를 포함한 상태를 nextState로 전달함.

debounce
- keyup 이벤트 발생을 지연시킴.
- DB에 저장하는 시간을 늦춤으로써 효율적으로 데이터를 전송하기 위함.
timer변수를 설정하여setTimeout으로 setItem을 1초마다 발생하도록 함. 이때clearTimeout으로 onEditing이 발생할 때 timer가 초기화되도록 해주어야 함.
API로 GET, POST하기
- api에서 반환하는 자료, 필요로하는 파라미터를 파악해 해당 값 전달하기.
async,await사용하기.
- Editor.js에서
updateDocument를 이용해 api 서버에PUT한 후, App.js에서 라우터의 경로가 일치할 때fetchDocument를 하고 해당 값을 구조분해 하여 setState함.
✍️ 목록에서 문서 가져오기
- nav에서
addEventListener로 목록을 클릭하면 해당 id 값으로 url을push함.
- docEditPage에서
fetchPost로 state를 그림.
깨알 포인트
- padding-top을 주어야 자식 노드들까지 padding이 적용됨 + padding으로 선택해야 정확한 노드 선택 가능.
classList.add를 추천.className은 여러 class를 설정하기 어려움.
- render에다 이벤트를 걸면 버블링 발생으로 중복 실행됨.
- 하위 노드를 만들 땐 재귀와 depth를 이용해 style 등의 값을 컨트롤 하기!
- id값을 확인하는 코드는 중복되는 경우가 많으니
getId라는 함수를 만들기!
📑 문서 생성, 삭제
- 새로운 문서 만들기 버튼을 누르면
POST로 id값을 전달받아 해당 id로 편집 가능한 페이지의 url을 만듦.
reqeust함수에 서버에서 제공된 title, parent 값을 넣어 전달함.
- 반환된 값 중 id를 구조분해하여 url로
push함.
- 이를
postNewDocument라는 함수로 만들어 새로운 문서 생성이 필요한 곳에 전달.

Nav 목록 생성
this.postNewDocument = async (parentId) => { const document = await postDocument(parentId); document.title = "Untitled" document.documents = document.documents ?? []; push(`/documents/${document.id}`) return document; }
- 부모 id를 파라미터로 받아 document를 불러옴.
- document에 대한 정보를 처리함.
- document가 가진 id로 url을
push함.
- document를 return함 → 반환된 값으로
setState를 이용해 새로운 상태를 그리기 위함.
- 돔 객체는 HTML의 tree node 중 하나를 말하고, 컴포넌트는 로직이 있는 UI 뭉치를 의미함.
-
request를 받아 새로운 상태를 그린다는 개념이 잡혀 있어야 함.
- validate코드를 작성할 땐 객체인지 배열인지 구별하고 해당 형식 안에 어떤 값이 어떤 형태로 들어가 있는지 확인해야 함.
- Content-Type으로 보내는 데이터가 json임을 명시해주지 않으면 서버측에서 일반적인 text문으로 받아들여 제대로 요청이 전달되지 않음.Nav 목록 삭제
this.deleteDocument = async (id) => { await request(`/documents/${id}`, { method: "DELETE", }); push(`/`) }
- id를 파라미터로 받아 삭제함.
- root로 url을 이동함.
요소를 초기화하고 다시 그린다는 개념 잡기!
삭제, 생성에 필요한 함수 만들기
- doc으로부터 id를 찾는 함수 :
find를 이용하여 doc.id와 파라미터로 받은 id가 일치한다면 doc을 return하고, 그렇지 않은 경우 파라미터로 받은 doc을 순회하며 id를 찾음 (재귀함수).
- 루트로부터 doc을 생성하는 함수 :
doc을 파라미터로받아setState를 함.
- 부모로부터 doc을 생성하는 함수 :
parentId,doc을 파라미터로 받아 id로 parendDoc을 찾음. 찾은 값을 document로 지정하고 해당 document에 setState를 그림.
- 부모를 찾아 remove하는 함수 :
prentId와docId를 파라미터로 받아 부모 doc을 찾고 doc으로 지정, 페이지를 그림.delete함수로 해당 id를 지움.
상태에 대하여
상태가 필요할 때
→ 객체가 자율적으로 바뀔 때, 여러가지 인스턴스들이 개별적인 상태를 필요로 할 때!
기획이 확실히 이루어져야 함. 어디서 새로운 버튼이 필요한지, 새로운 버튼은 어떤 상태를 가지고 어떤 역할을 하는지 구분짓지 않으면 코드가 완성될 수 없음.