[Mission4/이혜준] - Project_Notion_VanillaJS#24
[Mission4/이혜준] - Project_Notion_VanillaJS#24solar3070 wants to merge 48 commits intoprgrms-fe-devcourse:3/#4_leehyejunfrom
Conversation
tooooo1
left a comment
There was a problem hiding this comment.
혜준님 같이 팀해서 너무 좋았어요!
마지막 리뷰하러 방문했어용. 프로젝트 하느라 고생 많이하셨습니다.
| @@ -0,0 +1,12 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| if (className === "no-sub-page") { | ||
| return; | ||
| } |
chunwookJoo
left a comment
There was a problem hiding this comment.
혜준님 노션 프로젝트 하시느라 고생하셨습니다!
꼼꼼한 성격답게 더미데이터로 체크하면서 코딩하신것 같아서 많이 배워가는 것 같습니다.
페이지 생성이나 삭제할때 토글리스트 닫히는건 혜준님이면 금방 하실것 같아요~~
한달동안 수고하셨고 다른 팀 가셔도 화이팅 하세용~
src/components/Editor.js
Outdated
| if (!isInitialize) { | ||
| $editor.innerHTML = ` | ||
| <input type="text" name="title" style="width: 600px;" value="${this.state.title}" /> | ||
| <textarea name="content" style="width: 600px; height: 400px;">${this.state.content}</textarea> |
There was a problem hiding this comment.
style.scss파일이 있는데 여기서 따로 지정해둔 이유가 있을까요??
그리고 혹시 textarea 사이즈 고정을 원하시면 resize:none 을 주셔도 좋을 것 같습니당
There was a problem hiding this comment.
해당 스타일링 코드는 다른 커밋에서 scss파일로 옮겨두었습니다!
사용자가 마음대로 늘리게 하고 싶어서 resizeL none은 주지 않았어요 :)
| const { id } = $li.dataset; | ||
| const { className } = e.target; | ||
| if (className === "post-delete") { | ||
| onDelete(id); |
There was a problem hiding this comment.
문서삭제 같은 경우는 사용자에게 한번 더 물어보는게 어떨까요??
(배포하신거 사용해보다가 바로 삭제돼서 당황쓰..)
There was a problem hiding this comment.
생각해보니 지금 사용자가 실수로 사용했을 경우를 방지하지 않고 있어서 충분히 당황할 수 있을 것 같네요.
모달 창으로 삭제 여부를 물어보거나 노션처럼 삭제 후 되돌리기 기능을 넣는 것도 좋을 것 같아요! 의견 감사합니다 👍
| removeItem(postLocalSaveKey); | ||
|
|
||
| this.setState({ | ||
| postId: post.id + "", |
There was a problem hiding this comment.
저는 이거 일일히 toString()으로 사용했는데 이렇게 하면 훨씬 간결해지네요 👍
| await request(`/documents/${postId}`, { | ||
| method: "DELETE", | ||
| }); | ||
| }; |
css/style.scss
Outdated
| font-size: 20px; | ||
| cursor: pointer; | ||
|
|
||
| &:hover { |
There was a problem hiding this comment.
아 &를 이럴때 쓰는거네요..
저는 hover 효과 줄 때
.toggle-button:hover 이런식으로 하나를 또 만들었는데ㅋㅋㅋ 배워갑니당
|
|
||
| .no-sub-page { | ||
| padding-left: 39px; | ||
| user-select: none; |
beni1026
left a comment
There was a problem hiding this comment.
안녕하세요 혜준님 코드리뷰 하러 왔습니다!
과제하시느라 수고 많으셨습니다 ~~
페이지 배포까지 하시다니 대단하세요!!
기능도 잘 작동하고 css 공들이신게 느껴질 정도로 노션과 똑같이 구현하셨네요 ~~ 👍
다음 과제도 화이팅 입니다!
| /* 컬러 코드*/ | ||
| $BROWN_10: #fbfbfa; | ||
| $BROWN_20: #ebebea; | ||
| $BROWN_30: #dddddc; | ||
| $BROWN_40: #d3d1cb; | ||
| $BROWN_50: #b5b5b2; | ||
| $BROWN_60: #908f8c; | ||
| $BROWN_70: #676662; | ||
| $BROWN_80: #5e5c57; | ||
| $BROWN_90: #37352f; No newline at end of file |
| export const renderPosts = (root, posts, sub, down = false) => { | ||
| let $ul = document.createElement("ul"); | ||
| if (down) { | ||
| $ul.classList.add("post-hide", "sub-post"); | ||
| } | ||
| root.appendChild($ul); | ||
|
|
||
| posts.forEach((post) => { | ||
| let $li = document.createElement("li"); | ||
| $li.setAttribute("data-id", post.id); | ||
| $li.innerHTML = ` | ||
| <div class="post"> | ||
| <div> | ||
| <span> | ||
| <button class="toggle-button" data-is-opened="false">▸</button> | ||
| </span> | ||
| <span class="post-title">${ | ||
| post.title ? post.title : "제목 없음" | ||
| }</span> | ||
| </div> | ||
| ${ | ||
| !sub | ||
| ? ` | ||
| <div class="button-group"> | ||
| <button class="post-create">✚</button> | ||
| <button class="post-delete">✖</button> | ||
| </div>` | ||
| : "" | ||
| } | ||
| </div> | ||
| `; | ||
|
|
||
| $ul.appendChild($li); | ||
| if (post.documents.length) { | ||
| renderPosts($li, post.documents, sub, true); | ||
| } else { | ||
| let $ul = document.createElement("ul"); | ||
| $ul.setAttribute("class", "post-hide"); | ||
| $ul.innerHTML = `<span class="no-sub-page">하위 페이지 없음</span>`; | ||
| $li.appendChild($ul); | ||
| } | ||
| }); | ||
| }; |
There was a problem hiding this comment.
코드 보니까 서브 페이지 링크 구현 방식에 대해 이해가 되네요 👍 저도 나중에 구현해보겠습니다
| export const toggle = ($button, $li) => { | ||
| const { dataset, classList } = $button; | ||
| const isOpened = JSON.parse(dataset.isOpened); | ||
| const $ul = $li.querySelector("ul"); | ||
| if ($ul) { | ||
| !isOpened ? $ul.classList.remove("post-hide") : $ul.classList.add("post-hide"); | ||
| } | ||
| dataset.isOpened = !isOpened; | ||
| if (!isOpened) { | ||
| classList.add("open-toggle"); | ||
| } else { | ||
| classList.add("close-toggle"); | ||
| setTimeout(() => { | ||
| classList.remove("open-toggle", "close-toggle"); | ||
| }, 300); | ||
| } | ||
| }; |
There was a problem hiding this comment.
토글 방식을 class를 이용해서 하는 방법이 있군요 배워갑니다 !!
fly1chop
left a comment
There was a problem hiding this comment.
안녕하세요 혜준님~ 프로젝트 하느라 수고 많으셨습니다~
배포까지 하셔서 직접 사용해볼수있어서 너무 좋았습니다 ^^ 스타일링도 아주 깔끔하게 하셨구요..!!
editing하는 부분에 의견 살짝 남겨봤습니다~
| let $ul = document.createElement("ul"); | ||
| $ul.setAttribute("class", "post-hide"); | ||
| $ul.innerHTML = `<span class="no-sub-page">하위 페이지 없음</span>`; | ||
| $li.appendChild($ul); |
| window.dispatchEvent(new CustomEvent("update-tree")); | ||
| } |
There was a problem hiding this comment.
저도 이 부분 어떻게 처리할지 고민 많이 했는데 커스텀 이벤트를 활용하는것이 좋네요..!! 이것도 나중에 변수로 만들어내면 더 깔끔할것같다는 생각입니다~
| this.setState({ | ||
| postId: post.id + "", | ||
| post, | ||
| }); |
There was a problem hiding this comment.
editing 할때마다 this.setState => editor.setState하게되어 저장하면 form focus가 없어집니다. 제 생각에는 editing할때는 setState 없이 api 업데이트만 하고 page reload, push state할때만 setState 하면 더 좋을것같습니다~
There was a problem hiding this comment.
오 안그래도 이 부분 고민했었는데 감사합니다! 더 고민해보겠습니다
process.env는 Node 환경에 존재하는 환경변수입니다. (
근본적으론 포스트들의 오브젝트 모델을 가지고 DOM과 싱크하는 형태로 구조를 바꾸고, 필요한 부분만 렌더링되게 하는 방식으로 구현되어야 전체가 다 렌더링되는 문제가 근본적으로 해결될 수 있을 것 같아요. 혹은, rerender를 할 때 열고 닫혀있는 페이지들의 리스트를 user state를 저장하는 오브젝트 따위를 따로 두고 렌더링되는 시점에 이 state가 반영되게 하는 식으로도 문제 우회는 가능할 것 같아요.
앱이 시작하는 시점에서 초기화하는 것은 괜찮은 것 같습니다!
공통화를 잘 하신 것 같습니다! :)
|
| }, | ||
| }); | ||
|
|
||
| this.route = () => { |
There was a problem hiding this comment.
route 보단 좀 더 구체적으로 어떤 동작을 하는지 설명하는 onRouteChange 같은 이름이 좋을 것 같아요!
|
|
||
| this.render = () => { | ||
| $postList.innerHTML = ""; | ||
| this.state.length && renderPosts($postList, this.state, false); |
There was a problem hiding this comment.
이런 경우엔 short-circuit보단 (&&) if문을 이용하는 것이 더 보기 좋습니다.
| await request(`/documents/${post.id}`, { | ||
| method: "PUT", | ||
| body: JSON.stringify(post), | ||
| }); |
There was a problem hiding this comment.
사용부에서 await을 어짜피 하기 때문에 await을 하기보단 그냥 return을 하는게 낫지 않을까 생각됩니다!
| let isInitialize = false; | ||
|
|
||
| this.render = () => { | ||
| if (!isInitialize) { |
There was a problem hiding this comment.
isInitialize 라는 별도의 변수를 두기보단, innerHTML이 비어있는지 여부로 체크도 가능할 것 같아요.
(가능하면 변수의 사용을 줄이는 것이 읽기 좋기에..)
📌 과제 설명
바닐라 JS만을 이용해 노션을 클로닝합니다.
현재 미완성된 기능이 많아서 코드리뷰 기간에도 계속해서 개발하고 스타일링하려고 합니다.
배포 링크
https://juns-notion.netlify.app/
페이지 생성, 수정, 삭제시 열려있던 하위 페이지들이 모두 닫히는 현상이 있습니다.. 😂
👩💻 요구 사항과 구현 내용
기본 요구사항
기본적인 레이아웃은 노션과 같으며, 스타일링, 컬러값 등은 원하는대로 커스텀합니다.
글 단위를 Document라고 합니다. Document는 Document 여러개를 포함할 수 있습니다.
보너스 요구사항
✅ PR 포인트 & 궁금한 점