노션 클로닝 프로젝트 회고

최익·2023년 10월 30일
1
post-thumbnail

프로젝트 목표

  • 컴포넌트간 의존성 최대한 제거
  • 단방향 데이터 흐름
  • 코드의 역할 분리
  • 가독성 높은 코드
  • 모든 설계가 끝날때까지 키보드에 손 대지 않기

5가지의 목표를 두고 프로젝트를 시작하였고, 정확히 지켜진건 5번 하나 뿐이다..ㅠㅠ ㅋㅋㅋㅋ

프로젝트 설계 👇

우선 설계가 끝날때까지 키보드에 손을 아예 대지 않았다. 최대한 꼼꼼하게 설계하여 코드를 작성하다가 막히더라도 데이터의 흐름이 잘못되거나, 순환 참조로 인해 무한 루프에 걸리거나, 데이터를 잘못 받아와지는 실수는 하고 싶지 않았기 때문이다(설계에만 도대체 몇 시간을 썼는지 모르겠다 ㅠ).

최대한 루트에서 시작해서 데이터를 하위 컴포넌트로 뿌려주는 식으로 데이터 흐름도를 그렸고, 콜백에 콜백에 콜백에 콜백에... 콜백에.. 콜백 지옥이 발생하지 않도록 데이터와 코드의 흐름을 잘 파악하기 위해 리액트에서 발생하는 PropsDrilling을 최대한 만들지 않기 위해 많은 고민을 했다.

SPA 형태로 프로젝트를 만들기 위해 History API를 사용했으며, 루트 App.js에서 API를 호출하고 하위 App.js에 뿌려주며 각 App.js는 API를 호출하고 뿌려주는 역할을 하고, document를 클릭해 페이지가 변경되면 url이 변경되고 변경된 url의 pathname을 루트의 App.js가 확인하여 다시 데이터를 내려주는 방식이다.

마크업 적용

이 부분이 가장 시간이 오래 걸렸던 부분이다. 편집기에 작성한 텍스트에 마크업 문법을 적용하기 위해 contenteditable = true를 설정한 div 엘리먼트의 innerText를 모두 TextScan 파일내 별도의 함수의 매개변수로 넘겨주어 별도의 함수에서 split으로 구분하고 정규식을 통해 리턴해주는 로직을 작성하였다.

여기서 겪은 난항은 마크업 적용이된 텍스트를 다시 수정하여 마크업을 적용할때가 문제였는데, 이 부분을 해결하기 위해 div가 focuson이 되면 벨로그처럼 마크업 문법이 다시 텍스트에 나타나고 그 위에 수정을 하고 TextScan 파일내 별도의 함수로 다시 보내면 마크업이 적용되게끔 작성하였다.

이때 focuson 이벤트가 한번만 실행되게 해주고싶어 아래와 같이 이벤트 핸들러를 작성하였다.

// 마크업 제거 함수
export const removeMarkup = ($target) => {
  $target.addEventListener(
    "focusin",
    (e) => {
      // 중간 코드 생략
    },
    // 이 부분이 이벤트 핸들러가 한번만 동작하게 해준다.
    { once: true }
  );
};

addEventListener의 세번째 인자로 { once: true } 를 적용해주면 이벤트가 한번만 실행이 되는데 이번 프로젝트에서 유용하게 사용했다 ㅎㅎ

엘리먼트 Resizing

노션의 메뉴바를 클릭하여 늘리거나 줄일 수 있는데, 이번 프로젝트에서 직접 적용을 해봤다.

엘리먼트 Resizing은 처음 시도해보았는데, 대충 이런 구조로 작성했다.

총 3개의 엘리먼트 -> left, center, right를 만들고 center 엘리먼트가 mousedown 될 때, 현재 e.clientX의 좌표를 구하고 mousemove 될 때 초기 clientX 값과 이동중인 clientX의 값을 계속 비교하여 크기를 늘리거나 줄이고 mouseup 될 때 등록된 프로퍼티와 이벤트를 제거해주면 메뉴바가 늘어나거나 줄어드는 형식으로 구현했다.

프로젝트를 진행하며 얻은점

  • API 흐름도 작성 및 설계
    • 단방향으로 데이터 흐름을 설계
  • 컴포넌트 의존성 제거
    • 각 컴포넌트의 의존성을 제거하기 위해 하나의 컴포넌트에서 다른 컴포넌트에 접근 하지 않았고, 최대한 컴포넌트의 기능을 분리
  • 디바운스
    • 최근 팀원들과 모딥다 스터디를 하면서 디바운스와 스로틀에 대해 공부하였었는데 마침 강의에도 디바운스가 나왔었고, 이번 노션 클로닝을 하면서 왜 디바운스를 사용해야하고, 어디에 디바운스를 사용하면 적절한지 알게 되었다.
  • 리액트가 아닌 바닐라JS에서의 SPA 구현
    • History API를 사용해 바닐라JS에서 SPA를 구현함으로써 History API와 SPA에 대해 이해
  • 엘리먼트 Resizing
    • 엘리먼트 Resizing 로직을 직접 작성하며 Resizing 하는 방법 체화
  • 커스텀 이벤트
    • 커스텀 이벤트를 사용하여 그 안에서 History API를 사용해 페이지를 라우팅 해주며 커스텀 이벤트의 적절한 사용 예시에 대해 이해
  • async/await, fetch, Promise
    • 강의와 모딥다에서 공부했던 내용을 직접 프로젝트에 적용해보면서 체화

아쉬웠던 점

  • 편집기 및 메뉴바 수정부분 -> 실시간 렌더링을 적용하지 못한 점
    • 처음 설계가 루트 App.js에서 하위 컴포넌트로 데이터를 내려주는 방식이었고, 새로고침이나 다른 페이지에 갔다오면 마크업 또는 메뉴바 업데이트가 적용되는 흐름이었어서 먼저 설계 방식대로 구현한 뒤 남은 시간에 실시간을 적용해보려했는데 적용하지 못해 아쉬움이 남아 조만간 기능 추가를 해 볼 예정이다.
  • 깔끔하지 못한 코드
    • 주어진 기간 내에 모든 기능이 돌아가도록 하다보니 코드를 가독성있게 깔끔하게 짜지 못한 것 같다. 이 부분은 멘토님과 멘토님 동기들과 함께 모각코 했을때 멘토님 동기분이 해주셨던 조언인데 지금은 처음부터 너무 깔끔하게 짜려고 하는 것 보다, 먼저 구현해 놓고 리팩토링 하는것도 좋은 방법이라고 조언해주셨다. 지금 코드가 깔끔하지 못하니 리팩토링 해 볼 예정이다.
  • 렌더링 설계
    • 루트에서 데이터를 내려주는 게 아닌, 각 컴포넌트별로 따로 렌더링이 될 수 있게 다시 설계하고 코드를 수정하여 Ajax사용하는 것 처럼 렌더링 해줄 수 있도록 리팩토링 해 볼 예정.

프로젝트를 진행하며 겪었던 어려움과 해결 과정은 추후 포스팅 할 예정입니다 😁

profile
https://choi-ik.tistory.com/ 👈🏻 여기로 블로그 이전했습니다 ㅎ

0개의 댓글