프로젝트 실습 - 뉴스 기사 관리 서비스
•
뉴스 기사 제공 웹 서비스 제작
◦
부트스트랩을 활용한 화면 구현
◦
DB 연동 (뉴스 DataBase에 새로운 기사를 등록, 조회, 삭제가 가능)
◦
이미지 파일 첨부 포함
•
프로젝트 구조
-- news_db 생성
DROP DATABASE IF EXISTS `news_db`;
CREATE DATABASE news_db DEFAULT CHARACTER SET = 'utf8mb4' COLLATE = utf8mb4_0900_ai_ci;
-- 생성된 DB 선택
USE news_db;
-- news 테이블 생성
CREATE TABLE news (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
img VARCHAR(255) NOT NULL,
date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
content VARCHAR(255) NOT NULL
);
SQL
복사
•
설정 파일 경로 확인
~/Desktop/test/java/webjsp/news (main)$ ls
mvnw mvnw.cmd pom.xml src target
~/Desktop/test/java/webjsp/news (main)$ cd src
~/Desktop/test/java/webjsp/news/src (main)$ ls
main test
~/Desktop/test/java/webjsp/news/src (main)$ cd main
~/Desktop/test/java/webjsp/news/src/main (main)$ ls
java resources webapp
~/Desktop/test/java/webjsp/news/src/main (main)$ ls
java resources webapp
~/Desktop/test/java/webjsp/news/src/main (main)$ cd webapp
~/Desktop/test/java/webjsp/news/src/main/webapp (main)$ ls
WEB-INF lib style.css
~/Desktop/test/java/webjsp/news/src/main/webapp (main)$ cd WEB-INF
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF (main)$ ls
views
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF (main)$ cd ls
cd: no such file or directory: ls
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF (main)$ ls
lib views
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF (main)$ cd views
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF/views (main)$ ls
NewsList.jsp NewsView.jsp
~/Desktop/test/java/webjsp/news/src/main/webapp/WEB-INF/views (main)$
SQL
복사
•
폴더 구조
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── news
│ │ │ ├── controller
│ │ │ │ └── NewsController.java
│ │ │ ├── dao
│ │ │ │ └── NewsDAO.java
│ │ │ ├── model
│ │ │ │ └── News.java
│ │ │ ├── service
│ │ │ │ └── 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
│ └── HelloServlet.class
├── generated-sources
│ └── annotations
└── news-1.0-SNAPSHOT
├── META-INF
│ └── MANIFEST.MF
├── WEB-INF
│ ├── classes
│ │ └── com
│ │ └── news
│ │ └── HelloServlet.class
│ └── web.xml
├── index.jsp
└── lib
└── mysql-connector-j-8.3.0.jar
HTML
복사
•
bootstrap 링크 적용 - css, js 적용
<!-- CSS -->
<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>
<!-- JS -->
<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>
HTML
복사
•
model 패키지 News 클래스 작성
package com.news.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class News {
private int id;
private String title;
private String img;
private String date;
private String content;
}
Java
복사
•
NewsDAO
package com.news.dao;
import com.news.model.News;
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;
}
Java
복사
•
ListController
package com.news.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ListController implements NewsController {
@Override
public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String view = "/WEB-INF/views/NewsList.jsp";
req.getRequestDispatcher(view).forward(req, res);
}
}
Java
복사
•
ViewController
package com.news.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewController implements NewsController {
@Override
public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String view = "/WEB-INF/views/NewsView.jsp";
req.getRequestDispatcher(view).forward(req, res);
}
}
Java
복사
•
NewsController
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
복사
•
NewsServlet
package com.news.controller;
import javax.servlet.ServletException;
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/*")
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 {
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
복사
•
View 꾸미기
<%@ 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 w-75 mt-5 mx-auto">
<h2 class="text-center mb-4">뉴스 목록</h2>
<hr/>
<ul class="list-group mb-4">
<li class="list-group-item list-group-item-action
d-flex justify-content-between
align-items-center">
<a>[1] 안세영 배드민턴 협회 은퇴 예고</a>
<span>2024-08-07</span>
<a><span class="badge bg-secondary"> × </span></a>
</li>
<li class="list-group-item list-group-item-action
d-flex justify-content-between
align-items-center">
<a>[2] 오늘 파리올림픽 남자 클라이밍 리드 이도현 출전 17시</a>
<span>2024-08-07</span>
<a><span class="badge bg-secondary"> × </span></a>
</li>
</ul>
</div>
</body>
</html>
HTML
복사
•
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>
</head>
<body>
<div class="container w-75 mt-5 mx-auto">
<h2 class="text-center mb-4">뉴스 목록</h2>
<hr/>
<ul class="list-group mb-4">
<li class="list-group-item list-group-item-action
d-flex justify-content-between
align-items-center">
<a>[1] 안세영 배드민턴 협회 은퇴 예고</a>
<div>
<span>2024-08-07</span>
<a><span class="badge bg-secondary"> × </span></a>
</div>
</li>
<li class="list-group-item list-group-item-action
d-flex justify-content-between
align-items-center">
<a>[2] 오늘 파리올림픽 남자 클라이밍 리드 이도현 출전 17시</a>
<div>
<span>2024-08-07</span>
<a><span class="badge bg-secondary"> × </span></a>
</div>
</li>
</ul>
<div class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
에러 발생 : 에러 메시지~~~
<button class="btn-close" data-bs-dismiss="alert"></button>
</div>
<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>
<label for="title" class="form-label">제목</label>
<input id="title" name="title" class="form-control" required />
<label for="img" class="form-label">이미지</label>
<input id="img" type="file" name="img" class="form-control" required />
<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
복사