TECH PLAY

Next.js

イベント

該当するコンテンツが見つかりませんでした

マガジン

技術ブログ

エス・エム・エスで開発を担当している髙木です。 今回は社内向けの書籍レビューサイトをClaude Codeで作った話と、運用してみてわかったことを率直に共有します。技術書の購入制度は世の中に広く受け入れられており、社内にあった形で運用されていると思います。よりよい活用を目指すためにレビューサイトを作ってみたという内容になっているため、同じ関心事を持っている方に読んでいただければと思います。 書籍購入制度について 弊社には書籍購入制度があります。 tech.bm-sms.co.jp これは福利厚生ではなく業務に必要な投資という位置づけで、かなり頻繁に利用されています。 tech.bm-sms.co.jp この制度を使っている身としては、以下の感覚がありました。 誰がどの本を買っているかが見えにくい よく買われている本がわからない 購入した本からどういうインプットが得られたかを聞く機会がない 誰がどの本を購入したかをスプレッドシートで管理されていたため、この情報を活用しようと考えました。記載されていた情報は、以下の6つでした。 No. 購入日 購入者の氏名 書籍タイトル 購入先のURL 購入サイト 最初考えていたのは統計を取ることでした。それだとどういう本が買われているという傾向だけがわかり、情報を活かせるイメージが湧きませんでした。それならば本の情報を投稿できる機能としてレビューサイトのような機能をもたせたら面白くなりそうと思い、社内用のレビューサイトを作ろうと思いました。 社内用のレビューサイトに価値はあるのか? 価値はあると考えていました。技術書の評価は、個人だけでなくチームの状況やコンテキストに大きく依存します。もちろん一般的なレビューサイトも参考になると思いますが、「自社の開発環境で役立つか」という観点では情報が不足しがちです。クローズドな環境であれば、企業内のコンテキストを前提としたレビューが投稿できます。「うちのプロジェクトではこう活用できた」といった実用性の高いレビューが集まるのではと考えました。またレビューの投稿のハードルも一般的なレビューサイトに比べると低く、投稿したことがない人も投稿できるような環境が作れるのではと考えていました。 もともと私が持っていた課題感を解消する面もあったため、レビューサイトの価値は高そう!という感覚がありました。 スプレッドシートに登録されたデータによる技術的な制約 冒頭でスプレッドシートがつかえそうという話をしましたが、実はこのデータをそのまま使うことにはいくつか課題がありました。 課題1:利用者の情報が氏名しかない このスプレッドシートにはユーザーの情報に関して氏名の情報しかありません。基本的にユーザーをユニークにする際、IDを付与したりメールアドレスを付与することが多いと思います。今回登録されている情報が氏名しかないため、氏名をベースにシステム設計を考える必要がありました。登録される情報自体を調整するという話もあるのですが、他部署が絡むことと今回のアプリケーションの価値が確認できていない中で運用の変更を依頼することに抵抗感があったため現状のスプレッドシートで進めることにしました。 さすがに氏名の入力自体は信頼性の高いものを使用したいため、Google認証を利用してアプリケーションを作ろうと考えました。弊社の社員はGoogleアカウントを持っているため、認証周りはすべてGoogleアカウントによせれば棚卸しなどの管理業務からも解放されるので一石二鳥でした。 課題2:本の情報がユニークではない このスプレッドシートに書かれている情報はタイトルと購入先のURLです。レビューサイトとしては書影などを使用したいため、これらをGoogle Books APIから本の画像や著者名を取得しようとしたとき、本のタイトルで検索をかけると異なる本を取得してしまうケースがあります。また記録されたタイトルに誤字があったりするため、それらの本が別の本として登録されてしまいます。 これらを回避するか許容するかはアプリケーションに求める品質次第ですが、今回は重複登録されることを許容しています。それぐらい本をユニークにするというのは面倒だったため、重複されることを前提としてアプリケーション設計をしました。 これらを踏まえたときに、認証方式とアプリケーションとしての設計が決まりました。商用サービスだったら絶対に許容できない設計ですが、社内限定だったら許容できるラインで設計を考えました。厳密にやろうとすると大変なところも、許容できるラインで作れば手がからなさそうでした。 Claude Codeでの実装 実装にはNext.jsを採用し、コードはすべてClaude Codeに書いてもらいました。実装期間は約2か月です。ただし業務の合間に進めていたので、注力すればもっと短期間で作れたと思います。体感的には1週間ちょっとぐらいだと思います。今だともっと早くできるかもしれないです。アイディアを形にするまでの期間が圧倒的に短いのは、LLMを活用する大きなメリットです。ひとりでプロダクトを作りつつ、情報の整理と実装の担当を分けられるのも良さだと思いました。 Google Cloudでインフラを組んだ経験がなく不安な面もありましたが、AWSを例に上げつつプロンプトを組むことで着実に組み上げられていきました。もともとある知識を別のインフラにも適用しつつ、構成管理もスムーズにできました。 一方で、生成されるコード量が多すぎてレビューがどんどん大変になっていきました。人間がボトルネックになりますが、同時にストッパーでもあります。このバランスを取っていかないと、Claude Codeを主体とした開発は難しいと感じました。最終的に説明責任を果たすのは人になるため、すべてを理解した状態を作るのか一定の信頼をおいてレビューの頻度を減らすのか、そのあたりのバランスを考え続けていく必要があると思いました。 作ってみたもののレビューが少ない… 構築したレビューサイト自体はよくできたと思っています。しかしほとんどレビューは投稿されませんでした。 悲しい気持ちは全くなく、やっぱりそうだよなぁという感覚が強いです。なぜなら僕自身もレビューを投稿できていないからです。 レビューを投稿するのは難しい レビューとして文章を書くとなったとき、本をどの程度読んだら投稿して良いものでしょうか? 目次だけ読んで書く 特定の章だけ読んで書く 全体を流し読みして書く 最後までしっかり読んで書く 人によってその本に期待するものは大きく違います。そのため本からの学び方も人によって異なり、その結果としてレビューの内容やタイミングも変わります。 つまり人によって書きたい内容やタイミングは異なります。この辺りがアプリケーションから提示されないと、自然と「最後までしっかり読んで書く」が前提となり、書くほどではないという感想になり書かなくなってしまうと思われます。したがってレビューの投稿タイミングを制御できていなかったことは、アプリケーションの思惑と実態がずれてしまってよくなかったと思っています。 改善するなら 今回うまく軌道に乗らなかったことを受けて、どういった改善をするのかをいい機会なのでこのタイミングで検討してみます。 改善案1:ステータスを追加する 「読み始めた」「読んでいる」「読み終えた」だけでなく、「途中でやめた」「積んでいる」といったステータスも含められると良いと思います。やめてしまった理由や、途中まで読んだ感想も立派なレビューになります。ステータスに応じて投稿できる内容を変えることで、完読しないと投稿できないという心理的なハードルを下げられると思っています。 たとえば「途中でやめた」ステータスでは、「どこまで読んだか」「なぜやめたか」だけを書けるシンプルな入力欄にする。「読んでいる」ステータスでは、気になったフレーズや章ごとのメモを気軽に残せるようにする。こうした設計であれば、本を読む過程そのものがレビューの素材になり、読み終えてからまとめなければという義務感が薄れると思います。 改善案2:もっと存在を知らせる そもそも社内における認知度が低く、書くハードル以前に認識されていないことで利用されていない可能性もあります。Slackで一度宣伝した程度なのでもっと存在をアピールするなどの活動は必要かもしれません。社内Wikiに使い方を書いたり、このサービスの思想を伝えたりすることは必要だと思いました。 個人的には付加価値を高めていったあとにしないと二の舞いになってしまうと思うので、利用者が少ないことを活かして改善活動を優先させようとは思っています。 レビューが投稿されればそれで良いのか? ここはなんとも言えないポイントだと思いました。ハードルを下げるということで質が下がっては意味がないと思います。とはいえ、何も価値のないサイトに人が訪れるほど暇ではないですし、訪れたからには価値を提供する必要があると思います。そういう場だからこそ自分でも価値を提供したいと思えると僕は考えています。 でも何もない状態のところに「レビューを投稿してください」というのは、ハードルが高く難しいのは事実です。なのでそこのハードルを下げつつも、投稿されているものに意味がある・価値がある状態を目指していくのが良いと思いました。 レビューサイトのもうひとつの目的 実はこのサイトにはもうひとつの目的がありました。 以前の記事で書いた「自発的に書きたい人が出てくる仕組みづくり」の一環として考えていました。 tech.bm-sms.co.jp テックブログで記事を書くには、文章を外部の人にもわかる形で構成する必要があります。書籍レビューという比較的カジュアルな場で文章でのアウトプットに慣れてもらい、テックブログへの橋渡しにできればと考えていました。 今回作成したアプリケーションでは、その目的に至る前段で止まってしまったので、もっと活発に使われるようになった先で改めて考えていこうと思いました。 最後に 動くアプリケーションを作るのが簡単になった一方で、使ってもらうことの難しさは変わっていないと思いました。社内という限定的な場ですら難しいので、世の中にアプリケーションを普及させていくハードルは変わらず高いのだろうと想像しました。 まだまだ足りないアプリケーションではありますが、もう少しメンテナンスしていってどう転ぶかを見ていきたいです。またテックブログで報告すると思います。
目次 はじめに ECR イメージスキャンとは 構成の全体像 検知の網羅性 通知のノイズ低減 認知のスピード コスト 試算の考え方 試算例 Terraform による構築 1. ECR スキャン設定 2. EventBridge ルール 3. SNS トピック 4. AWS Chatbot(Slack 通知) 実際の通知と運用 導入してみて まとめ はじめに こんにちは、開発本部開発1部トモニテグループのエンジニアの パンダム/rymiyamoto です。 2025年末に Next.js の React Server Components に DoS(サービス拒否)とソースコード露出の脆弱性が公開 され、App Router を使用するサービスでのアップグレード対応が求められました。 このように、利用しているフレームワークやライブラリに深刻な脆弱性が見つかることは珍しくありません。 こうした脆弱性が公開中のサービスに影響していないかを素早く把握できる体制を整えるべく、弊社でも ECR のイメージスキャンを導入しました。 本記事では、その取り組みの一つとして ECR のイメージスキャンを導入した際の設計・構築・運用について紹介します。 同じように ECR のイメージスキャンをこれから導入しようとしている方の参考になれば幸いです。 ECR イメージスキャンとは Amazon ECR のイメージスキャンは、コンテナイメージに含まれるソフトウェアの脆弱性(CVE)を検出する機能です。 スキャンには Basic Scanning と Enhanced Scanning の2種類があります。 項目 Basic Scanning Enhanced Scanning スキャンエンジン Clair(オープンソース) Amazon Inspector2 検出対象 OS パッケージの脆弱性 OS パッケージ + プログラミング言語パッケージ(npm, pip, Maven 等) スキャンタイミング プッシュ時 / 手動 プッシュ時 / 継続スキャン 料金 無料 有料(スキャンしたイメージ数に応じた従量課金) 構成の全体像 導入した構成は以下の通りです。 ECR Enhanced Scanning (Inspector2) ↓ 脆弱性検知 EventBridge Rule (CRITICAL のみフィルタ) ↓ SNS Topic ↓ AWS Chatbot → Slack チャンネルに通知 設計にあたって意識したのは以下です。 検知の網羅性 OS パッケージだけでなく言語パッケージもカバーしたかったため、Enhanced Scanning を採用しました。対応言語の詳細は公式ドキュメントを参照してください。 docs.aws.amazon.com 一方で、OS パッケージの脆弱性検知だけで十分なケースや、まずは無料で始めたいケースでは Basic Scanning も有力な選択肢です。自社の要件に合わせて検討してみてください。 通知のノイズ低減 すべての severity を通知すると対応が追いつかなくなるため、まずは CRITICAL に絞って運用を開始しました。実際に HIGH まで含めて試してみたところ、本当に対応すべき通知が埋もれかねないと感じたので、まずは CRITICAL で運用を開始し、必要に応じてフィルタを広げる方針としています。 認知のスピード 脆弱性の存在に気づかないことが一番のリスクなので、Slack への即時通知を組み込みました。Slack への通知方法としては EventBridge → Lambda で通知内容をカスタマイズする方法もありますが、今回はまず検知できる状態を素早く作ることを優先し、コードを書かずに構築できる AWS Chatbot を採用しました。 コスト Enhanced Scanning は Amazon Inspector2 の料金体系に基づきます。料金は以下の2つで構成されます(2026年4月時点)。 最新の料金は公式ドキュメントをご確認ください。 aws.amazon.com 初回スキャン: イメージがプッシュされた時のスキャン、$0.09 / イメージ 再スキャン: 継続スキャンにより新しい CVE が公開された際の自動再スキャン、$0.01 / イメージ 試算の考え方 スキャン頻度によってコストの構造が異なります。 スキャン頻度 発生するコスト 計算式 プッシュ時 初回スキャンのみ 月間プッシュ数 × $0.09 継続スキャン 初回スキャン + 再スキャン 上記 + 保持イメージ数 × 再スキャン回数/月 × $0.01 弊社では本番環境は継続スキャン、開発環境はプッシュ時スキャンで運用しています。本番環境では新しい CVE が公開されたタイミングでも即座に検知したいため継続スキャン、開発環境では脆弱性を含む実装が入った時点で素早く検知しつつコストも抑えたいためプッシュ時スキャンが適しています。 試算例 例えば、5つのリポジトリに対して月間100回プッシュし、本番では各リポジトリに2イメージを保持(計10イメージ)するケースで試算します。再スキャン回数は月にどれくらいの頻度で対象の CVE が新たに公開されるかに依存しますが、ここでは月15回程度を見込みました。 項目 計算式 コスト 初回スキャン 100 push × $0.09 $9.00 再スキャン 10 images × 15回 × $0.01 $1.50 月額合計 $10.50 実際のコストはリポジトリ数・プッシュ頻度・保持イメージ数によって変わるので、自社の運用に合わせて試算してみてください。 Basic Scanning(無料)と比較するとコストはかかりますが、言語パッケージの脆弱性検知や新規 CVE の自動再スキャンが得られることを考えると、検討する価値はあると思います。 Terraform による構築 1. ECR スキャン設定 まず ECR レジストリに対して Enhanced Scanning を有効化します。 resource "aws_ecr_registry_scanning_configuration" "this" { scan_type = "ENHANCED" rule { scan_frequency = "CONTINUOUS_SCAN" repository_filter { filter = "*" filter_type = "WILDCARD" } } } filter = "*" でレジストリ内のすべてのリポジトリをスキャン対象にしています。リポジトリを個別に指定する方法もありますが、新しいリポジトリを追加した際にスキャン対象への追加を忘れるリスクがあるため、ワイルドカードで全体を対象にしています。 scan_frequency は環境によって使い分けています。本番環境では CONTINUOUS_SCAN 、開発環境では SCAN_ON_PUSH を設定しています。 2. EventBridge ルール resource "aws_cloudwatch_event_rule" "ecr_scan_finding" { name = "ecr-scan-finding-notification" event_pattern = jsonencode ( { "source" : [ "aws.inspector2" ] , "detail-type" : [ "Inspector2 Finding" ] , "detail" : { "status" : [ "ACTIVE" ] , "severity" : [ "CRITICAL" ] , "resources" : { "type" : [ "AWS_ECR_CONTAINER_IMAGE" ] } } } ) state = "ENABLED" } resource "aws_cloudwatch_event_target" "ecr_scan_finding_sns" { rule = aws_cloudwatch_event_rule.ecr_scan_finding.name arn = var.ecr_scan_finding_sns_topic_arn } Enhanced Scanning では Inspector2 がスキャンエンジンとなるため、イベントソースは aws.inspector2 になります。 Basic Scanning の場合は aws.ecr になるので注意が必要です。 3. SNS トピック EventBridge から受け取ったイベントを AWS Chatbot に渡すための SNS トピックを作成します。 resource "aws_sns_topic" "ecr_scan_finding_topic" { name = "ecr-scan-finding-topic" } resource "aws_sns_topic_policy" "ecr_scan_finding_topic_policy" { arn = aws_sns_topic.ecr_scan_finding_topic.arn policy = data.aws_iam_policy_document.sns_ecr_scan_finding_topic_policy.json } data "aws_iam_policy_document" "sns_ecr_scan_finding_topic_policy" { # EventBridge からの Publish を許可 statement { sid = "AllowEventBridgeToPublishSNS" effect = "Allow" actions = [ "sns:Publish" ] principals { type = "Service" identifiers = [ "events.amazonaws.com" ] } resources = [ aws_sns_topic.ecr_scan_finding_topic.arn ] condition { test = "StringEquals" variable = "AWS:SourceAccount" values = [ data.aws_caller_identity.current.account_id ] } condition { test = "ArnEquals" variable = "aws:SourceArn" values = [ "arn:aws:events:$ { data.aws_region.current.name } :$ { data.aws_caller_identity.current.account_id } :rule/ecr-scan-finding-notification" ] } } # Chatbot からの Subscribe を許可 statement { sid = "AllowChatbotToSubscribe" effect = "Allow" actions = [ "sns:Subscribe" ] principals { type = "Service" identifiers = [ "chatbot.amazonaws.com" ] } resources = [ aws_sns_topic.ecr_scan_finding_topic.arn ] condition { test = "StringEquals" variable = "AWS:SourceAccount" values = [ data.aws_caller_identity.current.account_id ] } condition { test = "ArnEquals" variable = "aws:SourceArn" values = [ "arn:aws:chatbot::$ { data.aws_caller_identity.current.account_id } :chat-configuration/slack-channel/alert-to-slack" ] } } } SNS トピックポリシーでは、EventBridge からの Publish と Chatbot からの Subscribe のみを許可しています。 condition で発信元を絞ることで、意図しないリソースからの操作を防いでいます。 4. AWS Chatbot(Slack 通知) 最後に、SNS トピックのメッセージを Slack に転送する Chatbot の設定です。 resource "aws_chatbot_slack_channel_configuration" "chatbot_alert_to_slack" { configuration_name = "alert-to-slack" slack_channel_id = "XXXXXXXXX" # 通知先の Slack チャンネル ID slack_team_id = "XXXXXXXXX" # Slack ワークスペース ID iam_role_arn = var.chatbot_role_arn sns_topic_arns = [ var.ecr_scan_finding_topic_arn, # 他の通知用 SNS トピックもここに追加できる ] guardrail_policy_arns = [ "arn:aws:iam::aws:policy/ReadOnlyAccess" ] logging_level = "ERROR" } これで CRITICAL な脆弱性が検知された際に、Slack チャンネルに通知が届くようになります。 なお、AWS Chatbot では同じ Slack チャンネルに対して複数の configuration を作成できません。そのため configuration_name は alert-to-slack のように汎用的な名前にしています。こうしておけば、今後 WAF のアラートなど別の通知を追加したくなっても sns_topic_arns にトピックを足すだけで済みます。 実際の通知と運用 実際に届く通知は以下のような形式です。 最初は CVE の詳細まで Slack で確認できるものだと思っていたのですが、実際に届く通知には Inspector2 Finding というイベント名と対象の ECR イメージの ARN が表示されるだけで、CVE 名もパッケージ名も表示されませんでした。 そのため、EventBridge の input_transformer を使い、Chatbot のカスタム通知で通知内容を改善しました。 resource "aws_cloudwatch_event_target" "ecr_scan_finding_sns" { rule = aws_cloudwatch_event_rule.ecr_scan_finding.name target_id = "SendToSNS" arn = var.ecr_scan_finding_sns_topic_arn input_transformer { input_paths = { "severity" = "$.detail.severity" "title" = "$.detail.title" "description" = "$.detail.description" "repository" = "$.detail.resources[0].details.awsEcrContainerImage.repositoryName" } input_template = <<TEMPLATE { "version": "1.0", "source": "custom", "content": { "textType": "client-markdown", "title": ":rotating_light: ECR <severity> 脆弱性検出 [環境名 (AWSアカウントID)]", "description": "*重要度*: <severity>\n*リポジトリ*: <repository>\n*脆弱性*: <title>\n*詳細*: <description>" } } TEMPLATE } } ポイントは input_paths でイベントから必要な項目を抽出し、カスタム通知フォーマットで整形している点です。改善後の通知は以下のような形式です。 CVE-ID やパッケージ名、リポジトリ名が表示されるようになり、Slack 上で脆弱性の概要を把握できるようになりました。詳細な対応判断が必要な場合は Inspector2 のダッシュボードを確認する運用ですが、通知を見ただけで対応要否がわかることが増えました。 さらに通知内容を自由にカスタマイズしたい場合は、EventBridge → SNS → Chatbot の経路ではなく、EventBridge → Lambda で整形する方法もあります。 導入してみて CRITICAL に絞った判断はうまくいきました。最初の通知が来たときも「これは本当に対応が必要なものだ」と落ち着いて対処できたので、狙い通りでした。 一方で、Chatbot のデフォルトの通知では CVE の詳細が出ず、正直もう少し情報が出ると思っていました。実際に使ってみて初めて気づいた部分で、 input_transformer を使ってカスタマイズできることも後から知りました。 Terraform での複数環境展開やスキャン頻度の使い分けはすんなりいきました。 まとめ 今回は、フレームワークやライブラリの脆弱性に素早く対応できる体制づくりの一環として、ECR の Enhanced Scanning を導入した事例を紹介しました。 構成としては ECR Enhanced Scanning → EventBridge → SNS → Chatbot → Slack というシンプルなパイプラインですが、Terraform でコード化することで再現性のある形で複数環境に展開できました。 まず検知できる状態を作ることが第一歩、そこさえ超えれば運用しながら精度を上げていけます。本記事がその一歩を踏み出すきっかけになれば嬉しいです。 最後まで読んでいただきありがとうございました!
はじめに FigmaのデザインデータをもとにUIを実装する際、「デザインの読み取り」と「コードへの落とし込み」に時間がかかることはありませんか? 本記事では、Figma MCP(Model Context Protocol)とGitHub Copilotを組み合わせてReactでUI実装を行った際の工夫点や注意点をまとめます。 Figma MCPとは Figma MCP(Model Context Protocol)は、Figmaのデザイン情報を構造化データとしてLLMに渡すための仕組みです。 Figma MCPサーバーのガイド – Figma Learn - ヘルプセンター

動画

該当するコンテンツが見つかりませんでした

書籍