본문 바로가기
Project/Knoticle

[Knoticle] 좋은 UX를 위한 기능&도전 3 (markdown toc)

by Cafe Mocha 2022. 12. 16.

TOC

Knoticle 서비스는 여러 글을 모아 책으로 엮어주는 서비스이다.

이러한 서비스에서 책과 글의 TOC를 구현해 사용자가 글을 볼때 원하는 곳으로 이동하기 편하도록 개선했다.

 

TOC를 구현하는 방법은 문자열 전환과 Link 태그의 href를 통해 구현했다.

1. 목차 title, padding값 전환

목차에 글의 h1,h2,h3 태그의 title을 표현하기 위해 하기와 같이 구현했다.

  1. DB에 저장하고 있는 article.content값을 articleToc함수로 전달한다.
  2. TOC에 표현하기 위해 h1,h2,h3 태그의 값만 분류하고 title과 count로 데이터를 전환한다.
  3. styled-component값의 props로 count값을 내려서 들여쓰기 구분한다.
export const articleToc = (content: string) => {
  // 게시물 본문을 줄바꿈 기준으로 나누고, 제목 요소인 것만 저장
  const titles = html2markdown(content)
    .split(`\\n`)
    .filter((t) => t.includes('# '));

  // 예외처리 - 제목은 문자열 시작부터 #을 써야함
  const result = titles
    .filter((str) => str[0] === '#')
    .map((item) => {
      // #의 갯수에 따라 제목의 크기가 달라지므로 갯수를 센다.
      let count = item.match(/#/g)?.length;
      if (count) {
        // 갯수에 따라 목차에 그릴때 들여쓰기 하기위해 *10을 함.
        count *= 10;
      }

      // 제목의 내용물만 꺼내기 위해 '# '을 기준으로 나누고, 백틱과 공백을 없애주고 count와 묶어서 리턴
      return { title: item.split('# ')[1].replace(/`/g, '').trim(), count };
    });

  return result;
};

2. 글에 표현하기 위한 데이터 전환

knoticle은 마크다운 에디터를 사용해서 사용자의 글을 받고, html로 전환해서 DB에 저장한다.

뷰어페이지에서 글을 표현할때는 html을 dangerouslySetInnerHTML을 사용해서 표현해준다.

 

💡 innerHTML을 사용하면 DOM노드가 수정되었을때 수정된 것을 알 수 있는 방법이 없다고 합니다.
따라서 dangerouslySetInnerHTML을 사용하여 가상 DOM과 실제 DOM을 비교하여 변경된 것이 있다면 리렌더링이 될 수 있도록 해야합니다.

 

TOC 이동을 위해서는 html h1,h2,h3의 태그에 id 값을 추가해야한다.

동일하게 함수로 구현해서 정규식과 문자열 전환을 통해 id값에 title을 추가했다.

export const articleConversion = (content: string) => {
  const newArticle = content.split('\\n').map((v, idx) => {
    if (v.includes('h1') || v.includes('h2') || v.includes('h3')) {
      const title = v.replace(/<[^>]*>?/g, '');
      const result = v.split('');
      result.splice(3, 0, ' ', `id=${title}`);
      return result.join('');
    }
    return v;
  });

여기서 넣어준 id값을 toc Link의 href로 설정해준다.

{isArticleShown &&
	articleToc.map((article) => (
	<TocArticleTitle
		href={`#${article.title}`}
		key={article.title}
		count={article.count}
		>
			{article.title}
	</TocArticleTitle>
))}

이제 TOC의 제목을 클릭하면, 해당하는 태그의 위치로 이동할 수 있다!

정리

처음 구현할때는 id값을 통해 이동해줄 수 있는 방법을 모르고 어떤 방식으로 이동시켜줘야할지 막막했던 것 같다.

하지만 막상 구현해보니 html을 마크다운으로 전환해 title을 분류하고, 정규식을 통해 html 태그에 id를 추가하기도 하면서 재미있게 구현할 수 있었다.

프로젝트 마감까지 이제 시간이 얼마 남지 않아서 이동까지만 구현했지만, 프로젝트가 끝난 후 IntersectionObserver를 활용해 화면 위치에 따라서 TOC에 표시해주는 방식으로 개선할 예정이다.