숏코드
숏코드는 마크다운 콘텐츠 안에서 쓸 수 있는 재사용 가능한 템플릿 조각입니다. 커스텀 숏코드는 templates/shortcodes/에 두는 Jinja2 템플릿입니다. 템플릿 언어 레퍼런스는 문법을 참고합니다.
숏코드 사용
콘텐츠 파일에서는 두 가지 문법이 동작합니다:
{{ shortcode_name(arg1="value", arg2="value") }}
또는 명시적으로:
{{ shortcode("shortcode_name", arg1="value") }}
블록 숏코드 닫기
블록 숏코드는 두 가지 닫기 스타일을 지원합니다:
-
단순 닫기(bare closer):
{% note %}...{% end %} -
이름 있는 닫기(named closer) (권장):
{% note %}...{% endnote %} {% alert(type="info") %}...{% endalert %}
단순하지 않은 콘텐츠에는 이름 있는 닫기({% endNAME %})를 강력히 권장합니다. 여러 숏코드를 섞어 쓰거나 깊게 중첩할 때 마크다운을 읽고 유지보수하기가 훨씬 쉬워집니다.
코드 펜스 밖의 Markdown 페이지에서 숏코드 문법을 그대로 보이려면 raw 블록으로
감쌉니다. {% raw %}와 {% endraw %} 표식은 Markdown 출력에도 표시되며, 블록
안에 코드 펜스가 있어도 raw 영역이 분리되지 않습니다.
내장 숏코드
Hwaro에는 템플릿 파일 없이 바로 동작하는 내장 숏코드가 들어 있습니다.
파라미터는 이름을 지정하거나 각 숏코드에 표시된 순서대로 전달할 수 있습니다.
예를 들어 {{ youtube("VIDEO_ID") }}와 {{ gist("username", "gist_id") }}는
위치 인자를 사용하며, 따옴표로 감싼 빈 값도 해당 위치를 차지합니다.
youtube
YouTube 동영상을 삽입합니다.
{{ youtube(id="dQw4w9WgXcQ") }}
{{ youtube(id="dQw4w9WgXcQ", width="800", height="450") }}
{{ youtube(id="dQw4w9WgXcQ", start="30") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
id |
(필수) | YouTube 동영상 ID |
width |
560 |
플레이어 너비 |
height |
315 |
플레이어 높이 |
title |
YouTube Video |
접근성용 제목 |
start |
특정 타임스탬프(초 단위)부터 재생 |
vimeo
Vimeo 동영상을 삽입합니다.
{{ vimeo(id="123456789") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
id |
(필수) | Vimeo 동영상 ID |
width |
560 |
플레이어 너비 |
height |
315 |
플레이어 높이 |
title |
Vimeo Video |
접근성용 제목 |
gist
GitHub Gist를 삽입합니다.
{{ gist(user="octocat", id="abc123") }}
{{ gist(user="octocat", id="abc123", file="hello.rb") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
user |
(필수) | GitHub 사용자 이름 |
id |
(필수) | Gist ID |
file |
(없음) | 표시할 특정 파일 |
alert / callout
알림 상자를 표시합니다. 콘텐츠를 감싸는 블록 숏코드로 사용합니다.
단순 닫기와 이름 있는 닫기를 모두 지원합니다(복잡한 페이지에는 이름 있는 닫기를 권장합니다):
{% alert(type="warning", title="Caution") %}Be careful with this!{% end %}
{% alert(type="tip") %}Using named closer also works{% endalert %}
(명확성을 위해 이름 있는 닫기 스타일을 권장합니다.)
| 파라미터 | 기본값 | 설명 |
|---|---|---|
type |
info |
info, warning, danger, tip, success |
title |
(없음) | 선택적 제목 |
figure
캡션을 붙일 수 있는 이미지입니다.
{{ figure(src="/img/photo.jpg", alt="A photo", caption="My caption") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
src |
(필수) | 이미지 URL |
alt |
"" |
대체 텍스트 |
caption |
(없음) | 이미지 아래 캡션 |
width |
(없음) | 이미지 너비 |
height |
(없음) | 이미지 높이 |
tweet
트윗을 삽입합니다.
{{ tweet(user="jack", id="20") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
user |
(필수) | Twitter 사용자 이름 |
id |
(필수) | 트윗 ID |
codepen
CodePen을 삽입합니다.
{{ codepen(user="chriscoyier", id="gfdDu") }}
{{ codepen(user="chriscoyier", id="gfdDu", tab="css,result", height="400") }}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
user |
(필수) | CodePen 사용자 이름 |
id |
(필수) | Pen ID |
tab |
result |
기본으로 열릴 탭 |
height |
300 |
삽입 높이 |
title |
CodePen Embed |
접근성용 제목 |
위에서 (필수) 로 표시된 매개변수는 모두 URL이나 src에 그대로 들어갑니다.
빠뜨려도 렌더링은 성공하지만 아무 데도 가리키지 않는 임베드
(https://codepen.io//embed/gfdDu)가 나오므로, 배포된 페이지에서 깨진 임베드를
직접 찾는 대신 빌드가 경고합니다.
[WARN] Shortcode `codepen` is missing required argument `user` — it renders a broken embed. Usage: `{{ codepen(user="…", id="…") }}`.
include_code
소스 파일 전체나 일부를 펜스 코드 블록으로 넣습니다. 블록은 일반 코드 파이프라인을 그대로 거칩니다: 하이라이팅, 펜스 옵션, 복사 버튼, render-codeblock 훅.
{{ include_code(path="examples/site/config.toml") }}
{{ include_code(path="examples/site/config.toml", region="feeds", lines="2-8", title="config.toml", hl_lines="3") }}
| 매개변수 | 기본값 | 설명 |
|---|---|---|
path |
(필수) | 프로젝트 루트 기준 파일 경로 |
region |
(없음) | #region <name>과 #endregion <name> 사이의 줄만 |
lines |
(없음) | 줄 범위 a-b 또는 한 줄 a (1부터, 양 끝 포함; region과 함께 쓰면 영역 안에서 셉니다) |
lang |
파일 이름이나 확장자에서 추론(Makefile, Dockerfile, .rs). 클라이언트 하이라이트 모드에서는 highlight.js 이름 |
코드 블록 언어 |
title, hl_lines, hide_lines, linenos, linenostart, copy |
펜스 옵션으로 전달 |
영역 표시는 어떤 주석 기호 뒤에도 쓸 수 있어서 원본 파일에서도 올바른 코드로 남습니다:
module App
# #region server
def run
serve
end
# #endregion server
end
//, #, --, <!--, /*, ; 모두 쓸 수 있고, 이름 없는 #endregion은 가장 안쪽의 열린 영역을 닫습니다. 표시 줄과 선택한 영역 안의 중첩된 표시는 출력에서 빠집니다. region 없이 쓰면 파일을 그대로 넣습니다. 모든 줄에 공통인 앞쪽 들여쓰기는 제거됩니다.
include_md
마크다운 파일을 그 자리에 쓴 것처럼 렌더링합니다.
{{ include_md(path="shared/install.md") }}
{{ include_md(path="shared/install.md", region="brew") }}
| 매개변수 | 기본값 | 설명 |
|---|---|---|
path |
(필수) | 프로젝트 루트 기준 파일 경로 |
region |
(없음) | <!-- #region <name> -->과 <!-- #endregion <name> --> 사이의 줄만 |
포함된 파일 안의 숏코드도 펼쳐지고, 그 제목은 페이지의 목차와 제목 id에 함께 들어갑니다. 프론트 매터는 버립니다. 포함된 파일이 다른 파일을 다시 포함할 수 있으며 깊이는 8단계까지입니다. 순환 포함은 그 경로를 보여 주는 오류가 됩니다.
경로와 오류
두 숏코드 모두 프로젝트 루트 안의 파일만 읽습니다. 절대 경로, .., 프로젝트 밖으로 이어지는 심볼릭 링크, 빌드 출력 디렉터리, .hwaro/는 거부합니다. 파일이나 영역이 없거나, 줄 범위가 파일을 벗어나거나, 경로가 거부되면 페이지와 파일 이름을 담은 오류로 빌드가 실패합니다:
Error [HWARO_E_TEMPLATE]: {{ include_code(path="examples/app.cr", region="srv") }} in content/guide.md: region 'srv' not found
포함된 파일은 그것을 포함하는 페이지의 의존성입니다. --cache 웜 빌드와 hwaro serve 모두 파일이 바뀌면 페이지를 다시 렌더링하며, content/ 밖의 파일도 마찬가지입니다.
코드 스팬, 펜스 코드 블록, {% raw %} 영역 안의 호출은 글자 그대로 남습니다.
내장 숏코드를 덮어쓰려면
templates/shortcodes/에 같은 이름의 파일을 만들면 됩니다(예:templates/shortcodes/youtube.html). 사용자 템플릿이 항상 우선하며, 자기 계약을 스스로 정의하므로 위 경고는 적용되지 않습니다.
커스텀 숏코드 작성
숏코드 템플릿은 templates/shortcodes/에 둡니다.
숏코드 템플릿이 렌더링 중에 실패하면(예: 정의되지 않은 속성 접근, 존재하지
않는 템플릿을 {% include %}) 페이지 템플릿과 마찬가지로 빌드가
HWARO_E_TEMPLATE(종료 코드 4)로 실패합니다. 오류 메시지에는
templates/shortcodes/alert.html:3:5처럼 숏코드 파일과 줄 번호가 표시됩니다.
hwaro serve에서는 대신 오류 오버레이에 표시됩니다.
숏코드 템플릿의 구문 오류는 같은 파일과 위치를 담은 경고로 알립니다. Hwaro는 호출 위치에 HTML 주석을 표시하고 페이지 렌더링을 계속합니다.
예시: 알림 상자
templates/shortcodes/alert.html을 만듭니다:
{% if type and message %}
<div class="alert alert-{{ type }}">
{{ message | safe }}
</div>
{% endif %}
콘텐츠에서 사용:
{{ alert(type="warning", message="This is important!") }}
출력:
<div class="alert alert-warning">
This is important!
</div>
예시: YouTube 삽입
templates/shortcodes/youtube.html을 만듭니다:
{% if id %}
<div class="video-container">
<iframe
src="https://www.youtube.com/embed/{{ id }}"
frameborder="0"
allowfullscreen>
</iframe>
</div>
{% endif %}
콘텐츠에서 사용:
{{ youtube(id="dQw4w9WgXcQ") }}
예시: 캡션 있는 figure
templates/shortcodes/figure.html을 만듭니다:
<figure>
<img src="{{ src }}" alt="{{ alt | default(value='') }}">
{% if caption %}
<figcaption>{{ caption }}</figcaption>
{% endif %}
</figure>
콘텐츠에서 사용:
{{ figure(src="/images/photo.jpg", alt="A photo", caption="My caption") }}
예시: 이미지 갤러리(에셋 코로케이션)
페이지와 같은 디렉터리(페이지 번들)에 있는 이미지를 자동으로 나열하는 갤러리를 만들 수 있습니다.
templates/shortcodes/gallery.html을 만듭니다:
<div class="gallery">
{% for asset in page.assets -%}
{%- if asset is matching("[.](jpg|png)$") -%}
{% set image = resize_image(path=asset, width=240, height=180) %}
<a href="{{ get_url(path=asset) }}" target="_blank">
<img src="{{ image.url }}" alt="{{ asset }}" />
</a>
{%- endif %}
{%- endfor %}
</div>
콘텐츠에서 사용(페이지 번들 디렉터리 안에서):
{{ gallery() }}
마크다운 파일 옆에 있는 모든 JPG/PNG 이미지가 그리드로 렌더링됩니다.
블록 숏코드
블록 숏코드는 여는 태그와 닫는 태그 사이의 콘텐츠를 감쌉니다:
{% note() %}
This is the **body** content of the shortcode.
{% end %}
본문은 body 변수로 숏코드 템플릿에 전달됩니다. 마크다운 변환은 자동으로 적용되지 않으므로, 필요하면 템플릿에서 markdownify 필터를 사용합니다:
<div class="note">
{{ body | markdownify | safe }}
</div>
원문 그대로 쓰려면 body를 그대로 출력합니다:
<div class="note">{{ body }}</div>
중첩 숏코드
블록 숏코드는 최대 5단계까지 중첩할 수 있습니다:
{% outer() %}
Some text with {{ inner(type="info") }} inside.
{% end %}
인자 문법
이름 있는 인자
인자는 여러 따옴표 스타일을 지원합니다:
{{ alert(type="warning", message="Double quotes") }}
{{ alert(type='info', message='Single quotes') }}
{{ alert(type=danger, message=Unquoted) }}
따옴표 없는 값은 첫 공백에서 끝나므로(message=No quotes here는 No만 전달) 한 단어보다 긴 값은 따옴표로 감싸세요.
위치 인자
key=value 문법을 쓰지 않으면 인자가 _0, _1, … 로 할당됩니다:
{{ youtube("dQw4w9WgXcQ") }}
숏코드 템플릿에서는 {{ _0 }}으로 접근합니다:
<iframe src="https://www.youtube.com/embed/{{ _0 }}"></iframe>
내장 변수
숏코드에서 접근할 수 있는 값:
- 전달된 모든 인자
site객체page객체- 표준 필터와 함수
{# In shortcode template #}
<a href="{{ site.base_url }}/{{ url }}">{{ text }}</a>
팁
인자 검증
필수 인자는 항상 확인합니다:
{% if not url %}
<p class="error">Missing url parameter</p>
{% else %}
<a href="{{ url }}">{{ text | default(value="Link") }}</a>
{% endif %}
HTML에는 safe 필터 사용
HTML 콘텐츠를 전달할 때:
{{ content | safe }}
숏코드 정리
관련 숏코드를 묶어서 관리합니다:
templates/shortcodes/
├── alert.html
├── youtube.html
├── figure.html
├── code/
│ ├── tabs.html
│ └── snippet.html
└── social/
├── twitter.html
└── github.html
하위 디렉터리의 숏코드는 명시적 형식으로 templates/shortcodes/ 아래 경로를
적어 호출합니다: {{ shortcode("code/tabs", lang="crystal") }}. 짧은 형식은
이름만 받으므로 {{ code/tabs() }}는 글자 그대로 남습니다.