Backend
home

2024. 8. 7 - 실습 코드 정리

폴더 구조
├── mvnw ├── mvnw.cmd ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── news │ │ │ ├── controller │ │ │ │ ├── ListController.java │ │ │ │ ├── NewsController.java │ │ │ │ ├── NewsServlet.java │ │ │ │ └── ViewController.java │ │ │ ├── dao │ │ │ │ ├── NewsDAO.java │ │ │ │ └── NewsDAOImpl.java │ │ │ ├── model │ │ │ │ └── News.java │ │ │ ├── service │ │ │ │ ├── NewServiceImpl.java │ │ │ │ └── NewsService.java │ │ │ └── util │ │ │ └── ConnectionPool.java │ │ ├── resources │ │ └── webapp │ │ ├── WEB-INF │ │ │ ├── lib │ │ │ │ ├── jstl-1.2.jar │ │ │ │ └── mysql-connector-j-8.3.0.jar │ │ │ └── views │ │ │ ├── NewsList.jsp │ │ │ └── NewsView.jsp │ │ └── style.css │ └── test │ ├── java │ └── resources └── target ├── classes │ └── com │ └── news │ ├── controller │ │ ├── ListController.class │ │ ├── NewsController.class │ │ ├── NewsServlet.class │ │ └── ViewController.class │ ├── dao │ │ ├── NewsDAO.class │ │ └── NewsDAOImpl.class │ ├── model │ │ └── News.class │ ├── service │ │ ├── NewServiceImpl.class │ │ └── NewsService.class │ └── util │ ├── ConnectionPool$DBPool.class │ └── ConnectionPool.class ├── generated-sources │ └── annotations └── news-1.0-SNAPSHOT ├── META-INF │ └── MANIFEST.MF ├── WEB-INF │ ├── classes │ │ └── com │ │ └── news │ │ ├── controller │ │ │ ├── ListController.class │ │ │ ├── NewsController.class │ │ │ ├── NewsServlet.class │ │ │ └── ViewController.class │ │ ├── dao │ │ │ ├── NewsDAO.class │ │ │ └── NewsDAOImpl.class │ │ ├── model │ │ │ └── News.class │ │ ├── service │ │ │ ├── NewServiceImpl.class │ │ │ └── NewsService.class │ │ └── util │ │ ├── ConnectionPool$DBPool.class │ │ └── ConnectionPool.class │ ├── lib │ │ ├── jstl-1.2.jar │ │ ├── mysql-connector-j-8.3.0.jar │ │ ├── tomcat-dbcp-9.0.89.jar │ │ └── tomcat-juli-9.0.89.jar │ └── views │ ├── NewsList.jsp │ └── NewsView.jsp └── style.css
HTML
복사
util > ConnectionPool
package com.news.util; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import java.sql.Connection; import java.sql.SQLException; public class ConnectionPool { // 커넥션 풀 : DB와의 연결을 관리하는 도구 // 여러 개의 연결을 미리 만들어놓고 필요할 때마다 제공해준다. 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 = "xxxxxxx"; // 아파치에 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
복사
model > News
package com.news.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; @Data @AllArgsConstructor public class News { private int id; private String title; private String img; private String date; private String content; public News(String title, String img, String content) { this.title = title; this.img = img; this.content = content; } }
Java
복사
controller > NewController (인터페이스)
package com.news.controller; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public interface NewsController { void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException; }
Java
복사
controller > NewServlet
package com.news.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("/news/*") @MultipartConfig(maxFileSize = 1024 * 1024 * 2, location = "/경로 설정") public class NewsServlet extends HttpServlet { private static final long serialVersionUID = 1L; private Map<String, NewsController> controllerMap = new HashMap<>(); // 페이지 추가시 여기에 추가해준다 public NewsServlet() { controllerMap.put("/news/newsList", new ListController()); controllerMap.put("/news/newsView", new ViewController()); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String requestURI = req.getRequestURI(); NewsController controller = controllerMap.get(requestURI); if (controller == null) { // controller = controllerMap.get("/news/newsList"); // resp.setStatus(HttpServletResponse.SC_NOT_FOUND); // return; } controller.process(req, resp); } }
Java
복사
controller > ListController
package com.news.controller; import com.news.model.News; import com.news.service.NewServiceImpl; import com.news.service.NewsService; 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 NewsController { NewsService ns = new NewServiceImpl(); @Override public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { try { String action = req.getParameter("action"); if (action != null && action.equals("addNews")) { System.out.println("기사 추가"); ns.addNews(req); res.sendRedirect(req.getRequestURI()); return; // 중복 응답 방지 위해 return 처리 } else if (action != null && action.equals("deleteNews")) { System.out.println("기사 삭제"); ns.deleteNews(req); res.sendRedirect(req.getRequestURI()); // 재연결 return; } else { System.out.println("기사 전체 보기"); List<News> list = ns.getAll(); req.setAttribute("newsList", list); } } catch (Exception e) { e.printStackTrace(); req.setAttribute("error", e.getMessage()); } String view = "/WEB-INF/views/NewsList.jsp"; req.getRequestDispatcher(view).forward(req, res); } }
Java
복사
controller > ViewController
package com.news.controller; import com.news.model.News; import com.news.service.NewServiceImpl; import com.news.service.NewsService; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class ViewController implements NewsController { NewsService ns = new NewServiceImpl(); @Override public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { try { String action = req.getParameter("action"); if (action != null && action.equals("modifyNews")) { ns.modifyNews(req); res.sendRedirect("/news/newsView?id=" + req.getParameter("id")); return; } else { News news = ns.getNews(req); req.setAttribute("news", news); } } catch (Exception e) { res.sendRedirect("/news/newsList"); return; } String view = "/WEB-INF/views/NewsView.jsp"; req.getRequestDispatcher(view).forward(req, res); } }
Java
복사
service > NewsService (인터페이스)
package com.news.service; import com.news.model.News; import javax.servlet.http.HttpServletRequest; import java.util.List; public interface NewsService { // 뉴스 기사 추가 void addNews(HttpServletRequest req) throws Exception; // 뉴스 기사 전체 보기 List<News> getAll() throws Exception; // 뉴스 기사 보기 News getNews(HttpServletRequest req) throws Exception; // 뉴스 기사 삭제 void deleteNews(HttpServletRequest req) throws Exception; // 뉴스 기사 수정 void modifyNews(HttpServletRequest req) throws Exception; }
Java
복사
service > NewsServiceImpl
package com.news.service; import com.news.dao.NewsDAO; import com.news.dao.NewsDAOImpl; import com.news.model.News; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import java.util.List; public class NewsServiceImpl implements NewsService { NewsDAO newsDAO = new NewsDAOImpl(); @Override public void addNews(HttpServletRequest req) throws Exception { // 요청 파라미터 (title, img, content)를 가지고 News 객체 생성 String title = req.getParameter("title"); String content = req.getParameter("content"); System.out.println("제목과 내용 : " + title + ", " + 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); } // News 객체 생성 News news = new News(title, img, content); // DAO한테 DB에 넣어라~ 라고 시킨다. newsDAO.addNews(news); } // 전체 기사 조회 @Override public List<News> getAll() throws Exception { return newsDAO.getAll(); } // 특정 기사 불러오기 @Override public News getNews(HttpServletRequest req) throws Exception { int id = Integer.parseInt(req.getParameter("id")); return newsDAO.getNews(id); } // 특정 기사 삭제 @Override public void deleteNews(HttpServletRequest req) throws Exception { int id = Integer.parseInt(req.getParameter("id")); newsDAO.deleteNews(id); } // 특정 기사 수정 @Override public void modifyNews(HttpServletRequest req) throws Exception { // id 값 가져오기 int id = Integer.parseInt(req.getParameter("id")); News news = newsDAO.getNews(id); // 요청 파라미터를 가지고 News 객체 수정 String title = req.getParameter("title"); String content = req.getParameter("content"); news.setTitle(title); news.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); news.setImg(img); } // DAO한테 DB에 넣어라~ 라고 시킨다. newsDAO.modifyNews(news); } }
Java
복사
dao > NewsDAO (인터페이스)
package com.news.dao; import com.news.model.News; import java.sql.SQLException; import java.util.List; public interface NewsDAO { // 뉴스 기사 추가 void addNews(News news) throws Exception; // 뉴스 기사 전체 보기 List<News> getAll() throws Exception; // 뉴스 기사 보기 News getNews(int id) throws Exception; // 뉴스 기사 삭제 void deleteNews(int id) throws Exception; // 뉴스 기사 수정 void modifyNews(News news) throws SQLException; }
Java
복사
dao > NewsDAOImpl
package com.news.dao; import com.news.model.News; import com.news.util.ConnectionPool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class NewsDAOImpl implements NewsDAO { @Override public void addNews(News news) throws Exception { String sql = "INSERT INTO news (title, img, content) VALUES (?, ?, ?)"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setString(1, news.getTitle()); pstmt.setString(2, news.getImg()); pstmt.setString(3, news.getContent()); pstmt.executeUpdate(); } } @Override public List<News> getAll() throws Exception { String sql = "SELECT * FROM news"; List<News> newsList = 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("title"); String img = rs.getString("img"); String date = rs.getString("date"); String content = rs.getString("content"); News news = new News(id, title, img, date, content); newsList.add(news); } } return newsList; } @Override public News getNews(int id) throws Exception { String sql = "SELECT * FROM news WHERE id = ?"; News news = null; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setInt(1, id); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // while 이 아님 String title = rs.getString("title"); String img = rs.getString("img"); String date = rs.getString("date"); String content = rs.getString("content"); news = new News(id, title, img, date, content); } } return news; } @Override public void deleteNews(int id) throws Exception { String sql = "DELETE FROM news WHERE id = ?"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setInt(1, id); pstmt.executeUpdate(); } } @Override public void modifyNews(News news) throws SQLException { String sql = "UPDATE news SET title = ?, img = ?, date = NOW(), content = ? WHERE id = ?"; try ( Connection conn = ConnectionPool.DBPool.getDBPool(); PreparedStatement pstmt = conn.prepareStatement(sql); ) { pstmt.setString(1, news.getTitle()); pstmt.setString(2, news.getImg()); pstmt.setString(3, news.getContent()); pstmt.setInt(4, news.getId()); pstmt.executeUpdate(); } } }
Java
복사
views > NewsList.jsp
<%-- Created by IntelliJ IDEA. User: haminsung Date: 8/7/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"> </head> <body> <div class="container w-75 mt-5 mx-auto"> <h2 class="text-center mb-4">뉴스 목록</h2> <hr/> <c:choose> <c:when test="${not empty newsList}"> <ul class="list-group mb-4"> <c:forEach var="news" items="${newsList}" varStatus="s"> <li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"> <a href="/news/newsView?id=${news.getId()}">[${s.count}] ${news.getTitle()}</a> <div> <span>${news.getDate()}</span> <!-- x 버튼에 url 담아줌 --> <a href="/news/newsList?action=deleteNews&id=${news.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"> <div class="card card-body"> <form action="/news/newsList?action=addNews" 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="img" class="form-label">이미지</label> <input id="img" type="file" name="img" 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> <button class="btn btn-success mt-3">저장</button> </form> </div> </div> </div> </body> </html>
HTML
복사
views > NewsView.jsp
<%-- Created by IntelliJ IDEA. User: haminsung Date: 8/7/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> </head> <body> <div class="container mt-5 mx-auto"> <h2 class="text-center mb-4">${news.getTitle()}</h2> <hr/> <div class="card mx-auto"> <img src="/img/${news.getImg()}" alt="뉴스 이미지" /> <div class="card-body"> <h4 class="card-title">보도일자 : ${news.getDate()}</h4> <p class="card-text">내용 : ${news.getContent()}</p> </div> </div> <hr/> <a href="javascript:history.back()" class="btn btn-primary">👈🏻 뒤로 가기</a> <!-- 수정하기 버튼 만들어서 토글 형태로 수정 양식 나타나게 하기 --> <button class="btn btn-outline-success collapse-button" data-bs-toggle="collapse" data-bs-target="#modifyForm" aria-expanded="false" aria-controls="modifyForm"> 기사 수정 </button> </div> <div class="collapse" id="modifyForm"> <div class="card card-body"> <form action="/news/newsView?action=modifyNews" method="post" enctype="multipart/form-data"> <input type="hidden" name="id" value="${news.getId()}" /> <label for="title" class="form-label">제목</label> <input id="title" name="title" class="form-control" value="${news.getTitle()}" required /> <br/> <label for="img" class="form-label">이미지</label> <input id="img" type="file" name="img" class="form-control"/> <br/> <label for="content" class="form-label">기사 내용</label> <textarea id="content" rows="5" cols="50" name="content" class="form-control" required>${news.getContent()}</textarea> <button class="btn btn-success mt-3">저장</button> </form> </div> </div> </body> </html>
HTML
복사