•
폴더 구조
├── 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"> × </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
복사