Jekyll OpenGraph URL 미리보기 카드 구현 및 TOC 충돌 해결
jekyll-linkpreview 플러그인으로 북마크 카드를 구현할 때 발생한 목차(TOC) 오염 문제를 Liquid 커스텀 템플릿으로 해결한 트러블슈팅 기록입니다.
기술 글을 작성할 때 외부 공식 문서나 레퍼런스 링크를 자주 인용하게 됩니다. 노션(Notion)처럼 세련된 북마크 미리보기 카드를 구현하는 과정에서 발생한 Chirpy 목차(TOC) 오염 버그를 Liquid 커스텀 템플릿으로 깔끔하게 해결한 과정을 정리합니다.
1. 배경: 단순 텍스트 링크의 아쉬움
마크다운(Markdown)의 기본 하이퍼링크 문법([제목](URL))은 간편하지만, 독자 입장에서는 링크를 클릭하기 전까지 해당 사이트가 어떤 내용을 담고 있는지 직관적으로 파악하기 어렵습니다.
노션(Notion)의 웹 북마크(Web Bookmark) 기능처럼, 링크를 삽입했을 때 OpenGraph 메타데이터를 기반으로 제목, 설명문, 대표 썸네일, 파비콘 도메인이 포함된 카드 위젯으로 렌더링된다면 글의 전달력과 심미성을 크게 높일 수 있습니다.
1
2
3
4
5
<!-- 일반 마크다운 링크: 정보가 제한적임 -->
[스프링 공식 레퍼런스 문서](https://docs.spring.io)
<!-- 노션 스타일 링크 프리뷰: 시각적 맥락을 풍부하게 제공 -->
{% linkpreview "https://docs.spring.io" %}
이를 정적 블로그(Jekyll) 환경에서 구현하기 위해 jekyll-linkpreview 플러그인을 도입했습니다.
2. jekyll-linkpreview 플러그인 설치 및 구성
jekyll-linkpreview는 Jekyll 빌드 시점에 지정된 URL의 HTML을 크롤링하여 OpenGraph 메타 태그를 추출하고, 이를 HTML 카드 템플릿으로 렌더링해 주는 오픈소스 플러그인입니다.
flowchart LR
A["마크다운 내 linkpreview 태그"] -->|"Jekyll 빌드"| B["jekyll-linkpreview 플러그인"]
B -->|"HTTP 요청 & OG 크롤링"| C["외부 웹사이트"]
C -->|"og:title, og:image, og:description"| B
B -->|"_includes/linkpreview.html"| D["미려한 북마크 카드 UI"]
1) Gemfile 의존성 추가
프로젝트 루트의 Gemfile의 jekyll_plugins 그룹에 플러그인을 추가합니다:
1
2
3
4
# Gemfile
group :jekyll_plugins do
gem 'jekyll-linkpreview', '~> 0.3.0'
end
터미널에서 번들러를 통해 설치를 완료합니다:
1
bundle install
2) _config.yml 플러그인 등록
Jekyll이 빌드 파이프라인에서 해당 플러그인을 인식하도록 _config.yml에 등록합니다:
1
2
3
# _config.yml
plugins:
- jekyll-linkpreview
3. 핵심 문제 발생: 우측 목차(TOC) 오염 사태
플러그인을 설정한 뒤 본문에 {% linkpreview "https://github.com" %} 태그를 추가하여 로컬에서 확인해 보았습니다.
카드는 화면에 잘 나타났지만, 우측 목차(TOC) 사이드바에서 치명적인 문제가 발견되었습니다:
원인 정밀 분석
브라우저 개발자 도구로 jekyll-linkpreview의 기본 렌더링 HTML 소스 코드를 열어보았습니다:
1
2
3
4
5
6
7
8
9
10
<!-- jekyll-linkpreview 기본 내장 템플릿의 DOM 구조 -->
<div class="jekyll-linkpreview-wrapper">
<div class="jekyll-linkpreview-body">
<!-- 원인: 카드 타이틀을 <h2> 헤딩 태그로 렌더링함! -->
<h2 class="jekyll-linkpreview-title">
<a href="https://github.com">GitHub: Let’s build from here</a>
</h2>
<div class="jekyll-linkpreview-description">...</div>
</div>
</div>
- Chirpy 테마의 우측 목차 파서(
tocbot)는 본문(.content) 내의 모든<h2>와<h3>태그를 글의 주요 소제목으로 간주하고 사이드바 목차로 자동 등록합니다. - 그런데
jekyll-linkpreview의 기본 템플릿이 카드의 링크 제목을<h2>태그로 작성하고 있었습니다. - 결과적으로 본문에 외부 레퍼런스 링크를 2~3개만 넣어도, 글의 원래 목차가 외부 사이트의 이름들로 뒤덮여 목차로서의 기능을 완전히 상실하게 되는 현상이 발생한 것입니다.
4. 해결 방법: Liquid 커스텀 템플릿 오버라이드
다행히 jekyll-linkpreview는 사용자가 프로젝트의 _includes/ 디렉토리에 동일한 이름의 템플릿 파일을 두면, 내장 템플릿 대신 사용자 정의 템플릿을 사용하는 템플릿 오버라이드(Template Override) 기능을 지원합니다.
Step 1. _includes/linkpreview.html 템플릿 작성
_includes/linkpreview.html 파일을 생성하고, 문제가 되었던 <h2> 태그를 일반 <div class="jekyll-linkpreview-title"> 태그로 교체합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- _includes/linkpreview.html -->
<div class="jekyll-linkpreview-wrapper">
<div class="jekyll-linkpreview-wrapper-inner">
<div class="jekyll-linkpreview-content">
<div class="jekyll-linkpreview-body">
<!-- h2 대신 div로 교체하여 Chirpy TOC 수집 대상에서 제외 -->
<div class="jekyll-linkpreview-title">
<a href="" target="_blank" rel="noopener noreferrer"></a>
</div>
<div class="jekyll-linkpreview-footer">
<a href="" target="_blank" rel="noopener noreferrer"></a>
</div>
</div>
</div>
</div>
</div>
Step 2. 반응형 CSS 스타일링 (assets/css/linkpreview.css)
카드가 다크 모드와 모바일 환경에서도 조화롭게 어우러지도록 assets/css/linkpreview.css를 생성하고 스타일을 구성합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* assets/css/linkpreview.css */
.jekyll-linkpreview-wrapper {
margin: 1.5rem 0;
border: 1px solid var(--main-border-color, #e9ecef);
border-radius: 8px;
overflow: hidden;
background-color: var(--card-bg, #ffffff);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.jekyll-linkpreview-wrapper:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.jekyll-linkpreview-wrapper-inner {
display: flex;
justify-content: space-between;
}
.jekyll-linkpreview-title a {
font-weight: 600;
color: var(--text-color, #212529);
text-decoration: none;
}
.jekyll-linkpreview-description {
font-size: 0.875rem;
color: var(--text-muted-color, #6c757d);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
5. 실전 팁: OpenGraph가 없는 사이트 대응 (linkpreview_nog.html)
운영 과정에서 마주칠 수 있는 또 하나의 엣지 케이스는 OpenGraph 메타태그가 아예 없는 오래된 사이트나 프로토콜 상대 URL(//cdn.example.com/...)을 사용하는 이미지를 크롤링할 때 발생하는 Jekyll 빌드 에러입니다.
이를 방지하기 위해 OpenGraph가 없는 경우를 위한 전용 템플릿인 _includes/linkpreview_nog.html도 함께 마련해 두면 어떤 외부 링크를 첨부하더라도 빌드가 안전하게 유지됩니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- _includes/linkpreview_nog.html -->
<div class="jekyll-linkpreview-wrapper">
<div class="jekyll-linkpreview-wrapper-inner">
<div class="jekyll-linkpreview-content">
<div class="jekyll-linkpreview-body">
<div class="jekyll-linkpreview-title">
<a href="" target="_blank" rel="noopener noreferrer"></a>
</div>
<div class="jekyll-linkpreview-footer">
<a href="" target="_blank" rel="noopener noreferrer"></a>
</div>
</div>
</div>
</div>
</div>
6. 최종 검증 및 적용 결과
로컬 서버에서 다시 확인한 결과, 외부 링크 카드가 세련된 북마크 형태로 우아하게 렌더링되면서도 우측 목차(TOC)에는 본문의 실제 목차만 깔끔하게 보존되는 것을 확인했습니다.
1
2
<!-- 실제 사용 예시 -->
{% linkpreview "https://github.com/cmsong111/blog" %}
7. 마치며
기본 제공되는 플러그인이라도 테마의 내장 기능(TOC 등)과 결합될 때 예상치 못한 마크업 충돌이 일어날 수 있습니다.
단순히 플러그인 사용을 포기하는 대신, Liquid 템플릿 오버라이드 메커니즘을 활용하여 시각적 완성도와 테마 기능 무결성을 모두 지켜낼 수 있었습니다.
다음 포스트(5편)에서는 Chirpy 테마의 카테고리 페이지를 단순한 게시글 목록 나열에서 벗어나, 주제별 시리즈 배너와 상세 소개 박스가 담긴 ‘시리즈 허브’로 커스터마이징하는 방법을 다루어 보겠습니다.

