KiSAKU
技術系約5分で読めます

ソース解説(5) 文字列を自分で組み立てる場所

公開

このサイトで、HTMLを文字列として自分で組み立てている場所は2つあります。RSSフィードとサイトマップです。

画面に出るページは、部品を組み合わせれば勝手に出来上がります。この2つは違います。1行ずつ並べて、最後に文字列としてつなぎます。

連載「ソース解説」(全10回)

第5回。前回:速くしたら、デプロイが止まった 次回:スクロールを見る場所を1つにまとめた(近日公開)

この記事で扱うこと

自分で組み立てる場所に何が必要になるか。具体的には、エスケープ、日付の形式、そして省略していい項目とそうでない項目の見分け方です。

1文字でフィード全体が壊れる

まずここから。記事のタイトルに「&」や「<」が入ることは普通にあります。

順番 元の文字 書き出す形
1 & &amp;
2 < &lt;
3 > &gt;
4 二重引用符 &quot;
5 単引用符 &apos;

これを通さずにタイトルを書き込むと、XMLとして読めなくなります。

壊れ方が「1記事だけ出ない」ではない

HTMLは多少おかしくてもブラウザが解釈してくれますが、XMLは1か所でも壊れると全体が読めません。

つまり、タイトルに「&」が入った記事を1本出した瞬間に、購読アプリ側では何も表示されなくなります。その1本だけが抜けるのではありません。

そして & の置き換えがいちばん最初にあるのが重要です。後ろに回すと、< を &lt; に変えた後の & をもう一度変換してしまい、&amp;lt; になります。

日付の名前を、自分で持っている

RSSが定める日付は「Sun, 24 Aug 2026 13:27:40 GMT」の形です。英語の曜日と月名を使います。

const DAYS = ["Sun", "Mon", "Tue", ...];
const MONTHS = ["Jan", "Feb", "Mar", ...];

日付を文字にする機能はブラウザにもありますが、結果が端末の言語設定に左右されます。日本語の環境で「日」「8月」と出てしまうと、フィードとしては読めません。

形式が決まっているものは、こちらで名前を持っているほうが確実でした。

時間帯の表記が無い文字列

WordPressから届く日付は「2026-08-24T13:27:40」の形です。末尾に時間帯の表記がありません。

const iso = HAS_TIMEZONE.test(gmt)
  ? gmt
  : gmt + "Z";

すでに時間帯が付いているかを確かめて、無ければ Z(世界標準時)を足してから解釈します。

HAS_TIMEZONE は、末尾に時間帯の表記があるかを判定する正規表現です。実物は1行に書いてありますが、長くて読む邪魔になるので、ここでは名前を付けて抜き出しています。

足さないと、見る人の環境で日付がずれる

時間帯の表記が無い文字列は、解釈する側の地域として扱われることがあります。日本で動かすと9時間ずれた値になります。

表示は普通に出ます。「少し前の日付だな」と思うだけで、ずれていることに気づけません。

無い項目は、書かない

組み立てる側でいちばん判断が要るのがここでした。

pubDate ? "<pubDate>" + pubDate + "</pubDate>" : null,
summary ? "<description>" + summary + "</description>" : null,

そして最後に、null の行を落としてからつなぎます。

.filter((line): line is string => line !== null)
.join("\n")

日付や説明が取れなかったとき、空のまま項目だけ書くこともできます。そうしないのは、空の項目が「値が無い」ではなく「空という値がある」と読まれるからです。

サイトマップ側でも同じ判断をしています。

if (post.modified) {
  entry.lastModified = new Date(post.modified);
}
無いときに今日の日付を入れてはいけない

更新日が取れないときに今日の日付を埋めると、「毎日更新されている」と伝えることになります。

形としては揃いますが、内容は嘘です。何度か見に来られて、毎回変わっていないと分かれば、そのうち信用されなくなります。書かないほうが正確でした。

更新日時に、生成時刻を使わない

フィード全体の更新日時も同じ考え方です。

const lastBuildDate = toRfc822(posts[0]?.dateGmt);

作った瞬間の時刻ではなく、いちばん新しい記事の日付を使っています。

生成時刻にすると、中身がまったく変わっていなくても毎回新しく見えます。フィードは1時間ごとに作り直しているので、記事を1本も書いていない日でも「24回更新された」ことになります。

作り直す間隔を、用途で分けている

このサイトには、作り直す間隔が3種類あります。

対象 間隔 理由
記事ページ・一覧 5分 読む人が直接見る
RSS 1時間 購読アプリ側も数十分おきにしか来ない
サイトマップ 1時間 載っているかだけが問題

RSSを5分にしても、届く速さは変わりません。相手が見に来る間隔のほうが長いからです。短くして得があるのは、読む人が直接開くページだけでした。

サイトマップは、XMLを書いていない

最後に1つ。フィードは文字列を組み立てていますが、サイトマップは違います。

export default async function sitemap():
  Promise<MetadataRoute.Sitemap> {
  return [...staticEntries, ...glossaryEntries, ...postEntries];
}

配列を返すだけで、XMLは自動で作られます。RSSに同じ仕組みが無いので、そちらだけ手で書いています。

手で書ける場所では、手で書かない。エスケープも日付の形式も、自分で持てば自分で間違えられるようになります。

今回の学び

まず、厳しい形式ほど、壊れ方が全か無かになるということ。XMLは1文字で全体が読めなくなります。多少おかしくても表示されるHTMLに慣れていると、この差は見落とします。

次に、値が無いときに、形を揃えるために埋めないこと。更新日が無いのに今日の日付を入れると、形は整いますが意味が変わります。空欄のまま出すか、項目ごと書かないかを選ぶほうが正確でした。

そして、間隔は相手の都合で決めること。購読アプリが1時間おきにしか来ないなら、こちらが5分ごとに作り直しても誰も早く受け取れません。速くする対象を選ぶのも、相手が誰かを見てからでした。

この記事を共有する

Xでポストはてブ
← ブログ一覧へ戻る