blog

사용자 정의 템플릿의 헥소 테마 개발

이 기사는 주로 테마 개발자로서 테마의 사용자 정의 수정에서 테마의 확장 성이 더 나은 테마를 만드는 방법에 대한 것이며, 달성하는 방법의 테마를 더 쉽게 업그레이드 할 수 있습니...

Oct 13, 2025 · 8 min. read
シェア

이 기사는 주로 테마 개발자로서 테마의 사용자 정의 수정에서 테마를 더 나은 확장 성을 갖도록 만드는 방법에 대해 구현하는 방법의 테마를 업그레이드하는 것이 더 평평 할 수 있습니다.

여기에 문제가 있습니다.

Hexo는 테마 패키지를 설치하는 두 가지 방법을 제공합니다:

  • 테마 패키지 파일 바로 아래의 테마 디렉토리에서 직접 테마를 변경하면 사용자가 테마를 쉽게 변경할 수 있지만 마법 변경 후에는 테마를 업그레이드하기가 더 어려워집니다.
  • npm을 통해 테마 패키지를 설치하면 사용자가 테마를 업그레이드하기에는 더 편리하지만 기존 테마를 확장하기는 쉽지 않습니다.

사용자가 테마를 사용자 정의하려는 경우 기본적으로 첫 번째 설치 방법을 통해서만 테마를 수정하고 소스 코드를 수정해야만 테마를 수정할 수 있습니다. 이로 인해 테마가 일부 버그 또는 테마 반복 N 버전을 수정할 때 사용자가 테마를 업그레이드하려는 경우 문제가 더 번거로워집니다.

쉽게 업그레이드하고 개인화할 수 있는 방법이 있을까요? 대답은 '예'입니다. 즉, 테마 패키지를 배포하는 npm 방법을 통해 몇 가지 마법을 통해 확장 할 수있는 기능이 있으므로이 문서에서이를 달성하는 방법을 설명합니다.

템플릿

헥소에서는 테마의 템플릿이 웹사이트 페이지 프로그램의 방식을 결정하는데, 비슷한 구조의 다른 페이지가 있을 경우 레이아웃 (레이아웃)을 통해 동일한 구조를 재사용할 수 있으며, 유사한 부분은 부분 불러오기를 통해 일반적인 부분 템플릿으로 추출하여 템플릿 재사용의 효과를 얻을 수 있습니다.

이것은 템플릿의 재사용을 처리하기 위해 테마 개발에서 헥소이며, 로컬 템플릿은로드 할 위치가 필요한 독립적 인 구성 요소로 이해할 수 있습니다. 사용자가 로컬 템플릿을 교체하려는 경우 사용자가 새 템플릿을 제공하도록 한 다음 사용자가 제공 한 템플릿을로드하도록 할 수 있으며, 이는 사용자가 단어의 개성을 확장하기 위해 테마의 경우 소스 코드를 수정하지 않는 사용자에게 달성하지 않는 것입니다.

Partial

Hexo가 부분 템플릿을 로드하는 방법을 확인하려면 Hexo 소스 코드의 Partial 구현을 살펴보면 ctx.theme를 호출하여 해당 뷰를 가져온 다음 render를 호출하여 렌더링하는 것을 볼 수 있습니다.

const { dirname, join } = require("path");
module.exports = (ctx) =>
 function partial(name, locals, options = {}) {
 const viewDir = this.view_dir;
 const currentView = this.filename.substring(viewDir.length);
 const path = join(dirname(currentView), name); // 현재 찾을 경로에 따르면 부분 템플릿 경로
 const view = ctx.theme.getView(path) || ctx.theme.getView(name); // 경로에 보기 일치
 const viewLocals = { layout: false };
 // Partial don't need layout
 viewLocals.layout = false;
 return view.renderSync(viewLocals);
 };

헥소에는 소스 디렉토리 파일과 테마 패키지 파일 두 가지 종류의 파일 처리가 있습니다. 헬퍼 함수 등록에서 ctx는 실제로 헥소 런타임의 인스턴스이고, 위의 ctx.theme는 테마 파일 처리의 박스이며, 헥소에서 제공하는 api 통해 getView 뿐만 아니라 setView, removeView 메서드도 제공하는 것을 확인할 수 있습니다.

setView 코드를 보면 새 보기를 재설정하면 기존 보기를 덮어쓰는 것을 볼 수 있는데, 이는 테마에서 로컬 템플릿을 직접 재정의할 수 있음을 의미합니다.


 setView(path, data) {
 const ext = extname(path);
 const name = path.substring(0, path.length - ext.length);
 this.views[name] = this.views[name] || {};
 const views = this.views[name];
 views[ext] = new this.View(path, data);
 }

수정 예시

예를 들어 헥소 테마 비동기화를 재정의하려면 생성하기 전 훅에서 테마의 기본 사이드바 템플릿을 재정의합니다.

hexo.on("generateBefore", () => {
 hexo.theme.setView("_partial/sidebar/index.ejs", "<div>111</div>");
});

실행하면 사이드바 템플릿이 작성된 대로 111로 바뀐 것을 확인할 수 있습니다.

테마 구현

위의 접근 방식을 통해 테마 기본 템플릿 기능을 재정의하여 실제로 달성 할 수 있지만 사용자가 직접 수정하도록하는 것은 매우 비우호적이며 테마로 이동하여 로컬 템플릿 정보의 경로를 확인해야하며 테마 기본 템플릿 로직을 재정의하여 자체 로딩 파일 콘텐츠를 작성해야합니다.

이 작업의 일부는 처리 내에서 테마에 내장 될 수 있으며 사용자가 자신의 템플릿을 작성하고 해당 템플릿을 교체해야 할 필요성을 알릴 수 있습니다. 대략적인 프로세스는 다음과 같습니다:

경로를 통해 간단하게 재정의할 수 있도록 기본 구성을 제공할 수도 있습니다.

이와 유사하게 구성에서 테마에 사용된 로컬 템플릿을 구성하면 테마에 사용된 로컬 템플릿이 구성에 표시됩니다.

layout:
 path: layout
 # layout
 main: _partial/main
 header: _partial/header
 banner: _partial/banner
 sidebar: _partial/sidebar/index
 footer: _partial/footer

그런 다음 로컬 템플릿을 로드할 때 구성 정보를 직접 읽고 사용자가 layout.header를 재정의하면 테마가 자동으로 새 템플릿을 사용합니다.

<%- partial(theme.layout.header) %>

템플릿 로딩 구현

위의 구성을 기반으로 레이아웃 경로 구성이 템플릿이 있는 디렉터리를 가리키도록 하여 저장소 경로를 사용자 지정할 수 있도록 합니다.

layout:
 path: layout

먼저 구성에 따라 템플릿이 존재하는 절대 경로를 가져오고, 루트 디렉터리를 가져와서 헥소 예제에 따라 전체 경로 위치를 스플라이스할 수 있습니다.

const { resolve } = require("path");
const layoutDir = resolve(hexo.base_dir, hexo.theme.config.layout.path);

그런 다음 파일과 디렉터리를 수신하는 것입니다. 헥소-f를 직접 사용하면 추가 종속성을 설치하지 않아도 되고, 신규, 삭제, 수정 및 폴더 변경을 수신할 수 있으므로 다양한 이벤트에 대응하여 다양한 작업을 수행할 수 있습니다.

const { watch } = require("hexo-fs");
watch(layoutDir, {
 persistent: true,
 awaitWriteFinish: {
 stabilityThreshold: 200,
 },
}).then((watcher) => {
 watcher.on("add", (path) => /** 템플릿 설정하기*/);
 watcher.on("change", (path) => /** 템플릿 설정하기*/);
 watcher.on("unlink", (path) => /** 템플릿 제거하기*/);
 watcher.on("addDir", (path) => /** 폴더 추가, 재귀적으로 설정 템플릿 순회하기*/);
});

위는 템플릿을 로드하는 설정이므로 사용자 정의 템플릿 이름이 테마의 템플릿 이름과 충돌하여 테마의 템플릿을 재정의하는 것을 방지하기 위해 합의된 접두사를 추가하여 이름을 변경하지 않도록 템플릿을 설정하는 데 필요한 모든 것은 간단한 패키지를 사용할 수 있습니다.

const setView = (fullpath) => {
 const path = "async" + fullpath.replace(layoutDir, ""); // 비동기는 고정 접두사입니다.
 hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));
};

위의 처리, 사용자 정의 템플릿은 정상적으로 로드할 수 있지만 사용자 정의 템플릿과 다른 템플릿을 도입할 때 일부 템플릿 엔진에서 경로가 정상적이지 않은 문제가 발생합니다. 보기 인스턴스 정보를 보면 루트 디렉터리가 실제로 존재하는 반면 node_modules의 디렉터리를 가리키는 것을 볼 수 있습니다.

뷰 소스를 보면 해당 소스가 this를 가져오는 것을 볼 수 있습니다. _theme.base, this. _theme.base에서 테마가 저장된 디렉토리인 theme_dir까지, 그리고 마지막으로 renderer.compile을 통해 렌더링할 템플릿을 설정하여 잘못된 수신 경로를 생성합니다.

위의 코드를 수정한 이유를 알고 있으므로 경로 정보를 기반으로 뷰로 돌아가도록 설정한 다음 수동으로 설정합니다.

const setView = (fullpath) => {
 const path = "async" + fullpath.replace(layoutDir, ""); // 비동기는 고정 접두사입니다.
 hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));
 const view = hexo.theme.getView(path);
 view.source = fullpath; // 수정 경로
 view._precompile(); // 렌더러 초기화 불러오기
};

위의 내용을 Hexo의 생성 전에 배치합니다:

const { resolve } = require("path");
const { watch, readdirSync, statSync } = require("hexo-fs");
hexo.on("generateBefore", () => {
 const layoutDir = resolve(hexo.base_dir, hexo.theme.config.layout.path);
 const setView = (fullpath) => {
 const path = "async" + fullpath.replace(layoutDir, ""); // 비동기는 고정 접두사입니다.
 hexo.theme.setView(path, readFileSync(fullpath, { encoding: "utf8" }));
 const view = hexo.theme.getView(path);
 view.source = fullpath; // 수정 경로
 view._precompile(); // 렌더러 초기화 불러오기
 };
 watch(layoutDir, {
 persistent: true,
 awaitWriteFinish: {
 stabilityThreshold: 200,
 },
 }).then((watcher) => {
 watcher.on("add", (path) => setView(path));
 watcher.on("change", (path) => setView(path));
 watcher.on("unlink", (path) => {
 const path = "async" + path.replace(layoutDir, "");
 hexo.theme.removeView(path);
 });
 watcher.on("addDir", (path) => loadDir(path));
 });
 const loadDir = (base) => {
 let dirs = readdirSync(base);
 dirs.forEach((path) => {
 const fullpath = resolve(base, path);
 const stats = statSync(fullpath);
 if (stats.isDirectory()) {
 loadDir(fullpath);
 } else if (stats.isFile()) {
 setView(fullpath);
 }
 });
 };
 loadDir(layoutDir);
});

여기서는 주요 기능과 최적화할 다른 항목의 구현에 대해서는 설명하지 않으며, 소스 코드의 전체 구현을 확인할 수 있습니다.

예시

헥소 테마 비동기화를 예로 들어 루트 디렉터리에 새 레이아웃 디렉터리를 만들고 디렉터리 아래에 다음 구조의 sidebar.ejs 파일을 추가합니다:

┌── 블로그
│ └── 레이아웃
│ └── 사이드바.ejs
│ └── 스캐폴드
│ └── 출처
│ └── 테마

sidebar.ejs 약간의 콘텐츠 추가

<div>111</div>

config.async.yml의 레이아웃 구성을 변경하여 기본 사이드바 템플릿을 대체합니다.

layout:
 sidebar: async/sidebar

이를 실행하면 동일한 효과가 나타나지만 사용자가 보기에 단순화된 것을 확인할 수 있습니다.

결론

위의 접근 방식을 통해 npm을 사용하여 테마를 설치할 수 있지만 일부 영역의 사용자 정의 교체도 지원하여 테마 버전이 반복적으로 업그레이드될 때 목적을 개인화할 수 있지만 사용자가 더 편리하게 업데이트 및 업그레이드할 수 있습니다.

Read next

새해에는 프론트엔드 암호화에 대해 알아봅시다!

안전을 위해 프론트 엔드에서는 인터페이스를 통해 백엔드로 전달하기 전에 로그인 비밀번호, 이름, ID 번호, 휴대폰 번호 및 기타 정보를 암호화해야 하는 경우가 많으므로 몇 가지 암호화 알고리즘을 알아야 하며, 가장 일반적으로 사용되는 알고리즘은 RSA 및 AES입니다.

Oct 13, 2025 · 3 min read