Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9832d02
🍱 feat: svg 파일 추가
dmswl98 Nov 16, 2022
b15a0c5
✨ feat: 기본 파일 추가
dmswl98 Nov 16, 2022
8bab2c6
✨ feat: api 로직 분리
dmswl98 Nov 16, 2022
5e7817f
✨ feat: 404 페이지 추가
dmswl98 Nov 16, 2022
f82c1b4
✨ feat: 라우팅 로직 설계
dmswl98 Nov 16, 2022
f8e2b62
✨ feat: Sidebar 컴포넌트 구현
dmswl98 Nov 16, 2022
5f2857f
✨ feat: Editor 컴포넌트 구현
dmswl98 Nov 16, 2022
d537924
✨ feat: PostPage 컴포넌트 구현
dmswl98 Nov 16, 2022
272d1e9
✨ feat(PostPage): debounce를 이용한 수정 기능 구현
dmswl98 Nov 16, 2022
bdaf1ad
💩 chore: 불필요한 코드 삭제 및 수정
dmswl98 Nov 16, 2022
9c5673b
✨ feat(SidebarNav): 클릭된 문서 표시
dmswl98 Nov 16, 2022
2c5333a
✨ feat(Sidebar): 현재 문서가 삭제된 경우 루트 페이지로 리다이렉트되도록 구현
dmswl98 Nov 16, 2022
ce0ea52
✨ feat: 에디터 하단의 하위 문서 링크를 클릭한 경우 Sidebar에 열람 중인 문서임을 표시
dmswl98 Nov 16, 2022
889f7bd
✨ feat(Editor): 텍스트 스타일 메뉴 구현
dmswl98 Nov 17, 2022
95990c7
♻️ refactor: 불필요한 코드 삭제 및 코드 분리
dmswl98 Nov 17, 2022
1fea468
📝 docs: 요구 사항 및 구현 사항 기록
dmswl98 Nov 17, 2022
408e099
✨ feat: Sidebar가 열려있을 때 Sidebar 닫기 버튼 숨기기
dmswl98 Nov 18, 2022
064c959
✨ feat: Sidebar가 열려있을 때 Sidebar 닫기 버튼 숨기기
dmswl98 Nov 18, 2022
4c87e69
Revert "✨ feat: Sidebar가 열려있을 때 Sidebar 닫기 버튼 숨기기"
dmswl98 Nov 18, 2022
3b6572d
Merge branch 'main' of https://github.com/dmswl98/FEDC3-4_Project_Not…
dmswl98 Nov 19, 2022
662f7df
🐛 fix: 파일명 변경 및 에디터 하단의 하위 문서 링크 에러 해결
dmswl98 Nov 19, 2022
9572f27
🐛 fix: PostPage 폴더명을 Document로 변경
dmswl98 Nov 19, 2022
b69f3e2
🚀 fix: netlify 404 에러 해결
dmswl98 Nov 19, 2022
270f2d2
🐛 fix(DocumentEditor): 스크롤 시 발생한 텍스트 스타일 메뉴의 위치 에러 해결
dmswl98 Nov 20, 2022
ddd6679
📝 docs: readme 수정
dmswl98 Nov 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
api-endpoint.js
45 changes: 36 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
# 📌 4주차 프로젝트[Project1]

## 필수 프로젝트

- 프로젝트 기한
- 프로젝트 수행 기간 : 2022년 11월 8일(화) ~ 2022년 11월 16일(수)
- 멘티 코드 리뷰 기간 : 2022년 11월 17일(목) ~ 2022년 11월 20일(일)
- 멘토 코드 리뷰 기간 : 2022년 11월 17일(목) ~ 2022년 11월 22일(화)
- 코드 리뷰 반영 기간 : 2022년 11월 23일(수) ~ 2022년 11월 25일(금)
- 내용
- **Day 17 [프로젝트] 노션 클로닝 요구사항** 확인 부탁드립니다.
![image](https://user-images.githubusercontent.com/76807107/203049404-1093e5f9-f895-457a-be7c-b087694c85f9.png)

## 배포 주소

https://dmswl98-notion-clone-project.netlify.app/

## 기본 요구사항

- [x] 글 단위를 Document라고 합니다. Document는 Document 여러개를 포함할 수 있습니다.
- [x] 화면 좌측에 Root Documents를 불러오는 API를 통해 루트 Documents를 렌더링합니다.
- [x] Root Document를 클릭하면 오른쪽 편집기 영역에 해당 Document의 Content를 렌더링합니다.
- [x] 해당 Root Document에 하위 Document가 있는 경우, 해당 Document 아래에 트리 형태로 렌더링 합니다.
- [x] Document Tree에서 각 Document 우측에는 + 버튼이 있습니다. 해당 버튼을 클릭하면, 클릭한 Document의 하위 Document로 새 Document를 생성하고 편집화면으로 넘깁니다.
- [x] 편집기에는 기본적으로 저장 버튼이 없습니다. Document Save API를 이용해 지속적으로 서버에 저장되도록 합니다.
- [x] History API를 이용해 SPA 형태로 만듭니다.

## 추가 요구사항

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

## 추가 구현사항

- [x] Sidebar 숨기기, 펼치기 기능
- [x] 텍스트 스타일(굵게, 기울기, 밑줄, 삭선, 색깔) 모달 구현
- [x] 상위 문서를 삭제한 경우, 내부 하단 문서까지 모두 삭제하도록 구현

## 개선 사항

- [ ] 첫 화면일 때 사이드 바의 숨기기 버튼 없애기
- [ ] 사이드 바가 펼쳐져 있을 때, 사이드 바 숨기기 버튼 없애기
- [ ] 스크롤 시 텍스트 스타일 모달이 제 위치에 뜨지 않는 문제 해결하기
- [ ] XSS 취약점 개선
- [ ] 문서의 제목을 변경할 경우 Sidebar에 변경된 제목을 낙관적 업데이트로 반영하기
- [ ] 문서를 생성했을 때 새로 생성된 문서로 이동 및 랜더링하기
1 change: 1 addition & 0 deletions _redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>최은지님의 Notion</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/index.js"></script>
</body>
</html>
59 changes: 59 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Sidebar from "./components/Sidebar/Sidebar.js";
import DocumentPage from "./components/Document/Document.js";

import { getDocumentContent } from "./api/api.js";

import { validateInstance } from "./utils/validation.js";
import { initRouter } from "./utils/router.js";

export default function App({ $target }) {
validateInstance(new.target);

const sidebar = new Sidebar({ $target });

const documentPage = new DocumentPage({
$target,
initialState: {
documentId: 0,
title: "",
content: "",
},
onUpdateTitle: () => {
sidebar.setState();
},
});

this.route = async () => {
const { pathname } = window.location;

if (pathname === "/") {
sidebar.setState();
} else if (pathname.indexOf("/documents/") === 0) {
const [, , documentId] = pathname.split("/");

const documentData = await getDocumentContent(documentId);

Choose a reason for hiding this comment

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

p6) document의 내용을 가져오는 함수 getDocumentContent의 반환값을 저장하는 변수이니 documentContent라는 변수명이 더 적당하지 않을까라는 의견을 남깁니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

오호 생각치 못했던 부분..! documentContent 네이밍이 더 좋은 것 같아요! 꼭 반영하겠습니당


const { title, content, documents } = documentData;
documentPage.setState({
documentId,
title,
content,
documents,
});

sidebar.setState();
}
};

const init = () => {
this.route();

initRouter(this.route);

window.addEventListener("popstate", () => {
this.route();
});
};

init();
}
29 changes: 29 additions & 0 deletions src/api/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { request } from "./index.js";
Copy link

Choose a reason for hiding this comment

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

이렇게 api함수들을 따로 정리하니까 보기 좋네요!

ask) request가 정의된 파일 이름이 왜 index.js인지 물어봐도 될까요:)?


export const getRootDouments = async () => {
return await request("/documents");
};

export const getDocumentContent = async (documentId) => {
return await request(`/documents/${documentId}`);
};

export const createDocument = async (documentData) => {
return await request(`/documents`, {
method: "POST",
body: JSON.stringify(documentData),
});
};

export const modifyDocuments = async (documentId, documentData) => {
return await request(`/documents/${documentId}`, {
method: "PUT",
body: JSON.stringify(documentData),
});
};

export const deleteDocument = async (documentId) => {
return await request(`/documents/${documentId}`, {
method: "DELETE",
});
};
22 changes: 22 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { API_END_POINT } from "../../api-endpoint.js";

export const request = async (url, options = {}) => {
try {
const res = await fetch(`${API_END_POINT}${url}`, {
...options,
headers: {
"Content-Type": "application/json",
"x-username": "dmswl12345",
},
});

if (!res.ok) {
throw new Error("API 처리 중");
}

return res.json();
} catch (err) {
console.error(err);
history.go(-1);

Choose a reason for hiding this comment

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

P5 : 오호 뒤로가기 처리를 하는 군요!

만약에 제가 구글로 들어간 후, 잘못된 주소로 접속, 예를 들면 새로운 탭을 키고 https://dmswl98-notion-clone-project.netlify.app/documents/00101과 같이 잘못된 documentId로 들어가면 구글로 튕기네요! 사용자 입장에선 not found가 떠서 잘못된 주소임을 가르켜 주는 것이 아니라 뒤로 튕겨버리니 익숙치 않은 경험이 될 수 있을 것 같습니다! history.push('/') 을 통해 홈으로 리다이렉팅 시키는 것은 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

저도 그 점을 인지하고 있었는데 까먹고 고치지 못했네요ㅎㅎ 꼭 반영해야겠군요 🤯

Copy link

@Kal-MH Kal-MH Nov 21, 2022

Choose a reason for hiding this comment

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

p5) 에러가 발생되었을 때, history.go(-1)을 통해서 한 단계전으로 돌아가는 군요!

사실 SidebarNav에서 onDeleteDocument()를 통해서 문서를 삭제했을 때, 삭제한 문서의 id를 통해서 push()를 호출하는 것으로 보입니다.
삭제된 문서를 조회하는 과정에서 request error가 발생했고, history.go(-1)를 통해서 존재하는 문서까지 거슬러 올라가면서(이해한 게 맞나요?) App.js에서 다수의 에러메세지가 뜨는 것을 발견했어요! 삭제를 할 때마다 에러메세지가 계속 쌓일 거 같은데, 사용자 입장에서는 잘 보이지 않고 동작도 잘 이뤄지기 때문에 넘어가도 되는 부분인 것도 같습니다!

고민이 되는 부분이네요 어떻게 생각하시나요:)?

Copy link
Member Author

Choose a reason for hiding this comment

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

history.go(-1)는 딱 이전 페이지를 보여주는데 만약, 문서들을 조회하고 조회한 문서들이 삭제되면 이전 페이지에 해당하는 문서가 삭제된 경우라 계속 에러가 발생하는 것 같아요. 저장된 데이터에는 삭제된 문서가 없어 fetch할 수 없기에... 이 부분은 승준님 말씀처럼 루트로 되돌리는 것이 나을 것 같고 꼭 반영해야겠어요!

}
};
15 changes: 15 additions & 0 deletions src/assets/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions src/assets/document.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/double-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/menu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/assets/trash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions src/components/Document/Document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import DocumentHeader from "./DocumentHeader.js";
import Editor from "./DocumentEditor.js";

import { modifyDocuments } from "../../api/api.js";

import { validateInstance } from "../../utils/validation.js";
import { debounce } from "../../utils/debounce.js";

export default function Document({ $target, initialState, onUpdateTitle }) {
validateInstance(new.target);

const $document = document.createElement("div");
$document.classList.add("document");

this.state = initialState;

new DocumentHeader({ $target: $document });

const editor = new Editor({
$target: $document,
initialState,
onEditTitle: (id, data) => {
debounce(async () => {
Copy link

Choose a reason for hiding this comment

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

debounce를 따로 모듈로 빼서 동작하게 하는군요 저도 바로 적용해야겠습니다:)

await modifyDocuments(id, data);
onUpdateTitle();
}, 100);
},
onEditContent: (id, data) => {
debounce(async () => {
await modifyDocuments(id, data);
}, 300);
},
});

this.setState = (nextState) => {
this.state = nextState;
editor.setState(this.state);

this.render();
};

this.render = () => {
$target.appendChild($document);
};
}
Loading