jekyll의 메커니즘을 이해하고 커스터마이징하기
천보성 팀장
2019-04-15
편집자 주
-PHP 기반의 서비스를 기준으로 설명했다.
-서버의 프로그램은 ‘서버 스크립트’로 표기했다.
-HTML/html: 약어로 사용할 경우엔 대문자, 파일명으로 사용할 경우엔 소문자로 표기했다.
목차
jekyll이 어렵게 느껴지는 이유
jekyll은 모든 화면을 미리 만들어둔다.
서버 스크립트 없이 검색 기능을 어떻게 만들까?
이미지 캡션 추가
이미지 사이즈 대응
부록: 글 반영 과정, 도메인 연결 방법, 추가 옵션에 대하여
Overview
기술 블로그인 브랜디 랩스를 관리하기에 jekyll은 안성맞춤인 도구입니다. 1년 넘게 탈 없이 잘 사용하고 있죠. 물론 커스터마이징을 하려면 고생이 이만저만이 아닙니다. 그 과정은 jekyll을 이용한 Github 블로그 만들기에도 잘 나와있습니다. 도대체, jekyll은 왜 이리도 어려운 걸까요? 브랜디 랩스를 사례로 설명하겠습니다.
jekyll이 어렵게 느껴지는 이유
일반적인 웹서비스는 정적 리소스와 동적 스크립트의 조합으로 이뤄집니다. 예를 들어 PHP 서비스에서는 정적인 부분을 아파치 웹서버로, 동적인 부분을 PHP 스크립트로 작동합니다.
하나의 게시글이 생기면 PHP 스크립트가 데이터베이스에 row 생성을 요청합니다. 게시글 등록 요청을 마치고, 글 목록 화면 요청을 한다면 데이터베이스에서 등록된 글목록을 정리해 HTML 양식으로 응답값을 만들어줄 것입니다.
하지만 jekyll은 컨셉부터 다릅니다. 아주 생소한 메커니즘을 갖고 있습니다. 파일 기반의 데이터를 정적인 리소스로 빌드해서 서비스하죠. 게시글마다 md 파일이나 html 파일을 생성합니다. 글을 작성하고 배포하기 위한 빌드를 진행하면 응답할 html 화면을 만들고, 파일로 저장해 준비합니다. 이 상태에서 유저가 특정 화면을 요청하면 미리 생성한 html 파일을 찾아 꺼내주기만 하면 되죠. 다시 말해, 데이터베이스를 조회하고 HTML 양식으로 응답값을 만드는 과정이 생략되는 것입니다.
jekyll은 모든 화면을 미리 만들어둔다.
jekyll은 유저가 요청할 수 있는 모든 화면을 미리 빌드하는 방식을 씁니다. 앞서 다뤘던 브랜디 랩스의 gnav 영역의 회사소개, 채용 화면도 미리 빌드해둬야 합니다. 저자를 소개하는 프로필 페이지도 마찬가지죠. 글이 많아지면서 점점 길어지는 글 목록 화면도 예외는 아닙니다. 글 목록을 보여주는 화면이 많아지만 페이지 수만큼 미리 만들어야 합니다.
위의 이미지는 jekyll이 동작하는 메커니즘을 간단히 정리한 것입니다. jekyll을 커스터마이징하려면 완전히 새로운 관점으로 접근해야 합니다. 지금부터는 브랜디 랩스의 검색 기능 구현 과정을 살펴보면서 커스터마이징을 자세히 알아보겠습니다.
서버 스크립트 없이 검색 기능을 어떻게 만들까?
검색을 하려면 작성된 모든 글의 제목과 내용에 원하는 키워드가 있는지 찾아야 합니다. 하지만 검색어는 변동값이므로 미리 빌드하는 방식으로는 커버할 수 없습니다. 검색어마다 화면을 미리 만들 수 없기 때문입니다.
이럴 때는 클라이언트 스크립트는 활용해야 합니다. 서버 스크립트를 쓸 수 없기 때문에 어쩔 수 없는 선택이기도 합니다. 검색에 필요한 정보를 json 파일로 빌드시키고 자바 스크립트를 이용해서 검색하도록 했습니다.
먼저 최상위 경로에 search.json을 만듭니다. 파일 시작점에 아래와 같은 패턴이 있다면 빌드 대상으로 인식됩니다.
---
---
이전에 쓴 jekyll 문서를 PDF로 배포하기에서 pdf.html 파일을 만들 때도 비슷한 방법을 사용했습니다.
---
---
[
{% for post in site.posts %}
{
"title" : "{{ post.title | escape }}",
"category" : "{{ post.category }}",
"tags" : "{{ post.tags | join: ‘, ’ }}",
"url" : "{{ site.baseurl }}{{ post.url }}",
{% if post.author %}{% for author in site.data.authors %}{% if post.author == author.name %}
"author" : "{{author.koname}}",
"email" : "{{author.email}}",
{% endif %}{% endfor %}{% endif %}
"date" : "{{ post.date }}",
"content" : "{{ post.content | strip_html | replace: "\", ‘’ | replace: ‘"’, ‘\"’ | replace: ' ‘,’ ' | replace: '
‘, ’ ' }}"
} {% unless forloop.last %},{% endunless %}
{% endfor %}
]
▲서머리 데이터를 만드는 json 파일
search.json은 모든 페이지의 제목과 내용을 정리해 json으로 만들어야 하기 때문에 site.posts
변수를 이용해 만들었습니다. post
내용에는 글의 저자, 작성일, 제목, 내용 등 필요한 정보가 있으니 출력하면 됩니다. json을 만드는 것이므로 내용에 “가 들어가면 안되 "으로 치환시켰습니다. 마지막으로 HTML 태그는 검색에 필요하지 않기 때문에 luqid strip_html 함수를 이용해 제거했습니다.
http://labs.brandi.co.kr/search.json
위의 URL을 클릭하면 브랜디 랩스에서 검색에 사용하는 json을 볼 수 있습니다. 빌드하면 search.json이 만들어지는 것을 확인할 수 있습니다. 이제 json을 로딩하고 해당 키워드를 가진 글을 찾아내기만 하면 됩니다. json 내에 제목과 내용에 입력한 키워드가 있을 때 아래와 같은 UI로 표현했습니다. 기능 구현은 Simple-Jekyll-Search를 이용했습니다. 1)
이미지 캡션 추가
블로그는 이미지를 많이 사용하고, 상황에 맞게 노출도 해야 합니다. 아래 이미지는 최종적으로 적용한 이미지와 캡션의 결과 화면입니다.
{% include figure.html file="/assets/2019/20190415/05.png" alt="05" caption="커스터마이징한 gnav 영역" width="fitcontent" border="true" %}
위와 같이 구성하려고 html과 css를 다음과 같이 구성했습니다.
<figure>
<img src="http://labs.brandi.co.kr///assets/20180514/top.png" alt="07">
</figure>
<figcaption>커스터마이징한 Gnav영역</figcaption>
▲캡션 html 소스
figure {
margin: 1em auto;
}
figcaption {
text-align: center;
font-weight: bold;
color:#999;
}
▲캡션에 관련된 css 소스
이미지는 가운데 정렬했고, 캡션 텍스트도 옅은 회색으로 가운데 정렬했습니다. 하지만 편집을 담당하는 장근우 대리는 개발자가 아니므로 태그를 입력해달라고 하기엔 무리가 있었습니다. 좀 더 편리한 방식이 없을지 고민하다가 liquid 템플릿의 include 기능을 쓰면 되겠다는 생각이 들었죠. 아래는 브랜디 랩스 원고에 이미지를 넣을 때 쓰는 liquid 문법입니다.
{% include figure.html file="/assets/easydebug/5.png" alt="07" caption="커스터마이징한 Gnav영역" %}
liquid 템플릿 엔진에서 include할 때 추가 파라미터를 전달할 수 있습니다. file, alt, caption은 파라미터로 전달하고, include되는 파일에서 전달할 내용을 바탕으로 프로그램을 구현할 수 있습니다.
<figure>
<img src="{{site.url}}/{{ include.file }}" alt="{{include.alt}}">
</figure>
<figcaption>{{include.caption}}</figcaption>
▲ /_includes/figure.html
이미지 사이즈 대응
대부분은 이미지는 화면에 꽉 차지만, 어떤 이미지는 사이즈가 너무 작아 원래의 사이즈로 보여줘야 했습니다.
{% include figure.html width="fitcontent" border="true" file="/assets/easydebug/5.png" alt="07" caption="커스터마이징한 Gnav영역" %}
▲사이즈와 외곽 테두리 선에 스펙을 추가했다.
추가 전달 인자를 넣고, figure.html 파일에서도 사이즈 대응을 했습니다.
<figure {% if include.width == ‘fitcontent’ %} class="fitcontent" {% elsif include.width %} class="percent{{include.width}}" {% endif %}>
<img src="{{site.url}}/{{ include.file }}" alt="{{include.alt}}" {% if include.border%} style="border:1px solid #ccc;" {%endif%}>
</figure>
<figcaption>{{include.caption}}</figcaption>
▲완성된 /_includes/figure.html 파일
figure {
margin: 1em auto;
}
figure.percent100 { width: 100%; }
figure.percent90 { width: 90%;}
figure.percent80 { width: 80%;}
figure.percent70 { width: 70%;}
figure.percent60 { width: 60%;}
figure.percent50 { width: 50%;}
figure.percent40 { width: 40%;}
figure.percent30 { width: 30%;}
figure.percent20 { width: 20%;}
figure.percent15 { width: 15%;}
figure.percent10 { width: 10%;}
figure.percent5 { width: 5%;}
figure.fitcontent { width: fit-content;}
figcaption {
text-align: center;
font-weight: bold;
color:#999;
}
▲완성된 css
이제 원하는 사이즈를 지정해 이미지 상황별 적절한 대응을 할 수 있게 되었습니다.
Conclusion
jekyll은 브랜디 랩스를 운영하기에 아주 유용한 도구입니다. 기본 템플릿도 훌륭하지만 상황과 편의에 맞게 변경하면 개성 있는 기술 블로그를 만들 수 있을 겁니다. 물론 커스터마이징이 어려울 수 있지만 jekyll의 메커니즘을 이해한다면 금방 적응할 수 있을 겁니다. 이제 블로그를 만들 모든 준비가 끝났습니다. 자, 도전해봅시다!
부록
1.글 반영 과정
jekyll을 이용해서 글을 작성했나요? 이제 Github 저장소에 push하면 글이 반영될 겁니다. push하는 과정을 보면 빌드된 파일을 push하는 게 아니라, 원본에 해당하는 md파일 또는 html 파일을 push하는 걸 알 수 있습니다. push하면 Github page에 바로 반영되지 않고, 몇 분 정도 걸립니다. 이것을 통해 작성한 글이 저장소에 push되면 스케줄러나 트리거에 의해 빌드된다는 걸 유추할 수 있습니다. 아마도 빌드 결과를 위한 저장소가 따로 있고, 빌드된 결과가 저장되는 것이라 예상합니다.
2.도메인 연결 방법
jekyll 서비스에서는 구매한 도메인을 간편하게 연결할 수 있습니다. 프로젝트의 가장 위쪽에 CNAME 파일을 만들고 push하면 금방 적용됩니다.
3.추가 옵션에 대하여
자료를 조사하던 중에 공식 사이트의 빌드 추가 옵션을 찾았지만 0.2초 정도로 큰 차이가 없었습니다. 만약 별도의 옵션이 없다면 빌드 결과는 _site 폴더로 모일 겁니다.
참고
1) GitHub - christian-fei/Simple-Jekyll-Search: A JavaScript library to add search functionality to any Jekyll blog.