TECH PLAY

Nuxt.js

イベント

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

マガジン

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

技術ブログ

SCSKの畑です。 今年度の Web アプリケーション開発関連のテーマは大体書きたいもの書けたからもう良いかなと思ってたんですが、本件がそれなりに大変だったことを今更思い出したので備忘として残しておこうと思います。   背景 本 Web アプリケーションの開発を始めたのが 2024 年の 5 月頃だったと思うのですが、その時点での最新版は Nuxt.js が 3.x 系、Nuxt UI が 2.x 系でした。事前調査で Nuxt.js は 2.x 系と 3.x 系で仕様がかなり異なることが分かっていたので最初から 3.x 系を入れたのですが、Nuxt UI の 3.x 系のリリースは今調べたら2025 年の 3 月ということでそもそも選択肢に上がらず。 今年度も当初は(他に優先すべきタスクがあったこともあり)特に移行することは考えていなかったのですが、お客さんから要望頂いた機能を実装するのに以下 URL のコンポーネントをどうしても使いたくなってしまい。更にその頃には既に Nuxt.js / Nuxt UI 共に 4.x 系がリリースされ始めており、Nuxt.js はまだしも Nuxt UI は 2.x からそろそろ上げておかないと EOL になってしまうかも?と思ったこともあって、少し手が空いたタイミングでやってしまうことにしました。 Vue Table Component - Nuxt UI A responsive table element to display data in rows and columns. ui3.nuxt.com ちなみに Nuxt UI 4.x では 2.x や 3.x では有料だった Pro コンポーネントが使用できるようになったため一気に 4.x に移行してしまうことも考えたのですが、その場合 Nuxt.js も 4.x 系への移行が必要になりそうだったので今回のタイミングでは断念しました。   移行ガイド 公式から移行ガイドが出ているので、まずはそれを見ながら進めていくことになります。特に 2.x / 3.x の非互換については「Changes from v2」セクション以降にまとまっているため、このセクションの内容については必ず確認しておきましょう。 Migration - Nuxt UI A comprehensive guide to migrate your application from Nuxt UI v2 to Nuxt UI v3. ui3.nuxt.com ただし、残念ながら非互換となる項目が網羅されている訳ではないようで、他にも動かないコンポーネントが大量に出てくる有様だったため、最終的にはほぼ全てのコンポーネントについて Nuxt UI のドキュメントとにらめっこしながら修正していくことになりました。 ということで、あくまで今回のケースに関する内容にはなりますが、上記 URL 以外の観点で修正が必要だったコンポーネントとその内容をざっくりまとめてみました。   個別に修正したコンポーネント 以下、順番に記載していきます。   FileUpload (Input から変更) 一部画面でブラウザからファイルをアップロードするために UInput コンポーネントを使用していたのですが、3.x に移行後は正常に動作しなくなってしまいました。3.x のマニュアルを見る限り使用方法は変わらないように見受けられたので原因が良く分からなかったのと、複数ファイルを同時にアップロードする要件も出てきたことから、コンポーネント自体を 3.x で追加された UFileUpload に変更することで解決しました。今思うと、UForm の validate のロジックに原因があった可能性が高そうですが・・ Vue FileUpload Component - Nuxt UI An input element to upload files. ui3.nuxt.com   Modal 2.x のサンプル実装だと以下 URL のように UCard と合わせて使用されているのですが、これをそのまま 3.x で動かしたところ UCard 部分が悪さをしているのか画面レイアウトがおかしなことになってしまいました。画面レイアウト上 UCard の使用がマストではなかったため、使用しない実装に変更しました。 Modal - Nuxt UI Display a modal within your application. ui2.nuxt.com   Progress インジケータの進捗状況を示す value プロパティが v-model ディレクティブに変更されています。使い方自体はこれまでと大きく変わりません。   SelectMenu プルダウンメニューのコンテンツを指すプロパティが options から items に変更されている他、プルダウンメニューにおけるラベルと値をコンテンツのプロパティにバインドする方法も変わっています。 2.x だと option-attribute でラベル、value-attribute で値のプロパティを指定していましたが、3.x ではラベルのプロパティは label 固定で、値のプロパティを指定する場合は value-key を使用します。なお、2.x/3.x どちらも値のプロパティを指定しない場合は選択したメニュー項目に対応する全ての値がバインドされるようです。   Table テーブルのコンテンツ(行データ)を指すプロパティが rows から data に変更されている他、列情報の定義方法(指定すべきプロパティ)も変更されています。また、テーブルデータを変換・加工してテーブル内に表示する場合や、何らかのアクションボタンなどをテーブルデータとは別の列として表示したい場合の実現方法が変わっています。 2.x の場合は列定義に対象列の情報のみを含めた上で、template 構文の中で列定義(key プロパティ)に対応した名前付きスロットを定義して行う形式でした。以下、該当部分を抜粋した実装例です。 <UTable :columns="TableCols" :rows="TableRows" :loading="!TableLoadStatus">     <template #status-data="{ row }">         <div class="flex items-center place-content-center">             <MTStatusBudge :table_name=row.name :display_normal=true class="ml-2"/>         </div>     </template>     <template #update-data="{ row }">         <UButton icon="i-material-symbols-edit" size="2xs" variant="outline" @click="getSettingModal('update_table', row.name)"/>     </template>     <template #delete-data="{ row }">         <UButton icon="i-material-symbols-delete" color="pink" size="2xs" variant="outline" @click="deleteTable(row.name, row.logi_name"/>     </template> </UTable> <script setup lang="ts"> TableCols.value = [   {label: "論理名", key: "logi_name", sortable: true }, {label: "物理名", key: "name", sortable: true }, {label: "編集可能組織", key: "groups" }, {label: "ステータス", key: "status", sortable: true }, {label: "ロック元テーブル", key: "locked_by", sortable: true }, {label: "編集者", key: "editor", sortable: true }, {label: "承認者", key: "author", sortable: true }, {label: "更新内容", key: "temp_changes" }, {key: "update" }, {key: "delete" }, ] </script>   3.x の場合は、以下のように template 構文で定義していた情報も列定義に含めるような形式になっているようです。編集・削除機能をボタンからプルダウンメニューに変更しているため、2.x の実装例とは等価になっていない部分がありますが。 Vue Table Component - Nuxt UI A responsive table element to display data in rows and columns. ui3.nuxt.com <UTable :columns="TableCols" :data="TableRows" :loading="!TableLoadStatus"> <script setup lang="ts"> TableCols.value = [ {header: "論理名", accessorKey: "logi_name" }, {header: "物理名", accessorKey: "name" }, {header: "編集可能組織", accessorKey: "groups" }, { header: "ステータス", accessorKey: "status", meta: { class: { th: 'text-center', td: 'text-center' } }, cell: ({ row }) => { return h( MTStatusBudge, { table_name: row.getValue('name'), display_normal: true } ) } }, {header: "ロック元テーブル", accessorKey: "locked_by" }, {header: "編集者", accessorKey: "editor" }, {header: "承認者", accessorKey: "author" }, {header: "更新内容", accessorKey: "temp_changes" }, { id: 'actions', cell: ({ row }) => { return h( 'div', { class: 'text-right' }, h( UDropdownMenu, { content: { align: 'end' }, items: getActions(row), 'aria-label': 'Actions dropdown' }, () => h(UButton, { icon: 'i-lucide-ellipsis-vertical', color: 'neutral', variant: 'ghost', class: 'ml-auto', 'aria-label': 'Actions dropdown' }) ) ) } }] </script>   Tabs 2.x ではタブの切替イベントを以下のように @change イベントで検知できたのですが、3.x ではこの仕組みが使えなくなっているようでした。実際の画面では選択されているタブに応じて表示するデータを変更する実装としていたため、影響が大きかったです。また、初期選択されているタブを指定する方法も変更されており、以下のように Tab_Items 内のインデックス値を指定する方法は使えず、合わせて実装の変更が必要となりました。 <UTabs :items="Tab_Items" :default-index="1" @change="onChangeTabs"> <script setup lang="ts"> const Tab_Items = ref([{ label: 'オリジナルデータ表示', icon: 'material-symbols:table-chart-outline', }, { label: '更新差分表示', icon: DiffTabIcon.value, }, { label: 'リレーションシップ(ERD図)表示', icon: 'material-symbols:dashboard-2-outline', }]) const onChangeTabs = (index: number) => { const tab_item = Tab_Items.value[index] // 以下、具体的なタブ切替時の処理内容を記述 // } </script>   一方 3.x における代替手段はというと、移行ガイドには @change の代わりに @update:modelValue を使用する旨記載があったものの 、Tabs の場合はタブの選択状態も合わせて変更する必要があるためその処理と合わせてどう実装するのかが良く分からず。v-model ディレクティブを使用する必要がありそうなことは分かったものの、2.x のように Tab_Items 内のインデックス値を指定しても正常に動作せず、どのような値を指定すべきか分からなかったのであれこれ試行錯誤する羽目になりました。 Vue Tabs Component - Nuxt UI A set of tab panels that are displayed one at a time. ui3.nuxt.com 結論としては、Tab_Item に value プロパティを追加した上でそのプロパティの値を指定することで対応するタブを選択することができました。タブ切替時の処理を含めて考えると上記 URL のサンプル通り v-model に computed() を指定するのが筋が良さそうだったのでそれも踏まえて以下のような実装としています。最も、このサンプルが正直分かり難かったのが実装に手間取った理由というか、@change からの移行パスとして分かるような形で書いておいて欲しかったところではありますが・・ <UTabs :items="Tab_Items" v-model="Tab_Activate"/> <script setup lang="ts"> const Tab_Items = ref([{ label: 'オリジナルデータ表示', icon: 'material-symbols:table-chart-outline', value: 'original_data', }, { label: '更新差分表示', icon: DiffTabIcon.value, value: 'diff_data', }, { label: 'リレーションシップ(ERD図)表示', icon: 'material-symbols:dashboard-2-outline', value: 'erd_view', }]) // 初期選択されるタブを変更 const currentTabValue = ref<string>('diff_data') const Tab_Activate = computed({ get() { return currentTabValue.value }, set(value: string) { currentTabValue.value = value // valueに基づいて対応するタブ項目を検索 const tabItem = Tab_Items.value.find(item => item.value === value) // 以下、具体的なタブ切替時の処理内容を記述 // } }) </script>   Toast(旧 Notification) 移行ガイドの内容以外で 1 点使い勝手が大きく変わっているところがありました。画面上に表示した特定のポップアップを削除する場合の方法が変更されています。 2.x の場合は以下のように、ポップアップ表示(toast.add)時に任意の id を定義した上で、その id をポップアップ削除(toast.remove)の引数に指定することで対象のポップアップを削除します。 toast.add({ id: 'toast_sample', title: 'toastのサンプル表示です。'}) toast.remove('toast_sample') 3.x の場合はこの方法が使用できなくなっており、 その代わりに toast.add の返り値として返却された id を toast.remove 時に指定する方法に変更されているようです。ただ、これが 3.x のマニュアルのどこにも書いておらず、調べるのに結構苦労しました。。 const toast_info = toast.add({title: 'toastのサンプル表示です。'}) toast.remove(toast_info.id)   NavigationMenu(旧 HorizontalNavigation, VerticalNavigation) 移行ガイドだとサラッと名前が変わっている程度に受け取れなくもないのですが、実態としては上記 2 つのコンポーネントが統合されているので、両方のコンポーネントを使用している状態で単純に名前を置換しただけだと画面がえらいことになります。メニューの並べ方 (horizontal or vertical) は orientation オプションで指定します。 他、2.x ではメニュー構造のカスタマイズをするためには #default スロットを使用する必要がありましたが、3.x の場合は children オプションでメニューをネストできるため、その目的で #default スロットを使用していた箇所を変更しました。VerticalNavigation の場合は 以下 URL の通り Accordion と組み合わせることでメニューのネスト構造を実現していた箇所もあったのでそちらも合わせて変更しています。相対的にシンプルな実装にはなったのでこの変更自体は良かったですが、変更箇所は多岐に渡りました。 Accordion - Nuxt UI Display togglable accordion panels. ui2.nuxt.com   まとめ 来年は Nuxt.js / Nuxt UI 共に 4.x 系に上げないといけないかなーと思っています。どちらも 2.x 系から 3.x 系に上げるのよりは大変じゃないよ!みたいなことが書いてあったので、いまのところは楽観視していますが。それより先にバックエンド処理に使用している Lambda の Python バージョンアップをまずやらないといけなさそうなのがちょっと厄介そうです。 ざっと書いたこともあり全量を網羅できているかちょっと怪しいので、もし他に思い出したら追記しようと思います。 本記事がどなたかの役に立てば幸いです。
この記事は、Amplify Japan User Group の池田 健人 氏 ( @ikenyal ) に寄稿いただきました。 こんにちは、AWS Community Builder 兼 AWS User Group Leaders、そしてAmplify Japan User Groupの運営メンバーであり「AWS Amplify Conference 2026 by Amplify Japan User Group」の実行委員長の池田( @ikenyal )です。 2026年1月20日(火)、Amplify Japan User Groupは日本で初となるAWS Amplifyの年次カンファレンス「AWS Amplify Conference 2026 by Amplify Japan User Group」を目黒セントラルスクエアにて開催しました。どうやら、日本初だけでなく、世界でもAWS Amplifyの年次カンファレンスは初のようです。 構想から準備まで、運営チーム一丸となって走り抜けたこのイベント。当日は多くの方にご来場いただき、まさに「Amplifyの熱量」が凝縮された1日となりました。 今回は、初開催となった「AWS Amplify Conference 2026 by Amplify Japan User Group」の目的と当日の様子をお伝えします。 開会挨拶(Amplify Japan User Group・カンファレンス実行委員長 池田 健人・アマゾン ウェブ サービス ジャパン合同会社 常務執行役員 技術統括本部長 巨勢 泰宏) イベント当日の開会挨拶にて、本カンファレンスの目的をお伝えしました。 開催日時:2026年1月20日(火) 10:00–17:00・懇親会 17:30–19:30 会場:目黒セントラルスクエア 21F セミナールーム 主催:Amplify Japan User Group Amplify Japan User Group主催となる初のAmplifyの年次カンファレンスが始動し、今回がその初回です。 「AWS Amplify Conference 2026 by Amplify Japan User Group」では「新規事業を加速させるAmplifyの魅力を探る」というテーマを掲げています。 Amplifyは「スタートアップでリソースが少ない環境でもアプリケーションを作れるもの」と思われることも多いですが、エンタープライズ企業・大企業においても、社内の新規事業の立ち上げや効果検証を高速に実施する武器としても大いに役立ちます。 今回のカンファレンスでは、是非そのような大企業の方々にもAmplifyの良さ・有用性を感じ取っていただき、このAmplifyのユーザーコミュニティをスタートアップ・エンタープライズの垣根なく拡大させて盛り上げていきたい思いもこめたテーマです。 なお、スライドでも利用しているカンファレンスのロゴですが、海外からも「日本のAmplifyカンファレンス行きたい」と思ってもらえるよう、「日本らしさ」を意識し、カタカナや富士山をあしらい、Amplifyのモチーフのロケットを添えたデザインになっています。 カンファレンスは1日かけてAmplifyの理解を深め、明日からの活用イメージを持っていただけるよう、様々なコンテンツをご用意しました。 午前のハンズオンでまずは体験していただき、午後には米国からAmplifyの開発を行っているプロダクトチームによる登壇や、実際にAmplifyを活用している企業の事例・ノウハウを学べる時間をご用意しました。また、最後には懇親会も開催しました。 ※当日は都合により一部登壇者・登壇内容に変更がありました そして、開会挨拶の締め括りとして、アマゾン ウェブ サービス ジャパン合同会社 常務執行役員 技術統括本部長の巨勢さんからご挨拶・メッセージをいただきました。ご挨拶の後、ハンズオンの様子もご覧になったり、本カンファレンスに強い関心を抱いていただいていたのが印象的であり、実行委員長として感慨深い光景でした。 ハンズオン(講師:Amplify Japan User Group 足立 優司) 午前中のコンテンツはハンズオン。 Amplify Japan User Group運営の足立が講師を務めました。この日のために足立が中心となり、ハンズオンの内容を作成しました。 この「AWS Amplify Gen2 ハンズオン ― Dev ToolとAIが変える、次世代アプリケーション開発体験 ―」というハンズオンでは、AWS Amplify Gen2を用いて、ログイン機能付きのTodoアプリケーションを実装しながら、フロントエンドからバックエンド、デプロイまでの一連の開発フローを体験できるよう設計されています。 ハンズオン資料は公開されていますので、是非体験してきてください。 https://github.com/ototrip-lab/amplify-gen2-workshop 本ハンズオンのポイントは大きく三つあります。 1. Amplify Gen2を「実装レベル」で習得 宣言的なバックエンド定義、フロントエンドとの連携、開発フロー全体を実際に手を動かしながら学べます。 Qiitaなどで公開されているAmplify Gen2の最新記事を読んで「何をしているのか」「なぜこう書くのか」が理解できる状態を目指します。 2. Dev Tool開発体験(Kiro × AWS MCP サーバー) Kiro CLIとAWS MCPサーバーを組み合わせた、AI支援を前提とした最新のdev toolチェーンを実プロジェクト形式で体験できます。 AIによるガイド付き実装 Amplify Gen2の設計・実装を考えながら進める開発体験 単なるコード生成に留まらない、実務を意識したAI活用 「AIをどう開発に組み込むか」を具体的に掴める内容です。 3. 事前準備はアカウント登録だけ 参加前に必要なのは、以下の三つのアカウントを用意するだけです。 AWSアカウント AWS Builder ID GitHubアカウント GitHub Codespacesを利用するため、ローカル環境構築や複雑なセットアップは不要です。 すぐにハンズオンに集中できます。 特に、三つ目に挙げた事前準備の簡略化はワークショップ形式のハンズオンでは非常に重要なポイントです。当日は実際に手を動かす時間が一番大切なため、可能な限り準備に困らないよう意識したハンズオン設計になっています。 Amplifyセッション 午後のセッションは「Amplifyセッション」と「事例紹介セッション」の二部構成です。最初に「Amplifyセッション」としてAmplifyに関する知識を習得したうえで、後半の「事例紹介セッション」でより具体的なイメージを掴められるよう構成しています。 Amplify入門(アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト 稲田 大陸) SA 稲田さんからは「AWS Amplify入門 仕様からリリースまで一気通貫 生成AI時代のフルスタック開発」というテーマでAmplifyを用いたモダンな開発の流れを伝える内容でした。 Kiroの由来が「岐路」だという話は聞いたことありましたが、それはプロジェクトのコードネームがそのままプロダクト名になったこと。Kiroのお化けキャラクターは開発中に社内でも極秘開発されていた際に「バレないようにお化け」にしていたものが、これもそのまま公式のキャラクターになったこと。Kiroが「AWS Kiro」でも「Amazon Kiro」でもないのは、AWSに関係なく幅広く使えるツールであることを示すために何も頭につかない「Kiro」になったこと。公式ドキュメントからは知ることのできない、たくさんのKiroトリビアも披露していただきました。 https://speakerdeck.com/inariku/aws-amplify-conference-2026-shi-yang-kararirisumade-qi-tong-guan-sheng-cheng-ai-shi-dai-nohurusutatukukai-fa コミュニティ紹介(Amplify Japan User Group・カンファレンス実行委員長 池田 健人) 本カンファレンスを主催するコミュニティ「Amplify Japan User Group」の紹介をしました。 「Amplify Japan User Group」はAWS Amplifyの利用者・開発者が主体となり、相互にAWS Amplifyの利用・開発をサポートするために、主に日本国内で活動するグループです。 2020年にコミュニティSlackがAWS主導で開設され、2021年にコミュニティのWebサイトが開設されました。 そして、このコミュニティは日本で三つしかないAWS公認コミュニティの一つだというのも特徴です。 JAWS-UG Amplify Japan User Group AWS Startup Community コミュニティでは主に三つの領域の活動をしています。 一つ目が「交流の場の提供」です。 Amplify利用者、なかにはコントリビュータの人もいたりしますが、その参加者が交流、質問、相談をできる場として用意しています。 コミュニティの初期の頃はSlackで提供していましたが、運用上Slackも無料プランを使わざるを得ない状況だと過去ログが見えなくなる点が懸念として上がり、今も継続しているDiscordに移行しました。 イベント情報もDiscordでご案内しますので、是非Discordにご参加ください。 https://discord.gg/2wVQ2D53Na 二つ目は「情報の提供」です。 Webサイトを公開しており、イベント情報、イベントレポート、学習リソースなどの情報をまとめています。 なお、このサイトは管理者が一方的に情報提供をするものではなく、コミュニティ全体で作り上げていくものなので、是非コンテンツ追加のPull Requestをお願いします。 そのようなPull Requestも立派なコミュニティ参加ですので、お気軽にトライしてみてください。 https://aws-amplify-jp.github.io/ そして三つ目が「ミートアップ・カンファレンスの開催」です。 年に数回のオンラインミートアップの開催と、年に1度のオフラインカンファレンスを提供しています。 ミートアップに関しては登壇者を募集する形式なので、Amplifyを使ってみて得られた知見やノウハウを是非発表してみてください。 ここまででご紹介したDiscord、イベント、登壇に関するリンクはこちらの通りですので、是非コミュニティ活動にご参加ください。 Discord https://discord.gg/2wVQ2D53Na Amplify Boost Up ・カンファレンスへの参加 https://aws-amplify-jp.connpass.com/ Amplify Boost Up での登壇 https://github.com/aws-amplify-jp/amplify-meetup-cfp そして、これらを支えるのがコミュニティの運営です。 本カンファレンスの開催も含め、全員ボランティアで運営をしています。 以前にこの運営体制の記事を出したので是非ご興味ありましたらご覧いただければと思います。 https://zenn.dev/ikenyal/articles/75637f3d3ce52a また、本カンファレンスですが、ここに掲載している運営メンバーを中心にAWSの社員の方々にもご支援・ご協力いただいて開催できております。改めて関係者の皆さま、そして参加者の皆さまに感謝の意を表します。 The Future of Amplify Gen2 And How We’re Bringing Everyone Along(AWS Amplify Product Team, Senior Product Manager Praneeta Prakash・Software Development Manager Joey Wang) 米国から来日中のAmplifyプロダクトチームのPraneetaさんとJoeyさんによる講演です。 英語による講演でしたが、Zoomのリアルタイム翻訳とDiscordによる要約による言語サポートを実施しました。 Joeyさんからは、まだまだAmplify利用者としてもGen1利用者が多いなか、Gen2の特徴を改めて伝えるものでした。 そして、まだ公開された間もないGen1からGen2へのマイグレーションツールの紹介もありました。 https://github.com/aws-amplify/amplify-cli/blob/gen2-migration/GEN2_MIGRATION_GUIDE.md Praneetaさんからは、Amplifyのロードマップに関するメッセージがありました。 生成AIの進歩によってAmplifyはどう変化していくのか。AmplifyはGen2ではAWS CDKをネイティブサポートしています。それによって、AIがより読みやすくなり、AIからの利用が容易になっています。 CDKとAmplifyは距離の近い隣のチームで開発されています。CDKを初日で分かる人は少ないかもしれませんが、AmplifyはAWSを知らなくても使えるようにしていくことを引き続き目指していきます。 事例紹介セッション 午後の後半は「事例紹介セッション」として、実際にAmplifyを利用している企業・プロダクトの事例紹介を行いました。 サイトの本番公開までにあと2ヶ月しかなかった時(株式会社すかいらーくホールディングス 福田 誠) すかいらーくホールディングス 福田さんからは、リリースまでに2ヶ月という期間しかない状況において、Amplifyによってそれをどう実現したかというお話でした。 そして、福田さんは午前のハンズオンに参加され、その時間に開発したルーレットで会場参加者向けにプレゼント企画も実施していただきました。 ADRで「なぜ」を残す開発:AWS Amplifyで実現した薬局のWEB問診票(株式会社エムティーアイ Leinikka Marko Kristian) エムティーアイのLeinikkaさんの発表です。 ADRを残す大切さや、Nuxtのサポートやプレビュー環境の自動構築等のAmplifyを選定した理由を、許容したポイントも添えて紹介いただきました。 クラウド知識ゼロからAmplifyで始める新規事業サービス開発(三菱電機株式会社 的場 祐弥) 三菱電機の的場さんからは、AWSもクラウドもReactも、何も知らない状態からAmplifyで新規事業へ挑戦してリリースまで果たし、そこからより深いAWSの理解や継続開発につなげていったお話をいただきました。 初めてのモバイルアプリ内製化:Amplifyを用いたバックエンド開発の道のり(シチズン時計株式会社 Rudolf Yoga Hutama・シチズン・システムズ株式会社 浅原 一葉) シチズン時計のRudolfさんとシチズン・システムズの浅原さんによる発表です。 外部委託していたアプリを内製化する際に、育成のためにフロントエンド開発とバックエンド開発の期間を分けて開発する計画を策定。結果としては1ヶ月の開発期間の短縮に成功し、その短縮できた期間でスコープ外にしていたログイン機能を追加開発を実現させました。 質疑応答 当初予定していたピュアポムメディアラボ 青木さんが都合により来場が間に合わなくなり、質疑応答の時間を設けました。 予定していなかった質疑応答コーナーですが、会場からは続々と質問をいただき盛り上がりました。笑顔もあふれる質疑応答の時間になりました。 懇親会 イベントの最後は懇親会も開催。参加者・登壇者・運営が一緒になって会話や食事を楽しみました。 おわりに 今回は年次カンファレンスの立ち上げという貴重な体験をすることができました。結果として、多くの皆さまに参加いただき、楽しんでいただけて一安心しております。参加者アンケートでも、5段階のうち、すべての回答が4以上の評価でした。さらには7割の方に5点満点評価をいただきました。 これも、参加いただいた参加者の皆さま、登壇者の皆さま、そして運営メンバーだけでなくご支援・ご協力いただいたAWSの皆さまのおかげです。 来年はさらにパワーアップしたカンファレンスになるよう頑張りたいと思います! 著者について 池田 健人(Ikeda, Kento) AWS Community Builder、AWS User Group Leaders。Amplify Japan User Group 運営メンバー、AWS Amplify Conference 2026 実行委員長。 和智 大二郎(Wachi, Daijiro) アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト
クロスイノベーション本部、新卒1年目の大岡叡です。 今回は、1月29日にプレビュー公開されたAWS Deployment SOPsを使ってみたので、その内容を報告します。 AWS Deployment SOPsを使ってみた結果、簡単なプロンプトを一度与えるだけで静的Webサイトをデプロイでき、CodePipelineのCI/CDについても簡単に構築することができました。この検証を通じて、非エンジニアがインフラ構築をして簡単なアプリケーションのデプロイを行う日も遠くないのではないかと思いました。 AWS MCP Serverとは AWS Knowledge Tools AWS API Tools Agent SOP Tools AWS Deployment SOPsとは 検証の前提 検証1:Next.jsアプリケーションのデプロイ 検証内容 実装してくれたCDKのコード Deployment SOPsなしの場合 検証2:CodePipelineのセットアップ 検証内容 実装してくれたCDKのコード まとめ 参考文献 AWS MCP Serverとは AWS Deployment SOPsの説明をするうえで前提となる AWS MCP Server について説明します。AWS MCP Serverは8つのツールを提供しており、 AWS Knowledge Tools 、 AWS API Tools 、 Agent SOP Tools の3つのカテゴリに分類されています。 AWS Knowledge Tools aws___search_documentation :APIリファレンス・ベストプラクティス・サービスガイドを含む、すべてのAWSドキュメントを横断的に検索して関連情報を見つける。 aws___read_documentation :AWSドキュメントページを取得し、AIアシスタントが利用しやすいようにMarkdown形式に変換する。 aws___recommend :AWSドキュメントページに関連するトピックや、他のユーザーがよく一緒に閲覧しているコンテンツに基づいて、おすすめのドキュメントページを取得する。 aws___list_regions :リージョンID(例:ap-northeast-1)と名称(例:Asia Pacific (Tokyo))のペアの一覧を取得する。 aws___get_regional_availability :サービス、機能、SDK API、CloudFormationリソースについて、AWSリージョンごとの利用可否情報を確認する。 AWS API Tools aws___call_aws :AWS APIの呼び出しを実行する。 aws___suggest_aws_commands :関連するAWS APIの説明と構文ヘルプを取得する。AIモデルの学習データに含まれていない可能性がある、新しくリリースされたAPIにも対応できる。 Agent SOP Tools aws___retrieve_agent_sop :Agent SOPsの検索または特定のSOPsの詳細情報の取得をする。 💡 Agent SOPsとは Agent SOPsとは、Claude CodeのようなAIアシスタントがAWS関連のタスクを行う際の標準作業手順書(Standard Operating Procedures)のことです。例えば、以下のようなSOPが用意されています。 SOP名 説明 create-secrets-using-best-practices ローテーション機能とKMSを備えたSecrets Managerによるシークレット作成 create-budget アラート機能付きのAWS Budgets作成 setup_cloudwatch_alarm_notifications SNS経由でのCloudWatchアラーム通知セットアップ application-failure-troubleshooting CloudWatchログを分析してアプリケーション障害をデバッグ AWS Deployment SOPsとは AWS Deployment SOPsは、 aws___retrieve_agent_sop のツールが取得するデプロイ用のSOPのことです。 以下の4つのSOPがDeployment SOPsです。 SOP名 説明 deploy-webapp アプリケーションがDeployment SOPsでサポートされているものか確認し、適切なDeployment SOPを選択する。 deploy-frontend-app AWS CDKのコードを生成し、アプリケーションをデプロイした後、デプロイしたWebアプリのURLを提供する。 setup-pipeline AWS CodePipelineを使用してパイプラインを作成し、GitHubに変更がプッシュされると自動的にアプリケーションの検証とデプロイを行う。 document-deployment デプロイに関するドキュメントを生成し、進捗を管理する。 Deployment SOPsがサポートしているアプリケーションタイプは以下のとおりです。 SPA:React, Vue, Angular, SvelteKit SSG:Next.js (static export), Nuxt, Gatsby, Hugo, Jekyll, Docusaurus, Astro, Eleventy Static websites そして、Deployment SOPsは2つのユースケース「AWSへのアプリケーションのデプロイ」と「CodePipelineのセットアップ」をサポートしています。今回はこの2つのユースケースを実際に試してみました。 検証の前提 今回はコーディングエージェントとしてClaude Codeを使用しました。 Skills等のカスタマイズ系のものは使用しないようにしました。 AWS CLI(認証情報設定済み)やGit CLIなどの必要なツールはインストール済みです。 プロジェクトのルートで、以下のような .mcp.json を配置しました。 { " mcpServers ": { " aws-mcp ": { " command ": " uvx ", " args ": [ " mcp-proxy-for-aws@latest ", " https://aws-mcp.us-east-1.api.aws/mcp " ] } } } あらかじめ以下のようなTODOアプリケーションをNext.jsの静的エクスポートで実装しました。これはプロジェクトのルートディレクトリにtodo-appというディレクトリ名で配置しました。 検証1:Next.jsアプリケーションのデプロイ Claude Codeの挙動ベースで検証内容を報告します。結果としては、簡単なプロンプトを一度与えるだけでCloudFront + S3の構成を構築してデプロイを行い、CloudFrontのURLを共有してくれました。 検証内容 最初に「AWS MCPのAWS Deployment SOPsを用いて、todo-appをAWSにデプロイしてください。」というプロンプトをClaude Codeに投げました(ちなみに、この検証で投げたプロンプトはこれだけです)。すると、AWS MCP Serverの aws___retrieve_agent_sop を用いてdeploy-webappのSOPを取得してきて、アプリケーションがDeployment SOPsでサポートされているタイプか分析してくれました。 アプリケーションのファイルの読み込みが完了した後、AWS MCP Serverの aws___retrieve_agent_sop を用いてdeploy-frontend-appのSOPを取得してきてくれました。そして、Next.jsの静的エクスポートはデプロイ可能ということで、デプロイ開始宣言をしてくれました。 続いて、 DEPLOYMENT_PLAN.md を生成してくれました。これ以降はこのプランに従って自律的に実装を進めてくれました。 --- sop_name: deploy-frontend-app, setup-pipeline repo_name: aws-deployment-sops-test app_name: TodoApp app_type: Frontend Application (Next.js Static Export) branch: main created: 2026-02-01 last_updated: 2026-02-01 --- # Deployment Plan: TodoApp Coding Agents should follow this Deployment Plan, and validate previous progress if picking up the Deployment in a new coding session. **IMPORTANT**: Update this plan after EACH step completes. Mark the step `[x]` and update `last_updated` timestamp. ## Phase 1: Gather Context and Configure - [x] Step 0: Inform User of Execution Flow - [x] Step 1: Create Deployment Plan - [] Step 2: Create Deploy Branch - [] Step 3: Detect Build Configuration - [] Step 4: Validate Prerequisites - [] Step 5: Revisit Deployment Plan ## Phase 2: Build CDK Infrastructure - [] Step 6: Initialize CDK Foundation - [] Step 7: Generate CDK Stack - [] Step 8: Create Deployment Script - [] Step 9: Validate CDK Synth ## Phase 3: Deploy and Validate - [] Step 10: Execute CDK Deployment - [] Step 11: Validate CloudFormation Stack ## Phase 4: Update Documentation - [] Step 12: Finalize Deployment Plan - [] Step 13: Update README.md そして、最終的にCloudFrontのURLを提供してくれました。このURLをクリックしたら実際にデプロイされたアプリケーションが正常に動作していることを確認できました。 実装してくれたCDKのコード 実際にClaude Codeが生成してくれたCDKのコードを以下に示します。このコードによって、CloudFront(Distribution, CloudFront Function)、S3(Webサイト用)、S3(CloudFrontアクセスログ用)、S3(S3アクセスログ用)などが作成されます。 import * as cdk from "aws-cdk-lib" ; import * as cloudfront from "aws-cdk-lib/aws-cloudfront" ; import * as s3deploy from "aws-cdk-lib/aws-s3-deployment" ; import { CloudFrontToS3 } from "@aws-solutions-constructs/aws-cloudfront-s3" ; import { Construct } from "constructs" ; export interface FrontendStackProps extends cdk.StackProps { environment : string ; buildOutputPath : string ; } export class FrontendStack extends cdk.Stack { public readonly distributionDomainName : string ; public readonly bucketName : string ; constructor ( scope : Construct , id : string , props : FrontendStackProps ) { super (scope, id, props); const { environment , buildOutputPath } = props; const isProd = environment === "prod" ; const removalPolicy = isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY; // CSP via CloudFront Function - permissive policy for 3rd-party CDNs/APIs const cspFunction = new cloudfront.Function( this , "CspFunction" , { runtime : cloudfront.FunctionRuntime.JS_2_0, code : cloudfront.FunctionCode.fromInline( ` function handler(event) { var response = event.response; response.headers['content-security-policy'] = { value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https: data: blob:; style-src 'self' 'unsafe-inline' https:; font-src 'self' https: data:; img-src 'self' https: data: blob:; connect-src 'self' https: wss:; frame-src 'self' https:; media-src 'self' https: blob:; worker-src 'self' https: blob:; object-src 'self' https:; manifest-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" }; return response; } ` ), comment : "Adds Content-Security-Policy header" , } ); // Extension rewrite function for Next.js static export (trailingSlash: false) // Rewrites /path to /path.html const extensionRewriteFunction = new cloudfront.Function( this , "ExtensionRewriteFunction" , { runtime : cloudfront.FunctionRuntime.JS_2_0, code : cloudfront.FunctionCode.fromInline( ` function handler(event) { var request = event.request; var uri = request.uri; if (!uri.includes('.') && uri !== '/') { request.uri = uri + '.html'; } return request; } ` ), comment : "Rewrites /path to /path.html for Next.js static export" , } ); const cloudfrontToS3 = new CloudFrontToS3( this , "CFToS3" , { bucketProps : { removalPolicy , autoDeleteObjects : !isProd, versioned : false , enforceSSL : true , } , loggingBucketProps : { removalPolicy , autoDeleteObjects : !isProd, lifecycleRules : [{ id : "DeleteOldLogs" , enabled : true , expiration : isProd ? cdk.Duration.days( 3650 ) : cdk.Duration.days( 7 ) }] , enforceSSL : true , } , cloudFrontLoggingBucketProps : { removalPolicy , autoDeleteObjects : !isProd, lifecycleRules : [{ id : "DeleteOldLogs" , enabled : true , expiration : isProd ? cdk.Duration.days( 3650 ) : cdk.Duration.days( 7 ) }] , enforceSSL : true , } , insertHttpSecurityHeaders : false , cloudFrontDistributionProps : { comment : ` ${ id } - ${ environment } ` , defaultRootObject : "index.html" , defaultBehavior : { responseHeadersPolicy : cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS, functionAssociations : [ { function : cspFunction, eventType : cloudfront.FunctionEventType.VIEWER_RESPONSE, } , { function : extensionRewriteFunction, eventType : cloudfront.FunctionEventType.VIEWER_REQUEST, } , ] , } , priceClass : cloudfront.PriceClass.PRICE_CLASS_100, enableIpv6 : true , httpVersion : cloudfront.HttpVersion.HTTP2_AND_3, minimumProtocolVersion : cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, } , } ); const websiteBucket = cloudfrontToS3.s3Bucket!; const distribution = cloudfrontToS3.cloudFrontWebDistribution; new s3deploy.BucketDeployment( this , "DeployWebsite" , { sources : [ s3deploy.Source.asset(buildOutputPath) ] , destinationBucket : websiteBucket, distribution , distributionPaths : [ "/*" ] , prune : true , memoryLimit : 512 , } ); this .distributionDomainName = distribution.distributionDomainName; this .bucketName = websiteBucket.bucketName; // Outputs new cdk.CfnOutput( this , "WebsiteURL" , { value : `https:// ${ distribution.distributionDomainName } ` , description : "CloudFront distribution URL" , exportName : ` ${ id } -WebsiteURL` , } ); new cdk.CfnOutput( this , "BucketName" , { value : websiteBucket.bucketName, description : "S3 bucket name" , exportName : ` ${ id } -BucketName` , } ); new cdk.CfnOutput( this , "DistributionId" , { value : distribution.distributionId, description : "CloudFront distribution ID" , exportName : ` ${ id } -DistributionId` , } ); new cdk.CfnOutput( this , "DistributionDomainName" , { value : distribution.distributionDomainName, description : "CloudFront domain name" , exportName : ` ${ id } -DistributionDomain` , } ); if (cloudfrontToS3.s3LoggingBucket) { new cdk.CfnOutput( this , "S3LogBucketName" , { value : cloudfrontToS3.s3LoggingBucket.bucketName, description : "Bucket for S3 access logs" , exportName : ` ${ id } -S3LogBucket` , } ); } if (cloudfrontToS3.cloudFrontLoggingBucket) { new cdk.CfnOutput( this , "CloudFrontLogBucketName" , { value : cloudfrontToS3.cloudFrontLoggingBucket.bucketName, description : "Bucket for CloudFront access logs" , exportName : ` ${ id } -CloudFrontLogBucket` , } ); } cdk.Tags.of( this ). add ( "Stack" , "Frontend" ); cdk.Tags.of( this ). add ( "aws-mcp:deploy:sop" , "deploy-frontend-app" ); cdk.Tags.of(cloudfrontToS3). add ( "Stack" , "Frontend" ); cdk.Tags.of(cloudfrontToS3). add ( "aws-mcp:deploy:sop" , "deploy-frontend-app" ); } } 以上より、「AWS MCPのAWS Deployment SOPsを用いて、todo-appをAWSにデプロイしてください。」と一言お願いをすれば、それなりの構成でインフラを構築してくれて、アプリケーションをデプロイできることを確認できました。これなら非エンジニアの方でも簡単かつ安全にアプリケーションをデプロイできるのではないでしょうか。 Deployment SOPsなしの場合 Deployment SOPsを使用せずにClaude Codeに「todo-appをAWSにデプロイしてください。」とお願いしたらどうなるか試してみました。ここでは非エンジニアがデプロイする想定で、条件などを一切与えずにお願いしました。こちらも検証1と同様、Claudeの指示に基本従う方針で進めました。 最初にdeploy方法の選択を迫られました。RecommendedのAWS Amplifyを選択しました。 デプロイを進めてくれて、最終的にAmplifyのURLを表示してくれました。 しかし、このURLにアクセスしたところ、以下のようなWebページでした。 Amplifyへのデプロイ手順はCloudFront + S3構成と比べて複雑ではないものの、単に「デプロイして」と依頼するだけでは、現状のClaude Codeでは自律的にタスクを完遂することが難しいようです。 検証2:CodePipelineのセットアップ Claude Codeの挙動ベースに検証内容を報告します。CodePipelineが構築され、GitHubにプッシュするだけで変更が自動的にデプロイされる仕組みが整いました。 検証内容 最初に「AWS MCPのAWS Deployment SOPsを用いて、CodePipelineをセットアップしてください。」というプロンプトをClaude Codeに投げました。すると、AWS MCP Serverの aws___retrieve_agent_sop を用いてsetup-pipelineのSOPを取得してきて、CodePipelineのセットアップを開始してくれました。 次に以下のような確認をされました。lintエラーの修正だけお願いしておきました。 検証1と同様にDEPLOYMENT_PLAN.mdを更新しながらCDKのコード実装など色々やってくれた後に、以下の認証をお願いされました。これは手順通り対応しました。 その後、パイプラインのデプロイなど色々やってくれてタスクが完了しました。 CodePipelineの動作を確認するために以下のようにh1の中身に変更を加えて、deploy-to-awsブランチにプッシュしたところ、CodePipelineがトリガーされてビルド→デプロイが実行され、Webページに変更内容が反映されました。 実装してくれたCDKのコード 実際にClaude Codeが生成してくれたCDKのコードを以下に示します。CodePipeline、CodeBuildプロジェクト、S3バケット(アーティファクト用)が構築されます。 import * as cdk from "aws-cdk-lib" ; import * as codebuild from "aws-cdk-lib/aws-codebuild" ; import * as codepipeline from "aws-cdk-lib/aws-codepipeline" ; import * as pipelines from "aws-cdk-lib/pipelines" ; import { Construct } from "constructs" ; import { FrontendStack } from "./frontend-stack" ; export interface PipelineStackProps extends cdk.StackProps { codeConnectionArn : string ; repositoryName : string ; branchName : string ; } export class PipelineStack extends cdk.Stack { public readonly pipeline : pipelines.CodePipeline ; constructor ( scope : Construct , id : string , props : PipelineStackProps ) { super (scope, id, props); const source = pipelines.CodePipelineSource. connection ( props.repositoryName, props.branchName, { connectionArn : props.codeConnectionArn, triggerOnPush : true , } ); const synth = new pipelines.ShellStep( "Synth" , { input : source, commands : [ // Install dependencies "(cd todo-app && npm install)" , "(cd infra && npm install)" , // Quality checks "(cd todo-app && npm run lint)" , // Secret scanning "npx -y @secretlint/quick-start '**/*'" , // Build frontend "(cd todo-app && npm run build)" , // CDK synth "cd infra" , "npx tsc" , `npx -y cdk synth --context codeConnectionArn=" ${ props.codeConnectionArn } " --context repositoryName=" ${ props.repositoryName } " --context branchName=" ${ props.branchName } "` , ] , primaryOutputDirectory : "infra/cdk.out" } ); this .pipeline = new pipelines.CodePipeline( this , "Pipeline" , { pipelineName : "TodoAppPipeline" , selfMutation : true , pipelineType : codepipeline.PipelineType.V2, synth , synthCodeBuildDefaults : { buildEnvironment : { computeType : codebuild.ComputeType.MEDIUM, buildImage : codebuild.LinuxBuildImage.STANDARD_7_0 } , partialBuildSpec : codebuild.BuildSpec.fromObject( { version : "0.2" , phases : { install : { "runtime-versions" : { nodejs : "latest" , } , } , } , } ), } , } ); // Deploy stage for production const deployStage = new cdk.Stage( this , "Deploy" , { env : { account : this .account, region : this . region } , } ); new FrontendStack(deployStage, "TodoAppFrontend-prod" , { stackName : "TodoAppFrontend-prod" , environment : "prod" , buildOutputPath : "../todo-app/out" , } ); this .pipeline.addStage(deployStage); // Build pipeline to enable property access this .pipeline.buildPipeline(); cdk.Tags.of( this ). add ( "Stack" , "Pipeline" ); cdk.Tags.of( this ). add ( "aws-mcp:deploy:sop" , "setup-pipeline" ); new cdk.CfnOutput( this , "PipelineName" , { value : "TodoAppPipeline" , description : "CodePipeline Name" , } ); new cdk.CfnOutput( this , "PipelineArn" , { value : this .pipeline.pipeline.pipelineArn, description : "CodePipeline ARN" , } ); } } まとめ AWS Deployment SOPsを用いて、静的エクスポートしたNext.jsアプリケーションをデプロイしたり、CodePipelineのセットアップを行いました。特に問題なくAIがすべての作業を完了してくれたことは驚きでした。近い将来、非エンジニアがインフラ構築をしてアプリデプロイをする日が来るのではないかと期待に胸が膨らみました。 最後までお読みいただきありがとうございました! 参考文献 https://docs.aws.amazon.com/aws-mcp/latest/userguide/what-is-mcp-server.html https://docs.aws.amazon.com/aws-mcp/latest/userguide/getting-started-aws-mcp-server.html https://docs.aws.amazon.com/aws-mcp/latest/userguide/understanding-mcp-server-tools.html https://docs.aws.amazon.com/aws-mcp/latest/userguide/agent-sops.html https://docs.aws.amazon.com/aws-mcp/latest/userguide/agent-sops-deployment.html https://aws.amazon.com/jp/about-aws/whats-new/2025/01/aws-announces-deployment-agent-sops-in-aws-mcp-server-preview/ 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @ooka.toru レビュー: @miyazawa.hibiki ( Shodo で執筆されました )

動画

書籍