byebyehaikikyou のすべての投稿

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

Mattermost Dockerのconfig.jsonの指定

Mattermostのdockerコンテナで、mattermostコマンドを使って設定更新操作を行なうと、あらかじめ作成しておいたconfig.jsonが何故が初期化されてしまう事象に遭遇してしまった、??。(再現性を確認できていないが、pluginをenableやdeleteして何らかのschedulerが動くタイミングで発生している?)

そこで、本記事ではMattermostの設定方法について整理する。

本動作検証バージョンはv5.37.4である。

MattermostのConfiguration

Mattermostは、設定情報をconfig.jsonファイルで管理する。通常、mattermost/configディレクトリにあるconfig.jsonファイルが対象となる。環境変数MM_CONFIGでconfig.jsonファイルのパスを指定することもできる。設定情報は、System Consoleや直接ファイルを編集することで更新できる。設定を更新するとconfigの変更を検知してリロードしてくれる。

v.5.10からはデータベースで構成管理をすることができる。

ファイルで管理する場合

環境変数MM_CONFIGにconfig.jsonファイルのパスを指定する。指定がない場合は、config/config.jsonが対象となる。

ローカルテスト環境で起動する場合は、以下のようにする。Dockerコンテナ環境が必要である。macであれば、Docker Desktop For Mac(個人利用は無料:ライセンスは公式を確認のこと)を使うとよいだろう。

参考:Setting up Your Development Environment – Mattermost

データベースで管理する場合

MM_CONFIGにDatabase DSNを指定する。mysqlの場合は以下のようになる。

参考:Configuration in the Mattermost Database

起動時にカスタムのデフォルト設定を適用したい場合は、以下の環境変数を指定する。

現在有効な設定はデーターベースに接続して確認できる。

mattermostコマンドでconfigを更新してみる。

データベースのConfigurationsのレコード件数を見ると新たにレコードが増えていることが分かる。

値も反映されている。

configファイルの変更検知を停止する

Mattermostでは、構成の変更を検知するconfig watcherが動いており、構成情報の変更を検知して反映する。本機能を止めるには、以下のオプションを起動時に指定する。

config watcherを停止して何度かmattermostコマンドでpluginのenable/deleteを繰り返した限りでは、config.jsonの予期せぬ上書きに遭遇はしなかった。

なお、v5.38からはconfig.jsonの自動リロードはDeprecatedとなっているようである。設定のリロードは、mmctlコマンドで明示的に行なう、となっている。(以下リンクより)

参考:Configuration Settings

今後は、configの変更後、mmctlコマンド(mmctl config reload)で反映という手順になるのであろう。

なお、config watcherを停止した場合、configの変更はmmctlコマンドでリロードしないと反映されなかった。これは、データベース管理でも同様であった。config watcherを停止した場合は、反映漏れがないように気をつけたい。

参考

nginx http/3を動かす

NginxでHTTP/3を動かしてみます。HTTP/3はQUICというプロトコルに基づいているようです。詳しくは理解していないので追々学んでいきたいと思います。まずは、ローカルで動かしてどのようなデータやり取りが行われているのかwiresharkで覗くところから。

Dockerコンテナでビルドします。gistでレシピが公開されているのでそれに倣います。

 

ビルドすると以下のエラーとなってしまいました。

どうも上記のオプションは--with-http_v3_moduleに統合された?ようなので削除します。

参考: https://hg.nginx.org/nginx-quic/rev/33226ac61076

最終的に以下のようにしました。

nginx.conf

ビルドしてコンテナ起動します。

curlでアクセスしてみます。

RFC9000 https://www.rfc-editor.org/rfc/rfc9000.html

参考

WiresharkのTLS Record Layerのhttp-over-tls

WiresharkでTLS Layerのパケットを調べていた時に、表示されていたプロトコルが何を指しているのか疑問になったので調べてみた。

具体的には以下に表示されている部分である。HTTPでないプロトコルだと思っていたけれどhttp-over-tlsと表示されていたので、HTTP??と思ってしまった。Record部分は、暗号化されていて読めないはずなのに何故HTTPと分かったのか?、という疑問である。

ポート番号で判断している?

もし、パケットの中身を見てHTTPと判断しているのならば、ポート番号を変えてアクセスしてもhttp-over-tlsと表示されるはずである。

ということで試してみた。

Dst Portを8443としてみた。ApacheでListen 8443 httpsと設定し自己署名証明書をセットしてある。

結果、http-over-tlsと表示されてはいなかった。実際には、Webブラウザで、Webページhttps://<address>:8443/にアクセスしているので、HTTPプロトコルでデータを送受信しているはずである。このことから、Wiresharkはポートのみで判断して表示しているのではと推測できる。

Wiresharkのプロトコル解釈に関する記事

検索するとPortで判断しているような記述があり、その線が濃厚そうだ。

Wiresharkのソースはどうなってる?

念のためソースも少し確認しておく。大まかな予想をつけてソースを眺めてみる。以下の箇所がTLSデータの情報表示部分だと思われる。

プロトコルのTreeの部分に表示されている%s Record Layer: %s Protocol: %sの部分が該当箇所であろう。http-over-tlsの表示箇所であるdissector_handle_get_dissector_nameを追ってみると、以下のようになっており、app_handleの変数nameのポインタを返している。

app_handleは、以下の部分で設定されているようである。

pinfo->srcportpinfo->destportの値をもとにapp_handleを決定しているように見える。portの値をもとに決定しているとすると、ここに443の数値を入れると、http-over-tlsとなるはず。

以下のようにソース修正しビルドしてみる。

上記ではDestPortを443にしている。結果は以下のとおりとなった。DestPortは60881であるがhttp-over-tlsと表示されている。SrcPortのケースも同様に443とすると、http-over-tlsと表示されることが分かった。

TLSのレコードプロトコル

念のためTLSのレコードプロトコル構造を確認しておく。上位レイヤであるHTTPのヘッダとボディのデータはペイロードに含まれる。

  • TCPヘッダ
    • Content-Type(1 Byte)
    • Version(2 Bytes)
    • Length(2 Bytes)
    • ペイロード
  • MAC

まとめ

WiresharkのTLSレコードのhttp-over-tlsの表示は、SrcまたはDestのPortを見て決定している。

ただし、session->app_handleが決定済みのケースは、今回追っていない。

参考

Wiresharkのビルド mac

Wiresharkのmacでのビルドの覚書き。

私の環境ではパッケージでQtを入れていたのでCMAKE_PREFIX_PATHでポイントしている。他にもライブラリが必要なものがあるかもしれない。その場合は、依存を見て解決していく。cmakeを使ってビルドを行なうという流れだけ理解しておけばよい。

Qtが複数入っている場合、以下のようにエラーになった。brewで入れていたライブラリを削除するとうまくいった。

ビルドに成功するとrunディレクトリにアプリケーションであるWireshark.appディレクトリがあることがわかる。

ビルド環境

  • mac OS Catalina 10.15.7
  • Intel Core i5

参考

 

「ビジネスデザインのための行動経済学ノート」を読んだ

技術以外のインプット。

人の行動原理を知り、よりよい選択や行動を実践するためのヒントを得たい、という動機から「行動経済学」に関する書籍を探索していたところ、イラストが多く学びやすそうであった本書を手にとり読んでみました。結論から言って、技術中心で心理学や経済学にあまり馴染みのなかった私にとってはすごく分かりすく、非常に為になった難解であろう言葉を分かりやすく説明してくれており、人の心理や行動原理から、どのように考えればよいかのヒントを「活用方法」としてまとめてくれています

本書は、メディアプラットフォームのnoteでUXデザイナーのジマタロ氏が連載していた記事をわかりやすく再構成したものらしいです。note公式アカウントで紹介されているので以下にリンクを掲載しておきます。

https://note.com/info/n/n5df965318ae2

さて、人はどのようにして意思決定を行なっているのでしょうか?

「よく考えた上で判断した(ほんとうに?何かに影響受けてない?)」「お得な買い物をしたなあ(お得にみえてるだけでは?)」「こうすることが相手にとってベストだ(自分の思い込みでは?)」、など冷静になってよくよく考えてみると、何でその時はそう考え行動したのだろうか、という経験が誰しもあるのではないでしょうか。人間は合理的なようで、非合理な行動をとってしまうもの。行動経済学の立脚点はそこにあるようです。人は環境や感情に影響を受けるものなのだと。なるほど。

例えば、ある商品があったとして500円で売られていたものが特価で280円になった場合はお得な感じがしますが、300円が280円になってもあまりお得な感じがしません。それぞれ基準(アンカー)となる価格が500円、300円となり、基準値からの振れ幅で考えてしまうためです。これは、アンカリング効果と呼ばれるそうです。また、特に先に入ってくる情報に影響を受けてしまい、アンカーと一致するようなイメージを想起することをプライミング効果と呼ぶそうです。美味しそうな食べ物を見た後に「ケー○」という表示を見て「ケース」でなく「ケーキ」を想起してしまうのはプライミングによるものと言えます。私たちの思考や行動が、思ってもいないようで実はその時々の状況に影響を受けてしまっている、ということです。あまり意識したことはなかったけれど、なるほど、納得できます。アンカリングやプライミングの効果を活用する方法としては、順番を意識する、ということ。料金や資料など、xxの順で見せる、と言った具合に。

他にも、ユーザーが行動を移すときに後押しをうながすための実践的なテクニック(ナッジ)など、サービスや企画を考える人やデザインする人にとっては参考になる情報が詰まってます。

私は本書を読み行動経済学に強く関心をひかれ、その後すぐに熱の冷めないまま以下2つの書籍も気になり購入し一気に読みました。上の「ファスト&スロー」は著名な本ですがやや学術的、下の「ケースメソッドMBA 行動経済学」はどちらかと言うとビジネスよりで実践的でしょうか。

ファスト&スロー

 

こちらは有名な本で、「プロスペクト理論」で知られるノーベル経済学賞を受賞されたダニエル・カーネマン著の書籍です。文庫本タイプで上・下巻あります。理論の背景や歴史、心理学実験の内容や結果についての考察など、より深い洞察を得たい方には本書がおすすめです。Amazonで注文したところ、届いた書の帯に「東大でいちばん読まれた本 東大生協本郷書籍部2014年9/1〜30文庫ランキング1位」と書かれておりました。書店で見かけたら、システム1(速い思考=直感)が大いに働くであろうキャッチコピーであることは間違いありませんね 笑。

システム1:早い思考、直感、コントロールしている感覚がない
システム2:じっくり考える思考、複雑な計算など頭を使う

ケースメソッドMBA 行動経済学

 

名古屋商科大学ビジネススクールの社会人受講生を対象とした行動経済学の授業の一部の記録をもとに加筆・再構成をした書籍となっています。

講義の様子が伝わるような体で書かれていて読みやすいです。講義の白熱している雰囲気、臨場感が伝わってきます。理論を現実のケースをもとに考察しているところがイメージがつきやすく分かりやすいのではないでしょうか。討議のなかでの生徒の様々な視点からの考え方にもふれることができ、なるほどそういう考え方もあるなと学びがあります。

 

 

NuxtJS+socketioで認証フィルタをはさむ(passport)

NuxtJSでサーバ側はexpress、websocketを使うのにsocket.ioを使っていて、ユーザー認証をpassportで行なう場合の覚書き。

今回試すこと

axiosを使ってサーバAPIでログインし、その後socket.ioでwebsocketに接続する。やりたいことは、サーバAPIで開始したセッションをsocket.ioで引き継ぎ利用することである。

これには、socket.ioのミドルウェアを使って開始済みのセッションを読み込めば、socket.ioのリクエストでセッションを扱える。気をつける点は、socket.ioのミドルウェア関数インターフェースがexpressのそれと異なっているので、正しく情報を引き継げるようミドルウェアをつないでやることである。具体的には、socket.ioのドキュメントにもあるとおり以下のようにすればよい。

socket.ioのロード

今回は、NuxtJSのmoduleのlistenフックを使ってsocket.ioサーバをnuxtのhttpサーバ に統合する。serverMiddlewareで、/apiにexpressのappをマウントする。また、Nuxtのlistenフックを使ってsocket.ioサーバを統合するための設定をmodulesに定義する。

サーバ側

今回は動作検証のため、セッションはメモリストアを使っている。socket.ioとセッションストアを共有するため、共通の定義を使うようにしている。

server/index.js

server/session.js

server/io.js

クライアント側

クライアント側は、ログインしてセッション確認するだけのためのインターフェースにする。以下のような感じ。

ログインしていない状態の時。

ログインしてsocket.ioにつながった時。

Viewテンプレートはveutifyを使う。

pages/index.vue

NuxtJSは、Universalモードで動かしているのでSSRを行なう。sessionは、asyncDataフックを使ってコンポーネントのdataに統合する。

検証用プログラムは以下

https://github.com/moritetu/nuxt-with-socket.io-passport

参考リンク

vuex-persistedstateによるstateデータの永続化

NuxtJSのSSRモードで、vuex-persistedstateによるデータ永続化を適用してしまいSSRのfetchライフサイクルでstoreに設定したデータが上書きされてしまった。これは、私がvuex-persistedstateの使用方法を誤っただけである。SSRモードで動かす場合は、クライアントサイドで永続化したいpathにのみ適用すべし(自分への戒め)。

stateが上書きされてしまったケース

具体的には以下のような使い方をしてしまっていた。

  1. vuex-persistedstateをpluginとしてロード
  2. クライアントサイドでstoreにデータ書き込み(この段階でlocalStorageにデータが書かれている)
  3. SSRでサーバサイドでfetch処理でstateにデータを書き込む

期待する動きは、3で初回ページアクセスしてサーバサイドでcommitしたstateがクライアントサイドで復元される、である。

しかし、3番目のステップでSSRで設定したstateデータがクライアントサイドで復元されると期待するものの、vuex-persistedstateプラグインの永続化処理により、以前にlocalStorageに書き込んだ値で上書きされてしまう、という訳である

プラグインロードのタイミング

NuxtJSでは以下のようにpluginとして組み込める。クライアント再度でpluginがロードされるタイミングは、ライフサイクルでは以下となる。

  • Receives the HTML
  • Loading assets (e.g. JavaScript)
  • client-side Nuxt plugin ★ここ
    • in order as defined in nuxt.config.js
  • Vue Hydration
  • Middleware
  • Global middleware
  • Layout middleware
  • Route middleware
  • asyncData (blocking)
  • beforeCreate (Vue lifecycle method)
  • created (Vue lifecycle method)
  • The new fetch (top to bottom, siblings = parallel) (non-blocking)
  • beforeMount (Vue lifecycle method)
  • mounted (Vue lifecycle method)

引用:https://nuxtjs.org/docs/concepts/nuxt-lifecycle/

Stateのrestoreのタイミング

サーバーサイドでcommitされたstateの情報は、ページアクセスのレスポンスデータの中でシリアライズされて送信される。そして、アプリケーション初期化のタイミングでstateに復元される。

store.replaceStateの復元段階では、まだプラグインの初期化はされていない。プラグインの初期化が呼ばれるのは、コメント // Plugin executionの以降になるので、上記で復元されたstateは上書きされてしまう。

参考リンク

Nuxt.jsでsocket.ioを使うー nuxt-socket-ioライブラリ

Nuxt.jsでsocket.ioを使ったアプリケーションを作る続きです。

前回、Nuxt.jsでsocket.ioライブラリを使うやり方について整理してみました。socket.ioライブラリを直接使う方法で、3つのアプローチで試してみました。

Nuxt.jsでsocket.ioを使うやり方

今回は、Nuxt.jsでsocket.ioライブラリをすぐにアプリ内から使うことができる『nuxt-socket-io』というライブラリを使ってみたいと思います。

外部ページ(nuxt-socket-io):https://nuxt-socket-io.netlify.app

nuxt-socket-io

nuxt-socket-ioライブラリは、socket.ioを使ったサーバ側、クライアント側のプログラムを実装しやすくするためのインターフェースを提供してくれています。具体的には、以下の特徴があるようです(公式Docより)。

  • 複数のio socketのconfiguration
  • socket毎のnamespaceに対応したconfiguration
  • socket.ioのサーバ定義の自動登録
  • socket ioステータス
  • 自動エラーハンドリング
  • デバッグロギング
  • 自動teardown
  • vuexモジュール機能
  • ダイナミックAPIサポート
  • Nuxtライタイムconfigのサポート
  • ミドルウェアの自動登録

nuxt-socket-ioライブラリのサーバ側は内部的にはNuxtのlistenフックを使って実装されているようです。自分でlistenフックを使って実装するのもよいと思いますが、ライブラリを使うとconfiurationや利用が少し楽になるかと思います。

nuxt-socket-ioのインストール

使用方法

最小限の設定は以下のような感じになるかと思います。

nuxt.config.js

  • modulesでnuxt-socket-ioを定義します。これにより、プラグインとして$nuxtSocketが使えるようになるとともに、サーバ側でのsocket.ioライブラリの利用が可能になります。
  • 設定は、modulesのオプション、または、io: {} 、または、RuntimeConfig、$nuxtSocketの引数で渡せます。サーバ側の定義は、modulesのオプションまたはioで定義します。

サーバ側

サーバ側の定義は、デフォルトで以下のパスに定義ファイルを置くことになっています。このパスは、io: { servers: ... } オプションで変えることができます。server/io.jsはデフォルトのネームスペースを指しています。

server/io.js

  • middlewareは、io.useに渡されます。
  • setIOは、ioオブジェクトの生成後に呼ばれるフックです。
  • default functionは、socket.onのハンドラになります。関数名がイベント名になります。返り値は、ackに渡されます。

socketに対して自分でハンドラ定義をしたい場合は、以下のようにすると可能ではありますが、基本はreturnで関数定義が返されることを期待されている?ようです。

server/io.js

クライアント側

page/index.vue

チャネル

異なるネームスペースを使用する場合の例です。

server/io/chat.js

定義はメインネームスペース(/)の場合と同じです。

page/index.vue

参考リンク

  • https://nuxt-socket-io.netlify.app
  • https://www.npmjs.com/package/nuxt-socket-io
  • https://github.com/richardeschloss/nuxt-socket-io

Nuxt.jsでsocket.ioを使うやり方

Nuxt.jsでWebSocket、今回はsocket.ioを使う方法についての備忘録です。他によいアプローチがあるかもしれません。他の方の事例を参考にしながら、自分なりに整理してみました。

以下の3つのアプローチで記述してみます。

  1. moduleでserver.listenの上書き
  2. listenフック
  3. nuxt program

server.listenの上書き

これは、公式サンプルで書かれている内容です。

render:beforeフックの中で、listenメソッドを上書きしています。元々の処理では、protocolに応じたserverオブジェクトが作られlistenが呼ばれます。上記例では、プロトコルはhttpとしてserverオブジェクトを生成してsocket.ioライブラリに渡しています。

examples/with-sockets/io/index.js

引用)https://github.com/nuxt/nuxt.js/blob/dev/examples/with-sockets/io/index.js

listenフック

server.listenの以降で呼ばれるlistenフックを使います。serverは既にlistenが呼ばれています。

参考)https://stackoverflow.com/questions/63172001/where-do-i-add-this-websocket-code-in-the-new-nuxt-js-setup-since-it-does-not-ha

nuxt program

nuxtをプログラムから使用する方法です。これも、公式のサンプルに例があります。以下例では、公式サンプルを少し触っています。

参考)https://github.com/nuxt/nuxt.js/blob/dev/examples/with-sockets/server.js

server/index.js

server/io.js

サンプルプログラムは以下のとおり。

https://github.com/moritetu/nuxt-with-socket.io

参考リンク

AWS SAP認定試験に合格

AWS 認定ソリューションアーキテクト – プロフェッショナル(SAP)の認定試験を受け合格することができました。ぎりぎり合格なので何ともですが、1回目の試験で無事合格できて良かったです。

あまり参考になりませんが、私の受験記について書いてみます。

受験時のスペック

  • 仕事はシステムエンジニアをしています、業界歴は10年以上、現在は主にWebサービスの開発をしています。
  • IPAの高度情報処理試験はDB・NW・SCの3種類を取得済みで、広く浅くITの知識はあります(と思ってます)。
  • AWSについては、業務でここ1年弱くらい本格的に触っているレベルです。使っているAWSのサービスは、スタンダードなWebサービスで使用されるもので、EC2やALB、RDS、S3といった代表的なサービスになります。その他のサービスについては、本格的に使用した経験はないですが、AWSの各サービスがどのオープンソースソフトウェア(OSS)に対応するのか、といったイメージは大体つくという感じです。

試験対策

以下の2つを使用しました。

1. AWS認定ソリューションアーキテクト-プロフェッショナル

AD

AWSの各サービスについての詳細な説明が書いてあるような教科書的な本ではなく、どちらかというと問題演習が中心です。1回分の模擬テストもついてます。解説が丁寧でわかりやすいので、解説を読むだけで勉強になります。正解でない選択肢については、その理由が説明されており、選択肢を絞り込む思考が身につくと思います。解説を読んで理解が不足しているなと思うサービスがあれば、実際のAWSを触ったりBlackBeltを読むことで補完できます。

私は、本書の問題は8〜9割正解できる状態で試験に臨みました。

2. AWS 認定ソリューションアーキテクト プロフェッショナル模擬試験問題集(全5回分375問)

AWS 認定ソリューションアーキテクト プロフェッショナル模擬試験問題集(全5回分375問)

Udemyの講座で、模擬試験5回分です。

問題数が豊富でボリュームがあり、解説も豊富です。ただ、私にとっては問題文および解説文ともに日本語が少し難しく感じました。理解できたかというと怪しいです。1.の参考書を読んだ後に本模擬試験を受けたところ、4〜5割くらいしか正解できませんでした。

試験に向けては、本講座を一周したのと、前日に再度問題の解説をざっと読み返したくらいです。その際、特にどのような点が問われそうか、というポイントだけ抑えるように意識して読み返しました。メインは、1の参考書を使用しました。

学習時間

1の参考書を理解できるようになるまでです。実務経験の有無など個人差があり、学習時間はあまり参考にならないとは思いますが、私の学習の雰囲気を書いてみます。

私の場合は、土日に2時間くらい、仕事で早く帰った日は夜1〜2時間程度のペースで学習を進めました。学習は、1.の参考書を初めから順番に読み進め、イメージしにくかったり理解が難しいところはAWSのサービスの画面を操作してみたり、マニュアルやBlackbeltなどを眺めてみたり、という感じで進めていきました。期間は大体2〜3ヶ月くらいだったと思います。ただ、実際には試験直前くらいまで仕事がたてこんでしまい、平日はあまり学習ができず、本格的に集中して取り組んだのは試験2週間前くらいでしょうか。

期限がないとダラダラと学習を長引かせてしまうと思ったので、ある程度腹をくくって学習半ばで試験日を先に決めてしまうことにしました。結果的に良い意味で追い込むことが出来たと思います。

試験場所

試験方式はオンラインとテストセンター試験が選択できます。私は、いつも試験センターで試験を受けているので、今回も会場受験を選択しました。試験センター方式では、困ったときには会場の試験監督員の方に直接相談できるという安心感があるので、私は会場試験が好みです(相談したことはないですが)。今回も焦らず受験できました。

試験当日の準備

私は朝が弱く会場に余裕を持って入りたかったため、午後の試験を選択しました。

試験では、本人確認書類が2種類必要なので忘れずに。私の場合は、運転免許証とクレジットカードの2点を提示しました。試験は3時間の長丁場なので忍耐力がいります。トイレは事前にしっかり済ませること。また試験中にトイレに行きたくならないよう、コーヒー等は控えめにした方が良いかもしれません。

試験

印象としては難しいと感じました。明らかに誤りである選択肢は除いて、残りの選択肢を絞り込めないものが多々ありました。確証が持てない問題は見直しチェックを入れて飛ばしていきましたが、おそらく2、30問ほどあった気がします。。序盤から結構続いたのでしんどかったです。実際、試験中に何度か諦めそうになりました。

解答は、1問約2分のペースで進めました。最後まで解いた段階で30分の時間が余っていました。これは予定通りでした。しかし見直しチェックをつけた問題が多く、30分では数問だけしか見直すことが出来ませんでした。

正直ダメかなとも思いましたが、結果的には合格することが出来ました。最後まで諦めなかったことと、時間配分をきっちりできたのが大きかったと思います。

さいごに

SAPでは、想定されるビジネスシナリオの要求を正しく理解し、AWSのサービスをどのように組み合わせれば良いのか、を導く必要があります。出題されるビジネスシナリオはバラエティに富んでます。どの試験にも言えるかとは思いますが、学習においては漠然と問題の解を覚えるのでなく、AWSの各サービスの特徴と仕組みを理解し、どのようなケースでそのサービスが有効なのか、そしてWell-Architectedの原則に沿っているか、といった点を抑えていくことが重要だと思いました。本質を抑えることで、問題文がアレンジされたとしても正解を導くことができるようになると思いました。