404 Not Found

404 Not Found


nginx

Template Engines and SSR: EJS, Pug, and Server-Side Rendering in Practice

1. ストーリー:チャーリーの管理ツール・インターフェース

チャーリーはスタートアップ企業のバックエンド開発者です。チームでは、注文内容、ユーザー統計、システムのステータスを確認するための社内管理ツールが必要でした。同僚たちはReactやVueの使用を提案しましたが、チャーリーは、ページ数が少なく操作も単純なこのような社内ツールには、それらのフレームワークは重すぎると感じました。テンプレートエンジンを用いたサーバーサイドレンダリングで十分だと考えたのです。彼はEJSを選択しました。EJSは基本的にHTMLにいくつかのタグを追加しただけのものなので、非常に簡単に始められるからです。わずか1日で、彼はExpressとEJSを用いて、再利用可能なレイアウトを備えた完全な管理インターフェースを構築した。

学習内容


2. テンプレートのレンダリングプロセス

テンプレートエンジンの主な機能は、テンプレートファイルデータを結合して最終的なHTML文字列を生成し、それをブラウザに返すことです。

100%
flowchart LR
    A[Browser Request] --> B[Express Routing]
    B --> C[Retrieve Data]
    C --> D["res.render()"]
    D --> E[EJS / Pug Compilation]
    E --> F[Generate HTML]
    F --> G[HTTP Response]

この一連のプロセスは、次のように要約できます:

  1. ブラウザがHTTPリクエストを送信する
  2. エクスプレスルートは、対応するハンドラ関数と紐付けられます
  3. ハンドラーは、データベースまたはAPIからデータを取得します。
  4. res.render() を呼び出し、テンプレート名とデータを渡す
  5. テンプレートエンジンは、テンプレートとデータをHTMLにコンパイルします
  6. HTMLはHTTPレスポンスとしてブラウザに返されます

3. EJSの基本構文

EJS(Embedded JavaScript)は、最も直感的なテンプレートエンジンです。テンプレートは単なるプレーンなHTMLであり、<% %>タグを使ってロジックが埋め込まれています。

▶ 例:EJSの4つの主要なタグ

JAVASCRIPT
// <%= %> Output the escaped value (Safe, prevents XSS)
<p>Hello, <%= userName %>!</p>

// <% %> Execution Logic,No output
<% if (isAdmin) { %>
  <span>Admin Panel</span>
<% } %>

// <%- %> Output Raw HTML (Not escaped, use with caution)
<%- articleHtml %>

// <%- include %> Importing Subtemplates
<%- include('partials/header') %>

(1) EJS 構文クイックリファレンス

タグ 機能 出力 エスケープ
<%= %> 出力変数の値 はい はい <%= name %>
<%- %> オリジナルコンテンツを出力 はい いいえ <%- html %>
<% %> 実行ロジック なし <% if (x) { %>
<%# %> コメント なし <%# comment %>
<%- include('x') %> サブテンプレートを含める はい いいえ <%- include('nav') %>

▶ 例:EJSのループと条件分岐

JAVASCRIPT
<ul>
  <% items.forEach(function(item) { %>
    <li>
      <strong><%= item.name %></strong>
      <% if (item.onSale) { %>
        <span class="badge">Sale</span>
      <% } %>
    </li>
  <% }); %>
</ul>

4. Express + EJS の設定

▶ 例:Express + EJS の完全なプロジェクトの設定

プロジェクトの構成:

TEXT
views/
  partials/
    header.ejs
    footer.ejs
  layout.ejs
  index.ejs
app.js
BASH
npm init -y
npm install express ejs
JAVASCRIPT
const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Dashboard',
    user: { name: 'Charlie', role: 'admin' },
    stats: { orders: 128, users: 56 }
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

(1) 主要なAPIのドキュメント

API 目的
app.set('view engine', 'ejs') デフォルトのテンプレートエンジンを設定する レンダリング時にファイル拡張子を指定する必要はありません
app.set('views', path) テンプレートディレクトリを設定 デフォルトは ./views
res.render(view, data) テンプレートをレンダリングして応答する res.render('index', {title: 'Hi'})
res.render(view, data, callback) レンダリング後のHTML文字列を取得 メール送信などの場面で利用可能

▶ 例:res.render の 2 番目の引数としてデータを渡す

JAVASCRIPT
app.get('/profile/:id', (req, res) => {
  const user = { id: req.params.id, name: 'Alice', bio: 'Full-stack dev' };
  res.render('profile', {
    user: user,
    pageTitle: user.name + "'s Profile",
    isLoggedIn: true
  });
});

テンプレート内で変数を名前で直接参照するには:

JAVASCRIPT
<h1><%= pageTitle %></h1>
<p>Name: <%= user.name %></p>
<p>Bio: <%= user.bio %></p>

5. Pugの基本構文

Pug(旧称 Jade)は、終了タグの代わりにインデントを使用するため、構文が非常に簡潔ですが、EJSに比べて習得の難易度が高くなっています。

▶ 例:Pugの基本構文

TEXT
//- Pug Template
doctype html
html
  head
    title= pageTitle
  body
    h1= message
    p Welcome to #{siteName}

コンパイル済みHTML:

TEXT
<!DOCTYPE html>
<html>
  <head><title>My Page</title></head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to MySite</p>
  </body>
</html>

(1) Pug構文クイックリファレンス

構文 機能
tag= value プロパティのバインディング h1= title
#{expr} 補間 p Hello #{name}
- code JSを実行 - const x = 1
if/else if/else 状態 if (admin) ...
each val in arr ループ each item in list
extends layout レイアウトを継承 extends layout
block name 定義/埋め込みブロック block content
include path サブテンプレートのインポート include header

▶ 例:Pugのループと条件分岐

TEXT
ul
  each item in items
    li
      strong= item.name
      if item.onSale
        span.badge Sale

6. 部分ビューとレイアウトの再利用

テンプレートエンジンの最大の利点は、すべてのページでヘッダー、フッター、HTMLの骨格を繰り返し記述する必要がなくなることです。

▶ 例:EJSレイアウト + パーシャルビュー

views/partials/header.ejs

JAVASCRIPT
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>

views/partials/footer.ejs

JAVASCRIPT
<footer>&copy; 2026 My App</footer>

views/layout.ejs

JAVASCRIPT
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('partials/header') %>
  <main>
    <%- body %>
  </main>
  <%- include('partials/footer') %>
</body>
</html>

EJSにはネイティブなレイアウト機能がありません。レイアウトは、express-ejs-layouts ミドルウェアを使用するか、手動でファイルを読み込むことで実装する必要があります。

BASH
npm install express-ejs-layouts
JAVASCRIPT
const expressLayouts = require('express-ejs-layouts');
app.use(expressLayouts);
app.set('layout', 'layout');

▶ 例:Pugのレイアウト継承

views/layout.pug

TEXT
doctype html
html
  head
    title= title
    link(rel="stylesheet" href="/style.css")
  body
    include partials/header
    block content
    include partials/footer

views/index.pug

TEXT
extends layout

block content
  h1= pageTitle
  p Welcome!

(1) レイアウト戦略の比較

プロパティ EJS Pug
レイアウトの継承 ミドルウェア express-ejs-layouts が必要 ネイティブ extends + block
現地輸入 <%- include('partial') %> include partial
子テンプレートへのパラメータの渡し方 <%- include('x', {data}) %> 親テンプレートを介したデータの渡し方
マルチブロックのサポート 限定的 複数のブロックに対するネイティブサポート

7. テンプレートエンジンの比較

(1) EJS 対 Pug 対 Handlebars

特集 EJS Pug Handlebars
構文スタイル HTML + JS タグ インデントあり、終了タグなし Mustache {{}}
学習曲線 最低
論理的推論 完全なJS 完全なJS 制限あり(任意のJSは不可)
アーキテクチャ/継承 ミドルウェアが必要 ネイティブ対応 追加の設定が必要
出力エスケープ <%= %> = または #{} {{}}
元の出力 <%- %> !{} {{{}}}
ユースケース クイックスタート / 社内ツール シンプルさを最優先 / フルスタックプロジェクト セキュリティを最優先 / マルチデバイス対応レンダリング
コミュニティ活動

  1. 8 SSRとCSRの比較

(1) 主な相違点の比較表

ディメンション SSR(サーバーサイドレンダリング) CSR(クライアントサイドレンダリング)
レンダリング場所 サーバー ブラウザ
初回画面表示速度 速い(HTMLが即座に読み込まれる) 遅い(JSの読み込みと実行を待たなければならない)
SEOへの対応度 低(コンテンツがクローラーに認識されない可能性があります)
インタラクションの複雑さ 低(リクエストごとにページが更新される) 高(SPA、ページ更新なし)
サーバー負荷
主な技術 EJS / Pug / PHP React / Vue / Angular
ユースケース 社内ツール / ブログ / SEOページ 複雑なインタラクション / SPAアプリケーション

▶ 例:SSRとCSRにおける回答の違い

SSRの動作—ブラウザは完全なHTMLを直接受け取ります:

TEXT
GET /about
→ Server-Side Rendering HTML Return in Full
→ Display directly in the browser,No need to wait JS

CSRへの対応—ブラウザは空のシェルとJSバンドルを受信します:

TEXT
GET /about
→ Server Response <div id="app"></div> + bundle.js
→ Browser Download JS,Execute Rendering
→ Users view the content(Duration of the white screen)

8. 総合的な例:EJSのブログページ

再利用可能なレイアウト、投稿一覧、投稿詳細、ローカルナビゲーションなどを含む、Express + EJS による完全なブログアプリケーションを構築します。

(1) プロジェクトの構成

TEXT
blog/
  views/
    partials/
      header.ejs
      footer.ejs
      nav.ejs
    index.ejs
    post.ejs
  app.js

▶ 例:アプリケーションのエントリポイント app.js

JAVASCRIPT
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');
app.use(expressLayouts);
app.set('layout', 'layout');

const posts = [
  { id: 1, title: 'Getting Started with Node.js', author: 'Charlie', date: '2026-06-20', excerpt: 'Learn the basics of Node.js runtime.' },
  { id: 2, title: 'Understanding Express Middleware', author: 'Alice', date: '2026-06-25', excerpt: 'Deep dive into middleware patterns.' },
  { id: 3, title: 'Template Engines Compared', author: 'Bob', date: '2026-07-01', excerpt: 'EJS vs Pug vs Handlebars showdown.' }
];

app.get('/', (req, res) => {
  res.render('index', { title: 'Blog Home', posts: posts });
});

app.get('/post/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) return res.status(404).send('Post not found');
  res.render('post', { title: post.title, post: post });
});

app.listen(3000, () => {
  console.log('Blog running on http://localhost:3000');
});

▶ 例:ローカルナビゲーションのパーシャルファイル partials/nav.ejs

JAVASCRIPT
<nav class="blog-nav">
  <a href="/">All Posts</a>
  <% posts.forEach(function(p) { %>
    <a href="/post/<%= p.id %>"><%= p.title %></a>
  <% }); %>
</nav>

▶ 例:ローカルヘッダーのパーシャルファイル partials/header.ejs

JAVASCRIPT
<header class="site-header">
  <h1><%= title %></h1>
</header>
JAVASCRIPT
<footer class="site-footer">
  <p>&copy; 2026 My Blog. Powered by Express + EJS.</p>
</footer>

▶ 例:記事一覧ページのビュー / index.ejs

JAVASCRIPT
<h2>Latest Posts</h2>
<div class="post-list">
  <% posts.forEach(function(post) { %>
    <article class="post-card">
      <h3><a href="/post/<%= post.id %>"><%= post.title %></a></h3>
      <p class="meta">By <%= post.author %> on <%= post.date %></p>
      <p><%= post.excerpt %></p>
    </article>
  <% }); %>
</div>

▶ 例:投稿詳細ページの views/post.ejs

JAVASCRIPT
<h2><%= post.title %></h2>
<p class="meta">By <%= post.author %> on <%= post.date %></p>
<div class="post-body">
  <p><%= post.excerpt %></p>
  <p>Full article content goes here...</p>
</div>
<a href="/">&larr; Back to all posts</a>

アプリを起動した後の画面は次のようになります:

TEXT
http://localhost:3000       → Article List Page
http://localhost:3000/post/1 → Article Details Page
http://localhost:3000/post/2 → Second Article

❓ よくある質問

Q 現在でもSSRは必要ですか?
A SSRは、内部管理ツールや、SEO要件の高いページ(ブログやECサイトの商品ページなど)、そしてフロントエンドフレームワークによる複雑さを伴わない、シンプルでコンテンツ重視のサイトにおいては、依然として理想的な選択肢です。
Q EJSとPug、どちらが優れていますか?
A EJSはネイティブのHTMLに近い形式で、習得が容易であり、迅速な開発に適しています。一方、Pugはより簡潔な構文ですが、インデントのルールに慣れる必要があるため、コードの簡潔さを重視するチームに適しています。初心者にはEJSをお勧めします。
Q テンプレート内でレイアウトを再利用するにはどうすればよいですか?
A EJSでは、express-ejs-layoutsミドルウェアを使用してレイアウトの継承を実装しています。また、<%- include() %>を使用して部分ビューを手動でインポートすることも可能です。Pugでは、extendsblockを組み合わせてレイアウトの継承を実装する機能がネイティブにサポートされています。
Q res.render の 2 番目のパラメータとは何ですか?
A これはデータオブジェクトであり、そのプロパティは res.render('index', {title: 'Hi'}) のようにテンプレート内で直接変数として使用できます。テンプレート内では <%= title %> と記述します。
Q テンプレートエンジンはホットリロードに対応していますか?
A 開発中は、nodemon を使用してファイルの変更を監視し、サービスを自動的に再起動させることができます。また、livereload ミドルウェアを使用することで、ブラウザの自動リフレッシュを有効にでき、手動でページを更新する必要がなくなります。
Q <%- と <%= の違いは何ですか?
A <%= XSS攻撃を防ぐために、HTMLエスケープされた値を出力します。これは安全なオプションです。<%- エスケープされていない生のコンテンツを出力します。信頼できるコンテンツの場合、またはサブテンプレートを埋め込む場合にのみ使用してください。
Q テンプレート内で async/await を使用できますか?
A EJS テンプレートでは、最上位の await はサポートされていません。ルート内で非同期のデータ取得をすべて完了させた後、res.render() を使用して結果をテンプレートに渡す必要があります。

📖 まとめ


📝 練習問題

  1. このレッスンにあるすべてのコード例を完成させ、それぞれが正しく動作することを確認してください。
  2. 包括的な例を修正し、独自の拡張機能を追加する
  3. 公式ドキュメントを確認し、このレッスンで扱われていないAPIを1~2つ見つけ、それらのテストコードを作成してください。
  4. 振り返り:このレッスンで学んだことを、実際のプロジェクトにどのように活かしますか?
  5. このレッスンで学んだことと、これまでのレッスンの内容を組み合わせて、小さなプロジェクトを作成してみてください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%