gulp-nunjucks-render 모듈을 이용한 HTML작업

모질라의 자바스크립트 템플릿 언어인 nunjucks을 gulp로 활용해서 HTML작업을 하는 방법을 소개합니다. 이 글에서는 마크업 작업자의 HTML산출물 관리를 위한 측면에서 관련 내용을 다룹니다.

샘플프로젝트의 디렉토리 구조

소스파일의 HTML디렉토리는 레이아웃과 본문 페이지, 그리고 공통으로 사용되는 common 디렉토리로 구성됩니다.

{
  "name": "html-tpl",
  "scripts": {
    "start": "gulp default"
  },
  "devDependencies": {
    "del": "^2.2.2",
    "gulp": "^3.9.1",
    "gulp-nunjucks-render": "^2.2.1",
    "gulp-watch": "^4.3.11",
    "run-sequence": "^1.2.2"
  }
}

샘플에서는 위와같은 모듈을 사용했습니다.

'use strict';

var gulp = require('gulp'),
	runSequence = require('run-sequence'),
	watch = require('gulp-watch'),
	nunjucksRender = require('gulp-nunjucks-render'),
	del = require('del');

/* ---------------------------------------------------------------------------------- */

// Clean html
gulp.task('clean-html', function() {
	return del('./dist/html');
});

// html-tpl
gulp.task('html-tpl', function() {
	var manageEnvironment = function(environment) {
		environment.addFilter('tabIndent', function(str, numOfIndents, firstLine) {
			str = str.replace(/^(?=.)/gm, new Array(numOfIndents + 1).join('\t'));
			if(!firstLine) {
				str = str.replace(/^\s+/,"");
			}
			return str;
		});
	};
	
	return gulp.src('./src/html/page/**/*.html')
		.pipe(nunjucksRender({
			envOptions: {
				autoescape: false
			},
			manageEnv: manageEnvironment,
			path: [
				'./src/html'
			]
		}))
		.on('error', function(e) {
			console.log(e);
			this.emit('end');
		})
		.pipe(gulp.dest('./dist/html'));
});

// html
gulp.task('html', function() {
	runSequence('clean-html', 'html-tpl');
});
 
gulp.task('watch', function() {
    watch(['./src/**/*.html'], function() {
		gulp.start('html');
	});
});
 
gulp.task('default', [
	'html',
	'watch'
]);

nunjucks에서 indent필터를 지원하지만 space만 지원합니다.
템플릿이 랜더링될때 들여쓰기 출력이 신경쓰여서 gulpfile.js 에 manageEnv옵션을 이용해서 tabIndent이라는 필터를 추가했습니다.  템플릿 내에서 tabIndent로 탭문자 들여쓰기 필터를 사용할 수 있습니다.

{% macro include(fileName) -%}
	{% include fileName ignore missing -%}
{% endmacro -%}
<!DOCTYPE html>
<html>
<head>
	<title>{{ pageVar.title }}</title>
</head>
<body>

<div class="wrap">
	<!-- header -->
	{{ include("common/header.html") | tabIndent(1) }}

	<!-- content -->
	{% filter tabIndent(1) %}
	{% block content %}{% endblock %}
	{%- endfilter %}

	<!-- footer -->
	{{ include("common/footer.html") | tabIndent(1) }}
</div>
	
</body>
</html>

최상위 레이아웃에서는 include 를 랩핑하는 매크로를 선언했습니다.
include할 때 tabIndent 필터를 적용해서 코드를 간결히 하려는 목적입니다.

{% block content %}{% endblock %} 영역은 각 페이지 본문 내용이 출력되는 block입니다. filter tabIndent로 감싸서 들여쓰기 적용한 모습입니다.

{% set pageVar = { title: 'Home', pageNum: '1' } %}
{% extends "layout/layout.html" %}

{% block content -%}
<section>
	<h2>hello {{ include("common/world.html") }}</h2>
	<p>welcome</p>
</section>
{%- endblock %}

본문 파일에서는 set 태그로 현재 페이지의 변수를 지정하고 레이아웃이나 코드조각에서 활용할 수 있습니다. 그리고 현재 페이지에 적용할 레이아웃 템플릿을 지정합니다. 본문 내부에서도 다른 코드조각을 include할 수 있습니다.

<header>
	<h1>Site</h1>
	{{ include("common/nav.html") | tabIndent(1) }}
</header>
<nav>
	<a href="#"{%if pageVar.pageNum == '1' %} class="active"{% endif %}>Home</a>
	<a href="#"{%if pageVar.pageNum == '2' %} class="active"{% endif %}>About</a>
	<a href="#"{%if pageVar.pageNum == '3' %} class="active"{% endif %}>Contact</a>
</nav>

header파일에서 nav파일을 include했고, nav파일에서는 페이지에서 선언한 변수인 pageVar를 참조해서 현재 페이지에 해당하는 링크에 active클래스를 출력해주고 있습니다.

nunjucks 템플릿 관련 설명은 https://mozilla.github.io/nunjucks/templating.html 에서 참조할 수 있습니다.