노션 클로닝 회고
자바스크립트와 더 친해진 것 같다..!
2023-07-18
노션 프로젝트 회고록
보러가기노션 클로닝 회고를 kpt방법으로 진행해보려고 합니다! kpt방법은
- Keep : 프로젝트를 진행하면서 간직하고 싶은 좋았던점/잘했던점
- Problem : 프로젝트를 진행하면서 겪었던 문제점/어려웠던 점
- Try : Problem 중 해결된 사항에 대한 해결 방법
이 세 단계에 거쳐서 회고를 하는 것입니다!.
프로젝트 요구사항
먼저 기본 요구 사항은 다음과 같습니다
-
글 단위를 Document라고 합니다. Document는 Document 여러개를 포함할 수 있습니다 .
-
화면 좌측에 Root Documents를 불러오는 API를 통해 루트 Documents를 렌더링합니다 .
-
Root Document를 클릭하면 오른쪽 편집기 영역에 해당 Document의 Content를 렌더 링합니다.
-
해당 Root Document에 하위 Document가 있는 경우, 해당 Document 아래에 트리 형 태로 렌더링 합니다.
-
Document Tree에서 각 Document 우측에는 + 버튼이 있습니다. 해당 버튼을 클릭하 면, 클릭한 Document의Document로 새 Document를 생성하고 편집화면으로 넘깁니다 .
-
-
편집기에는 기본적으로 저장 버튼이 없습니다. Document Save API를 이용해 지속적 으로 서버에 저장되도록 합니다.
-
History API를 이용해 SPA 형태로 만듭니다.
-
루트 URL 접속 시엔 별다른 편집기 선택이 안 된 상태입니다.
-
/documents/{documentId} 로 접속시, 해당 Document 의 content를 불러와 편집기 에 로딩합니다.
-
다음으로 보너스 요구사항은 다음과 같습니다.
-
기본적으로 편집기는 textarea 기반으로 단순한 텍스트 편집기로 시작하되, 여력이 되면 div와 contentEditable을 조합해서 좀 더 Rich한 에디터를 만들어봅니다.
-
편집기 최하단에는 현재 편집 중인 Document의 하위 Document 링크를 렌더링하도록 추가합니다.
-
편집기 내에서 다른 Document name을 적은 경우, 자동으로 해당 Document의 편집 페 이지로 이동하는 링크를 거는 기능을 추가합니다.
-
그외 개선하거나 구현했으면 좋겠다는 부분이 있으면 적극적으로 구현해봅니다!
기본사항만큼은 제대로 지키는 것을 목표로 개발을 진행했습니다
최종 컴포넌트 구조
좌측의 Document들의 리스트들과 관련된 컴포넌트들을 DocumentPage에서, 편집기와 관 련된 컴포넌트들은 DocumentEditPage에서 조율하도록 하였습니다.
Keep
가장 좋았던 점은 컴포넌트를 어떻게 독립적으로 구성하면 좋을지, 컴포넌트는 어떤 역할을 해야만 하는지 배운 것입니다.
기존 제가 짰던 코드들을 보면, 되게 명령형적으로 코드를 짠 것을 볼 수 있습니다.. 부끄럽지만,실제 제가 1년 전에 ..짰던 코드를 보면
DOM요소를 가져와서 -> 어떤 연산을 해! 하는 명령형 코드로 작성된 것을 볼 수 있습니다.
이러면 코드를 다시 사용하기에도 모호해지고, 특정 파일에만 종속적인 코드가 되어버립니다!
const FindEqualItem = document.querySelector('.item.OperCalculate');
/*
수식을 저장할 거임
*/
const StoreExpress = document.querySelector('.item.StoreExpresstion');
/*
=가 아닌 item 전부 찾아서 클릭하면 문자열 덧붙임
*/
const FindAllItem = document.querySelectorAll('.item:not(.OperCalculate)');
Array.from(FindAllItem).forEach((EachItem) => {
EachItem.addEventListener('click', () => {
FindAllItem[0].innerHTML += EachItem.innerHTML;
});
});
/*
AC->누르면 둘 다 초기화 해줌
*/
const Init = document.querySelector('.item.OperInit');
Init.addEventListener('click', () => {
FindAllItem[0].innerHTML = '';
StoreExpress.innerHTML = '';
});
이렇게 한 파일안에서 종속적인 코드가 아닌, 독립적으로 다시 쓸 수 있는 컴포넌트를 만들고 설계하는 법을 배운게 가장 좋았던 점입니다!
이번 과제를 하면서 가장 크게 중점을 둔 것은 아래의 3가지 항목입니다!
- 컴포넌트는 데이터를 그리는 데에만 집중한다.
- 데이터를 가져오거나 이벤트를 등록하는 콜백은 외부에서 주입해 최대한 독립적이게 작동하게 만든다.
- 컴포넌트마다 상태를 갖고, 해당 상태에 따라 랜더링을 어떻게 할지 결정한다.
실제로 이렇게 명확히 컴포넌트의 역할에 집중하다보니, 틀린부분을 찾았을때 고치기 에도 매우 쉬워졌습니다. 추가적인 요구사항이 들어왔을떄에도 잘 적용할 수 있을 것 같았어요!
Problem
과제를 하면서 마주친 문제는 총 2가지입니다.
- 첫번쨰 문제는 위의 구조도에서 편집기의 제목이 바뀌면 문서 리스트에 있는 해당 문서의 제목이 즉각적이게 바뀌어야 하지만, 문서의 리스트는 DocumentPage, 편집기 는 DocumentEditPage에 위치하고 있었습니다.
편집중일떄 -> 문서의 리스트의 상태를 바꿔서 문서의 리스트들을 다시 그려야 할 필요가 있었습니다.
- 두번쨰 문제는 API호출 실패의 대한 처리입니다. API호출을 실패할 때 어떤 조치를 취해야 할지가 어려웠습니다.
과제뿐만 아니라,, 실제 현업에서 어떻게 API 실패를 처리하는지 가장 궁금하기도 했던 주제였습니다!
Try
- 첫번쨰 문제는 편집기가 작동해서 제목이 바뀌었을떄, 리스트를 다시 그리는 함수를 props로 계속 전달해서 해결했습니다.
//App.js
//편집기의 제목이 바뀌면 리스트들을 다시 그리는 함수를 props로 전달!!
const documentEditPage = new DocumentEditPage({
$target,
initalState: {
documentId: 'new',
document: {
title: '',
content: '',
},
},
onChangeEditorTitle: () => {
documentPage.render();
},
});
다만 이 방법이 적절한가?를 스스로 생각해보았을떄, 지금 프로젝트보다 더 복잡한 구조, 컴포넌트가 많이 생기는 구조에서는 적절치 못하다는 느낌을 받았습니다.
어쩌면 지금의 구조보다 더 복잡한 구조가 된다면, 전역적으로 관리할 저장소가 필요하다는 느낌을 받았어요!
- 두번쨰 API호출 실패에 대한 문제는 단순히 최대한 상세하게 에러 코드를 분기하고, 해당 에러를 alert으로 띄우는 것으로 해결하였습니다.
const request = async (url, options = {}) => {
const res = await fetch(`${API_ENDPOINT}${url}`, {
...options,
headers: {
'Content-type': 'application/json',
'x-username': `${USER_NAME}`,
},
});
if (res.ok) {
return res.json();
}
switch (res.status) {
case 400:
throw new Error(API_ERROR.ERROR_400);
case 401:
throw new Error(API_ERROR.ERROR_401);
case 403:
throw new Error(API_ERROR.ERROR_403);
case 404:
throw new Error(API_ERROR.ERROR_404);
default:
throw new Error(API_ERROR.ERROR_DEFAULT);
}
};
느낀점
노션 클로닝 프로젝트 동안, 컴포넌트의 역할, 자바스크립트로 컴포넌트를 어떻게 만드는지 많이 깨달았던 것 같습니다. 개발을 계속 하면서 제가 데브코스에서 꼭 이루고싶었던, 순수 자바스크립트를 잘 알고, 잘 다루는 사람에 조금이나마 가까워 진 느낌 이였어요.
실제 과제의 코드는 아래의 링크에서 보실 수 있습니다!
PR링크