ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [티스토리] 자동 목차 생성 방법 (기존 문제 해결)
    웹 개발/일반 2024. 2. 18. 02:19

    웹 개발의 가장 처음이 티스토리 관련이라니 살짝 아쉽긴 하지만,

    개발 블로그로 거듭나기 위해서는 꼭 필요한 작업이었으므로..

    또 기존 방법이 가지고 있던 문제도 해결했으므로... 만족...

    아래는 그 문제들로, 이것에 대한 해결 방법을 찾고 있는 분들은 바로 문제 해결 방법 쪽으로 넘어가셔도 좋습니다!

    • '카테고리 글 더 보기' 플러그인 관련 문제
    • 스크롤 전에 목차가 안 보이는 문제
    • 왼쪽 표시바의 중앙이 안 맞는 문제(?)
    • 첫 번째 콘텐츠 시작 전에 해당 목차에 있다고 표시되는 문제(?)

    일단 내가 사용하는 Letter 스킨 기준이라 다른 스킨에서는 다르게 작동할 수 있는데,

    그래도 위에서 언급한 네 가지 문제를 해결하는 데 조금이라도 도움이 될까 싶어 작성해봅니당


    자동 목차 생성

    사실 조금만 검색해보면 자동으로 목차를 생성하는 방법에 대한 글이 많다.

    그래서 그 어떤 글보다도 깔끔하게 정리해보려 한다. 팔로미.

    나 역시 Tocbot라는 라이브러리를 사용했다.

    일단 결과물부터 보여드리겠다. 진짜 내가 본 티스토리 목차 중에 제일 깔끔함.

     

    기본적으로 웹은

    1. 틀을 정의하는 HTML
    2. 스타일을 결정하는 CSS

     

    로 구성되어 있다.

    그리고 여기서 더 복잡하고 다양한 작업을 처리하기 위해 HTML의 <script> 태그 안에 javaScript 코드가 들어간다.

    우리는 이 세 부분을 모두 건들일 것이다!

     

    0. 편집 메뉴로 들어가기

    티스토리 블로그 관리 페이지로 들어가, 좌측 메뉴 중 꾸미기 > 스킨 편집 메뉴를 찾아 누른다.

     

    그럼 새로운 페이지가 열리면서 편집할 수 있는 창이 뜨는데, 여기서 우측의 html 편집 버튼을 누르자.

     

    그럼 이런 식으로, 개발자에게는 익숙한, 비개발자에게는 조금 무서워 보이는 창이 등장한다.

     

    여기까지 오면 절반은 성공했다. 시작이 반이라고들 하지 않는가!

     

    1. HTML 작성

    차근차근 따라해보자. 일단 두 개의 코드 블럭을 추가해주면 된다.

     

    • 아래의 코드를 </head> 바로 위에 추가해준다.
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.25.0/tocbot.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.25.0/tocbot.min.js"></script>

     

    • 아래의 코드를 <s_permalink_article_rep> 태그 바로 아래에 추가해준다. (팁: ctrl+f로 검색할 수 있음)
    <div class="toc toc-fixed" ></div>

     

    2. CSS 작성

    이제 상단의 HTML 탭 옆에 있는 CSS 탭으로 넘어가자.

    아래의 코드를 가장 아래쪽에 추가해준다. 참고로 나중에 설명할 3번 문제(?)가 해결된 버전이다.

    /* custom */
    /*tocbot*/
    .toc-absolute {
      display: flex;
      position: absolute;
      margin-top:165px;
    }
    .toc-fixed {
      position: fixed;
      top: 165px;
    }
    
    .toc {
      right: calc((100% - 850px) / 2 - 300px);
      width: 250px;
      padding: 10px;
      box-sizing: border-box;
    }
    
    .toc-list {
      margin-top: 10px !important;
      padding-bottom: 5px !important;
      font-size: 0.9em;
    }
    
    .toc > .toc-list li {
      margin-bottom: 10px;
    }
    
    .toc > .toc-list .toc-link::before {
      margin-top: -0.45em;
      align-items: center;
      align-content: center;
    }
    
    .toc > .toc-list li:last-child {
      margin-bottom: 0;
    }
    
    .toc > .toc-list .node-name--H7 {
      display: none;
    }
    
    .toc-default-header {
      display: none;
    }
    
    .toc > .toc-list li a {
      text-decoration: none;
    }
    
    .toc-list-item .is-collapsed{
      max-height: 3000px;
    }

     

    앞서 HTML 탭에서 <head> 태그 아래에 추가한 css link를 빼고 여기에 스타일을 직접 작성하면 완전 제대로 된 커스터마이징을 할 수 있지만, 일단 완성하고 생각하자. 킵고잉온~

     

    3. script 작성

    아래의 <script> 태그 + javaScript를 </body> 바로 앞에 추가해준다.

    세미콜론 붙였다 안 붙였다 제멋대로긴 하지만... 일단은...

    이 스크립트 역시 나중에 설명할 1번, 4번 문제가 해결된 버전이다.

    <script>
      var content = document.querySelector('.article_view')
      var startContent = document.createElement("h7");
      startContent.classList.add('toc-default-header');
      content.insertBefore(startContent, content.firstChild)
      var headings = content.querySelectorAll('h1, h2, h3, h4, h5, h6, h7')
      var headingMap = {}
    
      Array.prototype.forEach.call(headings, function (heading) {
          if (heading.parentElement.classList.contains('another_category')) {
            heading.classList.add('another_category_header')
          }
          else {
            var id = heading.id ? heading.id : heading.textContent.trim().toLowerCase()
                      .split(' ').join('-').replace(/[\!\@\#\$\%\^\&\*\(\):]/ig, '')
            headingMap[id] = !isNaN(headingMap[id]) ? ++headingMap[id] : 0
            if (headingMap[id]) {
              heading.id = id + '-' + headingMap[id]
            } else {
              heading.id = id
            }
          }
        })
    
      tocbot.init({
        tocSelector: '.toc',
        contentSelector: '.article_view',
        headingSelector:'h1, h2, h3, h4, h7',
        hasInnerContainers: false,
        ignoreSelector: '.another_category_header'
      });
    
      $(document).ready(function(){
        var toc = $('.toc');
        toc.addClass('toc-absolute');
    
        var toc_top = toc.parent().offset().top;
        $(window).scroll(function() {
          if ($(this).scrollTop() >= toc_top) {
            toc.addClass('toc-fixed');
            toc.removeClass('toc-absolute');
          } else {
            toc.addClass('toc-absolute');
            toc.removeClass('toc-fixed');
          }
        });
    
      });
    
    </script>

     

    그럼 끝이다!

    여기서 끝내도 되지만, 앞서 언급한 문제 + 스크롤 전에 목차가 안 보이는 문제를 해결하고 싶으면 아래까지 읽으면 좋을 것 같다.

    만약 여기까지 했는데 아예 안 보인다면, 에러 상황마다 어떻게 해결할 수 있는지 설명해주는 다른 글을 참고하는 것이 좋을듯.

    이 글은 아래 문제 해결이 핵심이고, 자동 목차 생성 방법은 진짜 뚝딱하면 간단하게 완성하고 싶은 사람을 위해 작성하고 있어가지고...

    헤헤ㅔ...

     

    자동 목차 생성 시 발생하는 문제 해결

    이게 핵심이다. 열심히 찾아보지는 않았지만, 대부분 이 문제를 해결하지 못한 것 같아 목차 생성하는 김에 해결했다.

    여기서부터는 이해를 위한 설명이 좀 추가될 것 같다. 원리를 알아야 다른 문제가 발생했을 때 해결할 수 있으니까!

    사실 앞서 공유한 코드에 2번을 제외한 나머지 문제가 해결되어 있어서,

    이 블로그와 똑같이 동작하는 목차를 사용하고 싶으면 2번만 보면 된다.

     

    1. '카테고리 글 더 보기' 플러그인 관련 문제 해결

    일단 문제 원인을 파악해보자.

    Crome에서 F12를 눌러 devTool을 열어 확인해보면, 아래와 같이 카테고리의 다른 글이 <h4> 태그로 생성되는 것을 알 수 있다.

    (저기 class="another_category_header"은 내가 넣어준 부분 - 아래에서 설명 예정)

     

    여기서 문제는, 티스토리의 글을 작성할 때 선택하는 제목1, 제목2, 제목3이 각각 <h2>, <h3>, <h4> 태그에 대응된다는 것이다.

    그래서 이때까지는 제목3이나 '카테고리 글 더 보기' 플러그인 중 하나를 포기해야 했을 것이다.

    하지만 둘 다 포기 못 하는 나, 바로 해결해버렸다.

    요지는 저 부분에 해당하는 h4 태그를 tocBot에서 찾지 못하도록 막아야하는 것이다.

    분명 특정 태그를 제외하는 속성이 있을 것이라 생각한 나는 공식 문서를 찾아봤고, 발견해버렸다, ignoreSelector 속성을!

     

    Tocbot

    Tocbot builds a table of contents (TOC) from headings in an HTML document. This is useful for documentation websites or long markdown pages because it makes them easier to navigate. This library was inspired by Tocify, the main difference is that Tocbot us

    tscanlin.github.io

     

    다만 우리가 제외하고 싶은 h4 태그에는 아무런 속성이 없어서, 직접 추가해주었다. 그 코드가 바로 아래의 코드다.

    Array.prototype.forEach.call(headings, function (heading) {
      if (heading.parentElement.classList.contains('another_category')) {
        // '카테고리 글 더 보기' 플러그인에 해당하는 h4 태그에 클래스 추가
        heading.classList.add('another_category_header')
      }
      else {
        var id = heading.id ? heading.id : heading.textContent.trim().toLowerCase()
                  .split(' ').join('-').replace(/[\!\@\#\$\%\^\&\*\(\):]/ig, '')
        headingMap[id] = !isNaN(headingMap[id]) ? ++headingMap[id] : 0
        if (headingMap[id]) {
          heading.id = id + '-' + headingMap[id]
        } else {
          heading.id = id
        }
      }
    })
    
    tocbot.init({
    tocSelector: '.toc',
    contentSelector: '.article_view',
    headingSelector:'h1, h2, h3, h4, h7',
    hasInnerContainers: false,
    ignoreSelector: '.another_category_header' // '카테고리 글 더 보기' 플러그인에 해당하는 h4 태그에 추가해준 클래스 제외
    });

     

    다행히 사진에서 보이는 것처럼 바로 위 부모한테 'another_category'라는 클래스가 부여되어 있어서 쉽게 해결할 수 있었다.

    이제 제목3이랑 카테고리 글 더 보기 플러그인을 모두 쓸 수 있다!

     

     

    2. 스크롤 전에 목차가 안 보이는 문제 해결

    이것도 많이 겪는 문제로 알고 있다. 이것도 1번 문제처럼 devTool로 디버깅해봤는데, 이게 있는데 안보이는 거라 참 요상했다.

    이런 경우는 여러 원인이 있겠지만, 역시 css의 overflow 옵션 문제겠거니 싶었다.

    사실 처음에는 코드 중 absolute <-> fixed 왔다갔다 하는 부분이 가장 큰 차이라, position: absolute 부분을 파봤는데 아니었다.

     

    그래서 대망의 해결 방법은, CSS 탭에서 inner_index 클래스의 overflow 속성을 visible로 변환하는 것이다.

     

    그럼 짜잔! 스크롤을 위로 올렸을 땐 안 보이던 목차가 이제 보인다.

    이렇게만 해도 해결이 되지만, 혹시 이로 인한 문제가 생길지 몰라 한 단계 아래의 요소에 overflow: hidden 속성을 추가해주었다.

     

     

    3. 왼쪽의 표시바가 글씨 중앙과 맞지 않는 문제(?) 해결

    이건 문제일까... 문제가 아닐까...

    일단 나한테는 엄청난 문제였다. 아니 이게 미묘하게 아래로 가있는 것 같았는데 진짜 아래로 가있었음.

    이건 CSS 쪽에 작성하면서 해결했다. 핵심은 margin-top: -0.45em (위에 CSS 코드 참고)

    이건 각자 커스터마이징한 코드에 맞게 적절히 변경하면 될 것 같다.

     

     

    4. 첫 번째 목차 시작 전에 해당 콘텐츠에 있다고 표시되는 문제(?) 해결

    이것도 나한텐 큰 문제였다.

    항상 글 시작하기 전에 주저리를 풀고 시작하는 나에게는

    아직 잡담 중인데도 불구하고 1. 뭐시기 솰라솰라에 highlight된 것이 몹시도 아쉬웠음.

     

    이건 실제로 보이지 않는 <h7> 태그를 추가하여 해결했다.

    var startContent = document.createElement("h7");
    startContent.classList.add('toc-default-header');
    content.insertBefore(startContent, content.firstChild)

     

    h7인 이유는... 다른 헤더 태그랑 같이 묶이지 않도록 하기 위해서...


    이렇게 개발 블로그를 위한 초석을 닦아봤다.

    사실 아직 CSS 커스터마이징은 안했지만... 곧... 호버 상태나 눌렸을 때나 스타일 적용시킬 예정...

     

    티스토리에서 바로 글을 쓰는 건 처음인데, 생각보다 괜찮은 것 같다.

    새롭게 웹 개발을 시작하기도 했고, 여기에 개발 관련 포스팅을 작성하겠다고 선언한만큼 열심히 관리해봐야겠다. 파이팅~

    '웹 개발 > 일반' 카테고리의 다른 글

    [웹 개발] 시작 전 간단한 리서치  (2) 2024.02.21