
🚩 목표

🔎 기본 동작 원리

- 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를 지움.
상태에 대하여
상태가 필요할 때
→ 객체가 자율적으로 바뀔 때, 여러가지 인스턴스들이 개별적인 상태를 필요로 할 때!
기획이 확실히 이루어져야 함. 어디서 새로운 버튼이 필요한지, 새로운 버튼은 어떤 상태를 가지고 어떤 역할을 하는지 구분짓지 않으면 코드가 완성될 수 없음.