데브코스의 첫 프로젝트가 시작되었다..! 이전에 로토님의 강의에서 vanillajs에서 리액트 같은 라이브러리의 상태관리, 라우터를 흉내내는 실습을 통해 많은 것을 배울 수 있었다. 이것을 토대로 약 10일 간의 노션 클로닝 프로젝트가 시작 되었고 난이도가 꽤 있었다. 이 기간이 학교 중간고사와 졸업프로젝트 제출이 겹쳐서 매우 힘든 주간이었다. 프로젝트 제출 기간이 시험이 끝나는 날이어서 요구사항을 거의 아예 수행하지 못해고 제출해서 아쉬웠지만 한편으로 다른 학생분들도 사이드 프로젝트나 학교 병행을 하면서 프로젝트를 수행하시는 것 같아서 시간관리나 열정이 부족했나 반성을 하게 되었다.
기존에는 vscode의 extension을 이용해서 린트와 프리티어를 사용하고 있었는데 실제 회사의 팀에서는 따로 이런걸 적용하고 프로젝트를 한다고 해서 나도 시험삼아 한번 적용해봤다..! 생각보다 어렵지 않았다. airbnb 규칙을 도입했는데 생각보다 많이 까다로워서 다음부턴 좀더 유연한 규칙을 선택하려고 한다.
위에서 설정한 EsLint 와 Prettier는 깃에 올라가있는 모습이 보기 좋지 않아서 gitignore 설정을 해줬다.
gitignore을 이용하면 Staging Area나 레포지토리에 추가되지 않기 때문에 추적되지 않는다.
.gitignore
파일을 생성 후 git에 추가하지 말아야 할 파일을 정의하면 된다.
폴더
[폴더이름]/
파일
[파일 명] (루트 디렉토리의 경우)
확장자
*.[확장자]
나같은 경우 다음과 같이 설정하였다.
## directory/
/node_modules
## file
package.json
package-lock.json
.prettierrc
.eslintrc.json
style.scss
style.css.map
한번 추적된 경우 반드시 삭제하고 다시 push해야 한다.
이런 동작을 한번에 해결해주는 코드가 있다.
git rm -r --cached .
후에 다시 커밋후 push해주면 된다.
기본적인 구조는 위와 같다.
크게 문서들을 관리하는 Sidebar, 본문을 관리하는 EditPage가 있다.
여기에 전역 store를 담는 컴포넌트를 추가할 예정이다.
노션 페이지에서 문서에 하위 문서를 추가할 때마다 차곡차곡 오른쪽으로 조금씩 밀려서 문서가 생성되는데 이것을 어떻게 구현해야 할지 애를 먹었다. padding값을 일시적으로 줘야하는데 js안에서 동적으로 depth 변수를 두어서 이것에 따른 padding값을 줘야 하나...? 하다가 너무 복잡해지는 것 같아서 다른 분의 코드를 참고하였다.
우선 다음의 구조를 이해해야 한다.
<ul>
<li>
오늘 할일
<ul>
<li>
산책 하기
<ul>
<li>성북천</li>
<li>중랑천</li>
<li>청계천</li>
</ul>
</li>
<li>운동 하기</li>
<li>봉사 가기</li>
</ul>
</li>
<li>
내일 할일
<ul>
<li>빨래 개기</li>
</ul>
</li>
<li>내일 모레 할일</li>
</ul>
리스트에 해당하는 <li>
태그에 <ul>
<li>
태그 구조를 만들면 리스트의 하위 리스트를 만들 수 있다.
[
{
"id": 1, // Document id
"title": "노션을 만들자", // Document title
"documents": [
{
"id": 2,
"title": "블라블라",
"documents": [
{
"id": 3,
"title": "함냐함냐",
"documents": []
}
]
}
]
},
{
"id": 4,
"title": "hello!",
"documents": []
}
]
데이터 구조는 위와 같아서 재귀를 생각해볼 수 있다.
const renderTree = (documents) => `
<ul>
${documents
.map(
(doc) => `
<li data-id="${doc.id}" class="document__item">
<div class="document__item__main">
<button class="toggle">
${
doc.documents && doc.documents.length > 0
? `<img src="/public/assets/img/open.svg" alt="페이지 토글 열기 이미지"/>`
: `<img src="/public/assets/img/close.svg" alt="페이지 토글 닫기 이미지"/>`
}
</button>
<button class="doc">
<img src="/public/assets/img/doc.svg" alt="페이지 이미지" />
</button>
<span class="text">${doc.title}</span>
<button class="delete">
<img src="/public/assets/img/delete.svg" alt="페이지 삭제 이미지" />
</button>
<button class="add">
<img src="/public/assets/img/add.svg" alt="페이지 추가 이미지" />
</button>
</div>
${
doc.documents && doc.documents.length > 0
? renderTree(doc.documents)
: ''
}
</li>
`,
)
.join('')}
</ul>
`;
$sidebartitle.innerHTML = renderTree(this.state);
map
안에서 <li>
태그를 만드는데 마지막에 하위 문서가 존재하면 함수를 다시 호출해 <ul>
<li>
구조를 만들어 뒤에 붙여서 원하는 구조를 완성시킨다.
$sidebartitle.addEventListener('click', (e) => {
const $button = e.target.closest('button');
const $li = e.target.closest('li');
// 버튼 선택
if ($li && $button && $button.className) {
const { id } = $li.dataset;
switch ($button.className) {
case 'toggle':
if ($li.querySelector('ul')) {
const $ul = $li.querySelector('ul');
$ul.classList.toggle('hidden');
if ($ul.className === 'hidden') {
$button.innerHTML = `<img src="/public/assets/img/close.svg" alt="페이지 토글 닫기 이미지"/>`;
} else {
$button.innerHTML = `<img src="/public/assets/img/open.svg" alt="페이지 토글 열기 이미지"/>`;
}
}
break;
case 'delete':
delDocument(id);
break;
case 'add':
addDocument(id);
break;
default:
break;
}
}
// 문서 클릭
else if ($li) {
const { id } = $li.dataset;
push(`/posts/${id}`);
}
});
현재 Sidebar 컴포넌트가 SidebarList컴포넌트를 관리하고 있다. SidebarList에서 추가나 삭제 이벤트가 일어난다면 Sidebar가 그것을 감지하여 서버로 데이터를 보내고 state를 업데이트하여 다시 sidebarList에 뿌려주는 역할을 하는데 토글 이벤트 같은 경우 직접적인 DOM조작이고, sidebarList의 토글 상태가 sidebarList 내부에서만 의미가 있기 때문에 sidebarList안에서 해당 로직을 두었다.
vercel 을통해서 배포를 하려는데 다음과 같은 폴더구조에서 자꾸만 오류가 났다.
이미지를 상대경로로 불러오든 절대경로를 불러오든 로컬에서는 잘되는데 배포만 하면 처음부터 404 페이지가 떴다. 모든 파일들의 경로를 확인하던 중 하나씩 지워가면서 배포를 하는 과정을 거쳐서 루트경로의 public/assets/img 폴더안의 이미지를 불러올때 문제가 있다는 것을 알게되었다.
<img src="/public/add.svg" alt="페이지 추가 이미지" />
이런식으로 절대경로로 불러오는 경우 에러가 난다.
<img src="/src/img/add.svg" alt="페이지 추가 이미지" />
이 경우는 로컬, 서버 모두 정상적으로 동작한다.
이유를 알 수가 없다.. 둘다 경로는 맞는데 배포만 하면 에러가 나네..?
다음은 okky에서 찾은 두가지 답변이다.
요약하자면 vercel의 기본 설정문제다. public이름을 가진 폴더에 이미지를 두면 public 하위의 파일을 자동으로 루트에 라우팅 한다고 한다. vercel 말고도 다른 호스팅 업체에서도 정적자원을 제공할때 이런 식으로 접근한다고 한다...
그러니까 만약 pulic/image.png
를 불러와도 사실상 /image.png
가 되는 것이다..ㅜㅜ
리액트에서만 public 폴더가 특별하게 여겨지는구나 정도로 생각했는데 프레임워크나 라이브러리를 사용하지 않아도 적용되었던 것이다.
다음부턴 안전하게 src안에서 이미지를 관리하자..!