쇼트코드 만들기

이 문서는 사용자 정의 쇼트코드를 만드는 방법을 설명합니다.

개요

숏코드는 다양한 콘텐츠를 생성하는 특별한 마크다운 지시어입니다. Quarto 숏코드는 Hugo 숏코드워드프레스 숏코드와 형식·기능이 비슷합니다.

예를 들어 아래 숏코드는 문서 메타데이터의 title을 출력합니다.

{{< meta title >}}

기본 제공 숏코드

Quarto가 기본 제공하는 숏코드는 다음과 같습니다.

숏코드 설명
version Quarto CLI 버전 출력
var _variables.yml 값 출력
meta 문서 메타데이터 값 출력
env 시스템 환경 변수를 출력
pagebreak 네이티브 페이지 분할 삽입
kbd 키보드 단축키 표현
video 문서에 비디오 삽입
include 다른 qmd 내용을 포함
embed Jupyter 노트북 셀 삽입
placeholder 플레이스홀더 이미지를 문서에 추가
lipsum 플레이스홀더 텍스트를 문서에 추가
contents 문서 내 콘텐츠 재배치

빠른 시작

여기서는 간단한 쇼트코드 확장을 만드는 방법을 설명합니다. 이를 위해 quarto create 명령을 사용합니다. VS Code, Positron, RStudio를 사용 중이라면 각 통합 터미널에서 quarto create를 실행하세요.

시작하려면 쇼트코드 확장을 만들고 싶은 상위 디렉터리에서 quarto create extension shortcode를 실행합니다:

Terminal
$ quarto create extension shortcode
 ? Extension Name › shorty

위와 같이 확장 이름을 묻는 프롬프트가 나타납니다. shorty를 입력하고 Enter를 누르면 쇼트코드 확장이 생성됩니다:

Creating extension at /Users/jjallaire/extensions/shorty/shorty:
  - Created README.md
  - Created _extensions/shorty/shorty.lua
  - Created _extensions/shorty/_extension.yml
  - Created .gitignore
  - Created example.qmd

VS Code, Positron, RStudio 안에서 실행했다면 확장 프로젝트를 여는 새 창이 열립니다.

_extensions/shorty/ 안의 파일 내용은 다음과 같습니다:

_extensions/shorty/_extension.yml
title: Shorty
author: J.J. Allaire
version: 1.0.0
quarto-required: ">=1.2.222"
contributes:
  shortcodes:
    - shorty.lua
_extensions/shorty/shorty.lua
return {
  ['shorty'] = function(args, kwargs, meta) 
    return pandoc.Str("Hello from Shorty!")
  end
}

마지막으로 example.qmd 파일에는 확장을 실행해 보는 코드가 들어 있습니다. 예를 들면 다음과 같습니다:

example.qmd
---
title: "Shorty Example"
---

{{< shorty >}}

쇼트코드를 개발하려면 example.qmd를 렌더/미리보기한 다음 shorty.lua를 수정하세요(shorty.lua를 변경하면 미리보기가 자동으로 새로고침됩니다).

개발

쇼트코드는 Lua로 만듭니다. Lua(또는 Pandoc 필터)에 익숙하지 않다면 다음 자료를 참고하세요:

  • Lua 개발 (Lua는 쇼트코드를 만드는 데 사용하는 언어입니다).

  • Lua API 참조. Quarto용 Lua 확장 API를 설명하며, 특히 quarto.shortcode.* 항목을 참고하세요.

쇼트코드는 하나 이상의 인자를 받아 Pandoc AST 노드(또는 노드 목록)를 반환하는 Lua 함수로 구현합니다.

다음은 Quarto에 기본 포함된 env 쇼트코드의 구현입니다:

env.lua
function env(args)
  local var = pandoc.utils.stringify(args[1])
  local value = os.getenv(var)
  if value ~= nil then
    return pandoc.Str(value)
  else
    return pandoc.Null()
  end
end

쇼트코드의 인자는 args(1차원 배열)로 전달되며, 각 인자는 Pandoc 인라인 목록(즉, 텍스트를 파싱한 마크다운 AST)입니다.

pandoc.utils.stringify()를 사용해 인라인을 일반 문자열로 변환한 다음, os.getenv()로 값을 가져옵니다.

이 쇼트코드는 다음과 같이 사용합니다:

{{< env HOME >}}

배포

확장 소스 코드가 GitHub 저장소에 있으면 GitHub 조직과 저장소 이름을 지정해 설치할 수 있습니다. 예:

Terminal
# install the current HEAD of the extension
quarto add cooltools/shorty

# install a branch or tagged release of the extension
quarto add cooltools/shorty@v1.2
quarto add cooltools/shorty@bugfix-22

GitHub 저장소 대신 간단한 gzip 아카이브로 확장을 묶어 배포할 수도 있습니다. 자세한 내용은 확장 배포 문서를 참고하세요.

예시

Quarto 팀이 작성한 쇼트코드 확장 소스를 살펴보면 도움이 됩니다:

확장 설명
fancy-text LaTeX와 BibTeX 같은 꾸밈 문자열을 여러 포맷에서 보기 좋게 출력합니다.
fontawesome HTML과 PDF 문서에서 Font Awesome 아이콘을 사용합니다.
video HTML 문서와 Revealjs 프레젠테이션에 비디오를 임베드합니다.

추가로 주석이 달린 예시를 아래에 제공합니다.

Raw 출력

쇼트코드는 렌더링 대상 포맷에 맞춰 출력을 조정할 수 있습니다. HTML에서는 풍부한 출력물을 조건부로 생성하면서도 PDF나 MS Word에서는 문서가 정상적으로 렌더링되게 하고 싶을 때 유용합니다.

pagebreak 쇼트코드는 여러 포맷에서 “native” 페이지 나누기를 생성합니다. 다음은 pagebreak 구현입니다:

pagebreak.lua
function pagebreak()
 
  local raw = {
    epub = '<p style="page-break-after: always;"> </p>',
    html = '<div style="page-break-after: always;"></div>',
    latex = '\\newpage{}',
    ooxml = '<w:p><w:r><w:br w:type="page"/></w:r></w:p>',
    odt = '<text:p text:style-name="Pagebreak"/>',
    context = '\\page'
  }

  if quarto.doc.isFormat('docx') then
    return pandoc.RawBlock('openxml', raw.ooxml)
  elseif quarto.doc.isFormat('pdf')  then
    return pandoc.RawBlock('tex', raw.latex)
  elseif quarto.doc.isFormat('odt')  then
    return pandoc.RawBlock('opendocument', raw.odt)
  elseif quarto.doc.isFormat('epub') then
    return pandoc.RawBlock('html', raw.epub)
  elseif quarto.doc.isFormat('html') then
    return pandoc.RawBlock('html', raw.html)
  elseif quarto.doc.isFormat('context') then
    return pandoc.RawBlock('context', raw.context)
  else
    -- fall back to insert a form feed character
    return pandoc.Para{pandoc.Str '\f'}
  end

end

pandoc.RawBlock() 함수로 대상 포맷에 맞는 raw 콘텐츠를 출력합니다. Raw 블록은 출력 파일로 그대로 전달되며 마크다운으로 처리되지 않습니다.

이 쇼트코드는 다음과 같이 사용합니다:

{{< pagebreak >}}

이름 있는 인자

앞의 예시는 단일 인자(env) 또는 인자 없음(pagebreak)을 사용합니다. 여기서는 이름 있는 인자 처리를 보여주기 위해 현재 git 리비전을 출력하는 git-rev 쇼트코드를 구현합니다. short 옵션으로 짧은 SHA1 값 또는 긴 값 중 무엇을 표시할지 결정합니다:

git.lua
-- run git and read its output
function git(command)
  local p = io.popen("git " .. command)
  local output = p:read('*all')
  p:close()
  return output
end

-- return a table containing shortcode definitions
-- defining shortcodes this way allows us to create helper 
-- functions that are not themselves considered shortcodes 
return {
  ["git-rev"] = function(args, kwargs)
    -- command line args
    local cmdArgs = ""
    local short = pandoc.utils.stringify(kwargs["short"])
    if short == "true" then
      cmdArgs = cmdArgs .. "--short "
    end
    
    -- run the command
    local cmd = "rev-parse " .. cmdArgs .. "HEAD"
    local rev = git(cmd)
    
    -- return as string
    return pandoc.Str(rev)
  end
}

여기서 새로 등장한 점은 다음과 같습니다:

  1. 쇼트코드 함수를 전역으로 정의하는 대신 쇼트코드 정의를 담은 테이블을 반환합니다. 이렇게 하면 쇼트코드로 등록되지 않는 헬퍼 함수를 만들 수 있고, 이름에 대시(-)가 들어가는 쇼트코드도 정의할 수 있습니다.

  2. 쇼트코드 핸들러에 kwargs라는 새 인자를 추가했습니다. 이는 쇼트코드의 이름 있는 인자를 담고 있습니다. args와 마찬가지로 kwargs의 값도 항상 Pandoc 인라인 목록이므로(마크다운을 인자로 받을 수 있음), short처럼 단순 불리언 값은 pandoc.utils.stringify()로 문자열로 변환한 뒤 "true"와 비교해야 합니다.

이 쇼트코드는 다음과 같이 사용합니다:

---
title: "My Document"
---

{{< git-rev >}}
{{< git-rev short=true >}}

메타데이터 옵션

경우에 따라 쇼트코드 동작에 영향을 주는 옵션을 제공하고 싶을 수 있습니다. 쇼트코드 핸들러의 세 번째 인자(meta)로 문서/프로젝트 수준 메타데이터에 접근할 수 있습니다.

이번에는 git-rev 쇼트코드의 다른 버전을 구현해, 리비전을 일반 텍스트가 아니라 GitHub 링크로 출력해 보겠습니다. 이를 위해 github.ownergithub.repo 메타데이터 값을 사용합니다:

git.lua
function git(command)
  local p = io.popen("git " .. command)
  local output = p:read('*all')
  p:close()
  return output
end

return {
  
  ["git-rev"] = function(args, kwargs, meta)
    -- run the command
    local rev = git("rev-parse HEAD")
    
    -- target repo
    local owner = pandoc.utils.stringify(meta["github.owner"])
    local repo = pandoc.utils.stringify(meta["github.repo"])
    local url = "https://github.com/" 
                .. owner .. "/" .. repo .. "/" .. rev 
    
    -- return as link
    return pandoc.Link(pandoc.Str(rev), url)
  end
}

argskwargs와 마찬가지로 meta 값도 항상 Pandoc 인라인 목록으로 제공되므로, 보통 pandoc.utils.stringify()로 문자열로 변환해야 합니다.

이 쇼트코드를 문서에서 사용하려면, 문서 옵션으로 GitHub 정보를 제공한 다음 원하는 위치에 쇼트코드를 삽입합니다:

---
title: "My Document"
github:
  owner: quarto-dev
  repo: quarto-cli
---

{{< git-rev >}}

쇼트코드 등록과 GitHub 메타데이터는 프로젝트 수준의 _quarto.yml 파일이나 디렉터리 수준의 _metadata.yml 파일에 넣어도 됩니다.

Raw 인자

Quarto >= 1.3에서는 raw_args 매개변수를 추가해 쇼트코드에 전달되는 인라인 스트림의 원본에도 접근할 수 있습니다. 예:

function shorty(args, kwargs, meta, raw_args)
  -- ...
end

컨텍스트 인식

Quarto >= 1.5에서는 쇼트코드가 호출된 컨텍스트를 함수 호출의 다섯 번째 매개변수로 받을 수 있습니다. contextblock, inline, text 중 하나의 문자열입니다:

  • contextblock이면 쇼트코드가 단독 블록으로 존재합니다
  • contextinline이면 쇼트코드가 여러 인라인 노드 중 하나입니다
  • contexttext이면 쇼트코드가 Pandoc 노드의 텍스트 필드 안에 있습니다. 예를 들면:
    • CodeBlock 또는 Code 요소의 내용
    • RawBlock 또는 RawInline 요소의 내용
    • Div, Span 등 Pandoc 요소의 속성 값
    • Link 요소의 URL
    • Image 요소의 소스
function shorty(args, kwargs, meta, raw_args, context)
  -- ...
end

이스케이프

변수 숏코드 사용법을 문서화할 때(예: 이 문서처럼) 숏코드가 실제로 처리되지 않도록 막아야 할 수 있습니다. 방법은 두 가지입니다.

  1. 다음과 같이 중괄호를 추가해 숏코드 참조를 이스케이프합니다.

    {{{< var version >}}}
  2. 숏코드 처리를 막고 싶은 코드 블록에 shortcodes=false 속성을 추가합니다.

    ```{shortcodes=false}
    {{< var version >}}
    ```