0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java研修】Servlet/JSP入門⑩ 総合演習:掲示板アプリを作ろう

0
Posted at

はじめに

Servlet/JSP入門の 最終回総合演習:掲示板アプリ(BBS) の作成です。

これまで9回にわたって学んできたServlet/JSPの知識を総動員して、実際に動く掲示板Webアプリケーションを一から作り上げます。

このアプリで使う技術

技術 使いどころ
①② Servlet, HTTP コントローラー、リクエスト処理
JSP ビュー(画面表示)
フォーム処理 メッセージ投稿フォーム
セッション ユーザー名の保持
MVCパターン アプリ全体の設計
EL式+JSTL JSPの記述
フィルター 文字エンコーディング

1. プロジェクト概要

掲示板アプリの機能

機能 説明
メッセージ一覧表示 投稿されたメッセージを新しい順に表示
メッセージ投稿 名前と本文を入力して投稿
メッセージ削除 投稿済みメッセージを削除
名前の記憶 セッションで前回入力した名前を保持

完成イメージ

┌─────────────────────────────────────────────┐
│  掲示板                                      │
├─────────────────────────────────────────────┤
│  名前: [田中太郎    ]                         │
│  メッセージ:                                  │
│  [                                          ]│
│  [                                          ]│
│  [投稿する]                                   │
├─────────────────────────────────────────────┤
│  #3 田中太郎 (2025/04/15 14:30)              │
│  今日はいい天気ですね!                        │
│                                [削除]        │
│─────────────────────────────────────────────│
│  #2 鈴木花子 (2025/04/15 14:25)              │
│  Javaの勉強中です。                           │
│                                [削除]        │
│─────────────────────────────────────────────│
│  #1 佐藤次郎 (2025/04/15 14:20)              │
│  はじめまして!                               │
│                                [削除]        │
└─────────────────────────────────────────────┘

2. プロジェクト構成(MVCパターン)

BulletinBoard/
├── src/main/java/
│   ├── model/
│   │   ├── Message.java             ← Model: メッセージデータ
│   │   └── MessageDAO.java          ← Model: データアクセス
│   ├── servlet/
│   │   ├── MessageListServlet.java  ← Controller: 一覧表示
│   │   ├── MessagePostServlet.java  ← Controller: 投稿処理
│   │   └── MessageDeleteServlet.java← Controller: 削除処理
│   └── filter/
│       └── EncodingFilter.java      ← Filter: 文字コード設定
├── src/main/webapp/
│   ├── WEB-INF/
│   │   ├── jsp/
│   │   │   ├── board.jsp            ← View: 掲示板画面
│   │   │   └── error.jsp            ← View: エラー画面
│   │   └── web.xml
│   └── css/
│       └── style.css                ← スタイルシート

MVCの役割分担

ブラウザ
  │
  ▼
[EncodingFilter] ← 文字コード設定
  │
  ▼
[Servlet (Controller)] ← リクエスト処理、ビジネスロジック呼び出し
  │        │
  │        ▼
  │   [MessageDAO (Model)] ← データの保存・取得
  │        │
  │        ▼
  │   [Message (Model)] ← データを保持するJavaBean
  │
  ▼
[JSP (View)] ← EL式+JSTLでHTMLを生成
  │
  ▼
ブラウザ

3. Step 1: Model - Message クラス

メッセージを表すJavaBeanクラスを作成します。

Message.java

package model;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Message implements Serializable {

    private static final long serialVersionUID = 1L;

    private int id;
    private String author;
    private String content;
    private LocalDateTime postedAt;

    // デフォルトコンストラクタ
    public Message() {}

    // パラメータ付きコンストラクタ
    public Message(int id, String author, String content, LocalDateTime postedAt) {
        this.id = id;
        this.author = author;
        this.content = content;
        this.postedAt = postedAt;
    }

    // getter / setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getAuthor() { return author; }
    public void setAuthor(String author) { this.author = author; }

    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }

    public LocalDateTime getPostedAt() { return postedAt; }
    public void setPostedAt(LocalDateTime postedAt) { this.postedAt = postedAt; }

    // フォーマット済みの日時を返すメソッド
    public String getFormattedDate() {
        if (postedAt == null) return "";
        return postedAt.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"));
    }
}

ポイント:

  • Serializable を実装しているのは、セッションに保存する可能性があるためです
  • getFormattedDate() は EL式で ${message.formattedDate} としてアクセスできます

4. Step 2: Model - MessageDAO クラス

データの保存・取得を管理するDAOクラスです。今回は ArrayList でメモリ上にデータを保持します(アプリを再起動するとデータは消えます)。

MessageDAO.java

package model;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MessageDAO {

    // メッセージを保持するリスト(アプリ全体で共有)
    private static final List<Message> messages = new ArrayList<>();
    private static int nextId = 1;

    // 初期データ(テスト用)
    static {
        messages.add(new Message(nextId++, "管理人", "掲示板へようこそ!ルールを守って楽しく使いましょう。",
                LocalDateTime.of(2025, 4, 1, 9, 0)));
    }

    // 全件取得(新しい順)
    public List<Message> findAll() {
        List<Message> result = new ArrayList<>(messages);
        Collections.reverse(result);
        return result;
    }

    // IDで1件取得
    public Message findById(int id) {
        for (Message msg : messages) {
            if (msg.getId() == id) {
                return msg;
            }
        }
        return null;
    }

    // 投稿
    public synchronized void insert(String author, String content) {
        Message msg = new Message(nextId++, author, content, LocalDateTime.now());
        messages.add(msg);
    }

    // 削除
    public synchronized boolean delete(int id) {
        return messages.removeIf(msg -> msg.getId() == id);
    }

    // 件数取得
    public int count() {
        return messages.size();
    }
}

ポイント:

  • static フィールドでデータを保持しているため、すべてのリクエストで同じデータを参照できます
  • synchronized で排他制御し、同時アクセスでのデータ不整合を防いでいます
  • 実際のアプリケーションでは、ここをDBアクセスに置き換えます(第7回のJDBC連携参照)

5. Step 3: Filter - EncodingFilter

すべてのリクエスト・レスポンスにUTF-8を設定するフィルターです。

EncodingFilter.java

package filter;

import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

@WebFilter("/*")
public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {}
}

6. Step 4: Controller - Servlet クラス

6.1 MessageListServlet.java(一覧表示)

掲示板のメインページを表示するServletです。

package servlet;

import java.io.IOException;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import model.Message;
import model.MessageDAO;

@WebServlet("/board")
public class MessageListServlet extends HttpServlet {

    private MessageDAO dao = new MessageDAO();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // メッセージ一覧を取得
        List<Message> messages = dao.findAll();
        request.setAttribute("messages", messages);
        request.setAttribute("messageCount", dao.count());

        // セッションから前回の投稿者名を取得(フォームの初期値用)
        HttpSession session = request.getSession();
        String lastAuthor = (String) session.getAttribute("lastAuthor");
        if (lastAuthor != null) {
            request.setAttribute("lastAuthor", lastAuthor);
        }

        // エラーメッセージがあれば取得
        String error = (String) session.getAttribute("error");
        if (error != null) {
            request.setAttribute("error", error);
            session.removeAttribute("error");
        }

        // 成功メッセージがあれば取得
        String success = (String) session.getAttribute("success");
        if (success != null) {
            request.setAttribute("success", success);
            session.removeAttribute("success");
        }

        // JSPにフォワード
        request.getRequestDispatcher("/WEB-INF/jsp/board.jsp").forward(request, response);
    }
}

6.2 MessagePostServlet.java(投稿処理)

メッセージの投稿を処理するServletです。

package servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import model.MessageDAO;

@WebServlet("/board/post")
public class MessagePostServlet extends HttpServlet {

    private MessageDAO dao = new MessageDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpSession session = request.getSession();

        // パラメータ取得
        String author = request.getParameter("author");
        String content = request.getParameter("content");

        // バリデーション
        if (author == null || author.trim().isEmpty()) {
            session.setAttribute("error", "名前を入力してください。");
            response.sendRedirect(request.getContextPath() + "/board");
            return;
        }
        if (content == null || content.trim().isEmpty()) {
            session.setAttribute("error", "メッセージを入力してください。");
            response.sendRedirect(request.getContextPath() + "/board");
            return;
        }

        // 文字数チェック
        if (author.trim().length() > 20) {
            session.setAttribute("error", "名前は20文字以内で入力してください。");
            response.sendRedirect(request.getContextPath() + "/board");
            return;
        }
        if (content.trim().length() > 500) {
            session.setAttribute("error", "メッセージは500文字以内で入力してください。");
            response.sendRedirect(request.getContextPath() + "/board");
            return;
        }

        // メッセージを保存
        dao.insert(author.trim(), content.trim());

        // 投稿者名をセッションに保存(次回のフォーム初期値)
        session.setAttribute("lastAuthor", author.trim());
        session.setAttribute("success", "メッセージを投稿しました!");

        // PRGパターン:POST後にリダイレクトでGET
        response.sendRedirect(request.getContextPath() + "/board");
    }
}

ポイント: PRGパターン(Post/Redirect/Get) を使っています。POST処理後にリダイレクトすることで、ブラウザの更新ボタンで二重投稿されることを防ぎます。

6.3 MessageDeleteServlet.java(削除処理)

メッセージの削除を処理するServletです。

package servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import model.MessageDAO;

@WebServlet("/board/delete")
public class MessageDeleteServlet extends HttpServlet {

    private MessageDAO dao = new MessageDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpSession session = request.getSession();

        String idStr = request.getParameter("id");

        if (idStr == null || idStr.isEmpty()) {
            session.setAttribute("error", "削除対象が指定されていません。");
            response.sendRedirect(request.getContextPath() + "/board");
            return;
        }

        try {
            int id = Integer.parseInt(idStr);
            boolean deleted = dao.delete(id);

            if (deleted) {
                session.setAttribute("success", "メッセージを削除しました。");
            } else {
                session.setAttribute("error", "指定されたメッセージが見つかりませんでした。");
            }
        } catch (NumberFormatException e) {
            session.setAttribute("error", "不正なIDが指定されました。");
        }

        response.sendRedirect(request.getContextPath() + "/board");
    }
}

7. Step 5: View - JSPファイル

7.1 board.jsp(掲示板メイン画面)

EL式とJSTLを使ってHTMLを生成します。

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fn" uri="jakarta.tags.functions" %>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>掲示板</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>掲示板</h1>
            <p class="subtitle">メッセージ数: ${messageCount}件</p>
        </header>

        <!-- エラーメッセージ -->
        <c:if test="${not empty error}">
            <div class="alert alert-error">
                <c:out value="${error}" />
            </div>
        </c:if>

        <!-- 成功メッセージ -->
        <c:if test="${not empty success}">
            <div class="alert alert-success">
                <c:out value="${success}" />
            </div>
        </c:if>

        <!-- 投稿フォーム -->
        <section class="post-form">
            <h2>メッセージを投稿</h2>
            <form action="${pageContext.request.contextPath}/board/post" method="post">
                <div class="form-group">
                    <label for="author">名前(20文字以内)</label>
                    <input type="text" id="author" name="author"
                           value="<c:out value='${lastAuthor}' />"
                           maxlength="20" required placeholder="名前を入力">
                </div>
                <div class="form-group">
                    <label for="content">メッセージ(500文字以内)</label>
                    <textarea id="content" name="content" rows="4"
                              maxlength="500" required placeholder="メッセージを入力"></textarea>
                </div>
                <button type="submit" class="btn btn-primary">投稿する</button>
            </form>
        </section>

        <!-- メッセージ一覧 -->
        <section class="message-list">
            <h2>投稿一覧</h2>

            <c:choose>
                <c:when test="${not empty messages}">
                    <c:forEach var="msg" items="${messages}">
                        <article class="message-card">
                            <div class="message-header">
                                <span class="message-id">#${msg.id}</span>
                                <span class="message-author"><c:out value="${msg.author}" /></span>
                                <span class="message-date">${msg.formattedDate}</span>
                            </div>
                            <div class="message-content">
                                <c:out value="${msg.content}" />
                            </div>
                            <div class="message-actions">
                                <form action="${pageContext.request.contextPath}/board/delete"
                                      method="post"
                                      onsubmit="return confirm('このメッセージを削除しますか?');">
                                    <input type="hidden" name="id" value="${msg.id}">
                                    <button type="submit" class="btn btn-danger btn-sm">削除</button>
                                </form>
                            </div>
                        </article>
                    </c:forEach>
                </c:when>
                <c:otherwise>
                    <p class="no-messages">まだメッセージがありません。最初の投稿をしてみましょう!</p>
                </c:otherwise>
            </c:choose>
        </section>

        <footer>
            <p>Servlet/JSP 掲示板アプリ - Java研修 総合演習</p>
        </footer>
    </div>
</body>
</html>

7.2 error.jsp(エラー画面)

<%@ page contentType="text/html; charset=UTF-8" isErrorPage="true" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>エラー</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
    <div class="container">
        <h1>エラーが発生しました</h1>
        <div class="alert alert-error">
            <p>申し訳ありません。予期しないエラーが発生しました。</p>
            <c:if test="${not empty pageContext.exception}">
                <p>エラー内容: <c:out value="${pageContext.exception.message}" /></p>
            </c:if>
        </div>
        <p><a href="${pageContext.request.contextPath}/board" class="btn btn-primary">掲示板に戻る</a></p>
    </div>
</body>
</html>

8. Step 6: CSS スタイル

style.css

/* ===== リセット & ベース ===== */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', Meiryo, sans-serif;
    background-color: #f5f5f5;
    color: #333;
    line-height: 1.6;
}

/* ===== コンテナ ===== */
.container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

/* ===== ヘッダー ===== */
header {
    background-color: #2196F3;
    color: white;
    padding: 20px;
    border-radius: 8px 8px 0 0;
    margin-bottom: 0;
}

header h1 {
    font-size: 24px;
}

.subtitle {
    font-size: 14px;
    opacity: 0.8;
    margin-top: 5px;
}

/* ===== アラート ===== */
.alert {
    padding: 12px 16px;
    border-radius: 4px;
    margin-bottom: 16px;
    border: 1px solid;
}

.alert-error {
    background-color: #ffebee;
    color: #c62828;
    border-color: #ef9a9a;
}

.alert-success {
    background-color: #e8f5e9;
    color: #2e7d32;
    border-color: #a5d6a7;
}

/* ===== 投稿フォーム ===== */
.post-form {
    background-color: white;
    padding: 20px;
    border: 1px solid #ddd;
    margin-bottom: 20px;
}

.post-form h2 {
    font-size: 18px;
    margin-bottom: 16px;
    color: #333;
}

.form-group {
    margin-bottom: 12px;
}

.form-group label {
    display: block;
    font-weight: bold;
    margin-bottom: 4px;
    font-size: 14px;
    color: #555;
}

.form-group input[type="text"],
.form-group textarea {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
    font-family: inherit;
    transition: border-color 0.3s;
}

.form-group input[type="text"]:focus,
.form-group textarea:focus {
    outline: none;
    border-color: #2196F3;
    box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
}

/* ===== ボタン ===== */
.btn {
    display: inline-block;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    font-size: 14px;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.3s;
}

.btn-primary {
    background-color: #2196F3;
    color: white;
}

.btn-primary:hover {
    background-color: #1976D2;
}

.btn-danger {
    background-color: #f44336;
    color: white;
}

.btn-danger:hover {
    background-color: #d32f2f;
}

.btn-sm {
    padding: 4px 12px;
    font-size: 12px;
}

/* ===== メッセージ一覧 ===== */
.message-list {
    margin-top: 20px;
}

.message-list h2 {
    font-size: 18px;
    margin-bottom: 16px;
    color: #333;
}

.message-card {
    background-color: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 16px;
    margin-bottom: 12px;
    transition: box-shadow 0.3s;
}

.message-card:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.message-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
    font-size: 14px;
}

.message-id {
    color: #999;
    font-size: 12px;
}

.message-author {
    font-weight: bold;
    color: #2196F3;
}

.message-date {
    color: #999;
    font-size: 12px;
    margin-left: auto;
}

.message-content {
    font-size: 15px;
    line-height: 1.7;
    margin-bottom: 8px;
    white-space: pre-wrap;
    word-wrap: break-word;
}

.message-actions {
    text-align: right;
}

.no-messages {
    text-align: center;
    color: #999;
    padding: 40px;
    font-size: 16px;
}

/* ===== フッター ===== */
footer {
    text-align: center;
    margin-top: 40px;
    padding: 20px;
    color: #999;
    font-size: 12px;
}

9. Step 7: web.xml(エラーページ設定)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         version="6.0">

    <!-- ウェルカムファイル(index.jspからServletへ転送) -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- エラーページ設定 -->
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/jsp/error.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/jsp/error.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/WEB-INF/jsp/error.jsp</location>
    </error-page>

</web-app>

index.jsp(プロジェクトルート直下に配置):

<%-- /board Servletへ転送 --%>
<% response.sendRedirect(request.getContextPath() + "/board"); %>

💡 <welcome-file> は静的ファイル名を指定する仕組みのため、Servletの URL パターン(/board)を直接指定しても動作しません。index.jsp を経由してリダイレクトするのが一般的な方法です。


10. 動作確認

アプリの起動

  1. プロジェクトを右クリック → 実行サーバーで実行
  2. ブラウザで http://localhost:8080/BulletinBoard/board にアクセス

動作チェックリスト

チェック項目 確認方法
初期メッセージが表示される アクセス直後に管理人のメッセージが見える
メッセージが投稿できる 名前と本文を入力して「投稿する」ボタン
投稿が一覧に反映される 新しいメッセージが一番上に表示される
名前が記憶される 投稿後、フォームの名前欄に前回の名前が入る
メッセージが削除できる 削除ボタンで確認ダイアログ後に削除
空入力でエラーが出る 名前やメッセージを空にして投稿
日本語が正しく表示される 文字化けしない

11. エラーハンドリング

バリデーションの流れ

ユーザー入力
  │
  ├─ 名前が空 → エラーメッセージ → リダイレクト
  ├─ メッセージが空 → エラーメッセージ → リダイレクト
  ├─ 名前が20文字超 → エラーメッセージ → リダイレクト
  ├─ メッセージが500文字超 → エラーメッセージ → リダイレクト
  └─ OK → 保存 → 成功メッセージ → リダイレクト

XSS対策

JSTLの <c:out> タグを使うことで、ユーザー入力のHTMLエスケープを行っています。

<!-- 安全: HTMLエスケープされる -->
<c:out value="${msg.content}" />

<!-- 危険: エスケープされない(使わないこと) -->
${msg.content}

12. 拡張アイデア

この掲示板アプリをベースに、以下の機能を追加してみましょう。

拡張1:ログイン機能

追加要素 説明
ログイン画面 ユーザー名とパスワードの入力フォーム
認証処理 DBまたはメモリ上のユーザーリストと照合
セッション管理 ログイン状態をセッションで管理
認証フィルター 未ログインユーザーをリダイレクト
自分の投稿のみ削除可能 メッセージの投稿者とログインユーザーを比較

拡張2:返信機能

  • メッセージに対する返信(親ID + 子メッセージ)
  • スレッド形式の表示

拡張3:検索機能

  • キーワードでメッセージを検索
  • 投稿者名で絞り込み

拡張4:ページング

  • 1ページあたりの表示件数を制限(例:10件)
  • 「次へ」「前へ」のリンク

拡張5:データベース連携

MessageDAO を JDBC を使ったDB版に置き換えます(第7回参照)。

// Before: ArrayList版
private static final List<Message> messages = new ArrayList<>();

// After: DB版
public List<Message> findAll() {
    String sql = "SELECT id, author, content, posted_at FROM messages ORDER BY id DESC";
    // PreparedStatement で実行 ...
}
CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    author VARCHAR(20) NOT NULL,
    content VARCHAR(500) NOT NULL,
    posted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

全ソースコード一覧

最後に、このアプリのすべてのファイルを一覧にします。

ファイル 場所 役割
Message.java src/main/java/model/ メッセージのJavaBean
MessageDAO.java src/main/java/model/ データアクセスオブジェクト
MessageListServlet.java src/main/java/servlet/ 一覧表示(GET /board)
MessagePostServlet.java src/main/java/servlet/ 投稿処理(POST /board/post)
MessageDeleteServlet.java src/main/java/servlet/ 削除処理(POST /board/delete)
EncodingFilter.java src/main/java/filter/ 文字エンコーディング設定
board.jsp src/main/webapp/WEB-INF/jsp/ 掲示板メイン画面
error.jsp src/main/webapp/WEB-INF/jsp/ エラー画面
style.css src/main/webapp/css/ スタイルシート
web.xml src/main/webapp/WEB-INF/ アプリ設定

まとめ

学んだこと キーワード
MVCパターンの実践 Model(Bean + DAO)、View(JSP)、Controller(Servlet)
フォーム処理 POST送信、バリデーション、PRGパターン
セッション活用 名前の記憶、フラッシュメッセージ
EL式+JSTL スクリプトレットなしのJSP
フィルター 文字エンコーディングの一元管理
エラーハンドリング バリデーション、エラーページ、XSS対策
CSS Webアプリの見た目を整える

シリーズ完結!

全10回のServlet/JSP入門シリーズをお読みいただきありがとうございました。

学習の振り返り

テーマ 主なスキル
環境構築とはじめてのServlet Eclipse + Tomcat、@WebServlet、doGet
HTTPリクエストとレスポンス getParameter、ヘッダー、ステータスコード
JSPの基礎 スクリプトレット、ディレクティブ、フォワード
フォーム処理(GET/POST) HTML form、doPost、PRGパターン
セッション管理とCookie HttpSession、Cookie、ログイン管理
MVCパターン Servlet + JSP の役割分担、リダイレクト
JDBC連携 Connection、PreparedStatement、DAOパターン
EL式とJSTL ${式}、c:if、c:forEach、fmt:formatDate
フィルターとリスナー Filter、@WebFilter、ServletContextListener
総合演習:掲示板アプリ 全技術を統合したWebアプリ開発

次のステップ

このシリーズで学んだServlet/JSPの知識は、以下の学習の土台になります。

ステップ 内容
Spring Boot Servlet/JSPの上位フレームワーク。実務で最も使われる
JPA / MyBatis JDBCの上位ORM。SQLを直接書かずにDB操作
Thymeleaf JSPの後継的なテンプレートエンジン
REST API JSON形式のAPI開発
セキュリティ Spring Security、CSRF対策、XSS対策

Servlet/JSPの仕組みを理解していれば、Spring Bootなどのフレームワークが「何をしているのか」が見えるようになります。基礎をしっかり固めて、次のステップに進みましょう!


シリーズ一覧:Servlet/JSP入門

  1. 環境構築とはじめてのServlet
  2. HTTPリクエストとレスポンス
  3. JSPの基礎
  4. フォーム処理(GET/POST)
  5. セッション管理とCookie
  6. MVCパターン(Servlet + JSP)
  7. JDBC連携(データベース操作)
  8. EL式とJSTL
  9. フィルターとリスナー
  10. 👉 総合演習:掲示板アプリを作ろう(本記事)

著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?