Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mission5/신호원] Project_Notion_Vanilla JS 과제 #45

Open
wants to merge 66 commits into
base: 4/#5_HowonShin
Choose a base branch
from

Conversation

howons
Copy link

@howons howons commented Jul 6, 2023

📌 과제 설명

노션 클로닝 프로젝트 🗒️

Jul-07-2023 17-12-08

<a>배포 링크</a>

레포지토리를 클론한 뒤 yarn & yarn start를 실행하면 webpack-dev-server로 실행할 수 있습니다.

Vanilla JS 로 작성하였고 각 js, html, css, svg 파일은 webpack 의 loader module을 통해 빌드하였습니다.

컴포넌트 구조

App

image
App 컴포넌트에서 Sidebar 와 Header를 렌더링하고 router를 설정합니다.

router

image
router 함수에서 url 을 기준으로 id에 맞는 Document나 Home 페이지를 렌더링합니다.

Sidebar

image
Sidebar는 root Document의 링크 묶음인 root Drawer를 가지고 있습니다.
Drawer는 Document의 링크인 DrawerItem 요소를 묶습니다.
DrawerItem은 하위 요소로 Drawer를 다시 가져 자신의 자식 문서 링크를 렌더링합니다.
이를 통해 트리 구조의 Document 링크들을 계속해서 렌더링할 수 있습니다.

Header와 Sidebar의 state는 state setter를 별개의 파일은 stateSetter.js에 등록하여 Document 컴포넌트에서도 state 조작이 가능하게 하였습니다.

👩‍💻 요구 사항과 구현 내용

기본 요구사항 구현 내용

  • 글 단위를 Document로 하고 Document는 Document를 여러개 포함할 수 있습니다.
  • 화면 좌측에 Root Documents를 불러오는 API를 통해 루트 Documents를 렌더링 합니다.
    • Root Document를 클릭하면 오른쪽 편집기 영역에 해당 Document의 Content를 렌더링 합니다.
    • 해당 Root Document에 하위 Document가 있는 경우, 해당 Document 아래에 트리 형태로 렌더링 합니다.
    • Document Tree에서 각 Document 우측에는 + 버튼이 있습니다. 해당 버튼을 클릭하면, 클릭한 Document의 하위 Document로 새 Document를 생성하고 편집화면으로 넘깁니다.
    • documentId에 해당하는 Document를 삭제합니다.
      • 만약 하위 documents가 있는 document를 삭제한 경우, 하위 documents 등은 상위 document가 없어지므로 root document로 인식됩니다.
  • 편집기에는 기본적으로 저장 버튼이 없습니다. Document Save API를 이용해 지속적으로 서버에 저장되도록 합니다.
  • History API를 이용해 SPA 형태로 만듭니다.
    • Root URL 접속 시엔 별다른 편집기 선택이 안 된 상태입니다.
    • /document/{documentId}로 접속 시, 해당 Document의 content를 불러와서 편집기에 로딩합니다.

추가 구현 내용

  • 현재 편집기에 렌더링된 Document는 사이드바에 있는 링크가 강조되도록 표시합니다.
  • Header 에는 부모에서 자신까지 루트가 표시되며 클릭하면 해당 Document를 렌더링합니다.
  • Document를 작성하고 저장완료 전에 페이지를 이동하여도 저장이 이어지도록 구현했습니다.
  • 편집기에 나타나있는 문서 삭제 시 Home 페이지로 이동
  • Home 페이지에 최근 사용한 문서 / 자주 사용한 문서 목록 나타내기

구현 미완료

  • 기본적으로 편집기는 textarea 기반으로 단순한 텍스트 편집기로 시작하되, 여력이 되면 div와 contentEditable을 조합해서 좀 더 Rich한 에디터를 만들어봅니다.
  • 편집기 최하단에는 현재 편집 중인 Document의 하위 Document 링크를 렌더링하도록 추가합니다.
  • 편집기 내에서 다른 Document name을 적은 경우, 자동으로 해당 Document의 편집 페이지로 이동하는 링크를 거는 기능을 추가합니다.

✅ 피드백 반영사항

✅ PR 포인트 & 궁금한 점

  • 현재 구조 중에 비효율적인 부분이 있는 지 궁금합니다.
  • Sidebar의 DrawerItem 에서 상태 업데이트와 클릭을 위해 커스텀 이벤트와 이벤트 리스너를 많이 사용하고 있습니다. 혹시 문제가 될 지 의견 부탁드립니다.
  • 로컬 스토리지 등의 도움없이 Sidebar의 상태 업데이트 시 open 상태를 유지하기 위해 부분적 렌더링을 구현했습니다. 혹시 로직에 오류가 없는 지 궁금합니다.
  • 빌드를 production으로 하면 문제가 생겨서 development mode로 배포했습니다. 원인을 아신다면 조언 부탁드립니다..

Copy link

@kutta97 kutta97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

호원님, 프로젝트 하시느라 정말 수고 많으셨습니다..!
스스로를 민달팽이라고 하시던데, 민달팽이 치고는 기능 구현능력이 뛰어나신 것 같습니다ㅎㅎ

코드 리뷰를 조금 늦게 시작했는데, 하루만에 코드리뷰를 끝내야 하다보니
시간이 없어서 호원님의 코드를 꼼꼼하게 리뷰해드리지 못한 점 죄송합니다ㅠㅠ

개인적으로 호원님의 코드를 읽으면서 전반적으로 좋았던 점은, 항상 코드의 성능 효율성을 위해 새로운 시도를 하시려는 부분이었습니다. 그리고 조금 아쉬웠던 점은 그러한 호원님의 코드 로직이 너무 복잡하다보니 코드를 읽고 이해하기가 어려웠다는 점이었습니다.

데이터가 한 방향으로만 흐르도록 하는 컴포넌트 패턴의 규칙을 최대한 따르면서, 각 컴포넌트는 최대한 하나의 일만 하도록 코드를 작성하는 연습을 해보시는 것도 좋을 것 같다고 생각합니다 :)

Q. 현재 구조 중에 비효율적인 부분이 있는 지 궁금합니다.

시간 관계상 코드를 꼼꼼하게 뜯어보지 못해서 비효율적인 부분이 있는지는 제대로 확인하지 못했습니다ㅠㅠ 다만, 사이드바의 Drawer 컴포넌트에서 render plan 부분이 비효율적이라기보다는, Drawer 의 state 와 Drawer item 의 state 를 sync 를 맞춰주기 위한 로직 코드가 너무 복잡해서 이해하기가 어려웠던 것 같습니다. 이 부분은 성능상으로는 비효율적이더라도, 다시 랜더링해주도록 코드를 작성하는 것도 코드 이해측면에서 좋을 것 같다고 생각합니다.

Q. Sidebar의 DrawerItem 에서 상태 업데이트와 클릭을 위해 커스텀 이벤트와 이벤트 리스너를 많이 사용하고 있습니다. 혹시 문제가 될 지 의견 부탁드립니다.

호원님의 방식은 Drawer > Drawer Item > Drawer > DrawerItem 이런식으로 DrawerItemDrawer 가 감싸주는 방식으로 이해했는데, 여기서 Drawer �Item 마다 이벤트 리스너를 달아주는 것이 문제가 될 수 있을 것 같긴 합니다. 그래서 개인적으로는 호원님이 DrawerItem 에서 처리해주신 이벤트위임 코드의 의미가 퇴색될 것 같다고 생각합니다.

Sidebar 에서 DrawerItem 들의 이벤트 위임을 해주도록 바꿔주시면 더 좋을 것 같다고 생각합니다.

Q. 로컬 스토리지 등의 도움없이 Sidebar의 상태 업데이트 시 open 상태를 유지하기 위해 부분적 렌더링을 구현했습니다. 혹시 로직에 오류가 없는 지 궁금합니다.

아하, 그런 이유로 rendering plan 코드가 탄생한 것이었군요. 저는 충분히 좋은 고민이었고, 그 고민에 대한 해결책을 위해 호원님께서 최선을 다하셨다고 느꼈습니다. 하지만, 제 생각에는 이 부분은 로직의 오류에 대한 고민보다는 좀 더 Component 패턴을 최대한 응용하여 가독성있는 코드를 작성하는 것이 더 좋을 것 같다고 생각합니다!

Q. 빌드를 production으로 하면 문제가 생겨서 development mode로 배포했습니다. 원인을 아신다면 조언 부탁드립니다..

혹시 이 부분은 어떤 문제가 발생하셨는지 공유 부탁드려도 될까요?

Comment on lines 12 to 13
registerStateSetter($sidebar);
patchSidebarState();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registerStateSetter() 와 patchSidebarState() 함수는 어떤 이유로 사용하지는건지 궁금합니다!

제 생각에는 각 컴포넌트의 setState 함수를 전역에서 가지고 있다가, 호출하여 사용하는 방법이 조금 어색한 것 같습니다...! 이런 방식으로 setState 함수를 사용하면 코드가 짧아진다는 장점이 있으나, 다른 개발자가 읽기에는 직관적이지 않고, 어떤 컴포넌트에서든 다른 컴포넌트의 setState 를 호출하여 사용할 수 있다는 점에서 휴먼에러가 발생하기 쉬울 것 같습니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document가 헤더나 사이드바의 상태 업데이트를 시켜줘야 하다보니 나름대로의 store? 를 구현해보려고 한 건데 데이터 흐름이 직관적이지 않게 된 것 같네요.

Comment on lines 38 to 43
<section class="document-title-section">
<textarea name="title" value="${this.state.title}"></textarea>
</section>
<section class="document-content-section">
<textarea name="content"></textarea>
</section>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

titlecontent 를 각각 다른 Component 로 분리하고, 각각의 이벤트리스너도 각 Component 에서 관리하도록 하는 방법도 좋을 것 같습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라고 썼는데, 지석 멘토님의 코멘트와 겹치는 내용이 있는거 같아서 첨언해 보자면, 아래의 quertSelectorAll 을 통해서 title 과 content 에 각각 이벤트 리스너를 달아주고, state 를 init 해주는 로직이 있는 것으로 보이는데, 한 함수 내에서 너무 많은 일을 하고 있는 것처럼 느꼈기 때문입니다.

지석멘토님의 조언처럼, 함수를 쪼개서 명시적으로 보이게끔 리팩토링하는 방법도 괜찮을 것 같구요, 제 경우에는 이런 경우 컴포넌트를 쪼개고 각 컴포넌트의 props 로 eventHandler 를 전달하는 방식을 선호합니다.

제 방식대로 리팩토링한다면... 약간 이런 느낌이 될 것 같네요.

const handleEditTitle = () => { ... };
const handleEditContent = () => { ... };

const $title = new Title({
  $target,
  initialState: { title: '제목' },
  onEdit: handleEditTitle
});

const $content = new Content({
  $target,
  initialState: { content: '내용' },
  onEdit: handleEditContent
});

this.setState = (nextState) => {
  const { title, content } = nextState;
  $title.setState({ title });
  $content.setState({ content });
};

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 나중에 editor 구현할 때 구체화하겠거니 하고 적당히 채워넣은 부분이라 뭉뚱그려진 느낌이 나버렸네요..
팀 스터디 때 개선해보겠습니다!

this.render();
};

this.init = once(() => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once 를 사용하여 한 번만 실행되도록 할 수 있군요!
호원님의 코드에서 좋은 것을 배워갑니다 ㅎㅎ

src/components/Header/Header.js Outdated Show resolved Hide resolved
src/components/Home/Home.js Outdated Show resolved Hide resolved
src/components/Sidebar/Drawer.js Outdated Show resolved Hide resolved
Comment on lines 42 to 66
this.renderingPlan.forEach((plan) => {
if (plan === 0) {
this.children[stateIdx].setState(this.state[stateIdx]);

$currentNode = $currentNode.nextSibling;
stateIdx += 1;
} else if (plan > 0) {
const $drawerItem = new DrawerItem({
$target: $drawer,
$sibling: $currentNode,
parent,
level,
});
$drawerItem.setState(this.state[stateIdx]);
this.children.splice(stateIdx, 0, $drawerItem);

stateIdx += 1;
} else {
const $tempNextNode = $currentNode?.nextSibling;
$drawer.removeChild($currentNode);
$currentNode = $tempNextNode;

this.children.splice(stateIdx, 1);
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drawer 에서 각각의 컴포넌트의 render plan 을 하나의 배열로 가지고 있고, 이 plan 에 따라서 rendering 해주는 방법인 것으로 이해했습니다.

setRenderingPlan 으로 미리 랜더링 계획을 계산해주도록 하는 부분은 좋은 시도였던 것 같으나, 저는 개인적으로 호원님의 해당 코드를 이해하기가 조금 어려웠던 것 같습니다. 또한, 각각의 DrawerItem 의 상태를 현재 rendering plan 과 동일하게 싱크를 맞춰주어야 하기 때문에 호원님의 코드에 다른 기능을 추가하거나 수정하기가 어려울 것 같다는 생각이 듭니다.

제 생각에는 모든 DrawerItem 을 그냥 처음부터 다시 생성, 랜더하도록 하는 편이 가독성 측면에서 더 좋지 않을까 생각합니다..!

Copy link

@hayamaster hayamaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

호원님 진짜 너무 고생하셨습니다! 컴포넌트도 잘 분리하시고, 코드도 잘 읽혔습니다!
진짜 노션인줄 알고 깜짝 놀랐습니다....🙄
항상 조용히 최고의 길을 걸으시는.... 저도 호원님처럼 꾸준하게 자신만의 길을 걷는 사람이 되고 싶어요!

몇가지 추가로 말씀드리고 싶은 것이 있는데, 어디에 적어야할지 몰라 여기에 적겠습니다!

  1. 기본사항 중에
    "Document Tree에서 각 Document 우측에는 + 버튼이 있습니다. 해당 버튼을 클릭하면, 클릭한 Document의 하위 Document로 새 Document를 생성하고 편집화면으로 넘깁니다."
    부분이 있습니다!
    하지만 새 페이지를 생성하였을 때, 생성된 페이지로 넘어가지 않습니다!
    호원님께서 이미 생성해두신 함수 routeToDocument를 활용하면 금방 수정하실 수 있을 것 같습니다!

  2. 상위 문서에서 하위 문서를 생성할 때, 1-2초간 상위 문서 이름이 '제목 없음'으로 변경되는 오류가 있습니다! 이 부분도 수정해주시면 더욱 좋은 UX가 될 것 같습니다!

너무 수고하셨습니다 ☺️

}

let timer;
const $document = document.createElement("section");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

시멘틱 태그를 적절하게 잘 사용하시는 것 같아요!!!
section 태그는 섹션(부분, 구역, 영역) 들을 그룹화 해서 분리하는 역할을 한다고 합니다! 참고
저는 그냥 무작정 div로 하는데 고쳐야 될 것 같습니다😓

this.render();
};

this.init = once(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once란 함수이름으로 단 한번만 실행된다는 것을 바로 알았습니다 ㅎㅎ
네이밍 센스👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 책에서 주워온 함수입니다. 😎

Comment on lines +7 to +9
if (!isConstructor(new.target)) {
return;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유효성 검사를 진짜 꼼꼼히 하신 것이 느껴집니다!
배우겠습니다!

Comment on lines 51 to 52
$recent.className = "home-recent-container";
$most.className = "home-most-container";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최근 사용한 문서와, 가장 많이 사용한 문서를 보여주고, 클릭시 해당 페이지로 이동하는 기능 너무 멋져요~!!
저도 기회가 되면 추가해 보고 싶습니다!

Comment on lines 73 to 78
.sort(
([aid, aval], [bid, bval]) => bval.lastUsedTime - aval.lastUsedTime
)
.slice(0, 10)
.map(([id, { title }]) => `<p data-id=${id}>${title}</p>`)
.join("")}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

크~ 알고리즘 문제를 매우 깔끔하게 푸시더니 역시 빛을 발휘하시네요👏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort, slice, map, join 다 저희가 머쓱이 하면서 많이 배운 부분인데 과제에서 빛을 발하시는 호원님 ㅎㅎ
능숙하게 사용하시는 모습 존경합니다! 저도 배우고 싶어요 ㅎㅎ

Comment on lines 150 to 151
window.removeEventListener("route-drawer", this.activate);
window.removeEventListener("title-updated", this.updateTitle);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 Item을 삭제하면 저절로 사라지지 않을까요?
따로 이벤트를 삭제해주는 부분을 추가해주신 이유가 궁금합니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 삭제하는 부분인거 같은데 삭제한 뒤의 결과물? 상태값을 result에 넣어주는 건가요?
result 보다는 좀 더 삭제가 정상적으로 되었다 또는 비정상적으로 되지 않았다는 의미를 담은 변수명을 지어주면 어떨까요?
delete에 res를 받는 부분인 거 같아서요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명시적으로 삭제해주지 않으면 메모리에 남아서 나중에 터지기 때문에 이벤트 위임을 쓴다는 글을 본 것 같아서 추가했습니다!
result는 확실히 어색한 것 같아서 response로 바꿨습니다.

Comment on lines 15 to 26
export const ERROR = {
NEW_MISSED: "Current component is declared without 'new' keyword.",
NONE_OBJECT_STATE: "Tried to set state using none object value.",
INVALID_DOCUMENT_STATE: "Tried to set document's state using invalid data.",
INVALID_DRAWER_STATE: "Tried to set drawer's state using invalid data.",
INVALID_DRAWERITEM_STATE:
"Tried to set drawer item's state using invalid data.",
INVALID_HEADER_STATE: "Tried to set header's state using invalid data.",
INVALID_HOME_STATE: "Tried to set home's state using invalid data.",
NONE_ARRAY_STATE: "Tried to set state using none array value.",
INVALID_REQUEST: "서버에 문제가 발생했습니다.\n 잠시 후 다시 시도해주세요.",
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 문구를 이렇게 따로 빼서 처리하는군요 👏🙄

Copy link
Member

@Lim-JiSeon Lim-JiSeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

과제 수행하시느라 고생하셨습니다~!
호원님 코드를 보면 정말 세심하신 것 같아요. 사소한 부분들을 최대한 챙기시고 고민하신 부분이 보입니다. 특히 new 방어코드, validation 배운 부분들도 적극적으로 활용한 모습 보고 저도 배워야겠다고 많이 느꼈어요!
피드백 반영하면서 저도 이런 부분들은 신경쓸 수 있도록 노력하겠습니다~!
다른 분들이 너무 좋은 말씀 많이 해주셔서... 피드백을 많이 못 드린것 같아 죄송합니다 ㅠㅜ

src/components/App.js Outdated Show resolved Hide resolved
$document.className = "document-container";
$document.innerHTML = `
<section class="document-title-section">
<textarea name="title" value="${this.state.title}"></textarea>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

title에 textarea를 사용하셔서 신기했어요.
여러 줄 입력을 고려해 input 대신 textarea를 선택하신 걸까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

임시로 대충 때웠던 흔적입니다 🥲
스터디 때 수정해보겠습니다.

import { stateSetters } from "@Utils/stateSetters";

export default function Home({ $target }) {
if (!isConstructor(new.target)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번에 저는 new 방어코드를 신경쓰지 못했는데 신경써서 수정해야겠습니다!

Comment on lines 73 to 78
.sort(
([aid, aval], [bid, bval]) => bval.lastUsedTime - aval.lastUsedTime
)
.slice(0, 10)
.map(([id, { title }]) => `<p data-id=${id}>${title}</p>`)
.join("")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort, slice, map, join 다 저희가 머쓱이 하면서 많이 배운 부분인데 과제에서 빛을 발하시는 호원님 ㅎㅎ
능숙하게 사용하시는 모습 존경합니다! 저도 배우고 싶어요 ㅎㅎ


stateSetters[NAME.HEADER]([]);
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최근 사용한 문서와 자주 사용한 문서 기능을 추가하셔서 유저 입장에서 더 좋은 것 같아요 💯

src/components/Sidebar/Drawer.js Outdated Show resolved Hide resolved
Comment on lines 150 to 151
window.removeEventListener("route-drawer", this.activate);
window.removeEventListener("title-updated", this.updateTitle);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 삭제하는 부분인거 같은데 삭제한 뒤의 결과물? 상태값을 result에 넣어주는 건가요?
result 보다는 좀 더 삭제가 정상적으로 되었다 또는 비정상적으로 되지 않았다는 의미를 담은 변수명을 지어주면 어떨까요?
delete에 res를 받는 부분인 거 같아서요.

INVALID_HOME_STATE: "Tried to set home's state using invalid data.",
NONE_ARRAY_STATE: "Tried to set state using none array value.",
INVALID_REQUEST: "서버에 문제가 발생했습니다.\n 잠시 후 다시 시도해주세요.",
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation과 error 처리 부분 깔끔하게 정리해주시고 세심하게 챙기시는 모습 저도 본받아야 겠네요...
이번 과제에서는 신경쓰지 못했는데 배워갑니다 ㅎㅎ

return await request(`/documents/${documentId}`, {
method: "DELETE",
});
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api를 정리해서 함수화한 코드가 더 사용하기도 좋고, 깔끔한 거 같아요!
저는 그때그때마다 작성했는데 이런 부분은 수정하는 편이 더 좋을 것 같네요.
항상 배워갑니다.

new ValidationError(ERROR.INVALID_HOME_STATE)
)
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼하게 validation 신경써주시는 모습 좋습니다 💯

@howons howons closed this Jul 11, 2023
@howons howons reopened this Jul 11, 2023
@howons
Copy link
Author

howons commented Jul 11, 2023

@arclien @kutta97 답글을 남길 곳이 없어서 production 배포 이슈는 여기에 올립니다.
production 배포 링크

router.js

 if (pathname === "/") {
    stateSetters[NAME.HOME](null);

이 부분에서 stateSetters[NAME.HOME] 이 function이 아니라는 오류가 납니다.
setState를 전역 변수에 등록하는 것보다 route 호출이 먼저 돼서 undefined가 돼버린 것 같은데 어째서 그런 건지는 예상이 안 갑니다..
production과 development mode 가 차이 나는 부분은 source-map 부분 뿐인 것 같은데 관련해서 주의해야 할 이슈 같은 게 있는 걸까요?

마지막 커밋에 base url 수정은 혜진님과 창욱님꺼 세팅 보면서 수정한 부분입니다. 해당 문제가 해결되지는 않더라구요.

howons added 23 commits July 16, 2023 14:18
setStateOf으로 감싸고 에러 처리 추가
DocumentContainer에서 route 관리하도록 분리
dispatch와 autoSave로 함수 분리
openParents와 sendHeaderDocsInfo를 activate 내부에서 통합 처리
click 핸들러 내부에서 action에 따른 로직 핸들러로 분리
마진 제거 및 커서 설정
삭제 문서 replaceState 대응
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants