-
Notifications
You must be signed in to change notification settings - Fork 28
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_VanillaJs 과제 #37
base: 4/#5_judahhh
Are you sure you want to change the base?
Conversation
4/#5 judahhh working
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드가 깔끔해 보여요 😄
과제하느라 고생 하셨습니다. 👏
|
||
const mainPage = new MainPage({ | ||
$target, | ||
initialState: "주다현", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p5: INITIAL_USER_NAME or DEFAULT_USER_NAME 이런식으로 문자열을 상수화를 해서 사용한다면 더 좋지 않을까 생각합니다. 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다현님 고생하셨습니다!
바닐라 JS에 익숙하지 않아 스트레스 많이 받으셨는데 과제 하시는거 보니까 점점 바닐라 JS에도 익숙해지시는거 같아요. 조금만 지나면 라이브러리 쓸 수 있으니 화이팅해요~
수고하셨어요 !
@@ -0,0 +1 @@ | |||
ApiKey.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p5: 이거는 혹시 무슨 이유로 추가하셨나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apiKey가 여기서 노션 api에 접근하기 위한 auth인데 gitignore로 지금 감춰놨습니다.
여기 github 프로젝트에서는 다른 사람이 확인할수 없으므로 포함시켜주는게 맞습니다. @judahhh
만약 감추고 싶다면 배포된 페이지 링크라도 있어야해요.
this.state = nextState; | ||
const document = this.state.document; | ||
if (document) { | ||
if (document.title === "제목없음") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p4: 이런 조건들은 상수로 빼도 괜찮을 것 같아요!
}); | ||
|
||
this.setState = async (nextState) => { | ||
if (this.state.documentId !== nextState.documentId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3: 내부에서 this.state내부 참조를 많이하는데
const {document, documentId} = this.state;
이런식으로 미리 빼두면 더 편하게 사용할 수 있을 것 같아요
const fetchDocument = async () => { | ||
const { documentId } = this.state; | ||
|
||
if (this.state.documentId !== null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p2: 위에서 구조분해로 빼놓고 여기서는 다시 안빼는 이유가 있을까요..?
export const displayDocumentList = (documents) => { | ||
return `<ul> | ||
${documents | ||
.map((document) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p3: 이런식으로 미리 빼두면 더 편할 것 같아요 : )
.map((document) => { | |
.map(({id,title,documents}) => { |
|
||
if (this.state.documentId === "new") { | ||
editor.setState( | ||
this.state.document || { | ||
title: "", | ||
content: "", | ||
} | ||
); | ||
} else { | ||
await fetchDocument(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p4: 빈 게시글로 상태변경하는 로직이 반복되는 것 같은데 이런식으로 할수도 있을 것 같아요?
if (this.state.documentId === "new") { | |
editor.setState( | |
this.state.document || { | |
title: "", | |
content: "", | |
} | |
); | |
} else { | |
await fetchDocument(); | |
} | |
function getEmptyDocument(){ | |
return {title:'',content:''} | |
} | |
if (this.state.documentId === "new") { | |
editor.setState( | |
this.state.document || getEmptyDocument() | |
); | |
} else { | |
await fetchDocument(); | |
} |
$documentList.addEventListener("click", (e) => { | ||
const { target } = e; | ||
const element = target.closest("[name]"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p4: 이런식으로도 바꿀수 있을것 같습니다! 그리고 element라는 변수명은 약간 추상적일수 있다고 생각하는데 조금더 구체적으로 네이밍하면 어떨까요?
$documentList.addEventListener("click", (e) => { | |
const { target } = e; | |
const element = target.closest("[name]"); | |
$documentList.addEventListener("click", ({target}) => { | |
const element = target.closest("[name]"); |
const element = target.closest("[name]"); | ||
|
||
if (element) { | ||
const id = element.dataset.id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p5: 비슷하지만 저는 요즘 이렇게 사용해요!
const id = element.dataset.id; | |
const {id} = element.dataset; |
textarea { | ||
border: none; | ||
text-align: justify; | ||
line-height: 1.8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p5: 저는 line-height에 px같은 단위나 %를 붙여서 사용했는데 단위를 안붙이면 어떻게 적용되나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 다현님 과제 하시느라 고생하셨습니다! 다현님 코드 보면서 뭔가 여러가지 알아가는 느낌이라 좋네요 늦은 시간에 죄송합니다 ㅠㅠ
|
||
this.render = async () => { | ||
await fetchDocuments(); | ||
$target.prepend($documentPage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런건 처음 봤네요 배워갑니다..
const { pathname } = window.location; | ||
if (pathname === "/") { | ||
removeDiv(".edit-page"); | ||
mainPage.render(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확실히 이렇게 메인 페이지가 따로 있는 게 좋은 선택인 것 같네요 ㄷㄷ
}); | ||
|
||
if (this.state.document.title) { | ||
if (this.state.document.title !== document.title) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.state.document.title 가 예상치 못한 값일 수 있어서 조건문이 두개인걸까요? 뭔가 아래 조건문 하나로도 될 것 같은 데 정확히는 모르겠네요..
|
||
new Header({ | ||
$target: $documentPage, | ||
initialState: "주다현", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사소한 문제 같긴하지만 이름은 또 쓰일 수도 있으니까 변수에 담아 쓰면 좋을 것 같네요!
src="https://kit.fontawesome.com/0ab4707042.js" | ||
crossorigin="anonymous" | ||
></script> | ||
</head> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<meta name="viewport" content="width=device-width, initial-scale=1.0">
도 있어야 할 것 같아요
if (this.state.documentId !== null) { | ||
const document = await request(`/${documentId}`); | ||
|
||
const getTempDocument = getItem(documentLocalSaveKey, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 함수는 쓰이지 않는것 같네요
$editPage.className = "edit-page"; | ||
this.state = initialState; | ||
|
||
let documentLocalSaveKey = `temp-document-${this.state.documentId}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클래스안에 이렇게 변화할수있는 변수가 있는 것은 좋지 않습니다. 나중에 React를 쓰더라도 마찬가지입니다.
this.getDocumentLocalSaveKey =()=> {
return `temp-document-${this.state.documentId}`;
};
const tempDocument = getItem(this.getDocumentLocalSaveKey(), {
title: "",
content: "",
});
위처럼 질의함수로 바꾸는 연습을 해보세요
clearTimeout(timer); | ||
} | ||
|
||
timer = setTimeout(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
debounce용으로 타이머를 onEditing에 넣으신 것으로 추측됩니다.
debounce를 따로 util/debounce.js
로 만들고 아래와 같이 사용할 수 있도록 변경해보세요
onEditing: debounce(async (document) => {
// ...
}, 500),
}); | ||
}; | ||
|
||
export const push = (element) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이름이 너무 범용적입니다. 좋은 네이밍을 고민해보세요
import { request } from "../api/api.js"; | ||
|
||
const ROUTE_EVENT_NAME = "click-event-route-change"; | ||
const ROUTE_EDIT_EVENT = "edit-event-route-change"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 이벤트는 안쓰는 것 같네요
const ROUTE_EVENT_NAME = "click-event-route-change"; | ||
const ROUTE_EDIT_EVENT = "edit-event-route-change"; | ||
|
||
export const editorRoute = (onRoute) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 작성하면 객체지향원칙에서 Open-Closed Principle (OCP)을 위반한 것입니다. 제가 만약 이 코드를 유지보수해야한다고 하면, 직접 수정해야 하므로 모든 로직이 제대로 제대로 돌아갈지 의심하면서 작성해야합니다.
뿐만 아니라 else if가 많아서 한번에 이해하기가 어렵네요.
const eventHandlers = {
'list': async (id, documentId) => {
if (id == null) {
id = documentId;
}
history.pushState(null, null, `/${id}`);
onRoute(null);
},
'remove-btn': (id, documentId) => {
if (documentId === id) {
history.pushState(null, null, "/");
}
onRoute(null);
},
.....
}
window.addEventListener(ROUTE_EVENT_NAME, async (e) => {
const { type, id } = e.detail;
const { pathname } = window.location;
const [, documentId] = pathname.split("/");
const handler = eventHandlers[type];
if (handler) {
await handler(id, documentId);
}
});
위처럼 작성해보는건 어떨까요?
} | ||
|
||
.document-list { | ||
font-size: 0.98em; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어떻게 font size가 결정될지 두근두근하면서 확인해야하는 ... font size네요
@@ -0,0 +1,23 @@ | |||
import { push } from "../utils/router.js"; | |||
|
|||
export default function Header({ $target, initialState }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헤더는 깔끔하게 잘 작성하셨네요
const $editor = document.createElement("div"); | ||
$editor.className = "editor"; | ||
$target.appendChild($editor); | ||
let isinitialize = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
카멜케이스에 isInitialize
따라 바꾸면 좋을 것 같습니다. 그리고 질의함수로 바꾸는걸 연습해보세요.
this.isInitialized = function() {
return isInitialize;
};
|
||
this.render(); | ||
|
||
$editor.addEventListener("keypress", (e) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
후에 React를 쓰실때도 참고사항이지만 keypress는 deprecated예정으로 keyup등을 사용해보세요
https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event
가장 어려운 노션 페이지 만드는 것을 잘 완성해내셨습니다. 다만 제가 결과를 확인할 수가 없는데, 배포된 페이지나 apiKey를 추가해야 할 것 같습니다.
|
📌 과제 설명
노션의 기본적인 기능을 구현하는 노션 클로닝 과제를 진행하였습니다.
👩💻 요구 사항과 구현 내용
기본 요구사항
파일 구조
📦src
┣ 📂api
┃ ┗ 📜api.js
┣ 📂components
┃ ┣ 📂editContainer
┃ ┣ 📂sidebar
┃ ┣ 📜DocumentEditPage.js
┃ ┣ 📜DocumentList.js
┃ ┣ 📜DocumentPage.js
┃ ┣ 📜Editor.js
┃ ┗ 📜Header.js
┣ 📂pages
┃ ┗ 📜MainPage.js
┣ 📂style
┃ ┗ 📜style.css
┣ 📂utils
┃ ┣ 📜ApiKey.js
┃ ┣ 📜displayDocumentList.js
┃ ┣ 📜removeDocumentList.js
┃ ┣ 📜router.js
┃ ┗ 📜storage.js
┣ 📜App.js
┗ 📜index.js
스크린샷
✅ 피드백 반영사항
최대한 함수분리를 하여 가독성 있는 코드를 작성하려고 노력하였습니다.
✅ PR 포인트 & 궁금한 점
컴포넌트 설계 시 이것보다 더 나은 방법이 있다면 알려주세요!