当社の新人エンジニア研修のWebアプリケーションとMVCモデル

当社の新人エンジニア研修のWebアプリケーションでは、MVCモデルを理解することを当面のゴールとしています。なぜなら、皆さんが実務につくと使用するであろう各種フレームワークがMVCモデルを考慮して作られているからです。

フレームワークの例としては、JavaのSpring Framework、C#のASP.NET Core MVC、PythonのDjango、Ruby on Railsなどが有名です。

この章では、MVCモデルの概略を紹介して、この後の章のガイド的な役割を果たします。

1. MVCモデルとは?

WebアプリケーションにおけるMVCモデルとは、主に以下の3つの要素から構成されるモデルです。

  • Model:アプリケーションのビジネスロジックやデータを表す部分(通常のJavaクラスで実装)
  • View:入出力(ユーザー画面)を担当する部分(Spring BootではThymeleafなどのテンプレートエンジンを使ったHTMLファイルが典型的)
  • Controller:ユーザーからのリクエストを受け取り、処理(Model)に振り分けて、処理結果をViewに返す部分(Spring Bootでは@Controller@RestControllerといったアノテーションを付けたクラスで構成)

これらの頭文字を並べてMVCモデルと呼ばれています。

データの流れ(Spring Bootの場合)

  1. ブラウザからのリクエストが、Spring Bootアプリケーション内のControllerクラスに送られます。
  2. Controllerメソッドで必要に応じてModelのインスタンスを生成し、ビジネスロジックを実行したり、データベースアクセスを行ったりします。
  3. Controllerは処理結果のデータをModelオブジェクトModelMap、あるいはmodel.addAttribute()などを使ってViewに渡します。
  4. View(Thymeleafなど) がControllerから渡されたデータを利用してHTMLをレンダリングし、最終的にブラウザへレスポンスとして返します。

MVCモデルの概念を図示すると、以下のようになります。(イメージ図)

[Browser] 
   ↓ (Request)
[Controller] -- (use) --> [Model]
   ↓ (forward data to)
[View] (HTML+データ) 
   ↓ (Response)
[Browser]

MVCモデルとは?

「Mのモデル」と「MVC“モデル”」はややこしいのですが、MVC“パターン” と呼ぶことで区別することがあります。MVCモデル(パターン)はデザインパターンの一種で、過去のエンジニアが解決してきた設計のベストプラクティスがまとめられています。あとでDAO【Data Access Object】パターンなども学ぶ予定です。


2. なぜMVCモデルなのか?

MVCモデルが考案された理由を一言で説明すると、画面とロジックを別々に開発するためです。

皆さんが目にするWebアプリケーションは、画面(UI)がとてもオシャレで洗練されていると思います。それもそのはず、それらの画面はプロのデザイナーが作っているからです。

一方、そのWebアプリケーションの機能を支えるロジック(プログラム)はプロのITエンジニアが作っています。

デザインと機能、この両方を一人の人間が担当するのは困難です。また、画面(View)と処理(ModelとController)を分けることで、不具合発生時に原因を特定しやすくなり、システムの更新もしやすくなるのです。

そのような理由から、Webアプリケーションの主流はMVCモデルになっているわけです。


3. MVCモデルの簡単な例

ここからは、最も単純な例を使ってMVCモデルを紹介しましょう。
以下は、Spring Bootプロジェクトにおけるサンプル構成例です(イメージ):

my-mvc-sample
 ┣ src
 ┃ ┣ main
 ┃ ┃ ┣ java
 ┃ ┃ ┃ ┣ com.example.demo
 ┃ ┃ ┃ ┃ ┣ DemoApplication.java  (Spring Boot起動クラス)
 ┃ ┃ ┃ ┃ ┣ controller
 ┃ ┃ ┃ ┃ ┃ ┗ GameController.java (Controller)
 ┃ ┃ ┃ ┃ ┣ model
 ┃ ┃ ┃ ┃ ┃ ┗ Kazuate.java       (Model)
 ┃ ┃ ┗ resources
 ┃ ┃ ┃ ┗ templates
 ┃ ┃ ┃    ┣ index.html          (View)
 ┃ ┃ ┃    ┗ result.html         (View)
 ┗ ...(テスト、設定ファイルなど)...

この中で、

  • Modelmodel パッケージの Kazuate.java
  • Viewtemplates フォルダ内の index.html および result.html
  • Controllercontroller パッケージの GameController.java

に対応します。

Modelクラス(例:Kazuate.java)

JavaSEの卒業課題などで作成したような数当てゲームのクラスがあるとします。かなり簡略化して以下のようなクラスです。

package com.example.demo.model;

import java.util.Random;

public class Kazuate {
    private int answer;
    private String message;

    public Kazuate() {
        Random random = new Random();
        this.answer = random.nextInt(10);
    }

    public void checkTheAnswer(int guess) {
        if (answer == guess) {
            this.message = "あたり";
        } else if (answer > guess) {
            this.message = "もっと大きいよ";
        } else {
            this.message = "もっと小さいよ";
        }
    }

    public int getAnswer() {
        return answer;
    }

    public void setAnswer(int answer) {
        this.answer = answer;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "デバック用 [answer=" + answer + ", message=" + message + "]";
    }
}

確認してみましょう

  1. このクラス名はなんですか?
    あなたの答え:
  2. フィールドはいくつあって、型は何で、名前は何ですか?
    あなたの答え:
  3. コンストラクタでは何をしていますか?
    あなたの答え:
  4. メソッドはいくつありますか? それぞれのメソッドの引数、戻り値の型、処理の内容を答えてください。
    あなたの答え:
  5. toString()メソッドの役割は何でしたか?
    あなたの答え:

これらの質問に答えられない場合、「新人エンジニア研修向けJava解説」に戻って復習の必要があります。

動作テスト用クラス

以下のようなテストクラスで、コンソール上で動かしてみることもできます。

package com.example.demo.model;

import java.util.Scanner;

public class KazuateTest {
    public static void main(String[] args) {
        System.out.println("0-9の整数で数を当ててください!");
        Scanner sc = new Scanner(System.in);
        int guess = sc.nextInt();
        sc.close();

        Kazuate k = new Kazuate();
        k.checkTheAnswer(guess);

        System.out.println(k.getMessage());
        System.out.println(k);
    }
}

コンソールでの挙動を確かめたうえで、これを**Webアプリケーションとして動かす(移植する)**イメージで進めます。


4. Spring Bootでの画面(View)とController

これまでJavaSEのコンソールアプリとして動かしていたKazuateクラスを、Spring BootアプリケーションとしてWebブラウザで遊べるようにしてみましょう。

入力画面(View: index.html 例)

以下はThymeleafを想定したHTMLテンプレートファイルの例です。(src/main/resources/templates/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>数当てゲーム</title>
</head>
<body>
    <h3>0-9の整数で数を当ててください!</h3>
    <form th:action="@{/game}" method="get">
        <input type="text" size="10" name="guess">
        <button type="submit">送信</button>
    </form>
</body>
</html>

  1. ファイル名がindexとなっているHTMLを「ウェルカムページ」に設定するにはどうする?
    (Spring Bootでは特別な設定をしなくても / にアクセスした際に index.html が優先的に表示されますが、カスタムの設定が必要な場合はapplication.properties等で設定する方法もあります)
  2. このフォームのHTTPメソッドはなんですか? また、このメソッドの特徴はなんでしたか?
    あなたの答え:
  3. フォームの部品には何がいくつ使われていますか? また、リクエストパラメータは送信先で何という名前で扱えますか?
    あなたの答え:
  4. 送信ボタンを押した後のアドレスバー(URL)はどうなっていますか?
    あなたの答え:

th:action="@{/game}"は、Thymeleafの書き方で、実行時に/gameというパスを指します。method="get"と指定したので、ブラウザからGET /game?guess=5のようにリクエストが送られます(例:5を入力した場合)。


Controllerクラス(例:GameController.java)

Spring Bootでは、以下のように@Controllerアノテーションと@GetMappingなどを使って、Controllerを実装します。MVCモデルでいえばControllerです。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.demo.model.Kazuate;

@Controller
public class GameController {

    @GetMapping("/game")
    public String doGetGame(@RequestParam(name = "guess", required = false) Integer guess,
                            Model model) {
        if (guess == null) {
            // guessが未指定なら入力画面にリダイレクトするなど
            return "redirect:/";
        }

        Kazuate k = new Kazuate();
        k.checkTheAnswer(guess);

        // 'k'という名前でKazuateオブジェクトをViewに渡す
        model.addAttribute("k", k);

        // result.html を返す
        return "result";
    }
}

  1. このクラスの名前は何ですか?
    あなたの答え:
  2. アノテーション(@Controller、@GetMappingなど)は何を意味していますか?
    あなたの答え:
  3. メソッドはいくつありますか? 仮引数、戻り値、処理の内容を簡単に説明してください。
    あなたの答え:
  4. @RequestParamアノテーションの役割は何ですか?
    あなたの答え:
  5. model.addAttribute("k", k); は何のために行っていますか?
    あなたの答え:

出力画面(View: result.html 例)

MVCモデルでいえばViewにあたる部分です。Thymeleafを使う場合、コントローラからmodel.addAttribute()で渡されたオブジェクトのプロパティを表示できます。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>結果画面</title>
</head>
<body>
    <h1>ゲームの結果は!</h1>
    <p th:text="${k.message}"></p>
    <br>
    <p th:text="${k}"></p>
</body>
</html>

  • th:text="${k.message}" は、model.addAttribute("k", k) で渡された Kazuateオブジェクトから getMessage()を呼び出して表示します。
  • th:text="${k}" は、toString()メソッドの結果を表示します。

確認してみましょう

  • result.htmlを直接ブラウザで開いてみた場合はどうなるか?
    (Thymeleafテンプレートとしての置き換えが行われないので、そのままのHTMLかエラーになる場合がある)
    あなたの答え:
  • Spring Bootを起動し、/game?guess=5をブラウザで開いた場合
    GameControllerが動き、Kazuateのインスタンスを生成し、結果をresult.htmlに反映して返す
    あなたの答え:

5. リクエストパラメータとModel/ELの関係

研修内容がWebアプリケーションに入ると急にファイル数やフォルダ構成が増え、「なんだか複雑…」と感じるかもしれません。

しかし、本質は今までやってきた「入力(Input)、処理(Process)、出力(Output)」と同じです。
違うのは、コンソール上ではなくブラウザやHTMLファイルを使う点、ControllerやViewといった役割分担が明確になっている点です。

  • JavaSEで言えば、Scannerで入力を受け取り、クラスのメソッドを呼んで処理し、System.out.println()で出力していました。
  • Sprint Bootでは、<form>で入力を受け取り、Controllerメソッドで処理を呼び出し、Thymeleaf(HTML)で結果を表示します。

6. Spring Bootの起動・停止

Spring Bootアプリケーションは、DemoApplicationといったメインクラス@SpringBootApplicationを付与し、mainメソッドを実行することで内部的にTomcatサーバーが起動します。別途サーバーをインストールして操作する必要はありません。

  • 起動mvn spring-boot:run または Eclipse 等のIDEでプロジェクトを右クリックして「Spring Boot Application」として実行
  • 停止:実行中のプロセスを停止 (IDEなら停止ボタン、ターミナルなら Ctrl+C など)

7. <まとめ:隣の人に正しく説明できたらチェックを付けましょう>

  • MVC(Model-View-Controller)モデルを使ってWebアプリケーションを構築する
  • Model:ビジネスロジックやデータを表すJavaクラス
  • View:Thymeleaf(またはHTML,CSS,JavaScript)などでユーザーに画面を見せる
  • Controller:ユーザーのリクエストを受け取り、Modelを操作し、Viewへデータを渡す
  • 分業と保守性の面から、WebアプリケーションではMVCモデルが広く採用されている
  • MVCモデルを理解することが本研修のゴール
  • MVCモデルとは、通常のJavaクラスであるModel、入出力を担当するテンプレートファイル(View)、ユーザーからの入力を受けてModelに処理を振り分けたりViewに返したりするControllerの3つから構成するモデルである
  • MVCモデルが考案された理由は画面とロジックを別々に開発するためである
  • JavaSEで利用・作成してきたクラスもおおむねModelとして使用できる

今回はMVCモデルについて学びました。


「研修のゴールはMVCモデルを理解することです」 最後までお読みいただきありがとうございます。