BASHA TECH
스프링으로 news 카테고리 만들어보기 본문
728x90
src/main/java
> com.big15.news.controller (패키지)
>NewsWebController (클래스)
package com.big15.news.controller;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.big15.news.dto.NewsDto;
import com.big15.news.model.NewsDao;
@Controller
@RequestMapping("/news")
public class NewsWebController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
final NewsDao dao;
@Autowired // DI => spring container control => IoC
public NewsWebController(NewsDao dao) {
this.dao = dao; // final 한 애를 생성자에서 초기화 할 수 있다.
}
// 파일 업로드 처리 => 디렉토리 설정
@Value("${news.imgdir}") // spring container에게 파일 속성값을 가져와서 fileDir에 넣어라는 명령어
String fileDir;
// 뉴스 등록
@PostMapping("/add")
public String addNews(@ModelAttribute NewsDto newsDto
, Model model, @RequestParam("file") MultipartFile file) {
logger.info("뉴스 등록 : " + newsDto);
// 저장 위치만 지정
File dest = new File(fileDir + "/" + file.getOriginalFilename());
logger.info("파일 저장 위치 : " + dest);
// 파일 저장
try {
file.transferTo(dest); // File에 unload
newsDto.setImg(dest.getName());
dao.insert(newsDto); // DB에 insert 처리
} catch (Exception e) {
e.printStackTrace();
logger.info("뉴스 추가 과정에서 문제 발생");
model.addAttribute("error", "뉴스가 정상적으로 등록되지 않았습니다.");
}
// 스프링이 문자열 안에 redirect:/ 로 되어있으면 sendRedirect 처리한다.
return "redirect:/news/list";
}
// 뉴스 삭제
@GetMapping("/delete/{newsid}")
public String deleteNews(@PathVariable int newsid, Model model) {
dao.delete(newsid);
return "redirect:/news/list";
}
// 뉴스 목록
@GetMapping("/list")
public String listNews(Model model) {
List<NewsDto> list;
try {
list = dao.select();
model.addAttribute("listNews", list); // request에서 add한다음 forward한다.
} catch (Exception e) {
e.printStackTrace();
} // DB에서 select한 것을 담고 (bean 처리)
return "news/listNews"; //return은 jsp가 받음 forwarding 해야함
}
// @GetMapping("/list")
// public String listNews(Model model) {
// List<NewsDto> list;
//
// list = dao.select(); // DB에서 select한 것을 담고 (bean 처리)
//
// model.addAttribute("listNews", list); // request에서 add한다음 forward한다.
// return "news/listNews"; //return은 jsp가 받음 forwarding 해야함
// }
// 뉴스 상세 => /news/{newsid}
@GetMapping("/{newsid}")
public String getNews(@PathVariable int newsid, Model model) {
NewsDto newsDto = dao.select(newsid); // DB에서 Select한것
model.addAttribute("newsDto", newsDto);
return "news/newsView"; // forward 한 것
}
}
com.big15.news.dto (패키지)
> NewsDto (클래스)
package com.big15.news.dto;
public class NewsDto {
private int newsid;
private String title;
private String img; // 파일명 저장
private String regdate;
private String content;
public int getNewsid() {
return newsid;
}
public void setNewsid(int newsid) {
this.newsid = newsid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
//alt+ s+s+s
@Override
public String toString() {
return "NewsDto [newsid=" + newsid + ", title=" + title + ", img=" + img + ", regdate=" + regdate + ", content="
+ content + "]";
}
}
com.big15.news.model (패키지)
> NewsDao (클래스)
package com.big15.news.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import org.springframework.stereotype.Component;
//import javax.naming.spi.DirStateFactory.Result;
import com.big15.news.dto.NewsDto;
@Component
public class NewsDao {
public static Connection getConnection() {
Connection conn = null;
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String userid = "ora_user";
String userpassword = "hong";
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, userid, userpassword);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void close(Connection conn, PreparedStatement pstmt) {
try {
pstmt.close();
conn.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
// 위에 있는 close랑 뭐가 다른 걸까? ? ? result 닫는 것
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
conn.close();
rs.close();
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 뉴스 추가 => insert => int
public void insert(NewsDto newsDto) { //throws Exception=> 안에서 에러나면 던진다는 것. 컨트롤러에서 에러를 해결을 해야함.
// 따라서 에러난 곳에서 해결하는 것이 좋다.
Connection conn = getConnection();
PreparedStatement pstmt = null; //웬만하면 connection뒤에 선언을 해주는 것이 맞다
StringBuilder sql = new StringBuilder();
sql.append("insert into news(newsid, title, img, content) ");
sql.append("values((select nvl(max(newsid)+1,1) from news), ");
sql.append("?,?,? )");
try {
pstmt = conn.prepareStatement(sql.toString()); //conn.preparedStatement~부터 처리 한다. 예외일 땐, catch로 빠져나감. 따라서 pstmt 자체가 아예 선언이 되지 않을 수 있다. //따라서 pstmt부터 선언을 밖에서 해줘야함.
pstmt.setString(1, newsDto.getTitle());
pstmt.setString(2, newsDto.getImg());
pstmt.setString(3, newsDto.getContent());
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, pstmt);
}
}
// 뉴스 전체 목록 추출 => select all
public ArrayList<NewsDto> select() throws Exception {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
ArrayList<NewsDto> newsArr = new ArrayList<>();
// 처리 코드
StringBuilder sql = new StringBuilder();
sql.append("SELECT newsid ");
sql.append(" , title ");
sql.append(" , TO_CHAR(regdate, 'YYYY-MM-DD HH24:mm:ss') AS regdate ");
sql.append(" FROM news ");
// try(conn;pstmt;rs){ //이렇게 쓰면 안에서 정상적인 처리가 끝나면 } finally {close(conn, pstmt);}를 안 쳐도 자동으로 종료 됨
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
rs = pstmt.executeQuery();
while(rs.next()) {
NewsDto n = new NewsDto();
n.setNewsid(rs.getInt("newsid"));
n.setTitle(rs.getString("title"));
n.setRegdate(rs.getString("regdate"));
newsArr.add(n);
}
} catch (Exception e) {
}
return newsArr;
}
// 뉴스 상세 추출 = > select where newsid =?
public NewsDto select(int newsid) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
NewsDto newsDto = new NewsDto();
// 처리 코드
StringBuilder sql = new StringBuilder();
sql.append("SELECT a.NEWSID ");
sql.append(" , a.TITLE ");
sql.append(" , a.img ");
sql.append(" , TO_CHAR(a.regdate, 'YYYY-MM-DD HH24:mm:ss') AS regdate ");
sql.append(" , a.CONTENT ");
sql.append(" FROM NEWS a ");
sql.append(" WHERE a.NEWSID = ? ");
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setInt(1, newsid);
rs = pstmt.executeQuery();
if(rs.next()) {
newsDto.setNewsid(rs.getInt("newsid"));
newsDto.setTitle(rs.getString("title"));
newsDto.setImg(rs.getString("img"));
newsDto.setRegdate(rs.getString("regdate"));
newsDto.setContent(rs.getString("content"));
} else {
System.out.println("해당 뉴스가 없다!!!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, pstmt, rs);
}
return newsDto;
}
//
// 뉴스 제목 검색 추출 = > select where 검색 제목
public ArrayList<NewsDto> select(String title) { //타이틀로 검색
ArrayList<NewsDto> newsArr = new ArrayList<>();
// ****** 처리 코드
return newsArr;
}
// 뉴스 삭제 = > delete where newsid = ?
public void delete(int newsid) {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "delete news where newsid = ?";
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setInt(1, newsid);
if(pstmt.executeUpdate() == 0) {
//insert, update, del은 int값으로 나오는 데 결과 값이 0이다. => 삭제된 것이 없다
System.out.println("삭제된 뉴스가 없다.");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
src/main/resources
> application.properties
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
server.port=62000
# 이미지 업로드 디렉토리 설정
news.imgdir=D:/big15/sts-dev/workspace/news/src/main/resources/static/img
# 파일 크기 설정
spring.servlet.multipart.max-file-size=1024MB
spring.servlet.multipart.max-request-size=1024MB
# static resource
#spring.mvc.static-path-pattern=/static/**
#spring.resources.static-locations=classpath:/static/
#spring.resources.add-mappings=true
src/main/resources
/static
/css, img, jsp 폴더 생성
src/main/resources
/templates
/html 폴더 생성
src
/main
/webapp
/WEB-INF 폴더 생성
/views 폴더 생성
/news 폴더 생성
> listNews.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>뉴스 관리 앱</title>
<!--https://www.w3schools.com/bootstrap5/bootstrap_get_started.php 여기 MaxCDN 복붙 -->
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container w-75 mt-5 mx-auto">
<h2>뉴스 목록</h2>
<hr>
<ul class="list-group">
<c:forEach items="${listNews }" var="news" varStatus="status">
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<a href="/news/${news.newsid }" class="text=decoration-none">
[${status.count}] [${news.title}] [${news.regdate}]
</a> <!-- 상세 -->
<a href="/news/delete/${news.newsid }">
<span class="badge bg-secondary">×</span>
</a> <!-- 삭제 -->
</li>
</c:forEach>
</ul>
<hr>
<c:if test="${error != null }">
<div class="alert alert-danger alert-dismissible fade show mt-3">
에러 발생 : ${error}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
</c:if>
<!-- 에러 처리 끝 -->
<!-- 뉴스 등록 시작 -->
<button class="btn btn-outline-info mb-3" type="button" data-bs-toggle="collapse"
data-bs-target="#addForm" aria-controls="addForm" aria-expanded="false">
뉴스 등록
</button>
<!--<input type="button" value="뉴스 등록">-->
<div class = "collapse" id="addForm">
<div class="card card-body">
<form action="/news/add" method="post" enctype="multipart/form-data">
<label class="form-label">제목</label>
<input type = "text" name="title" class="form-control">
<label class="form-label">이미지</label>
<input type = "file" name="file" class="form-control"> <!-- multipart방식이여야하고 method가 반드시 post여야함 -->
<label class="form-label">기사 내용</label>
<textarea rows="5" cols="50" class="form-control" name="content"></textarea>
<button type="submit" class="btn btn-success mt-3">저장</button>
</form>
</div>
</div>
<!-- 뉴스 등록 끝 -->
</div>
</body>
</html>
> newsView.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>뉴스 관리 앱</title>
<!--https://www.w3schools.com/bootstrap5/bootstrap_get_started.php 여기 MaxCDN 복붙 -->
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<!-- 전체 박싱 -->
<div class="container w-75 mt-5 mx-auto">
<h2>${newsDto.title}</h2>
<hr>
<!-- 카드 박싱 -->
<div class="card w-75 mx-auto">
<img class="card-img-top" src="/img/${newsDto.img }">
<div class="card-body">
<h4 class="card-title">${newsDto.regdate}</h4>
<p class="card-text">${newsDto.content}</p>
</div>
</div>
<a href = "javascript:history.back();" class="btn btn-primary"> Back </a>
</div>
</body>
</html>
728x90
반응형
'Computer > JSP' 카테고리의 다른 글
Decorator Pattern (0) | 2022.09.07 |
---|---|
프록시 패턴(Proxy Pattern) (0) | 2022.09.07 |
AOP & Filter & Listener (0) | 2022.09.05 |
this.getclass().getmethod (0) | 2022.09.01 |
Redirect (0) | 2022.09.01 |
Comments