처음부터 Hugo + Tailwind 기술 블로그 구축하기: INFINI Labs Blog를 사례로
I’m happy to translate the article for you, but I need the full text of the post. Could you please paste the content you’d like translated (excluding the source line you’ve already provided)? Once I have the article text, I’ll translate it into Korean while preserving the original formatting, markdown, and code blocks.
Source: …
1. 디렉터리 구조: 콘텐츠, 테마, 설정, 에셋은 어디에 있나요?
이 저장소는 네 개의 레이어로 생각할 수 있습니다:
1) 콘텐츠 레이어 – content/
content/english/posts/ # 블로그 포스트 (Markdown + front matter)
content/english/authors/ # 저자 페이지 (Markdown + front matter)
각 포스트는 front matter 로 시작합니다 (YAML/TOML/JSON 모두 지원; 이 레포는 YAML을 사용). 일반적인 필드 예시:
| 필드 | 목적 |
|---|---|
title / description | 페이지 제목 및 SEO |
date | 발행 시간 (정렬 및 표시 방식에 영향) |
categories / tags | 카테고리 및 태그 페이지에 사용 |
image | 커버 이미지 |
author | 저자 이름 (해당 저자 페이지와 연결) |
템플릿은 이러한 필드를 읽습니다. 예를 들어 themes/hugoplate/layouts/posts/single.html 은 커버 이미지, 저자, 카테고리, 발행일, 본문 내용, 목차(TOC)를 표시합니다.
2) 설정 레이어 – hugo.toml + config/_default/*
Hugo는 config/ 디렉터리 아래에서 설정을 분할하는 것을 지원합니다. 이 프로젝트는 다음을 사용합니다:
-
루트 설정:
hugo.tomltheme = "hugoplate" [outputs] home = ["HTML", "JSON"] # public/index.json 생성 [build] [build.buildStats] enable = true # 정확한 Tailwind 스캔 (아래 설명) -
언어 설정:
config/_default/languages.toml[languages.en] languageName = "English" weight = 1 contentDir = "content/english" -
사이트 파라미터:
config/_default/params.toml– 로고, 파비콘, 테마 색상, 공지 배너, 쿠키 배너, 사이드바 위젯 등을 정의합니다. -
Hugo 모듈:
config/_default/module.toml– 다음과 같은 모듈을 가져옵니다[[imports]] path = "github.com/gethugothemes/hugo-modules/images" [[imports]] path = "github.com/gethugothemes/hugo-modules/pwa" [[imports]] path = "github.com/gethugothemes/hugo-modules/seo-tools/basic-seo" [[imports]] path = "github.com/hugomods/mermaid"(이러한 import 때문에 CI에 Go가 필요합니다.)
3) 테마 레이어 – themes/hugoplate/
테마는 페이지 구조와 Hugo Pipes 를 정의합니다:
| 파일 | 목적 |
|---|---|
themes/hugoplate/layouts/_default/baseof.html | 기본 레이아웃 스켈레톤 |
themes/hugoplate/layouts/index.html | 메인 페이지 리스트 |
themes/hugoplate/layouts/posts/single.html | 포스트 상세 페이지 |
themes/hugoplate/layouts/index.json | JSON 검색 인덱스를 생성하는 템플릿 (중요) |
Note: 템플릿에서
{{ partial "image" ... }}를 볼 수 있지만, 테마 디렉터리 안에partials/image.html파일은 없습니다. 이 partial 은module.toml에서 import 된 hugo‑modules/images 모듈에서 제공됩니다. 이것은 흔히 쓰이는 패턴으로, 테마 + 모듈형 기능 입니다.
4) 에셋 레이어 – assets/ vs static/
이 두 디렉터리는 Hugo에서 동작 방식이 다릅니다:
| 디렉터리 | Hugo가 처리하는 방식 |
|---|---|
assets/ | Hugo Pipes 로 처리 (컴파일, 지문 추가, 압축 등). * 이미지 파일은 assets/images/... 아래에 두며, 빌드 시 public/images/... 로 출력됩니다.* 스타일/스크립트 소스 파일은 themes/hugoplate/assets/* 아래에 두고 Hugo Pipes 로 번들링합니다. |
static/ | 그대로 public/ 로 복사됩니다.static/assets/index-*.css, static/assets/index-*.js, pizza_wasm_bg-*.wasm 파일이 포함됩니다.baseof.html 은 /assets/index-*.css 와 /assets/index-*.js 를 하드코딩해서 import 합니다. |
2. Build Pipeline: What Happens From Markdown to Final HTML/CSS/JS?
1) Build entry points – package.json scripts
{
"scripts": {
"dev": "hugo server",
"build": "hugo --gc --minify --templateMetrics --templateMetricsHints --forceSyncStatic",
"project-setup": "node ./scripts/projectSetup.js"
}
}
dev는 로컬 Hugo 서버를 실행합니다.build는 전체 프로덕션 빌드를 담당하며, Node는 주로 PostCSS/Tailwind 용으로 사용됩니다.
project-setup 스크립트는 Hugoplate 템플릿에서 제공되며 exampleSite/ 레이아웃을 실제 프로젝트 레이아웃으로 이동시킵니다. 이 저장소에는 이미 themes/ 디렉터리가 존재하므로 스크립트는 보통 “Project already setup” 라는 메시지를 출력하고 아무 작업도 하지 않습니다—CI에 포함해도 안전합니다.
2) Hugo Modules – Why does CI install Go?
config/_default/module.toml은 여러 모듈(이미지, pwa, seo‑tools, mermaid 등)을 import합니다. 이 모듈들은 Hugo 빌드 중에 가져와지며 렌더링에 참여합니다. 저장소의 go.mod는 모듈 버전을 고정하는 역할을 하며, Hugo Modules의 의존성 매니페스트 역할을 합니다.
역할 분담
| Tool | Role |
|---|---|
| Hugo | 사이트를 렌더링 |
| Go | Hugo Module 의존성을 가져오고 관리 |
그래서 CI에서는 Hugo 와 Go 둘 다 설치합니다( netlify.toml 및 .github/workflows/hugo.yml 참고).
3) How does Tailwind generate only the CSS you actually use?
두 가지 설정이 이를 가능하게 합니다:
-
Hugo build stats –
hugo.toml에서 활성화[build.buildStats] enable = trueHugo는 템플릿을 렌더링하면서
hugo_stats.json을 생성하고, 생성된 HTML에 사용된 모든 클래스/토큰을 기록합니다. -
Tailwind config – stats 파일을 읽음
// tailwind.config.js module.exports = { content: ["./hugo_stats.json"], // …other Tailwind settings… };
Tailwind는 hugo_stats.json을 읽어 필요한 유틸리티 클래스만 매우 정확하게 생성할 수 있습니다. 이는 전체 트리를 스캔하면서 발생하는 많은 false positive와 큰 CSS 번들을 방지합니다.
추가로, hugo.toml은 stats 파일을 마운트하여 Tailwind가 접근할 수 있도록 합니다:
[[module.mounts]]
source = "hugo_stats.json"
target = "assets/watching/hugo_stats.json"
요약
- Content → Markdown (in
content/) → Hugo renders HTML. - Theme & Modules →
themes/hugoplate/+ Hugo Modules (imported viamodule.toml). - Assets → Processed by Hugo Pipes (
assets/) or copied verbatim (static/). - Tailwind → Uses
hugo_stats.jsonfor precise CSS generation. - Search →
index.jsongenerated from a dedicated template, consumed by a pre‑built UI instatic/assets/.
이 구조를 사용하면 사이트를 완전히 정적 도구만으로 빌드하고, 어떤 정적 호스트에도 배포할 수 있으며, 이미지 처리, PWA 지원, SEO 도우미, 오프라인 검색과 같은 강력한 기능도 그대로 활용할 수 있습니다.
Cache‑busting config so CSS rebuilds trigger correctly
이것은 Hugoplate‑based 설정에서 널리 사용되는 통합 방식입니다.
1) CSS/JS는 어떻게 번들링·압축·지문화(fingerprinting) 되나요?
themes/hugoplate/layouts/partials/essentials/style.html 에서 Hugo Pipes 흐름을 확인할 수 있습니다:
- 플러그인 CSS 수집 –
hugo.toml의params.plugins.css에서 가져옵니다. scss/main.scss컴파일 – Hugo Extended가 필요합니다.resources.Concat– 파일들을 병합합니다.css.PostCSS– PostCSS(Tailwind + autoprefixer)를 실행합니다.
프로덕션에서는minify | fingerprint | resources.PostProcess를 수행합니다.
출력은 다음과 같이 렌더링됩니다:
<link href="/assets/css/main-<hash>.css" integrity="..." rel="stylesheet">
JS도 themes/hugoplate/layouts/partials/essentials/script.html 에서 유사하게 처리됩니다.
런타임 사실
- 배포 후 사이트는 순수 정적 파일(HTML/CSS/JS/이미지)만 존재하며, 런타임 컴파일이 일어나지 않습니다.
- 지문화(fingerprinting) 은 캐시를 안정화합니다: 내용이 바뀌면 자산 URL도 바뀌어 브라우저·CDN이 오래된 자산을 제공하지 않게 됩니다.
Source: …
3. Search: index.json와 /static/assets/*는 어떻게 함께 작동하나요?
1) 인덱스는 어디서 오는가?
hugo.toml에
outputs.home = ["HTML", "RSS", "WebAppManifest", "JSON"]
가 설정되어 있고, JSON 출력은 baseName = "index" 로 지정됩니다. 빌드 과정에서 Hugo는 themes/hugoplate/layouts/index.json 템플릿을 사용해 public/index.json 을 생성합니다. 이 템플릿은 사이트 페이지들을 순회하면서 title, URL, tags, category, description, 그리고 순수 텍스트 콘텐츠와 같은 필드를 JSON 배열에 담습니다. 이 파일은 정적 사이트에서 클라이언트‑사이드 검색을 위한 훌륭한 데이터 소스가 됩니다.
2) 검색 UI는 어디서 오는가?
themes/hugoplate/layouts/_default/baseof.html 은 static/assets/ 에 있는 자산들을 하드코딩으로 import 합니다. 이 파일들은 Hugo에 의해 public/assets/ 로 복사되므로, 배포 후 브라우저가 직접 요청할 수 있습니다.
런타임 흐름
- 브라우저가 정적 페이지를 로드합니다.
- 검색 UI의 CSS/JS 를 로드합니다.
- UI 가
index.json(및 필요 시 WASM 자산) 을 가져와 브라우저 내에서 인덱스를 구축합니다. - 검색은 로컬에서 실행되며, 백엔드 API 가 필요하지 않습니다.
이 “빌드 시 생성하고 런타임에 소비한다”는 패턴은 정적 사이트를 강화하는 데 흔히 사용됩니다.
Source: …
4. 비슷한 프로젝트를 처음부터 만들기 (최소 실행 단계)
1) 툴체인 준비
- Hugo Extended (
>= 0.139.2) - Go (CI에서는 Hugo Modules용으로
1.23.3사용) - Node (PostCSS/Tailwind용; CI에서는 Node 20 사용, 로컬에서는 LTS 버전이면 모두 OK)
2) 의존성 설치 및 개발 시작
레포지토리에는 pnpm이 선언돼 있지만, 스크립트는 npm/yarn에서도 동작합니다.
pnpm install
pnpm dev # 또는: npm install && npm run dev
이 명령으로 Hugo 개발 서버가 시작됩니다.
3) 새 포스트 추가
content/english/posts/YYYY/ 아래에 Markdown 파일을 생성합니다. 예시:
---
title: "My First Post"
description: "A short summary"
date: "2025-12-20T09:00:00+08:00"
categories: ["Engineering"]
tags: ["Hugo"]
image: "/images/posts/2025/some-folder/cover.jpg"
author: "Rain9"
lang: "en"
category: "Technology"
subcategory: "Engineering"
draft: true
---
# Hello
Write something here.
Tip: 이미지 파일은
assets/images/posts/...아래에 두고/images/posts/...형태로 참조합니다. 빌드 후에는public/images/posts/...경로에 출력됩니다.
4) 프로덕션 빌드
pnpm build
출력 디렉터리는 public/이며 (netlify.toml: publish = "public" 참고).
5. 배포 및 CI/CD: Netlify / GitHub Pages / Vercel이 빌드하는 방식
이 저장소는 여러 호스팅 플랫폼을 지원합니다.
1) Netlify
netlify.toml
[build]
command = "yarn project-setup; yarn build"
publish = "public"
[build.environment]
HUGO_VERSION = "0.139.2"
GO_VERSION = "1.23.3"
2) GitHub Pages (GitHub Actions)
.github/workflows/hugo.yml 은 다음을 수행합니다:
- 저장소를 체크아웃합니다.
- Node를 설치합니다.
- Hugo Extended를 다운로드합니다.
- Go를 설치합니다.
npm run project-setup을 실행합니다.- 의존성을 설치합니다 (
npm install). - 빌드합니다 (
npm run build). public을 Pages 아티팩트로 업로드하고 배포합니다.
3) Vercel
vercel-build.sh 는 빌드 머신에서 다음을 실행합니다:
- Go를 설치합니다.
- Hugo Extended를 설치합니다.
npm run project-setup을 실행합니다.- 의존성을 설치합니다 (
npm install). - 빌드합니다 (
npm run build).
모든 플랫폼에서 최종적으로 수행되는 작업:
- 툴체인(Hugo + Go + Node)을 프로비저닝합니다.
- 정적
public/디렉터리를 생성합니다.
Wrap‑up: The Boundary Between Build‑time and Run‑time
Hugo 기반 블로그의 핵심 아이디어는 간단합니다:
| Phase | What Happens |
|---|---|
| Build‑time | Hugo가 콘텐츠, 템플릿, 모듈 및 자산을 정적 파일로 컴파일합니다. 또한 최소화, 지문 생성, 인덱스 생성을 수행합니다. |
| Run‑time | CDN/정적 서버는 그 파일들만 제공합니다. 브라우저는 소량의 프론트‑엔드 JS(예: 검색 UI)를 실행합니다. 백엔드 API는 필요하지 않습니다. |
이 경계를 이해하면 사이트 확장이 자연스러워집니다: 새로운 기능을 추가할 때 먼저 “빌드 타임에 데이터를 생성할 수 있나요?” 를 묻고, 그 다음 “브라우저가 이를 소비할 수 있나요?” 를 확인하세요.