노션클로닝 회고

·2022년 4월 22일
0

회고

목록 보기
1/1

들어가며

데브코스 프론트엔드과정에 어느덧 한달차
노션클론 프로젝트에 들어갔다.

시작은 창대 했다. 마크다운 에디터 + 템플릿을 쓸수있는 사이트를 만들자 라는 생각아래에 시작한 프로젝트.

문제는 정말 빨리 찾아왔다.

바로 마크다운을 하는부분, contentEditable에서 찾아왔다.

프로젝트 구현

시연

프로젝트 구조

파일구조

📦src
┣ 📂page
┃ ┣ 📂ErrorPage
┃ ┃ ┗ 📜ErrorPage.js
┃ ┣ 📂PostEditPage
┃ ┃ ┣ 📂Components
┃ ┃ ┃ ┗ 📜Editor.js
┃ ┃ ┗ 📜postEditPage.js
┃ ┣ 📂SideBarPage
┃ ┃ ┣ 📂Components
┃ ┃ ┃ ┗ 📜PostList.js
┃ ┃ ┗ 📜sideBarPage.js
┃ ┗ 📂store
┃ ┃ ┗ 📜store.js
┣ 📂utils
┃ ┣ 📜stateManage.js
┃ ┗ 📜storage.js
┣ 📜App.js
┣ 📜Components.js
┣ 📜api.js
┣ 📜index.html
┣ 📜main.js
┣ 📜mainStyle.css
┣ 📜reset.css
┣ 📜router.js
┗ 📜validation.js

컴포넌트 설계

상태 기반 렌더링을 하기 위해서
직접 dom접근보다는 컴포넌트 클래스를 만들고 상태를 넘겨서
해당 상태 기반으로 렌더링을 할 수 있게 설계했다.

코어컴포넌트에 기본 컴포넌트 구조를 설계후 각 컴포넌트들이 해당 컴포넌트를 상속하도록 구현

해당 그림과 같이
app에서는 각 page만 호출하고 route 함수를 가진다.

각 page는 각 컴포넌트를 위한 상태를 가지고 아래로 prop로 넘겨준다.
그리고 sideBar 페이지에서 해당 문서 클릭시 해당 문서의 id 값을 editpage에 넘겨주기 위해서

route를 통해서 id 값을 넘겨준다.

title 같은 경우에는 editpage와 실시간 동기화가 되어야 하기 때문에
pub-sub 모델로 만든 간단 상태 관리자로 만든 -store파일 documentState 객체로 상태를 관리했다.

프로젝트 후 회고

공부하고 배운점들

상태기반 렌더링

해당 프로젝트를 하기 전에도 바닐라 js로 상태 기반 렌더링을 한 적은 있다.
하지만 대부분 간단한 컴포넌트들로 구성된 개인적인 공부들 이였기 때문에
간단한 클래스들로 해결이 되었었다.

이번 프로젝트는 그것 보다는 구조가 커서
어떻게 컴포넌트에 상태를 구조 어떻게 렌더링을 해야 할지 좀더 고민을 했던 경험이 이였다
하지만 제대로 고민하지 않으면 스파게티 코드가 되는 문제가 있었다.

바닐라 js로 css in js 흉내내기

js 파일안에서 css도 해결을 하고 싶었다.

해서

const styleEditor = `
display:flex;
flex-direction:column;
margin: 1rem;
color: rgba(143, 84, 79, 1);
`;

과 같은 형태로 style변수를 만들어서 해결을 했지만

hover와 같은 가상선택자는 해당 인라인 style로 할 수 없었기 때문에
hover는 css 파일로 해서 오히려 코드의 복잡도를 늘리는 문제가 되었다.

selection 과 range같은 webApi 들에 대해서 알아본것

커서 caret가 계속해서 초기화 되는 문제를 해결 하기 위해서
mdn문서를 찾고 실험을 해본경험은 좋은 경험이였다.

결국 공식문서에서 보고 해결을 해야하는것이 꼭 필요한 역량이라고 생각해서 좋은 경험이였다.

Css 를 활용해본것

css를 이때까지는 가볍게만 했었는데

이번에는 그래도 좀 있어 보이게 만들고 싶어서
나름 flex도 활용하면서 각 tag가 어떻게 박스 모델이 잡히는지 조금은 이해를 한듯하다.

pub-sub구조의 이해

상태관리를 좀 더 잘해보기 위해서
구독 발행 모델을 활용하여서 간단한 함수를 구현해서 써본경험은 나쁘지 않았다.

하지만 특정 document를 delete 하는 기능에서 상태변경이 안되는 버그가 발생했다.

KTP 회고

Keep 계속해서 해나갈것

컴포넌트와 상태기반 렌더링

상태기반 렌더링과 컴포넌트로 사고하는것은 결국 후에 리액트라던지 뷰 같은 라이브러리 이해에도
많은 영향을 줄 것 같다

Problem 문제, 해결?

1. 명확하지 않은 코드 설계 ❌ 해결 하지 못함

처음 개발에 들어갔을때 명확한 구조보다는

간단하게 app에서 각 페이지를 호출하고 각 페이지가 각 컴포넌트를 구성 하는 폴더 설계만 하고 들어갔다

문제가 생긴것은 모든 컴포넌트가 상속하는 코어 컴포넌트의 명확하지 않은 설계였다.
그래서 해당 컴포넌트의 상속으로 해결이 안된다면 코어 코어컴포넌트에 계속 추가로 기능을 넣게 되면서 버그가 점차 발생을 하기 시작했다.

그러다 보니 각 매서드가 1가지 기능만을 구현하지 못하게 되었고
추상화도 제대로 되지않았다.

2. caret 이 튀는 문제 ❌ 해결 하지 못함

set State마다 innerHtml로 재 렌더링을 하니깐
caret이 처음으로 이동하는 문제가 생겼다..

해서 찾아보니깐 webApi중 커서를 기억하는 방법을 찾았고
section과 range api를 발견하였다.

해당 기능을 사용해서 caret위치를 기억하는데에는 성공했지만
한글이 자소분리가 되는 치명적인 버그가 생겼다,

3. 상태관리. 🛑 반 정도 해결

SideBar Page와 editor Page의 상태 공유

title에 경우 에디터와 side과 실시간으로 상태를 공유를해서 렌더링을 해야하는
기능 구현의 문제가 있었다.

해서 pub-sub형태의 상태관리 모듈을 간단하게 구현을 해보았다

get 해서 가져오는 전체 document의 상태만 documentState를 만들어 보관을 하였고
PostList 컴포넌트의 setState를 해당 상태에 구독 시켜서

title이 변경될시 documentState의 상태를 변경시켜서
자동으로 postList로 발행되도록 설계하였다.

4. 에디터 contentEditable 문제 ⭕️ 그럭저럭 해결

chrome은 cotentEditable에서 엔터를 칠시 자동으로 div를 추가 해준다.
cotentEditable 처음 입력시 첫 라인은 div로 감싸져있지 않았다.
해서 해당 코드로 해결을 했었다,

let richContent = "";
    if (this.state?.content) {
      richContent = this.state.content
        .split("<div>")
        .map((line) => {
          console.log(line);

          line = line.replace("</div>", "");
          if (line === "") return "";
          if (line.indexOf("# ") === 0) {
            line = line.replace(/[\#]{1}(.+)/g, "<div><h1>$1</h1></div>");
            console.log(line);
          } else {
            line = `<div>${line}</div>`;
          }
          return line;
        })
        .join("");
    }

2번쨰 문제는
라인 맨 앞에 캐럿을 두고 엔터를 치면 해당 라인의 태그가 복제되는 버그가 있엇다.

고민을 하다 멘토님의 조언으로
perventDefault로 기본 동작을 막고 해당 이벤트를 커스텀을 하기로 했다.
그리고 노션처럼 한줄 한줄을 contentEditable하게 개발을 했다.

멘토님 답변

⭕️ 그럭저럭 해결

Try 앞으로 해볼것

1번의 문제해결 - 리팩토링

1번의 문제 해결을 위해서 해당 프로젝트를 다시 리팩토링을 해볼 계획이다.
이번엔 함수형과 타입스크립트를 활용을 해볼 계획

그리고 다음 과제 및 프로젝트에서는 정기적으로 리팩토링 시간을 가져야 하겠다고 생각이 들었다.
옳은 방향으로 설계하는지 스스로 점검하는 시간이 필요하다

2번의 문제 해결

다른 동료분들의 구현을 보니
마크다운을 구현을 했으면서, 자소 분리는 안되는 것을 확인했다.

방법이 있다는 것을 알았으니 해당 코드를 참고해서 다시 리팩토링 시간에 해볼 생각이다.

터널시야를 벗어나기

2번 문제를 해결하면서 나에겐 터널시야가 있다는 것을 생각했다.

무조건 rendering 쪽에서 마크다운과 커서 값을 해결을 해야하고

실시간으로 커서값을 기억을 해야 한다 라는 문제 해결 방안에 집중한 나머지
다른 방안을 생각 해내지 못했다.

이벤트 쪽에서 해결을 할 수도 있었고
더 좋은 방법이 있을 수 있었지만

사고가 갇히면 나올 수 없다 더 유연한 사고와 시야를 가져야 한다.

앞으로 나아갈 방안

1-2시간에 한번 사고 및 코드 검열시간을 가지기

0개의 댓글