Backend
home
📋

미니 게시판 프로젝트

생성일
2024/08/07 07:42
태그
개인 프로젝트

프로젝트 주제

JSP를 활용하여 누구나 이용 가능한 게시판 개발

프로젝트 구조

ERD

Search
boards
이름
type
null 여부
primary key 여부
VARCHAR(255)
N
VARCHAR(255)
N
VARCHAR(255)
N
VARCHAR(255)
N
DATETIME
N
DATETIME
Y

사용 기술 스택

Language With Appliocation:
Java, JSP, HTML, CSS, JavaScript
DBMS : MySQL
Java Version : Java 17
IDE : Intellij IDEA Ultimate Edition (2023.2)
Framework : bootstrap 5.0.2

프로젝트 기능

게시글 작성
게시판 기본 목록 조회
특정 사용자 게시글 조회
게시글 수정
게시글 삭제

도메인 생성

Board
package com.board.model; import lombok.AllArgsConstructor; import lombok.Data; import java.time.LocalDateTime; @Data @AllArgsConstructor public class Board { private int id; private String title; private String content; private String author; private String img; private LocalDateTime created_at; private LocalDateTime updated_at; }
Java
복사

DB 연결

package com.board.util; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import java.sql.Connection; import java.sql.SQLException; public class ConnectionPool { public static class DBPool { private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String DB_URL = "jdbc:mysql://localhost:3306/news_db"; private static final String DB_USER = "root"; private static final String DB_PASSWORD = "Dmove1122!"; // 아파치에 DBCP (DataBase Connection Pooling) 라이브러리에서 제공하는 클래스를 사용 static final BasicDataSource dbcp = new BasicDataSource(); static { dbcp.setDriverClassName(JDBC_DRIVER); dbcp.setUrl(DB_URL); dbcp.setUsername(DB_USER); dbcp.setPassword(DB_PASSWORD); // 연결 수 관리 dbcp.setInitialSize(10); // 초기 연결 수 10개 설정 dbcp.setMaxTotal(50); // 최대 연결 수 50개 설정 dbcp.setMaxIdle(20); // 최대 유휴 연결 수 20개 설정 dbcp.setMinIdle(5); // 최소 유휴 연결 수 5개 설정 } public static Connection getDBPool() throws SQLException { return dbcp.getConnection(); } } }
Java
복사

컨트롤러

BoardController
package com.board.controller; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public interface BoardController { void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException; }
Java
복사
ListController
package com.board.controller; import com.board.model.Board; import com.board.service.BoardService; import com.board.service.BoardServiceImpl; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; public class ListController implements BoardController { BoardService bs = new BoardServiceImpl(); @Override public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { try { String action = req.getParameter("action"); // 글 추가 if (action != null && action.equals("addWrite")) { System.out.println("글 추가"); bs.addWrite(req); // 중복 응답 방지 res.sendRedirect(req.getRequestURI()); return; } else if (action != null && action.equals("deleteWrite")) { System.out.println("글 삭제"); bs.deleteWrite(req); res.sendRedirect(req.getRequestURI()); return; } else { // 작성글 전체 보기 System.out.println("글 전체 보기"); List<Board> boardList = bs.getAll(); req.setAttribute("boardList", boardList); } } catch (Exception e) { e.printStackTrace(); req.setAttribute("error", e.getMessage()); } String view = "/WEB-INF/views/BoardList.jsp"; req.getRequestDispatcher(view).forward(req, res); } }
Java
복사
ViewController
package com.board.controller; import com.board.model.Board; import com.board.service.BoardService; import com.board.service.BoardServiceImpl; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class ViewController implements BoardController { BoardService bs = new BoardServiceImpl(); @Override public void process(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { // 특정 작성글 보기(우선적으로) + 글 수정 로직 구현 try { String action = req.getParameter("action"); if (action != null && action.equals("modifyWrite")) { bs.modifyWrite(req); res.sendRedirect("/board/boardView?id=" + req.getParameter("id")); return; } else { Board board = bs.getWrite(req); req.setAttribute("board", board); } } catch (Exception e) { e.printStackTrace(); res.sendRedirect("/board/boardList"); return; } String view = "/WEB-INF/views/boardView.jsp"; req.getRequestDispatcher(view).forward(req, res); } }
Java
복사
BoardServlet
package com.board.controller; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; @WebServlet("/board/*") @MultipartConfig(maxFileSize = 1024 * 1024 * 2, location = "/Users/haminsung/Documents/testimage") public class BoardServlet extends HttpServlet { private Map<String, BoardController> controllerMap = new HashMap<>(); // 페이지 추가 public BoardServlet() { controllerMap.put("/board/boardList", new ListController()); controllerMap.put("/board/boardView", new ViewController()); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String requestURI = req.getRequestURI(); BoardController controller = controllerMap.get(requestURI); if (controller == null); controller.process(req, resp); } }
Java
복사

서비스

BoardService
package com.board.service; import com.board.model.Board; import javax.servlet.http.HttpServletRequest; import java.util.List; // 서비스 public interface BoardService { // 작성글 추가 void addWrite(HttpServletRequest req) throws Exception; // 작성글 전체 보기 List<Board> getAll() throws Exception; // 특정 작성글 보기 Board getWrite(HttpServletRequest req) throws Exception; // 작성글 삭제 void deleteWrite(HttpServletRequest req) throws Exception; // 작성글 수정 void modifyWrite(HttpServletRequest req) throws Exception; }
Java
복사
BoardServiceImpl
package com.board.service; import com.board.dao.BoardDAO; import com.board.dao.BoardDAOImpl; import com.board.model.Board; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import java.util.List; public class BoardServiceImpl implements BoardService { BoardDAO boardDAO = new BoardDAOImpl(); @Override public void addWrite(HttpServletRequest req) throws Exception { // 제목, 내용, 작성자, 이미지 String title = req.getParameter("title"); String content = req.getParameter("content"); String author = req.getParameter("author"); System.out.println("제목과 내용, 작성자 : " + title + ", " + content + ", " + author); // img 가져오기 Part part = req.getPart("img"); String header = part.getHeader("content-disposition"); System.out.println(header); int start = header.indexOf("filename="); String img = header.substring(start + 10, header.length() - 1); // img 저장 if (img != null && !img.isEmpty()) { part.write(img); } // Board 객체 생성 Board board = new Board(title, content, author, img); // DAO에 요청하여 DB에 Insert하기 boardDAO.addWrite(board); } @Override public List<Board> getAll() throws Exception { return boardDAO.getAll(); } @Override public Board getWrite(HttpServletRequest req) throws Exception { int id = Integer.parseInt(req.getParameter("id")); return boardDAO.getWrite(id); } @Override public void deleteWrite(HttpServletRequest req) throws Exception { int id = Integer.parseInt(req.getParameter("id")); boardDAO.deleteWrite(id); } @Override public void modifyWrite(HttpServletRequest req) throws Exception { // id값 가져오기 int id = Integer.parseInt(req.getParameter("id")); Board board = boardDAO.getWrite(id); // 요청 파라미터를 가지고 Board 객체 수정 String title = req.getParameter("title"); String content = req.getParameter("content"); board.setTitle(title); board.setContent(content); // img 가져오기 Part part = req.getPart("img"); String header = part.getHeader("content-disposition"); System.out.println(header); int start = header.indexOf("filename="); String img = header.substring(start + 10, header.length() - 1); // img 저장 if (img != null && !img.isEmpty()) { part.write(img); board.setImg(img); } // DAO에 접근 boardDAO.modifyWrite(board); } }
Java
복사

DAO

BoardDAO
package com.board.dao; import com.board.model.Board; import javax.servlet.http.HttpServletRequest; import java.sql.SQLException; import java.util.List; // DAO (Data Access Object) public interface BoardDAO { // 작성글 추가 void addWrite(Board board) throws Exception; // 작성글 전체 보기 List<Board> getAll() throws Exception; // 특정 작성글 보기 Board getWrite(int id) throws Exception; // 특정 작성글 삭제 void deleteWrite(int id) throws Exception; // 특정 작성글 수정 void modifyWrite(Board board) throws SQLException; }
Java
복사
BoardDAOImpl
package com.board.dao; import com.board.model.Board; import com.board.util.ConnectionPool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; public class BoardDAOImpl implements BoardDAO { private static final String[] info = {"title", "content", "author", "img", "created_at", "updated_at"}; @Override public void addWrite(Board board) throws Exception { String sql = "INSERT INTO boards (title, content, author, img) VALUES (?, ?, ?, ?)"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setString(1, board.getTitle()); pstmt.setString(2, board.getContent()); pstmt.setString(3, board.getAuthor()); pstmt.setString(4, board.getImg()); pstmt.executeUpdate(); } } @Override public List<Board> getAll() throws Exception { String sql = "SELECT * FROM boards"; List<Board> boardList = new ArrayList<>(); try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery(); ) { while (rs.next()) { int id = rs.getInt("id"); String title = rs.getString(info[0]); String content = rs.getString(info[1]); String author = rs.getString(info[2]); String img = rs.getString(info[3]); String created_at = rs.getString(info[4]); Board boards = new Board(id, title, content, author, img, created_at); boardList.add(boards); } } return boardList; } @Override public Board getWrite(int id) throws Exception { String sql = "SELECT * FROM boards WHERE id = ?"; Board board = null; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setInt(1, id); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { String title = rs.getString(info[0]); String content = rs.getString(info[1]); String author = rs.getString(info[2]); String img = rs.getString(info[3]); String created_at = rs.getString(info[4]); String updated_at = rs.getString(info[5]); board = new Board(id, title, content, author, img, created_at, updated_at); } } return board; } @Override public void deleteWrite(int id) throws Exception { String sql = "DELETE FROM boards WHERE id = ?"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setInt(1, id); pstmt.executeUpdate(); } } @Override public void modifyWrite(Board board) throws SQLException { String sql = "UPDATE boards SET title = ?, content = ?, author = ?, img = ?, updated_at = NOW() WHERE id = ?"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setString(1, board.getTitle()); pstmt.setString(2, board.getContent()); pstmt.setString(3, board.getAuthor()); pstmt.setString(4, board.getImg()); // pstmt.setString(5, board.getUpdated_at()); pstmt.setInt(5, board.getId()); pstmt.executeUpdate(); } } }
Java
복사

JSP

BoardList.jsp
<%-- Created by IntelliJ IDEA. User: haminsung Date: 8/8/24 Time: 9:35 AM To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <title>미니 게시판</title> <link href="${pageContext.request.contextPath}/style.css" rel="stylesheet"> <link href="${pageContext.request.contextPath}/starter-template.css" rel="stylesheet"> <link href="${pageContext.request.contextPath}/custom.css" rel="stylesheet"> </head> <body> <nav th:fragment="fragment-nav" class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> <a class="navbar-brand">Java JSP 미니 게시판</a> <div class="collapse navbar-collapse" id="navbarsExampleDefault_admin"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/board/boardList">게시판 목록</a> </li> </ul> </div> </nav> </br></br> <div class="container w-75 mt-5 mx-auto"> <h2 class="text-center mb-4">게시판 목록</h2> <hr/> <c:choose> <c:when test="${not empty boardList}"> <ul class="list-group mb-4"> <c:forEach var="board" items="${boardList}" varStatus="b"> <li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"> <a href="/board/boardView?id=${board.getId()}">[${b.count}] ${board.getTitle()}</a> <div> <span>${board.getCreated_at()}</span> <!-- x 버튼에 url 담아줌 --> <a href="/board/boardList?action=deleteWrite&id=${board.getId()}"> <span class="badge bg-secondary"> &times; </span> </a> </div> </li> </c:forEach> </ul> </c:when> <c:otherwise> <div class="no-news-msg">작성된 글이 없습니다.</div> </c:otherwise> </c:choose> <c:if test="${error != null}"> <div class="alert alert-danger alert-dismissible fade show mt-3" role="alert"> 에러 발생 : ${error} <button class="btn-close" data-bs-dismiss="alert"></button> </div> </c:if> <button class="btn btn-outline-success collapse-button" data-bs-toggle="collapse" data-bs-target="#addForm" aria-expanded="false" aria-controls="addForm"> 게시글 작성 </button> <div class="collapse" id="addForm"> <br/> <div class="card card-body"> <form action="/board/boardList?action=addWrite" method="post" enctype="multipart/form-data"> <label for="title" class="form-label">제목</label> <input id="title" name="title" class="form-control" required/> <br/> <label for="content" class="form-label">글 내용</label> <textarea id="content" rows="5" cols="50" name="content" class="form-control" required></textarea> <br/> <label for="author" class="form-label">작성자</label> <input id="author" name="author" class="form-control" required/> <br/> <label for="img" class="form-label">이미지</label> <input id="img" type="file" name="img" class="form-control" required/> <button class="btn btn-success mt-3">저장</button> </form> </div> </div> </div> </body> </html>
HTML
복사
BoardView.jsp
<%-- Created by IntelliJ IDEA. User: haminsung Date: 8/8/24 Time: 9:35AM To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>글 보기</title> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <!-- Custom styles for this template --> <link href="${pageContext.request.contextPath}/starter-template.css" rel="stylesheet"> <link href="${pageContext.request.contextPath}/custom.css" rel="stylesheet"> <script> function updateAlarm() { alert("수정되었습니다."); } </script> </head> <body> <nav fragment="fragment-nav" class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> <a class="navbar-brand">Java JSP 미니 게시판</a> <div class="collapse navbar-collapse" id="navbarsExampleDefault_admin"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/board/boardList">게시판 목록</a> </li> </ul> </div> <div class="collapse navbar-collapse" id="navbarsExampleDefault"> </div> </nav> <div class="container mt-5 mx-auto"> <table class="table table-striped" style="text-align: center; border: 1px solid #dddddd;"> <thead> <tr> <th colspan="2" style="background-color: #eeeeee; text-align: center;"><h3>${board.getAuthor()} 의 게시글</h3></th> </tr> </thead> <tbody> <tr> <td>글 제목</td> <td>${board.getTitle()}</td> </tr> <tr> <td>작성자</td> <td>${board.getAuthor()}</td> </tr> <tr> <td>작성일자</td> <td>${board.getCreated_at()}</td> </tr> <tr> <td>수정일자</td> <td>${board.getUpdated_at()}</td> </tr> <tr> <td>내용</td> <td>${board.getContent()}</td> </tr> </tbody> </table> <br/> <h5>첨부 이미지</h5> <div class="card mx-auto"> <img src="/img/${board.getImg()}" alt="게시글 이미지"/> </div> <br/> <a href="javascript:history.back()" class="btn btn-dark">메인으로</a> <!-- 수정하기 버튼 만들어서 토글 형태로 수정 양식 나타나게 하기 --> <button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#modifyForm" aria-expanded="false" aria-controls="modifyForm"> 글 수정 </button> <form action="/board/boardList?action=deleteWrite" method="post" class="d-inline" onsubmit="return confirm('삭제하시겠습니까?')"> <input type="hidden" name="id" value="${board.getId()}"/> <button class="btn btn-danger">삭제</button> </form> </div> <div class="collapse" id="modifyForm"> <br/> <div class="card card-body"> <form action="/board/boardView?action=modifyWrite" method="post" enctype="multipart/form-data" > <input type="hidden" name="id" value="${board.getId()}"/> <label for="title" class="form-label">제목</label> <input id="title" name="title" class="form-control" value="${board.getTitle()}" required/> <br/> <label for="content" class="form-label">글 내용</label> <textarea id="content" rows="5" cols="50" name="content" class="form-control" value="${board.getContent()}" required></textarea> <br/> <label for="author" class="form-label">작성자</label> <input id="author" name="author" class="form-control" value="${board.getAuthor()}" required/> <br/> <label for="img" class="form-label">이미지</label> <input id="img" type="file" name="img" class="form-control"/> <button class="btn btn-success mt-3" onclick="updateAlarm()">수정하기</button> </form> </div> </div> </body> </html>
Java
복사

CSS

custom.css
@import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css); @import url(http://fonts.googleapis.com/earlyaccess/hanna.css); * { font-family: 'Nanum Gothic'; } h1 { font-family: Hanna; }
CSS
복사
starter-template.css
body { padding-top: 5rem; } .starter-template { padding: 3rem 1.5rem; text-align: center; }
CSS
복사
style.css
@charset "UTF-8"; .no-news-msg { background-color: lightyellow; color: black; padding: 10px; border: 1px solid gray; border-radius: 5px; text-align: center; margin-top: 20px; }
CSS
복사