1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お客さんを呼びたいのに、制作指示まで店頭に出していた――ギフトサイトのnote欄を片づけた話

1
Last updated at Posted at 2026-10-31

ギフト紹介サイトを運営しています。集客には苦戦しています。

店に例えると、営業中の札を出して、商品も紹介のPOPも並べたのに、店長ばかりが棚を見て回っている状態です。店長の回遊だけは順調です。

そんな店で、客足より先に確認できたものがありました。

バックヤードの連絡メモです。売り場で。

自分が運営する After 6 Gift では、2026年10月11日に、商品紹介へ制作側の指示が出てしまう問題を修正しました。

今回、その修正を振り返るために、当時の修正前ソースからHTMLを生成し直しました。「贈りものの棚」には、次の一文が含まれていました。

医療的な効果や確実なストレス軽減は訴求しない。

これを読んで、贈り物を決める人はあまりいないと思います。

そもそも、商品を選ぶ人へ書いた文章ではありません。説明を書く側への指示です。実店舗なら、商品紹介のPOPに「店長へ。効能を言い切らないでください」と貼っているような状態でした。

「訴求しない」を、わざわざ訴求していました。

実装には生成AIを使っています。自分が一行ずつ書いたコードの武勇伝ではなく、自分のサイトで、情報の置き場所を後から整理することになった話です。

「紙」と書いた箱から、全部POPにしていた

原因は、商品データの note でした。

そこには、読者向けの「この品を贈る理由」と、制作側が守りたい注意や指示が、同居していました。

実データから、関係するところだけ抜き出します。

// 変更前の実データから抜粋
{
  note: "会話のきっかけになる表情と、触って遊べる用途を贈る。医療的な効果や確実なストレス軽減は訴求しない。"
}

前半は商品を選ぶ人への紹介。後半は、書き手への指示です。

一つの文章として読めてしまいますが、宛先が途中で変わっています。

note は10月7日の初版から読者向けの紹介文を入れてトップページへ表示しており、10月9日に追加した棚も同じ項目を使っていました。

10月10日に商品・記事を追加した際の調査データを確認すると、この文章が含まれていました。調査データの angle では「編集提案:」という接頭辞が付いていましたが、商品データの note には、その接頭辞を外した同じ文章が入っています。後半の「訴求しない」は残ったままでした。

「編集提案」のラベルは外れたのに、指示は残っていました。

調査段階で「どう紹介するか」を考えるための情報と、読者に見せる紹介文の境界が、データ上で分かれていなかった。履歴から確認できた混在の経緯は、ここです。

店でいえば、「紙」とだけ書いた箱に、商品POPの原稿と、店長への連絡メモを一緒に入れていたようなものです。

そして、その箱の中身を売り場へ出す処理が、これでした。

// 変更前の表示処理から抜粋。eは既存のHTMLエスケープ関数
'<p class="shelf-reason">' + e(p.note) + '</p>'

e は既存のHTMLエスケープ関数です。HTMLとして解釈させない処理はしていますが、文章の宛先まで仕分けてくれるわけではありません。

印刷機は真面目でした。渡された原稿を、全部印刷しただけです。

「ちゃんと表示できる」と「そこに表示してよい」は、別の話でした。

POPと連絡ノートを、別の場所へ

修正後は、公開用の文章を giftReason、制作側の情報を editorialNotes に分けています。

同じ商品の修正後のデータは、こうなりました。

// 変更後の同じ商品から抜粋
{
  giftReason: "会話のきっかけになる表情と、触って遊ぶ楽しさを。顔のある小さな雑貨が好きな友人への贈り物に。",
  editorialNotes: [
    "医療的な効果や確実なストレス軽減は訴求しない。"
  ]
}

売り場へ出すのは、公開用の文章だけです。

// 変更後の表示処理から抜粋
'<p class="shelf-reason">' + e(p.giftReason) + '</p>'

これで、商品POPとバックヤードの連絡ノートは、別の置き場所になりました。

旧 note へのフォールバックは置かれていません。公開文がないときに「とりあえず前の箱を使おう」とすると、また連絡メモを売り場へ出す経路が残るからです。

ただし、note を見つけ次第、全部消したわけではありません。

販売先ごとの offers[].note は、価格条件や購入時の注意など、読者に伝えるための項目です。こちらは用途を維持しています。

項目 店に例えると 今回の扱い
giftReason 商品紹介のPOP 棚へ表示
editorialNotes バックヤードの連絡ノート 棚の紹介文には使わない
商品直下の旧 note 宛先の違う紙が混ざった箱 公開用の参照を終了
offers[].note 購入条件の注意書き 用途を維持

同じ「注意書き」でも、お客さんに必要なものまで裏へしまうと困ります。名前だけでなく、どの場所で何を意味しているかを見る必要がありました。

実際の書き分けが分かりやすいのは、HARIOのボトルです。旧 note の末尾には「750mLと高さ30cmが相手の暮らしに合うかを判断軸にする。」とありました。

「判断軸にする」は書き手への指示です。でも、容量と高さそのものは、読者にも必要な判断材料です。修正後は、この指示を editorialNotes に置き、公開用の giftReason には「750mLの容量と高さ30cmが、相手の暮らしに合うか確かめて選びたい。」という形で残しています。冷蔵庫の高さや置き場所を確認する購入時の注意も維持しました。

指示の文体を外しても、判断材料は残す。連絡ノートを片づけるついでに、商品POPの必要な情報まで捨てない、という分け方です。

印刷する一行を変えたら、39商品分の仕分けが残った

表示処理の核心だけを取り出すと、変更は一行です。

- e(p.note)
+ e(p.giftReason)

ここだけ見ると、気軽な修正に見えます。

しかし、表示先を新しい項目へ切り替えるには、既存のデータもそろえなければなりません。コード履歴で確認できる修正範囲は、こうでした。

対象 修正内容
商品データ5ファイル・39商品 商品直下の公開用項目を移行
うち8商品 旧 note にあった書き手向けの表現を整理し、公開文を書き直し
記事本文2箇所 項目変更では直らない制作側の表現を整理
棚の表示処理 公開用項目だけを参照
関連テスト 公開文が出ることと、旧欄・制作メモが出ないことを確認

39件は当時の未公開候補を含む商品データの数で、棚の公開対象は36品です。8品は、変更前の note と変更後の giftReason を比較し、公開文を書き直した商品の数です。この8品はいずれも公開対象に含まれます。残る31品は、公開文を維持して項目名を切り替えています。

箱を二つに分けるだけなら簡単です。面倒なのは、すでに入っている紙を読み直し、どちらへ移すかを決める方でした。

今回の技術的負債は、そこにあったと捉えています。

note という名前自体が悪いわけではありません。二つの役割を持たせたままデータと表示処理がつながり、後から意味をそろえ直す作業が必要になったことです。

一文の誤りだけを直しても、同じ欄に次の制作メモを入れれば、同じことが起きます。そのたびに人が読み分ける構造を残すより、置き場所と参照先を分ける方が、次の商品でも守る約束をはっきりさせられます。

今回はCMSやデータベースへの移行をするほどの話ではなく、今の構成の中で、データ・表示処理・テストの役割をそろえる修正になりました。

店を建て直す前に、まず連絡ノートを売り場から下げる。今回の規模なら、それで手を付けられる負債でした。

「POPが出る」と「連絡ノートが出ない」を、別々に確かめる

この修正で追加された回帰テストは、新しい公開文が表示されることに加えて、旧 note や editorialNotes に入れたマーカーが、棚のHTMLへ含まれないことを確認しています。

今回の振り返りでも、変更前と修正後のソースを別々に展開し、それぞれの実際の renderGiftShelf を呼び出しました。

確認 変更前 修正後
実データの「医療的な効果や確実なストレス軽減は訴求しない。」が棚のHTMLに入るか 入る 入らない
同じ入力の giftReason のマーカーが出るか 出ない 出る
同じ入力の旧 note のマーカーが出るか 出る 出ない
同じ入力の editorialNotes のマーカーが出るか 出ない 出ない

旧版は、旧 note をそのまま出していました。修正後は、公開用の項目を読み、旧欄を使っていないことまで確認できます。

現在のコードには、棚の検索対象を公開用項目から作り、制作メモを含めないテストもあります。POPから下げた連絡メモが、検索用データへ回り込んでいても困るためです。

とはいえ、項目名を変えれば何を入れてもよいわけではありません。giftReason に制作指示を書けば、今度はそこから表示されます。

箱に「商品POP」と書いたところで、入れ間違いを物理的に防げるわけではない。データの役割を分けることと、正しい内容を入れることは、両方必要です。

参照する項目と出力の関係を試せる、Node.jsだけの縮小例

以下は実サイトの全文ではなく、参照項目の違いを取り出した縮小例です。上の表と商品件数は実装履歴の検証結果で、この例からサイト全体を再現するものではありません。

import assert from 'node:assert/strict';

// 実装の参照項目の違いを取り出した縮小例。サイト全体のコードではありません。
const escapeHtml = value => String(value ?? '').replace(/[&<>"']/g, c => ({
  '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
}[c]));
const before = product => `<p>${escapeHtml(product.note)}</p>`;
const after = product => `<p>${escapeHtml(product.giftReason)}</p>`;

const instruction = '医療的な効果や確実なストレス軽減は訴求しない。';
const oldProduct = {
  note: '会話のきっかけになる表情と、触って遊べる用途を贈る。' + instruction,
};
const newProduct = {
  giftReason: '会話のきっかけになる表情と、触って遊ぶ楽しさを。',
  editorialNotes: [instruction],
};
const oldHtml = before(oldProduct);
const newHtml = after(newProduct);
const markedHtml = after({
  ...newProduct,
  note: 'LEGACY_INTERNAL_NOTE',
  editorialNotes: ['PRIVATE_EDITORIAL_INSTRUCTION'],
});
const result = {
  oldInstructionVisible: oldHtml.includes(instruction),
  newInstructionVisible: newHtml.includes(instruction),
  publicTextVisible: newHtml.includes(newProduct.giftReason),
  legacyMarkerVisible: markedHtml.includes('LEGACY_INTERNAL_NOTE'),
  editorialMarkerVisible: markedHtml.includes('PRIVATE_EDITORIAL_INSTRUCTION'),
  instructionAssignedToPublicFieldVisible: after({ giftReason: instruction }).includes(instruction),
};
assert.deepEqual(result, {
  oldInstructionVisible: true,
  newInstructionVisible: false,
  publicTextVisible: true,
  legacyMarkerVisible: false,
  editorialMarkerVisible: false,
  instructionAssignedToPublicFieldVisible: true,
});
assert.equal(after({ giftReason: '<b>公開文</b>' }), '<p>&lt;b&gt;公開文&lt;/b&gt;</p>');
console.log(JSON.stringify(result, null, 2));
node reproduce-note-boundary.mjs

最後の確認は、あえて制作指示を公開用の giftReason に入れるものです。その場合は表示されます。

調査・検証の範囲

2026年10月31日、日本時間。Node.js v24.22.1/Linuxで、当時の変更前・修正後の実モジュールによるHTML生成、現在の関連テスト、掲載する縮小例を確認しました。10月11日の運用記録には公開版18への反映が記載されています。今回確認したのはコードと生成HTMLで、過去の配信やブラウザー画面を再観測したものではありません。実際に閲覧者が目にした件数、修正による工数・売上・集客の改善は計測していません。

また、editorialNotes は公開文に採用しない情報の置き場所であり、秘密情報へのアクセス制御ではありません。

実装、今回の履歴調査・再検証、この記事の構成・文章作成に生成AIを利用しています。

POPは売り場へ、連絡ノートはバックヤードへ

AIに実装を任せると、文章もデータも増やせます。今回、後から必要になったのは、その情報を「誰が読み、どこへ出すのか」に合わせて整理し直す作業でした。

次にデータや機能の追加を頼むときは、読者向けの文章と制作側の情報を別の項目として伝えたい。公開前も、「欲しい文章が出るか」に加えて、「別の宛先の文章を出していないか」を見たいと思います。

商品紹介のPOPは売り場へ。制作メモはバックヤードへ。39商品のデータを整理して、この二つの置き場所は分かれました。

ただ、一番肝心な問題は残っています。

お客さんが、なかなか来ない。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?