Jekyll
Introduction
Jekyll은 Hugo, Hexo와 함께 굉장히 많이 사용되는 SSG(Static Site Genertor) 입니다. Ruby로 개발되었고 Liquid 문법을 사용하여 템플릿을 통제할 수 있고, 여러가지 기능들 담은 Gem(RubyGem)을 추가하여 쉽게 여러가지 기능들을 만들어낼 수 있습니다.
Directory Structure
_config.yml
: Jekyll 사이트이 Config 파일입니다. Jekyll에서 사용할 변수, 옵션 등 여러가지 설정이 이 파일에 포함됩니다._posts
: 기본적인 Post 디렉토리입니다. 대다수 테마에선 블로그 글과 같이 날짜로 정렬되는 데이터가 여기에 포함됩니다._pages
: 기본적인 Page 디렉토리입니다.Gemfile
: Ruby Gemfile입니다. Jekyll plugin 또한 Gem 파일에 명시하여 설치할 수 있습니다.
참고로 config.yml에 exclude로 제외한 디렉토리를 빼면
_
가 붙은 디렉토리는 사이트에 포함되지 않고 없는 디렉토리는 내용 자체가 포함됩니다. 그래서 만약_data
란 디렉토리를 만들고 별도로 site에 포함(output: true)시키지 않는다면 사이트에 들어가지 않습니다. 그래서 빌드에서만 사용할 데이터를 별도로 넣고 관리할 수도 있습니다.
Usage
Basic
jekyll new
: 사이트를 생성합니다.jekyll new-theme
: 테마를 생성합니다.jekyll serve, server, s
: jekyll 사이트를 로컬에서 구동합니다.jekyll build
: 사이트를 빌드합니다. (_site 경로에 static file들이 생성됩니다.)jekyll doctor, hyde
: Warnings가 있는지 체크합니다.
주로 사용되는건 위와 같으며 이외에 여러가지 subcommands는 jekyll -h
로 확인하실 수 있습니다.
Advanced - Jekyll Compose Jekyll Compose 설치 시 Jekeyll Command에서 글을 쓰고 관리하는 기능들이 추가됩니다. 이 명령으로 생성한 페이지들은 Front matter의 일부가 자동으로 작성됩니다. Front matter 또한 사용자가 직접 원하는 포맷으로 설정이 가능합니다.
bundle exec jekyll post "My New Post"
: 새로운 Post를 만듭니다.bundle exec jekyll page "My New Page"
: 새로운 Page를 만듭니다.bundle exec jekyll draft "My new draft"
: 새로운 Draft 문서를 만듭니다.bundle exec jekyll compose "My New Wiki" --collection "wikis"
: 새로운 Collection 내용을 만듭니다.
draft # Creates a new draft post with the given NAME
post # Creates a new post with the given NAME
publish # Moves a draft into the _posts directory and sets the date
unpublish # Moves a post back into the _drafts directory
page # Creates a new page with the given NAME
rename # Moves a draft to a given NAME and sets the title
compose # Creates a new file with the given NAME
Bundle command
Ruby는 rvm, rbenv 그리고 bundler로 디렉토리 별 환경이 모두 다를 수 있습니다. 만약 시스템에 여러 ruby 환경이 구성되어 있다면 jekyll 명령을 직접 실행하는 것 보단 jekyll 디렉토리에서 bundler를 통해 구동하는게 가장 좋습니다.
bundle exec jekyll <subcommands> [options]
Go-to commands
post를 가장 최근 문서 한개만 빌드하여 서빙합니다.
bundle exec jekyll serve --limit-posts 1
--profile
옵션을 사용하여 jekyll serve 또는 build 시 파일 별 소모된 시간 등 자세한 내용을 볼 수 있습니다. 개인적으론 빌드 타임을 줄이거나, 잘못된 코드를 찾는데 자주 사용합니다.
bundle exec jekyll build --profile
Liquid
Liquid는 shopify에서 만든 오픈소스 Markup Language로 Jekyll에서 Layout, Posts 등 여러 곳에서 사용할 수 있는 템플릿입니다. Ruby로 개발되었기 때문에 Liquid 문법은 루비와 유사하고 많은 문법이 공유됩니다.
개인적으론 Template 관련 문법에선 가장 쉽다고 생각되네요.
<ul id="products">
{% for product in products %}
<li>
<h2>{{ product.name }}</h2>
Only {{ product.price | price }}
{{ product.description | prettyprint | paragraph }}
</li>
{% endfor %}
</ul>
Variables
Global Var
- site
- page
- content
Document Var
- content
- collection
- relative_path
# content
# output: Hello from my_collection.
{{ site.my_collection.first.content }}
# collection
# output: my_collection
{{ site.my_collection.first.collection }}
# relative_path
# output: _my_collection/item.md
{{ site.my_collection.first.relative_path }}
Collection Var
- directory
- docs
- files
- label
- output
- relative_path
# directory
# output: /Users/hahwul/jekyll-app/_my_collection
{{ site.my_collection.directory }}
# docs
# output: /my_collection/item.html
{{ site.my_collection.docs.first.url }}
# files
# output: 4
{{ site.my_collection.files | size }}
# label
# my_collection
{{ site.my_collection.label }}
# output
# output: true
{{ site.my_collection.output }}
# relative_path
# output: _my_collection
{{ site.collections.my_collection.relative_path }}
Filter
Filter는 Pipe(|
) 를 통해 값을 전달하여 처리하고 결과를 다시 받아오는 형태의 기능들입니다.
Array
# page.my_array is ['a', 'b', 'c']
# --------------------------------
# output: a, b, and c
{{ page.my_array | array_to_sentence_string }}
# output: a
{{ page.my_array | first }}
# output: a,b,c
{{ page.my_array | join: ', ' }}
# output: ["a","b","c"]
{{ page.my_array | jsonify }}
# output: c
{{ page.my_array | last }}
# output: 3
{{ page.my_array | size }}
# output: ['a','b','c']
{{ page.my_array | sort }}
# my_array => ['a','a','b','c','c','c','a']
# output: 'a', 'b', 'c'
{{ page.my_array | uniq }}
# output: my_array => ['a', 'b', 'c', 'd']
{% assign my_array = my_array | push: 'd' %}
# page.people is
# - name: "John"
# school: "Stanford"
# - name: "Jane"
# school: "Stanford"
# - name: "Joe"
# school: "Harvard"
# --------------------------------
# output: JohnJaneJoe
{{ page.people | map: "name" }}
# output: {"name"=>"John", "school"=>"Stanford"}{"name"=>"Jane", "school"=>"Stanford"}
{{ page.people | where: "school", "Stanford" }}
# output: {"name"=>"John", "school"=>"Stanford", "year"=>2016}{"name"=>"Joe", "school"=>"Harvard", "year"=>2015}
{{ page.people | where_exp: "item", "item.name contains 'Jo'" }}
# output: {"name"=>4, "items"=>[{"name"=>"John", "school"=>"Stanford", "year"=>2016}, {"name"=>"Jane", "school"=>"Stanford", "year"=>2017}], "size"=>2}{"name"=>3, "items"=>[{"name"=>"Joe", "school"=>"Harvard", "year"=>2015}], "size"=>1}
{{ page.people | group_by_exp: "item", "item.name | size" }}
# output: {,"name"=>"Stanford",,"items"=>[{,"name"=>"John",,"school"=>"Stanford",},{,"name"=>"Jane",,"school"=>"Stanford",}],}
{{ page.people | group_by: "school" }}
Date
# output: 09 Sep 2022
{{ site.time | date_to_long_string }}
# output: Fry, 09 Sep 2022 22:51:14 +0900
{{ site.time | date_to_rfc822 }}
# output: 09 Sep 2022
{{ site.time | date_to_string }}
# output: 2022-09-09T22:51:14+09:00
{{ site.time | date_to_xmlschema }}
# output: Fry, Sep 09, 22
{{ site.time | date: "%a, %b %d, %y" }}
Integer
# output: 2
{{ 1.2 | ceil }}
# output: 3
{{ 10 | divided_by:3 }}
# output: 1
{{ 1.2 | floor }}
# output: 3
{{ 4 | minus:1 }}
# output: 1
{{ 3 | modulo:2 }}
# output: 5
{{ 4 | plus:1 }}
# output: 2
{{ 1.8 | round }}
# output: 30
{{ 10 | times:3 }}
String
# output: http://example.com/mysite/images/dog.jpeg
{{ '/images/dog.jpeg' | absolute_url }}
# output: jekyll.jpg
{{ 'jekyll' | append: '.jpg' }}
# output: Static site generator
{{ "static site generator" | capitalize }}
# output: foo%2Cbar%3Bbaz%3F
{{ "foo,bar;baz?" | cgi_escape }}
# output: hello
{{ nil | default: "hello" }}
# output: static site generator
{{ "STATIC Site generator" | downcase }}
# output: &lt;p&gt;Jekyll&lt;/p&gt;
{{ "<p>Jekyll</p>" | escape }}
# output: I love Jekyll!
{{ ' I love Jekyll! ' | lstrip }}
# output: Hello <strong>Jekyll</strong>
{{ "Hello **Jekyll**" | markdownify }}
# output: 2
{{ "Hi Jekyll!" | number_of_words }}
# output: I love Jekyll
{{ 'Jekyll' | prepend: 'I love ' }}
# output: /mysite/images/dog.jpeg
{{ '/images/dog.jpeg' | relative_url }}
# output: I really like Jekyll
{{ 'I really really like Jekyll' | remove_first: 'really' }}
# output: I like Jekyll
{{ 'I really really like Jekyll' | remove: 'really' }}
# output: I kinda really like Jekyll
{{ 'I really really like Jekyll' | replace_first: 'really', 'kinda' }}
# output: I truly truly like Jekyll
{{ 'I really really like Jekyll' | replace: 'really', 'truly' }}
# output: I love Jekyll!
{{ ' I love Jekyll! ' | rstrip }}
# output: 6
{{ page.my_string | size }}
# output: ell
{{ "hello" | slice: 1, 3 }}
# output: the-config-yml-file
{{ "The _config.yml file" | slugify }}
# output: Use “Jekyll” — the static generator…
{{ 'Use "Jekyll" --- the static generator...' | smartify }}
# output: ['a', 'b']
{{ "a~b" | split:"~" }}
# output: Jekyll is cool
{{ "<p>Jekyll is cool</p>" | strip_html }}
# output: Hello there
{{ "Hello
there" | strip_newlines }}
# output: I love Jekyll!
{{ ' I love Jekyll! ' | strip }}
# output: I love Je...
{{ "I love Jekyll" | truncate: 12 }}
# output: I love...
{{ "I love Jekyll" | truncatewords: 2 }}
# output: STATIC SITE GENERATOR
{{ "static site generator" | upcase }}
# output: foo,%20bar%20%5Cbaz?
{{ "foo, bar \baz?" | uri_escape }}
# output: john%40example.com
{{ "john@example.com" | url_encode }}
# output: &lt;p&gt;Hi Jekyll&lt;/p&gt;
{{ "<p>Hi Jekyll</p>"| xml_escape }}
# output: sass to css code
{{ sass_code | sassify }}
# output: scss to css
{{ scss_code | sassify }}
Loop
# page.my_array => []
# output: There are no items!
{% for item in page.my_array %}
{{ item }}
{% else %}
There are no items!
{% endfor %}
# page.my_array => ['a','b','c']
# output: First! Not first Not first
{% for item in page.my_array %}
{% if forloop.first %}
First!
{% else %}
Not first
{% endif %}
{% endfor %}
# page.my_array => ['a','b','c']
# output: 1 2 3
{% for item in page.my_array %}
{{ forloop.index }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: 0 1 2
{% for item in page.my_array %}
{{ forloop.index0 }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: Not last Not last Last!
{% for item in page.my_array %}
{% if forloop.last %}
Last!
{% else %}
Not last
{% endif %}
{% endfor %}
# page.my_array => ['a','b','c']
# output: 3 3 3
{% for item in page.my_array %}
{{ forloop.length }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: a b
{% for item in page.my_array limit: 2 %}
{{ item }}
{% endfor %}
# page.my_array => ['a','b','c','d','e','f','g]
# output: c d e f g
{% for item in page.my_array offset: 2 %}
{{ item }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: c b a
{% for item in page.my_array reversed %}
{{ item }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: 3 2 1
{% for item in page.my_array %}
{{ forloop.rindex }}
{% endfor %}
# page.my_array => ['a','b','c']
# output: 2 1 0
{% for item in page.my_array %}
{{ forloop.rindex0 }}
{% endfor %}
Conditional
조건문들은 Ruby를 따라갑니다. 아래 문법들로 Jekyll 내부에서 분기할 수 있습니다.
- if
- elsif
- case
- operations
- unless
e.g
{% if page.name == 'about' %}
About Page
{% elsif page.name == 'contact' %}
Contact Page
{% else %}
Other Page
{% endif %}
Documents
Jekyll이 Documentaion이 잘 되어 있어서 공식 Documents를 참고하시면 Jekyll을 사용하는데 있어 큰 도움을 받을 수 있습니다.
Plugins
Lists
Useful Gem
- jekyll-paginate
- jekyll-sitemap
- jekyll-twitter
- jekyll-gist
- jekyll-youtube
- jekyll-asciinema
- jekyll-seo-tag
- picture-tag
- jimoji
- jekyll-responsive-image
- jekyll-compose
Security
Check List
- Misconfigration
- include and exclude
- Vulnerabilities
- DOM XSS
- DOM Clobbering
- Prototype Pollution
- Dependency Confusion (rubygem)
- Reverse Tabnabbing
- Broken Link
- Mixed Contents
- Sensitive/Secret Disclosure
- Liquid Injection
- CVEs
Articles
- Jekyll Collection 다루기
- Github page(Jekyll) build speed up!
- Jekyll feed.xml 최소화하기
- Jekyll에 Utterances, Giscus 댓글 적용하기