-
Notifications
You must be signed in to change notification settings - Fork 28
[Mission4/주천욱] - Project_Notion_VanillaJS #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
base: 3/#4_chunwook
Are you sure you want to change the base?
Changes from all commits
71e1223
1c3eac2
05f68f1
2703e9d
242561c
98fd172
efc3f7b
0785669
8a73357
46e9e55
445905f
7a4e603
ca1ef88
7ca2535
b1c68c2
fbca9c7
d8bf6e3
46ce2d0
21d7077
fe8a166
06c55d8
7d72a2f
7dce486
617497d
be1d071
2824447
d445e42
2e6a039
00813cd
a4249e0
a34cfae
d985d08
e160330
a7b67dd
75850fe
6eb102f
242d481
c488676
78d087c
bb931c3
5089d3e
bda6983
b0f28ae
fbd23f5
763cd7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /etc.txt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,17 @@ | ||
| # 📌 4주차 프로젝트[Project1] | ||
| # VanillaJS로 만든 노션 클로닝 프로젝트 | ||
|
|
||
| ## 필수 프로젝트 | ||
| ## 배포 주소 | ||
| [🔳 JooNotion](https://joo-notion.vercel.app/) | ||
|
|
||
| - 프로젝트 기한 | ||
| - 프로젝트 수행 기간 : 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 [프로젝트] 노션 클로닝 요구사항** 확인 부탁드립니다. | ||
| ## 기능 구현 | ||
| - History API를 이용해 SPA 형태로 개발 | ||
| - 문서 생성, 수정, 삭제 | ||
| - 하위문서 생성, 수정, 삭제 | ||
| - 하위문서의 토글 상태를 LocalStorage에 저장하여 새로고침해도 유지되도록 구현 | ||
| - 문서 편집 시 API 호출을 줄이기 위해 디바운싱 구현 | ||
| - 마지막 입력 후 1초간 입력이 없으면 자동 저장 | ||
|
|
||
| ## 프로젝트 회고 | ||
| 프로젝트를 진행하며 생긴 문제에 대해 고민하고 해결과정을 기록했습니다. | ||
| <br/> | ||
| [블로그 주소](https://woogie-97.tistory.com/10) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export const OPENED_LIST_KEY = "local-save-open-list"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <!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" /> | ||
| <link | ||
| href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap" | ||
| rel="stylesheet" | ||
| /> | ||
| <link rel="stylesheet" href="/src/assets/css/style.css" /> | ||
| <link rel="stylesheet" href="/src/assets/css/fontello.css" /> | ||
| <title>🔳 JooNotion</title> | ||
| </head> | ||
| <body> | ||
| <main id="app" class="global-container"></main> | ||
| <script src="/src/index.js" type="module"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import PostEdit from "./components/PostEdit.js"; | ||
| import PostsPage from "./components/PostsPage.js"; | ||
| import HomePage from "./pages/HomePage.js"; | ||
| import { initRouter } from "./routes/router.js"; | ||
| import NotFound from "./pages/NotFound.js"; | ||
| import { request } from "./api/index.js"; | ||
|
|
||
| export default function App({ $target }) { | ||
| const $postListContainer = document.createElement("div"); | ||
| const $homeContainer = document.createElement("div"); | ||
| const $postEditContainer = document.createElement("div"); | ||
|
|
||
| $homeContainer.className = "post-edit-container"; | ||
| $postListContainer.className = "post-list-container"; | ||
| $postEditContainer.className = "post-edit-container"; | ||
|
Comment on lines
+13
to
+15
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 클래스를 줄 때 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. className을 이용하면 안에있는 클래스는 모두 지우고 정의하는 방법이라서 되도록 사용하지 않습니다.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
대도록 (x) 되도록 (o) |
||
|
|
||
| $target.appendChild($postListContainer); | ||
| $target.appendChild($homeContainer); | ||
| $target.appendChild($postEditContainer); | ||
|
|
||
| this.state = { | ||
| selectedPost: {}, | ||
| }; | ||
|
|
||
| const homePage = new HomePage({ | ||
| $target: $homeContainer, | ||
| }); | ||
|
|
||
| const notFound = new NotFound({ | ||
| $target, | ||
| }); | ||
|
|
||
| /** | ||
| * PostList 감싸는 컴포넌트 | ||
| */ | ||
| const postsPage = new PostsPage({ | ||
| $target: $postListContainer, | ||
| }); | ||
|
|
||
| /** | ||
| * Editor 감싸는 컴포넌트 | ||
| */ | ||
| const postEdit = new PostEdit({ | ||
| $target: $postEditContainer, | ||
| initialState: this.state, | ||
| updatePost: (updatePost) => { | ||
| postsPage.setState(updatePost); | ||
| }, | ||
| }); | ||
|
|
||
| postsPage.setState(); | ||
|
|
||
| this.init = async () => { | ||
| const { pathname } = window.location; | ||
| if (pathname === "/") { | ||
| $postEditContainer.style.display = "none"; | ||
| $homeContainer.style.display = "flex"; | ||
| } else if (pathname.indexOf("/documents/") === 0) { | ||
| $postEditContainer.style.display = "flex"; | ||
| $homeContainer.style.display = "none"; | ||
|
|
||
| const [, , id] = pathname.split("/"); | ||
| if (id === "new") { | ||
| postEdit.setState({ | ||
| ...this.state, | ||
| id, | ||
| }); | ||
| } else { | ||
| const post = await request(`/documents/${id}`, { | ||
| method: "GET", | ||
| }); | ||
| postsPage.setState(post); | ||
| postEdit.setState(post); | ||
| } | ||
| } else { | ||
| notFound.render(); | ||
| $postListContainer.style.display = "none"; | ||
| $postEditContainer.style.display = "none"; | ||
| $homeContainer.style.display = "none"; | ||
|
Comment on lines
+56
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 style 부분은 className on/off 하는 방법은 어떨까요? |
||
| } | ||
| }; | ||
|
Comment on lines
+53
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pathname에 따라 렌더링을 이해하기 쉽게 잘 작성하신 것 같아요~ |
||
|
|
||
| this.init(); | ||
| initRouter(() => this.init()); | ||
|
|
||
| window.addEventListener("popstate", () => { | ||
| this.init(); | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| const API_END_POINT = "https://kdt-frontend.programmers.co.kr"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분은 따로 빼셔야 할 것 같습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vercel 환경변수에 넣으려고 시도했었는데 생각처럼 잘 안돼서 ㅠㅠ gitignore라도 해야겠네요 |
||
|
|
||
| export const request = async (url, options = {}) => { | ||
| try { | ||
| const res = await fetch(`${API_END_POINT}${url}`, { | ||
| ...options, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "x-username": "woogie", | ||
| }, | ||
| }); | ||
| if (res.ok) { | ||
| return await res.json(); | ||
| } | ||
| throw new Error("API 호출 실패!!!!!!!"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 호출 실패하면 안될텐데!!!!!! |
||
| } catch (error) { | ||
| alert(error); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| @font-face { | ||
| font-family: 'fontello'; | ||
| src: url('../font/fontello.eot?10205068'); | ||
| src: url('../font/fontello.eot?10205068#iefix') format('embedded-opentype'), | ||
| url('../font/fontello.woff2?10205068') format('woff2'), | ||
| url('../font/fontello.woff?10205068') format('woff'), | ||
| url('../font/fontello.ttf?10205068') format('truetype'), | ||
| url('../font/fontello.svg?10205068#fontello') format('svg'); | ||
| font-weight: normal; | ||
| font-style: normal; | ||
| } | ||
| /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ | ||
| /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ | ||
| /* | ||
| @media screen and (-webkit-min-device-pixel-ratio:0) { | ||
| @font-face { | ||
| font-family: 'fontello'; | ||
| src: url('../font/fontello.svg?10205068#fontello') format('svg'); | ||
| } | ||
| } | ||
| */ | ||
| [class^="icon-"]:before, [class*=" icon-"]:before { | ||
| font-family: "fontello"; | ||
| font-style: normal; | ||
| font-weight: normal; | ||
| speak: never; | ||
|
|
||
| display: inline-block; | ||
| text-decoration: inherit; | ||
| width: 1em; | ||
| margin-right: .2em; | ||
| text-align: center; | ||
| /* opacity: .8; */ | ||
|
|
||
| /* For safety - reset parent styles, that can break glyph codes*/ | ||
| font-variant: normal; | ||
| text-transform: none; | ||
|
|
||
| /* fix buttons height, for twitter bootstrap */ | ||
| line-height: 1em; | ||
|
|
||
| /* Animation center compensation - margins should be symmetric */ | ||
| /* remove if not needed */ | ||
| margin-left: .2em; | ||
|
|
||
| /* you can be more comfortable with increased icons size */ | ||
| /* font-size: 120%; */ | ||
|
|
||
| /* Font smoothing. That was taken from TWBS */ | ||
| -webkit-font-smoothing: antialiased; | ||
| -moz-osx-font-smoothing: grayscale; | ||
|
|
||
| /* Uncomment for 3D effect */ | ||
| /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ | ||
| } | ||
|
|
||
| .icon-trash:before { content: '\e803'; } /* '' */ | ||
| .icon-plus-1:before { content: '\e804'; } /* '' */ | ||
| .icon-down-open:before { content: '\f004'; } /* '' */ | ||
| .icon-right-open:before { content: '\f006'; } /* '' */ | ||
| .icon-circle-thin:before { content: '\f1db'; } /* '' */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| @import "./vars.scss"; | ||
|
|
||
| @mixin base-button { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. scss의 가장 큰 장점인것같아요! 알아두면 정말 많이 쓰일것같습니당 ㅎㅎ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mixin을 잘 많이 활용해서 좋네요 ^^ |
||
| button { | ||
| font-family: "Noto Sans KR", Sans-serif; | ||
| border: 0; | ||
| color: $color-base-font; | ||
| background-color: transparent; | ||
| } | ||
|
|
||
| button:hover { | ||
| cursor: pointer; | ||
| } | ||
| } | ||
|
|
||
| @mixin input-title { | ||
| .title { | ||
| cursor: text; | ||
| font-size: 2.5rem; | ||
| font-weight: 700; | ||
| line-height: 4rem; | ||
| outline: none; | ||
| } | ||
|
|
||
| .title:empty:before { | ||
| content: attr(placeholder); | ||
| font-size: 2.5rem; | ||
| font-weight: 700; | ||
| line-height: 4rem; | ||
| color: #ccc; | ||
| outline: none; | ||
| } | ||
| } | ||
|
|
||
| @mixin input-content { | ||
| .content { | ||
| height: 600px; | ||
| cursor: text; | ||
| font-size: 1rem; | ||
| line-height: 3rem; | ||
| outline: none; | ||
| } | ||
|
|
||
| .content:empty::before { | ||
| content: attr(placeholder); | ||
| top: 0; | ||
| font-size: 1rem; | ||
| line-height: 3rem; | ||
| color: #ccc; | ||
| outline: none; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // font | ||
| $color-home-font: #c7c7c7; | ||
| $color-base-font: #5e5e5e; | ||
|
|
||
| // background | ||
| $color-post-edit-background: #ffffff; | ||
| $color-post-list-background: rgb(251 251 250); | ||
|
|
||
| // hover | ||
| $color-post-list-hover: rgb(235, 235, 235); | ||
| $color-post-button-hover: rgb(209, 209, 209); | ||
|
|
||
| // button | ||
| $color-modal-close-btn-background: #6b6b6b; | ||
| $color-post-create-btn-background: rgb(35, 131, 226); | ||
| $color-white: #ffffff; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| @import "./libs/vars.scss"; | ||
| @import "./libs/mixins.scss"; | ||
|
|
||
| .container-modal { | ||
| display: none; | ||
|
|
||
| .modal { | ||
| z-index: 2; | ||
| position: absolute; | ||
| max-width: 970px; | ||
| margin: 0 auto; | ||
| width: 100%; | ||
| height: 400px; | ||
| top: 50%; | ||
| left: 50%; | ||
| transform: translate(-50%, -50%); | ||
| background-color: $color-post-edit-background; | ||
| padding: 2rem 4rem; | ||
| border-radius: 4px; | ||
| box-shadow: rgb(15 15 15 / 5%) 0px 0px 0px 1px, | ||
| rgb(15 15 15 / 10%) 0px 5px 10px, rgb(15 15 15 / 20%) 0px 15px 40px; | ||
|
|
||
| .modal-title { | ||
| position: relative; | ||
|
|
||
| h2 { | ||
| font-size: 1.5rem; | ||
| font-weight: bold; | ||
| } | ||
|
|
||
| .title-input-container { | ||
| position: relative; | ||
| margin-top: 1.5rem; | ||
| height: 300px; | ||
|
|
||
| @include input-title; | ||
| } | ||
| } | ||
|
|
||
| .modal-btn-container { | ||
| display: flex; | ||
| justify-content: end; | ||
| gap: 1rem; | ||
|
|
||
| button { | ||
| padding: 8px 1rem; | ||
| border-radius: 10px; | ||
| color: $color-white; | ||
| font-size: 1rem; | ||
| } | ||
|
|
||
| .close { | ||
| background-color: $color-modal-close-btn-background; | ||
| } | ||
|
|
||
| .create-post-btn { | ||
| background-color: $color-post-create-btn-background; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .overlay { | ||
| z-index: 1; | ||
| position: absolute; | ||
| top: 0; | ||
| left: 0; | ||
| width: 100%; | ||
| height: 100%; | ||
| background-color: rgba(0, 0, 0, 0.4); | ||
| } | ||
| } | ||
|
|
||
| .post-modal-show { | ||
| display: block; | ||
| } |
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.
App에서 이러한 Container들을 만들어주신 이유를 여쭤봐도 될까요?
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.
저 코드가 display:none을 하기위해서 추가된 코드에요.
처음엔 각각 렌더링을 하려고 했는데 App에서 pathname에 따라 렌더링 되는부분이 잘 안되더라고요..
404페이지에서 list가 보인다거나 그런 현상이 발생해서 임시방편으로 저렇게 넣어둔겁니다ㅎ.ㅎ
저도 혜준님 생각대로 불필요한 렌더링이 발생한다고 생각해서 저 부분은 변경하도록 하겠습니다~~