はじめに
RailsでAPIを作るぞ!ってなった時にいつも行っているテストやリンターなどの導入手順を備忘録がてら記事にしました。
また、本記事ではRailsアプリケーション自体は最小限の構成で作成し、
なるべく小規模かつ開発に必要なツールは最低限導入しておくことを念頭においています。
完成系
最初に完成系のrepositoryを上げておきます。
実際の実装が見たい場合は下記repositoryをご参照ください。
本記事で扱うこと
- dockerでmysqlをdbとする最小限のrailsアプリケーションを作成
- ヘルスチェック用endpointの実装
- テストツール(rspec)の導入
- リンターツール(rubocop)の導入
- API定義書、並びに関連ツール(OpenAPI, Swagger Editor, Swagger UI, committee)の導入
- CI(CircleCI)の導入
- CORSの設定
- JSON用テンプレートエンジン(jbuilder)の導入
本記事で扱わないこと
- dockerそのもののセットアップ
- 各種ツールの詳細な説明
- ヘルスチェック以外のendpointの実装
- FEアプリケーションの実装
- 本番環境へのデプロイ
- CDの実装
Railsアプリケーションの雛形の準備
dockeでRails, Mysqlを使用するための準備
API用のディレクトリと各種ファイルの作成
mkdir /minimal_api
cd /minimal_api
# docker関連のファイルを作成
touch {Dockerfile,entrypoint.sh,docker-compose.yml}
# gem関連のファイルを作成
touch {Gemfile,Gemfile.lock}
Docker関連のファイルを編集
公式のテンプレートを参考にDockerfile, docker-compose.yml, entrypoint.shを作成します。
FROM ruby:3.1.3
RUN apt-get update -qq && apt-get install -y vim
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app
# For rails credentials:edit
ENV EDITOR="vim"
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
ほとんどテンプレートと一緒ですが、今回はAPIの作成であることと、postgresではなくmysqlを使用することから、
node.jsもpostgres-clientも不要です。
また、rails credentialsを使用する時のために、vimをインストールし、環境変数のEDITORに"vim"をセットしておきます。
#!/bin/bash
set -e
rm -f /app/tmp/pids/server.pid
exec "$@"
entrypoint.shは特に変更していません。
version: '3.5'
services:
db:
image: mysql:8.0
ports:
- 3306:3306
restart: always
environment:
MYSQL_ROOT_PASSWORD: passw0rd
MYSQL_DATABASE: root
volumes:
- ./mysql_data:/var/lib/mysql
app:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/app
- bundle_data:/usr/local/bundle
ports:
- 3001:3000
depends_on:
- db
tty: true
stdin_open: true
volumes:
mysql_data:
bundle_data:
dbには、postgresqlではなくmysqlを使用しています。
appの方には、
tty: true
stdin_open: true
を指定し、コンテナの標準出力/標準入力に接続できるようにしています。
この設定はdebugする際に必要です。
また、mysqlのデータとinstall済みのgemのデータを永続化するため、mysql_dataとbundle_dataをvolumesに指定しています。
./mysql_data:/var/lib/mysql
はコンテナ内の/var/lib/mysqlをホストマシン側の./mysql_dataにマウントしています。
コンテナ内の/var/lib/mysqlにはmysqlのデータが格納されており、それを/mysql_dataにマウントすることで、コンテナを再起動したときでも、ホスト側の/mysql_dataにはコンテナを落とす前のデータが格納されているので、データが初期化されずにすみます。
逆にこれがないとコンテナを落とすたびにデータが消えるので注意してください。
また
bundle_data:/usr/local/bundle
もmysql_data同様、コンテナ内の/usr_local/bundleをホストマシン側のbundle_dataにマウントしています。
usr/local/bundleにはbundle install済みのgemの情報が格納されています。
これがないと、新しくgemを追加するたびにdocker-compose buildする必要があります。
こちらは必須というわけではないですが、あった方が開発上便利なので記載しています。
Gemfileの修正
source "https://rubygems.org"
gem "rails", "~> 7.0.4"
Gemfileではgemのソースを記載し、railsをinstallします。
今回はrailsのv7.0.4を使用します。
※ Gemfile.lockは空のままで大丈夫です。
ビルドする
これで準備は整ったので下記のコマンドでビルドしましょう。
docker-compose build
rails new
さて、ビルドがうまくいったら今度はrails newでアプリの雛形を作成します。
docker-compose run app rails new . --minimal --api -T -d=mysql
これを実行すると、カレンとディレクトリにRailsアプリケーションが作成されます。
ざっくり各オプションの解説をします。
--minimal
Rails 6.1で追加されたオプションで、
ActionCableやActionMailerどのRails特有の機能の生成を全てスキップして、
最小限の構成でRailsアプリケーションを生成できるオプションです。
もちろん必須ではないですが、一応必要十分というテーマなので今回はこのオプションを使用しています。
具体的にskipの対象になるものを知りたい方は下記をご参照ください。
--api
RailsをAPIモードで生成するためのオプションです。
これを使用するとAPIに必要な機能のみを使用するようにRails側でよしなに設定してくれます。
具体的には、
- アプリケーション起動時にAPIに不要なmiddle wareを読み込まないようにする
- ApplicationControllerがActionController::Baseではなく、ActionController::APIを継承する
- views, helpers, assertsを生成しないようにgeneratorを設定する
こちらも、詳細は下記をご参照ください。
-T
テスト関連のファイルの生成をスキップするためのオプションです。
Railsの標準ではminitestが採用されていますが、今回はrspecを使用するのでオプションでこれらの生成をスキップしています。
-d=mysql
使用するデータベースを指定するオプションです。
defaultではsqlite3が指定されていますが、今回はmysqlを使用するのでオプションで指定しています。
gitignoreに/mysql_dataを追記する
docker-compose.ymlでvolumesに指定しているmysql_dataをgit管理対象外にするためにgitignoreに追加します。
...
# Ignore volumed mysql_data # <= 追記(文言はお好みで)
/mysql_data # <= 追記
database.ymlを修正する
作成したmysqlサーバーに接続するため、
docker-compose.ymlで設定した内容通りに修正します。
...
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: minimal_api_dev
username: root
password: passw0rd
host: db
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: minimal_api_test
username: root
password: passw0rd
host: db
...
データベースの作成とマイグレーション
設定ができたらデータベースの作成とマイグレーションを行います。
docker runコマンドを使用しても良いですが、今回はdocker execを使用して、appコンテナ内部に入り、コマンドを実行します。
docker-compose exec app bash
(コンテナ内部) rails db:create db:migrate
http://localhost:3001 にアクセス
http://localhost:3001 にアクセスして、下記のような画面が表示されれば成功です。
開発ツール系の導入
この章ではテストツール、リンター、API定義書など開発を進めていく上で必要になってくるツールを導入します。
rspecの導入
rails-rspecのインストール
前述の通り今回は、テストツールとしてrspecを導入します。
Gemfileにrails-rspecを追記します。
...
group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri mingw x64_mingw ]
gem 'rspec-rails', '~> 6.0.0' # <= 追記
end
...
Gemfileを追記したらbundle installを実行しましょう、
再度
docker-compose exec app bash
でコンテナ内に入り、
bundle install
を実行します。
rspecがinstallできたら、同じくコンテナ内で、
rails g rspec:install
を実行します。すると下記4つのファイルが作成されます
- .rspec
- spec
- spec/spec_helper.rb
- rspec/rails_helper.rb
これでrspecの準備は整いました。
rspecを実行してみる
実際にrspecのテストを実装するためにヘルスチェック用のendpoint(GET /api/v1/health_checks)を作成します。
このエンドポイントにリクエストを送ると
{ "result": "ok" }
というJSON形式のレスポンスが返ってくることを期待します。
docker-compose exec app bash
でコンテナ内に入り、
rails g controller api/v1/healthchecks
を実行すると、healthchecks_controller.rb
, healthchecks_spec.rb
が下記のように作成されます。
class Api::V1::HealthchecksController < ApplicationController
end
require 'rails_helper'
RSpec.describe "Api::V1::Healthchecks", type: :request do
describe "GET /index" do
pending "add some examples (or delete) #{__FILE__}"
end
end
まずは、healthchecks_spec.rbに期待する結果を記載します。
require 'rails_helper'
RSpec.describe "Api::V1::Healthchecks", type: :request do
describe "GET /api/v1/healthchecks" do
subject(:send_request) { get "/api/v1/healthchecks" }
it "returns http success" do
expect(send_request).to eq 200
expect(JSON.parse(response.body, symbolize_names: true)).to match({ result: 'ok' })
end
end
end
シンプルなテストなので、特に説明は不要だと思いますが、
symbolize_namesのところだけ少し、補足します。
symbolize_namesはJSON.#parseのoptionでこれを指定するとparse結果として出力するhashの引数をシンボルで返してくれます。
これを使用しない場合は、該当行を下記のように書き換える必要があります。
expect(JSON.parse(response.body)).to match({ "result" => 'ok' })
正直使用するかしないかは好みだと思いますが、いちいち""
で囲ったり=>
で指定したりするのが少々面倒なので、筆者はsymbolize_namesを指定することが多いです。
再度コンテナ内に入り、rspecを実行してみます。
rspec
すると、まだ何も実装していないので当然エラーがでます。
ActionController::RoutingError:
No route matches [GET] "/api/v1/healthchecks"
これを期待するendpointにするため、healthchecks_controller.rbとroutes.rbを下記のように修正します。
class Api::V1::HealthchecksController < ApplicationController
def index
render json: { result: 'ok' }
end
end
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :healthchecks, only: %i(index)
end
end
end
修正が完了したら、再度rspecを実行してみましょう
rspec
# => 1 example, 0 failures
今度は成功するはずです。
これでrspecの導入と最初のテストの実装が完了しました!
rubocopの導入
rspecの導入が終わったので、今度はlinterであるrubocopの導入をします。
Gemfileに下記を追記します。
group :development do
gem 'rubocop', require: false #<= 追記
gem 'rubocop-rails', require: false #<= 追記
gem 'rubocop-rspec', require: false #<= 追記
end
rubocop-railsとrubocop-rspecはそれぞれrails, rspec向けのrubocop拡張機能を提供してくれるgemです。
Gemfileの修正が終わったら例のごとくbundle installします。
bundle install
gemのinstallが終わったら下記を実行します。
rubocop --auto-gen-config
すると
- .rubocop_todo.yml
- .rubocop.yml
が生成されます。
本記事ではデフォルトの設定のまま進めますが、結構厳し目なので適宜お好みで設定してください。
それでは、rubocopを実行してみましょう。
rubocop
大量の警告が出たと思います。
今度は-a or -Aオプションをつけてオートコレクト機能を使いましょう。
rubocop -a
or
rubocop -A
一気にコードの修正が行われたと思います。
再度オプションを外してrubocopを実行しましょう。
rubocop
警告が出なくなれば成功です。
これでrubocopの導入が完了しました!
OpenAPI(Swagger)の導入
APIの実装を進めるにあたってAPI定義書を作成するためOpenAPI(Swagger)の導入をしたいと思います。
docker-compose.ymlを下記の内容を追記します。
version: '3.5'
services:
...
swagger-editor:
image: swaggerapi/swagger-editor
ports:
- "8001:8080"
volumes:
- ./docs/openapi.yaml:/tmp/openapi.yaml
environment:
SWAGGER_FILE: /tmp/openapi.yaml
swagger-ui:
image: swaggerapi/swagger-ui
ports:
- "8002:8080"
volumes:
- ./docs/openapi.yaml:/usr/share/nginx/html/openapi.yaml
environment:
API_URL: openapi.yaml
...
swagger-editorはswagger.ymlをブラウザ上で編集するためのツールです。
volumesで、minimal_api/docs/openapi.yamlをコンテナ内の/tmp/openapi.yamlにマウントし、
SWAGGER_FILEで/tmp/openapi.yamlを指定することで、ホストマシン上にあるdocs/openapi.yamlをベースに編集を行うことができます。docs/openapi.yamlは後ほど作成します。
swagger-uiはswagger.ymlをブラウザ上でみやすい形に表示してくれるためのツールです。こちらも同様にdocs/openapi.yamlを閲覧できるようにvolumeとAPI_URLを指定しています。
そして、docsディレクトリを作成し、そこにopenapi.yamlを配置します。
openapi: 3.0.3
info:
title: minimal_api
version: 1.0.0
servers:
- url: http://localhost:3001
tags:
- name: HealthChecks
description: Endpoint to do healthchecks minimal_api
paths:
/api/v1/healthchecks:
get:
tags:
- HealthChecks
summary: get success response
operationId: healthCheck
responses:
'200':
description: return result ok
content:
application/json:
schema:
required:
- result
type: object
properties:
result:
type: string
example: ok
準備が終わったらbuildとupを行います。
docker-compose build
docker-compose up
http://localhost:8001, http://localhost:8002 にアクセスしてみて、下記のように表示されたら成功です。
Commiteeの導入
committeeはopenAPI等を使う際に便利な機能を提供してくれるMiddlewareです。
今回はrequest specでopenAPIに定義したスキーマに則ったレスポンスが返却されているかどうかのテストを行う為に使用します。
まずは、Gemfileにcommitee, commitee-railsを追記し、bundle installを実行します。
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: %i[mri mingw x64_mingw]
gem 'committee' # <= 追記
gem 'committee-rails' # <= 追記
gem 'rspec-rails', '~> 4.0.1'
end
bundle install
次にrails_helper.rbにcommittee用の設定を追記します。
...
RSpec.configure do |config|
...
# setting for committee
config.add_setting :committee_options
config.committee_options = {
schema_path: Rails.root.join('docs/openapi.yaml').to_s,
parse_response_by_content_type: false,
query_hash_key: 'rack.request.query_hash'
}
config.include Committee::Rails::Test::Methods
end
GET /api/v1/healthchecks
のテストケースにスキーマチェックを追加します。
...
RSpec.describe 'Api::V1::Healthchecks', type: :request do
describe 'GET /api/v1/healthchecks' do
subject(:send_request) { get '/api/v1/healthchecks' }
...
it 'returns correct response' do
send_request
assert_response_schema_confirm(200)
end
end
end
assert_schema_conform(200)
というところで、
GET /api/v1/healthchecks
のステータス200に定義されているスキーマと、
send_requestで取得したresponse.bodyのスキーマが一致しているかを確認しています。
確認のために、一回わざとスキーマを変更してみましょう。
module Api
module V1
class HealthchecksController < ApplicationController
def index
render json: { hoge: 'ok' }
# render json: { result: 'ok' }
end
end
end
end
テストを実行します。
rspec
すると、新しく追加したテストケースが失敗し、下記のようにエラーが表示されます。
Failure/Error: assert_schema_conform(200)
Committee::InvalidResponse:
#/paths/~1api~1v1~1healthchecks/get/responses/200/content/application~1json/schema missing required parameters: result
requiredに設定しているパラメータがないよ。
パラメータの名前はresultだよ。
と教えてくれています。
healthchecks_controllerを元に戻して、再度テストを実行します。
# frozen_string_literal: true
module Api
module V1
class HealthchecksController < ApplicationController
def index
render json: { result: 'ok' }
end
end
end
end
rspec
今度は成功したはずです。
これでcommitteを使用して、API定義書通りに実装されているということをテストできるようになりました!
commiteeによって検証が行われるのは、requiredに設定したパラメータのみです。
試しに、required部分をコメントアウトしてみましょう。
openapi: 3.0.3
info:
title: minimal_api
version: 1.0.0
servers:
- url: https://localhost:3001
tags:
- name: HealthChecks
description: Endpoint to do healthchecks minimal_api
paths:
/api/v1/healthchecks:
get:
tags:
- HealthChecks
summary: get success response
operationId: healthCheck
responses:
'200':
description: return result ok
content:
application/json:
schema:
#下記2行をコメントアウト
#required:
# - result
type: object
properties:
result:
type: string
example: ok
先程同様、間違えたプロパティを返すようにコントローラーを使用します。
module Api
module V1
class HealthchecksController < ApplicationController
def index
render json: { hoge: 'ok' }
# render json: { result: 'ok' }
end
end
end
end
この状態でテストを実行します。
rspec
すると、commiteeの方のテストケースは通ってしまいます。
このようにrequiredをつけていないとプロパティの検証が行われないので注意が必要です。
CircleCIの導入
テスト、リンターを実装したので、これらを自動実行するためのCIを導入します。
CIといってもいろいろな種類がありますが、本記事では、CircleCIを使用します。
前述した通り、詳細な説明はここではしませんが、CircleCIが何かよくわからない、
という方は下記の記事がとてもわかりやすかったので読んでみてください。
また、アカウントをお持ちでない方は下記から、アカウント登録してください。
.circle/config.ymlの作成
諸々の準備が終わったら、実装をスタートします。
まず、サイドバーのProjectsをクリックして、プロジェクト一覧ページに移動します。
当然ですが、github上に本アプリケーション用のrepositoryを作成していない場合、Projectsには表示されないので適宜repositoryを作成してください。
git操作やgithub上にrepositoryを作成する工程は本記事では扱いません。
次に、CIを導入したい、projectのSet Up Projects
をクリックします
今回作成したのは、minimal_appなので、minimal_appのSet Up Projects
をクリックしました。
すると、config.ymlファイルを選択するためのモーダルが表示されます。
表示されているオプションは
- すでにrepository上に存在している.circleci/config.ymlを使用する
- circleci側で用意しているcircleci/config.ymlを自動で作成された新しいbranchにcommitし、即CIの実行をする(CIの動作確認用)
- 編集可能なciのテンプレートを取得する
今回は、3番目のオプションを指定してみます。
すると、下記のようなEditorが開き、config.ymlのテンプレートが表示されます。
この中で、config.ymlの設定が行えます。
自分は下記のように設定しました。
version: 2.1
jobs:
test:
docker:
- image: cimg/ruby:3.1.3
environment:
BUNDLE_PATH: vendor/bundle
RAILS_ENV: test
- image: cimg/mysql:8.0
name: db
environment:
MYSQL_ROOT_PASSWORD: passw0rd
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
- v1-dependencies-
# set up
- run:
name: install gems
command:
bundle install
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
- run:
name: wait to setup db
command: dockerize -wait tcp://db:3306 -timeout 1m
- run:
name: setup db
command: bundle exec rails db:create db:schema:load
# run test
- run:
name: run test
command: bundle exec rspec --profile 10
# run linter
- run:
name: run linter
command: bundle exec rubocop
workflows:
sample:
jobs:
- test
ざっくり解説すると、大枠の流れは下記になっています。
- ruby, mysqlのイメージをビルド
- gemのインストール
- test用のdb準備(作成&スキーマ読み込み)
- rspecの実行
- rubocopの実行
その他で言えば、
2.のgemインストールの前後にrestore_cache, store_cacheを挟んでいます。
これは、依存関係(gem)のインストールを毎回実行するのではなく、変更された場合のみ新しい依存関係のインストールするようにするためです。
依存関係のキャッシュについては、公式ドキュメントに詳しく記載があるので、気になる方はご参照ください。
また、3.のtest用のdb準備の前に下記のコマンドを実行しています。
dockerize -wait tcp://db:3306 -timeout 1m
dockerizeはdockerでアプリケーションを実行するためのユーティリティツールで、
Dockerの公式ドキュメントでも紹介されています。
ここではtcp://db:3306
つまり、mysqlイメージが正常にビルドされ、起動するまで待機するのに使用しています。
これがないと、dbのセットアップがmysqlサーバーの起動を追い越し、下記のエラーが発生することがあるので注意です。
Mysql2::Error::ConnectionError: Can't connect to MySQL server on 'db:3306'
CIの実行
config.ymlの設定が終わったら、Editor画面右上にある、Commit and Runを実行すると、
circleci-project-setup
というブランチが作成され、config.ymlをそこにcommitし、
パイプラインが起動します。
Successとなっていたら成功です。
問題なく実行できたら、circleci-project-setup
をmainブランチにMergeしましょう。
ちなみに、git hub上でも、CIの結果が表示され、このマークをクリックするとCicleCIの画面に遷移することができます。
何か不備があって変更がある場合は、git fetchなどを使用して、手元にcircleci-project-setup
を持ってきて、
そこでconfig.ymlを編集し、commitしてpushを行うと、再度パイプラインが実行されます。
内容に不備がないのに、config.ymlのバリデーションで警告が出る場合は、
インデントがスペース4つ分になっているかどうか確認してください。
その他Railsの設定
CORSポリシーの設定
CORSポリシーとは
CORSとはオリジン間リソース共有(Cross-Origin Resource Sharing)のことで、
ざっくりいうと、他のオリジンから、アクセスを許可するしくみのことです。
オリジンとは、スキーム、ホスト、ポート番号を組み合わせて定義される値のことで、
https://localhost:80
のように表記されるものです。
Web上では、悪意のあるサイトからのアクセスを防ぐため、
同一オリジン間のアクセスしか許可できないようにする仕組み(Same-Origin Policy)があります。
しかし、このままだとSwagger UIからリクエストを送信した時のように、自分がアクセスを許可したいサイトからのアクセスもブロックされてしまいます。
特定のサイトからのアクセスを許可するためには、
このオリジンのサイトにはこのリソースの共有を許可するよ!という設定をしてあげる必要があります。
それがCORSポリシーというわけです。
詳しくは下記をご参照ください。
CORSポリシーの実装
Railsではrack-corsというgemを使うことでCORSの設定をすることができます。
そしてこのライブラリはrails newした際にコメントアウトした状態で生成されているので、コメントを解除し、bundle installします。
...
gem "rack-cors"
...
そして、config/initializers/cors.rbに必要な設定を追記します。
ちなみにこのファイルもrails . newの時に生成されています。
本記事では、FEの実装をしていないため、
仮にhttp://localhost:3000 でFEアプリケーションが動作している場合の例で設定してみます。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
if Rails.env.development?
origins 'localhost:3000'
end
resource '/api/v1/*',
headers: :any,
methods: [:get, :post, :patch, :put, :delete]
end
end
この設定で、http://localhost:3000 のオリジンを持つFEアプリケーションから、
api/v1以下のリソースにアクセスすることができるようになりました。
これで、CORSの設定は完了です。
originを複数指定したい場合は
origin 'localhost:3000'
origin 'localhost:4000'
ではなく、
origin 'localhost:3000', 'localhost:4000'
とします。
前者だと、localhost:4000
の方しか反映されないので注意してください。
jbuilderの導入
jbuilderは、Railsで標準的に使用されているJSON用のテンプレートエンジンです。
minimalオプションをつけるとskipされてしまうので、個別にinstallする必要があります。
...
gem 'jbuilder'
...
GET /api/v1/healthchecks
のendpointをjbuilderを使用する形に修正してみましょう。
app
ディレクトリの下にviews/api/v1/
ディレクトリを作成し、そこに
下記のindex.json.jbuilder
を配置します。
controllerから暗黙的にrenderするために、ディレクトリ名、ファイル名が重要なので、間違えないようにしてください。
# frozen_string_literal: true
json.result @result
次に、healthchecks_controller.rb
を次のように修正します。
# frozen_string_literal: true
module Api
module V1
class HealthchecksController < ApplicationController
def index
@result = 'ok' #<= ここを変更
end
end
end
end
rspecを実行して、テストが通れば成功です。
終わりに
これでAPIを実装する上で必要なものは最低限揃ったはずです。
本当は、この構成を使ってサンプルのCRUD APIを実装しようと思っていたのですが、
思いの外長くなってしまったのでまたの機会とさせていただきます。
最後まで読んでくださりありがとうございました。
参考