React(TypeScript)で簡単なWebシステムを開発してAWSにデプロイまでしたので、その方法を紹介します。間違っている可能性もあるので自己責任で参考にしてください。ブログを書く時間がないため、現時点ではバックエンドのみ書いています。今回は、簡易的なユーザー情報の管理システムを開発します。システムの概要は以下の通りです。Bootを使ってAPIを構築します。データベースは何でも良いですが、無料で提供されているPostgreSQLを使います。データベースへの接続はMyBatis(マイバティス)というフレームワークを使います。MyBatisを経由してPostgreSQLにSQLの発行を行い、データの取得や更新を行います。フロントエンドフロントエンドは、JavaScriptのライブラリであるReactを使います。型付けを行うためにTypeScriptを導入します。上記で構築したバックエンドのAPIにHTTPのリクエストを行い、データを取得して画面に表示したり、画面で入力された情報をデータベースに登録したりします。デプロイシステム開発が完了したら、AWSのクラウド環境にデプロイして公開を行います。Beanstalkを使用します。Codeを使用します。バックエンドの開発プロジェクトの作成Eclipseで「ファイル」⇒「新規」⇒「Springスターター・プロジェクト」をクリックします。名前の部分に任意の名前を入力します。タイプの項目で「Gradle」か「Maven」のどちらかをビルドツールとして選択できますが、今回はGradleを使います。依存関係のページでは、以下を導入します。必要に応じてその他も自由に入れてください。Bootのテンプレートエンジン。Driver(PostgreSQLが使える)プロジェクトを作成したら、build.gradleというファイルを開いてみてください。dependenciesという部分に先ほど選択した依存関係が記述されているはずです。'org.springframework.boot:spring-boot-starter-test'}implementationやruntimeOnlyなどは、依存関係のライブラリをどこに含めるかを指定します。implementationを選ぶと、コンパイル時およびビルドされたjarファイルに依存関係のライブラリが含まれます。runtimeOnlyを選ぶと、コンパイル時は含まれないが、jarファイルには含まれます。プロジェクトを作成した後にライブラリを追加したくなったら、上記のようにbuild.gradleのdependenciesに追記を行います。build.gradleを右クリックして「Gradle」⇒「Gradleプロジェクトのリフレッシュ」を実行します。すると、「プロジェクトと外部の依存関係」に追加されると思います。データベースの接続情報を設定PostgreSQLのデータベースやテーブルなどを作成したら、接続情報を構成ファイルに設定します。Bootにはapplication.propertiesという構成ファイルがデフォルトで用意されています。application.propertiesは「.yml(ヤムル)」という拡張子に変更することもできて、この記事ではYAML形式でサンプルを紹介しています。補足YAML形式に変更するには、application.propertiesを右クリックして「リファクタリング」⇒「名前変更」で「application.yml」を入力します。以下はデータベースの接続情報を設定するサンプルです。DBの名前やユーザ名、パスワードなどは変更してください。パスワードモデルの作成データベースから取得した情報や、画面で入力された情報などを格納するモデル(MVCのModel)を作成します。modelというフォルダを作成して、その中に以下のようなクラスを作成します。age;}今回はユーザー情報の管理システムなので、Personというクラスにしています。データベースのカラムに合わせて、ID、名前、年齢のフィールドを用意しています。Lombokを依存関係に追加しているため、@Getterと@Setterというアノテーションをつけると、GetterとSetterを自動で作成してくれます。そのため、getName()やsetAge(30)といった感じで値の取得や設定が可能です。Mapperインタフェースの作成mapperというフォルダを作成して、その中にMapperインタフェースを作ります。Mapperインタフェースには、以下のようにデータベースの取得や更新を行う際のメソッド名や引数、戻り値などを定義します。person);}例えば、getPersonというメソッドは、int型のidを条件にユーザー情報の取得を行い、先ほど作成したPersonクラスのオブジェクトが戻り値になります。updatePersonというメソッドは、画面で入力された情報を格納したPersonクラスのオブジェクトをもとにデータベースの更新を行い、戻り値はありません。@Mapperというアノテーションをつけることで、MapperインタフェースとしてSpringフレームワークから認識されます。上記のサンプルでは引数が一つですが、もしも複数になる場合は@Paramを使う必要があります。次に、Mapperのインタフェースに対応したXMLファイルを作成して、SQLを記述します。注意点としては、MapperのXMLは「src/main/resources」配下で、かつインタフェースと同じ階層に作る必要があります。以下のようなイメージです。(Mapperのインタフェース)src/main/java/com/example/demo/mapper/PersonMapper.java(MapperのXML)src/main/resources/com/example/demo/mapper/PersonMapper.xml上記のようにすることで、フレームワークがXMLファイルの場所を見つけられます。PersonMapper.javaとPersonMapper.xmlを同一階層ではなく同一フォルダに配置していたところ、ローカル環境では上手くいきましたが、AWS環境では以下のエラーが出ました。found)MapperのXMLには、以下のようにSQLを記述します。実際の例を見るとイメージがわきやすいと思います。#{id}</update></mapper>まず、mapperタグのnamespace属性に、Mapperインタフェースの名前空間を記述します。これによって、インタフェースとXMLが紐づきます。SELECTやUPDATEなどSQLの構文ごとに<select>や<update>といったタグが用意されています。idにはMapperインタフェースのメソッド名を記述することで、メソッドとSQLが紐づきます。SELECTの場合は、取得したデータの格納先をresultTypeに指定します。上記の例ではPersonクラスを指定しているので、Personテーブルのid、name、ageが、それぞれPersonクラスのid、name、ageに設定されます。注意点データベースのカラム名とresultTypeのフィールド名が異なる場合、そのままではマッピングできません。Personテーブルのカラム名が「user_name」で、Personクラスのフィールド名が「name」の場合です。このような場合は、以下のように「as」を使うことでマッピングできます。person※他にも、application.propertiesやresultMapを使う方法がありますが、ここでは割愛します。Mapperインタフェースの引数に設定した情報は、「#{}」という形式で取り出せます。「#{}」という形式で記述した場合、PreparedStatementになってSQLインジェクション対策ができます。勝手にエスケープされては困る場合は、「${}」という形式を使用します。サービスの作成データベースへのアクセスや業務ロジックなどを記述するサービスクラスを作成します。serviceというフォルダを作成して、その中に以下のようなクラスを作成します。{mapper.updatePerson(person);}}サービスクラスには@Serviceというアノテーションをつけます。これによってサービスであることを明示できると共に、DIコンテナに登録されます。DIについて理解するには、上記のコードのmapperを見ると分かりやすいです。通常、あるクラスのメソッドを利用するには、以下のようにインスタンスを生成します。mapper.getPerson(id);}まずは、Mapperインタフェースを実装したクラス(PersonMapperImpl)を作成します。そのクラスのオブジェクトをインスタンス化してメソッドを呼び出します。今回は実装クラスを作成していませんし、インスタンス化もしていません。実装クラスに関しては、MyBatisが自動で生成してくれます。インスタンス化に関しては、mapperというフィールドに@Autowiredというアノテーションをつけることで、フレームワークのDIコンテナがインスタンスを生成してフィールドに設定してくれます。@Autowiredをつけるだけでは不十分で、DIの対象となるクラスには、@Service、@Controller、@Componentといったアノテーションを付与する必要があります。DIの説明が終わったところで、メソッドの中身を見てみます。やっていることは非常にシンプルで、Mapperインタフェースのメソッドを呼び出します。これによって、対応するMapperのXMLに記述したSQLが実行され、その結果が戻り値として返ってきます。BootのAOPという仕組みを使って、メソッド全体をトランザクションとして処理してくれます。そのため、メソッドを抜けるまでにエラーがなければコミットを行い、エラーがあればロールバックしてくれます。このように、SpringフレームワークはDIや各種アノテーションを利用することで、コードを書く量を減らせることがメリットの一つです。コントローラの作成APIとして画面からのリクエストを受け付け、サービスの結果などをレスポンスとして返すコントローラを作成します。controllerというフォルダを作成して、その中に以下のようなクラスを作成します。service.getPerson(Integer.parseInt(id));}}通常のコントローラはビューと呼ばれる画面を返しますが、今回は画面部分をReactで別途作成するため、APIはデータの取得や更新のみを担当します。ビューではなくデータ自体をレスポンスのbodyに設定して返すには、メソッドに@ResponseBodyというアノテーションをつけます。今回のようにコントローラ全体がAPIのような場合には、コントローラに@RestControllerというアノテーションをつけることで対応できます。@RequestMappingというアノテーションをつけて、コントローラに紐づけたいURLを指定します。今回はAPIに対する全てのURLに「api」という文字を含めています。全てのリクエストに「api」をつける場合、application.ymlに設定することもできますが、今回は後述するAPI以外のコントローラもあるので使いません。/api@GetMappingというアノテーションは、HTTPのGETメソッドを受け付けます。アノテーションにはそのメソッドに紐づけたいURLの形式を指定できます。上記の例では「xxxx.com/api/person/1」や「xxxx.com/api/person/2」といったURLが該当します。メソッドの中身を簡単に説明すると、getPersonはidを受け取ってサービスに渡し、データベースで検索したPersonクラスのオブジェクトを返しています。URLに含まれる値は「@PathVariable("名前")」で取得できます。本来はidの検証なども必要ですが、ここでは分かりやすいように省略しています。次に、ユーザー情報を更新するメソッドを作成します。ApiError(null);}更新処理は一般的にHTTPのPUTメソッドを使います。そこで、@PutMappingというアノテーションをつけて、マッピングさせたいURLを記述します。@RequestBodyを引数につけることで、リクエストで送られたユーザー情報が自動で設定されます。入力検証を自動で行いたい場合、@Validatedというアノテーションを付与します。入力エラーの有無はBindingResultのhasErrorsメソッドで判定できるので、エラーがあったときにはApiErrorという自作のオブジェクトにエラー情報を設定して返します。Bootで入力検証を行うには、以下の3点が必要です。入力検証の内容をフィールドのアノテーションに追加メソッドの引数に@Validatedアノテーションを付与する依存関係の追加と@Validatedアノテーションの付与は既に実施しているので、二つ目の作業を行います。以下のように、検証対象のPersonクラスに対して、専用のアノテーションをつけます。"名前を入力してください。"名前は20文字以内で入力してください。"年齢は1~200までの半角数字を入力してください。age;例えば、@NotBlankをつければ必須チェックになり、@Lengthをつければ文字数のチェックができます。他にもアノテーションはあるので、調べてみてください。エラーメッセージをカスタマイズしたい場合は、上記のサンプルのようにmessageに設定します。今回のシステムは、Reactを使った「SPA(シングルページアプリケーション)」です。ユーザーには単一のHTMLを返して、その後はJavaScriptを用いて画面の内容が部分的に更新されます。そのため、以下のようなHTMLファイルを「src/main/resources/templates」の配下に作成します。href="/static/img/apple-touch-icon.png"></head><body><noscript>JavaScriptを有効化してください。src="/static/built/bundle.js"></script></body></html>タグは一部省略しているので、必要に応じて追加・変更してください。スタイルシートや画像などの静的ファイルは「src/main/resources/templates」直下の「static」フォルダに配置することを想定しています。ポイントは、bundle.jsの部分です。ReactおよびTypeScriptで開発したソースをbundle.jsにビルドして、それを読み込んでいます。補足エラーが発生したときのために、index.htmlの他にerror.htmlも用意しておくと良いと思います。Routerを使うことになりますが、初回のアクセスでは先ほど作成したindex.htmlを返す必要があります。トップページはもちろんのこと、どこか途中のページからアクセスやリロードされることもありえるため、全てのリクエストでindex.htmlを返すようにします。そこで、以下のようなコントローラを作成します。"index";}}allというメソッドは「index」という文字列を返しています。ここで指定した文字列はビューの名称を表しており、Springフレームワークが「src/main/resources/templates」配下から対応するHTMLを返します。このようにビューを返す場合は、@RestControllerではなく@Controllerというアノテーションをつけます。基本的には全てのURLに対してindex.htmlを返すのですが、スタイルシートや画像などの静的ファイルは除外しないとそれらが取得できなくなります。そこで、@GetMappingに指定するURLには、正規表現を使って「static/」から始まるURLを除外しています。通常staticフォルダの配下に置いたファイルは、以下のように「static」をつけないURLでアクセスできます。(静的ファイルの配置場所)src/main/resources/static/img/favicon.ico(静的ファイルのURL)sample.com/img/favicon.icoそれでは先ほどの正規表現が機能しないので、application.ymlを修正して静的ファイルのURLに「static」がつくようにします。