2025.02.26 MVC패턴 공부
Delete
코드의 흐름
뷰(삭제버튼 클릭)
→ 컨트롤러(전처리)
→ 모델(DB에 쿼리 전달)
→ 컨트롤러(삭제 후 리다이렉션)
→ 뷰(최종확인)
1. 등록한 도서 삭제하기
editBook.jsp(뷰) 페이지 에서 책 상세정보의 삭제 a태그 클릭
<a href="#" onclick="deleteConfirm('<%=bk.getBookId()%>')" class="btn btn-danger" role="button">삭제 »></a>
연결된 자바스크립트 코드 실행
<script type="text/javascript">
function deleteConfirm(id) {
if (confirm("해당 도서를 삭제합니다!!") == true)
location.href = "editBook?edit=deleteprocess&id="+id;
else
return;
}
</script>
deleteprocess와 해당 도서의 id를 파라미터로 editBook 호출
@WebServlet("/editBook")
public class Book_Edit_Controller extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//edit = "update" "delete" "deleteprocess"
String edit = req.getParameter("edit");
String id=null;
RequestDispatcher ds=null;
BookRepository br = BookRepository.getInstance();
ArrayList<Book> ar = null;
if(edit.equals("deleteprocess")) {
id=req.getParameter("id");//ISBN1234
System.out.println("삭제아이디:"+id);
br.deleteBook(id);
ar = br.getAllBooks();
req.setAttribute("list", ar);
ds = req.getRequestDispatcher("book/books.jsp");
}
else {
ar = br.getAllBooks();
req.setAttribute("edit",edit); //생략해도 리퀘스트는 유지됨
req.setAttribute("books", ar);
ds = req.getRequestDispatcher("book/editBook.jsp");
}
ds.forward(req, resp);
호출된 컨트롤러에서 삭제를 실행하기 전 전처리 과정을 거친다.
파라미터를 변수에 담고 edit 값이 deleteprocess인지 확인(삭제와 수정 같은 폼 공유)
삭제하려는 책의 id값을 id에 담고 북리파지토리 br에 있는 deleteBook() 메서드를 id를 파라미터로 실행시킨다.
//delete(BookRepository)
public void deleteBook(String id) {
try {
conn = DBConnection.connection();
System.out.println("deleteBook : "+id);
String sql = "delete from book where b_id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.executeUpdate();
}catch(Exception e) {}
}
북리파지토리(모델)에서 id를 받아서 쿼리를 통해 DB에 존재하는 해당 ID를 가진 도서의 튜플을 삭제한다.
ar = br.getAllBooks();
req.setAttribute("list", ar);
ds = req.getRequestDispatcher("book/books.jsp");
}
ds.forward(req, resp);
DB에서 삭제가 끝난 후 다시 컨트롤러로 돌아와 현재 도서정보를 담고 있는 북리파지토리로 부터 현재 등록된 모든 도서의 정보를 가져오고, 어레이리스트를 ("list", ar) 키,값의 형태로 가지고 book/books.jsp로 이동해 뷰에 현재 등록된 도서를 뿌려준다.
2. 게시글 삭제하기
view.jsp(뷰)에서 게시글 삭제 a태그를 클릭한다.
<a href="BoardDeleteAction?num=<%=notice.getNum()%>&pageNum=<%=nowpage%>" class="btn btn-danger"> 삭제</a>
a태그의 호출에 의해 num과 pageNum 파라미터를 가지고 컨트롤러로 이동한다.
@WebServlet("/BoardDeleteAction")
public class Board_Delete_Controller extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int num = Integer.parseInt(req.getParameter("num"));
int pageNum = Integer.parseInt(req.getParameter("pageNum"));
BoardRepository br = BoardRepository.getInstance();
br.deleteBoard(num);
req.setAttribute("pageNum", pageNum);
resp.sendRedirect("BoardListAction?pageNum="+pageNum);
}
받은 num과 pageNum을 변수에 저장하고 보드리파지토리와 연결해 deleteBoard()메서드를 num 파라미터를 가지고 실행한다.
public void deleteBoard(int num) {
try {
conn = DBConnection.connection();
String sql = "delete from board where num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, num);
pstmt.executeUpdate();
}catch(Exception e) {}
보드리파지토리(모델)에서 삭제할 게시글의 번호 num을 가지고 DB에 연결해서 삭제쿼리를 실행해 DB에서 해당 게시글을 지워버린다.
req.setAttribute("pageNum", pageNum);
resp.sendRedirect("BoardListAction?pageNum="+pageNum);
}
다시 컨트롤러로 돌아와 pageNum(게시판페이지번호) 값을 request에 저장하고 해당 게시판주소+페이지번호로 이동한다.(뷰이동)
Update
코드의 흐름
뷰(수정버튼 클릭)
→ 컨트롤러(파라미터를 전처리 한 후 모델호출)
→ 모델(전달받은 값에 해당하는 데이터를 DB에서 찾아 반환)
→ 컨트롤러(클라이언트가 요구한 수정 폼과 현재 데이터를 클라이언트에게 전달)
→ 뷰(전달받은 폼에 수정사항을 적용하고 수정완료 버튼 클릭)
→ 컨트롤러(전달받은 파라미터를 전처리하고 모델 호출)
→ 모델(전달받은 값을 바탕으로 DB에 업데이트)
→ 컨트롤러(반영된 결과를 보여주기 위해 뷰 이동)
→ 뷰(클라이언트에게 수정 결과를 보여줌)
1. 도서 정보 업데이트
editBook.jsp(뷰)에서 수정 a태그를 클릭한다.
<a href="updateBook?id=<%=bk.getBookId()%>" class="btn btn-success" role="button"> 수정 »></a>
id 파라미터를 가지고 호출 받은 컨트롤러로 이동한다.
@WebServlet("/updateBook")
public class Book_Update_Controller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
BookRepository br = BookRepository.getInstance();
Book bk = br.getOneBook(id);
req.setAttribute("book", bk);
RequestDispatcher ds = req.getRequestDispatcher("book/updateBook.jsp");
ds.forward(req, resp);
}
컨트롤러로 이동하여 전달받은 id를 변수에 저장하고, id를 파라미터로 북리파지토리에 있는 getOneBook()함수를 실행한다.
public Book getOneBook(String id) {
Book bk = new Book();
//데이터베이스 연결
try {
conn = DBConnection.connection();
//쿼리작성 및 실행
String sql = "select * from book where b_id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
rs = pstmt.executeQuery();
//ResultSet --> DTO로 변환
if(rs.next()) {
bk.setBookId(rs.getString("b_id"));
bk.setName(rs.getString("b_name"));
bk.setUnitPrice(rs.getInt("b_unitPrice"));
bk.setAuthor(rs.getString("b_author"));
bk.setDescription(rs.getString("b_description"));
bk.setPublisher(rs.getString("b_publisher"));
bk.setCategory(rs.getString("b_category"));
bk.setUnitsInStock(rs.getInt("b_unitsInStock"));
bk.setReleaseDate(rs.getString("b_releaseDate"));
bk.setCondition(rs.getString("b_condition"));
bk.setFilename(rs.getString("b_fileName"));
}
System.out.println("북리파지토리 : "+bk.getFilename());
}catch (Exception e) {}
return bk;
}
북리파지토리(모델)에서 해당 id를 바탕으로 책의 정보를 DB에서 가져온 후, bk 변수에 담아준다.
req.setAttribute("book", bk);
RequestDispatcher ds = req.getRequestDispatcher("book/updateBook.jsp");
ds.forward(req, resp);
}
다시 컨트롤러로 돌아와 북리파지토리로 부터 받은 bk를 리퀘스트에 넣고 수정 폼을 클라이언트에게 보여주기 위해 book/updateBook.jsp로 이동한다.
<%@ page contentType="text/html; charset=utf-8"%>
<%@ page import="dto.Book" %>
<html>
<head>
<link rel ="stylesheet" href ="./resources/css/bootstrap.min.css" />
<%
Book bk = (Book)request.getAttribute("book");
%>
<title>도서 수정</title>
</head>
<body>
<div class="container py-4">
<jsp:include page="/menu.jsp"/>
<div class="p-5 mb-4 bg-body-tertiary rounded-3">
<div class="container-fluid py-5">
<h1 class="display-5 fw-bold">도서 수정</h1>
<p class="col-md-8 fs-4">Book Updating</p>
</div>
</div>
<div class="row align-items-md-stretch">
<div class="col-md-5">
<img src="./resources/images/<%=bk.getFilename()%>" alt="image" style="width: 100%" />
</div>
<div class="col-md-7">
<form name="newBook" action="updateBook" method="post" enctype ="multipart/form-data">
<div class="mb-3 row">
<label class="col-sm-2">도서코드</label>
<div class="col-sm-5">
<input type="text" name="bookId" id="bookId" class="form-control" value='<%=bk.getBookId()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">도서명</label>
<div class="col-sm-5">
<input type="text" name="name" id="name" class="form-control" value='<%=bk.getName()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">가격</label>
<div class="col-sm-5">
<input type="text" name="unitPrice" id="unitPrice"class="form-control" value='<%=bk.getUnitPrice()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">저자</label>
<div class="col-sm-5">
<input type="text" name="author" class="form-control" value='<%=bk.getAuthor()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">출판사</label>
<div class="col-sm-5">
<input type="text" name="publisher" class="form-control" value='<%=bk.getPublisher()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">출판일</label>
<div class="col-sm-5">
<input type="text" name="releaseDate" class="form-control" value='<%=bk.getReleaseDate()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">상세정보</label>
<div class="col-sm-8">
<textarea name="description" id="description" cols="50" rows="2"
class="form-control" placeholder="100자 이상 적어주세요"><%=bk.getDescription()%></textarea>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">분류</label>
<div class="col-sm-5">
<input type="text" name="category" class="form-control" value='<%=bk.getCategory()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">재고수</label>
<div class="col-sm-5">
<input type="text" name="unitsInStock" id="unitsInStock"class="form-control" value='<%=bk.getUnitsInStock()%>'>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">상태</label>
<div class="col-sm-8">
<input type="radio" name="condition" value="new "<%if(bk.getCondition().equals("new")){%>checked<%}%> >신규도서
<input type="radio" name="condition" value="old"<%if(bk.getCondition().equals("old")){%>checked<%}%> > 중고도서
<input type="radio" name="condition" value="eBook"<%if(bk.getCondition().equals("eBook")){%>checked<%}%> > E-Book
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">이미지</label>
<div class="col-sm-8">
<input type="file" name="bookImage" class="form-control">
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-offset-2 col-sm-10 ">
<input type="submit" class="btn btn-primary" value="등록 " >
</div>
</div>
</form>
</div>
</div>
<jsp:include page="/footer.jsp"/>
</div>
</body>
</html>
북리파지토리로 부터 받은 정보를 컨트롤러를 거쳐 폼에다가 현재 등록된 도서의 정보를 보여주고 클라이언트가 수정한 후 등록 버튼을 클릭하면
<form name="newBook" action="updateBook" method="post" enctype ="multipart/form-data">
입력한 데이터를 가지고 post 방식으로 updateBook을 호출한다.
@WebServlet("/updateBook")
public class Book_Update_Controller extends HttpServlet {
doget 생략
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String realFolder = req.getServletContext().getRealPath("/resources/images");
int maxSize = 5 * 1024 * 1024; //최대 업로드될 파일의 크기5Mb
String encType = "utf-8"; //인코딩 타입
MultipartRequest multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
String bookId = multi.getParameter("bookId");
String name = multi.getParameter("name");
String unitPrice = multi.getParameter("unitPrice");
String author = multi.getParameter("author");
String publisher = multi.getParameter("publisher");
String releaseDate = multi.getParameter("releaseDate");
String description = multi.getParameter("description");
String category = multi.getParameter("category");
String unitsInStock = multi.getParameter("unitsInStock");
String condition = multi.getParameter("condition");
String fileName = multi.getFilesystemName("bookImage");
//unitPrice가 문자열이므로 숫자인 price변수를 사용
int price;
if (unitPrice.isEmpty())
price = 0;
else
price = Integer.valueOf(unitPrice);
//unitsInStock 문자열 변수이므로 stock이라는 정수변수를 대체사용
long stock;
if (unitsInStock.isEmpty())
stock = 0;
else
stock = Long.valueOf(unitsInStock);
//전처리 - 하나의 객체로 묶음
Book bk = new Book();
bk.setBookId(bookId);
bk.setName(name);
bk.setUnitPrice(price);
bk.setAuthor(author);
bk.setPublisher(publisher);
bk.setReleaseDate(releaseDate);
bk.setDescription(description);
bk.setCategory(category);
bk.setUnitsInStock(stock);
bk.setCondition(condition);
bk.setFilename(fileName);
BookRepository br = BookRepository.getInstance();
br.updateBook(bk);
resp.sendRedirect("read_All");
}
호출받은 컨트롤러로 이동하여 수정 폼에서 입력한 데이터들을 다시 컨트롤러 내에 있는 변수에 담는 전처리 과정을 가진다.
이 과정에서 사진이 변경될수도 있기 때문에 MultipartRequest객체를 이용한다.
unitPrice와 unitsInStock 변수는 정수형태이기 때문에 입력되지 않으면 0으로 설정하는 조건문을 별도로 작성한다.
그후 다시 bk 객체를 생성하여 수정했던 데이터들을 bk 객체에 담은 후 bk를 파라미터로 북리파지토리안의 updateBook() 메서드를 실행한다.
public void updateBook(Book bk) {
try {
//데이터베이스 연결
conn = DBConnection.connection();
//쿼리 작성 및 실행
if(bk.getFilename()!=null) {
String sql = "UPDATE book SET b_name=?, b_unitPrice=?, b_author=?, b_description=?, b_publisher=?, b_category=?, b_unitsInStock=?, b_releaseDate=?, b_condition=?, b_fileName=? WHERE b_id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, bk.getName());
pstmt.setInt(2, bk.getUnitPrice());
pstmt.setString(3, bk.getAuthor());
pstmt.setString(4, bk.getDescription());
pstmt.setString(5, bk.getPublisher());
pstmt.setString(6, bk.getCategory());
pstmt.setLong(7, bk.getUnitsInStock());
pstmt.setString(8, bk.getReleaseDate());
pstmt.setString(9, bk.getCondition());
pstmt.setString(10, bk.getFilename());
pstmt.setString(11, bk.getBookId());
pstmt.executeUpdate();
}
else
{
String sql = "UPDATE book SET b_name=?, b_unitPrice=?, b_author=?, b_description=?, b_publisher=?, b_category=?, b_unitsInStock=?, b_releaseDate=?, b_condition=? WHERE b_id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, bk.getName());
pstmt.setInt(2, bk.getUnitPrice());
pstmt.setString(3, bk.getAuthor());
pstmt.setString(4, bk.getDescription());
pstmt.setString(5, bk.getPublisher());
pstmt.setString(6, bk.getCategory());
pstmt.setLong(7, bk.getUnitsInStock());
pstmt.setString(8, bk.getReleaseDate());
pstmt.setString(9, bk.getCondition());
pstmt.setString(10, bk.getBookId());
pstmt.executeUpdate();
}
}catch(Exception e) {}
}
updateBook 메서드는 bk객체를 전달받아 사진이 null 인지 아닌지에 따라 조건문으로 나누어 쿼리문을 이용해 DB의 데이터를 수정한다.
resp.sendRedirect("read_All");
}
그후 컨트롤러로 돌아와 책정보를 모두 불러오는 컨트롤러를 호출하고
@WebServlet("/read_All")
public class Book_ReadAll_controller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//전처리 : 아무것도 가져오지 않았음
//모델이동
BookRepository br = BookRepository.getInstance();
ArrayList<Book> arr = br.getAllBooks();
//뷰이동
req.setAttribute("list", arr);
RequestDispatcher ds = req.getRequestDispatcher("book/books.jsp");
ds.forward(req, resp);
}
호출된 컨트롤러에 의해 데이터베이스에서 책의 정보를 조회하여 뷰를 통해 수정사항을 보여주게 된다.
2. 게시글 수정하기
자기가 쓴 게시글에서 수정사항을 수정하고 a태그를 클릭한다.
<input type="submit" class="btn btn-success" value="수정 ">
<form name="newUpdate" action="BoardUpdateAction?num=<%=notice.getNum()%>&pageNum=<%=nowpage%>" method="post">
폼의 데이터 전달형식이 post 이기 때문에 BoardUpdateAction 서블릿을 가진 컨트롤러의 dopost로 이동한다.
@WebServlet("/BoardUpdateAction")
public class Board_Update_Controller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int num = Integer.parseInt(req.getParameter("num"));
int pageNum = Integer.parseInt(req.getParameter("pageNum"));
String name = req.getParameter("name");
String subject = req.getParameter("subject");
String content = req.getParameter("content");
String Rday = LocalDate.now().toString();
Board bd = new Board();
bd.setNum(num);
bd.setName(name);
bd.setSubject(subject);
bd.setContent(content);
bd.setRegist_day(Rday);
BoardRepository br = BoardRepository.getInstance();
br.updateBoard(bd);
resp.sendRedirect("BoardListAction?pageNum="+pageNum);
}
}
게시글을 폼 형태로 제공하기 때문에 수정후 수정 버튼을 누르면 게시글의 정보가 파라미터로 들어오게 된다.
전달받은 파라미터를 컨트롤러 내 변수에 저장하고 모델로 이동하기 위해 bd 객체를 생성한 후 변수를 담는다.
보드리파지토리에서 updateBoard() 메서드를 실행하기 위해 bd를 파라미터로 실행한다.
public void updateBoard(Board bd) {
try {
conn = DBConnection.connection();
String sql = "update board set subject=?, content=? where num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, bd.getSubject());
pstmt.setString(2, bd.getContent());
pstmt.setInt(3, bd.getNum());
pstmt.executeUpdate();
}catch (Exception e) {}
}
updateBoard() 메서드를 이용해 자동으로 값이 저장되는 id, 조회수를 제외한 변경사항을 DB에 쿼리로 수정해준다.
resp.sendRedirect("BoardListAction?pageNum="+pageNum);
}
그 후 다시 컨트롤러로 돌아와 게시글의 페이지 번호와 함께 이동한다.
게시글을 표시하는 창과 폼을 동일하게 하여 도서정보 수정때 보다 훨씬 간단한 단계로 수정할수 있다.
3. 조회수 오르게 하기
기존의 Board_View_Controller에 다음과 같은 코드를 추가한다.
br.updateHit(num);
이 코드에 의해 게시글을 클릭해서 Board_View_Controller가 호출될때 마다 updateHit()메서드가 실행된다.
보드리파지토리에 있는 updateHit() 메서드를 num을 파라미터로 실행한다.
public void updateHit(int num) {
try {
conn = DBConnection.connection();
//먼저 조회수를 가져와야한다.
String sql = "select hit from board where num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, num);
rs = pstmt.executeQuery();
int hit = 0;
if(rs.next()) {
hit = rs.getInt("hit");
hit++; //조회수 증가
}
sql = "update board set hit=? where num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, hit);
pstmt.setInt(2, num);
pstmt.executeUpdate();
}catch (Exception e) {}
}
게시글의 번호를 가져와 조회수를 불러오고, 조회수를 +1 해준다.
즉 게시글을 클릭할때 마다 컨트롤러에 있는 이 메서드가 실행되고 조회수가 +1된 상태로 DB에 업데이트 되는 방식이다.
req.setAttribute("board", bd);
req.setAttribute("num", num);
req.setAttribute("page", pageNum);
RequestDispatcher rd = req.getRequestDispatcher("board/view.jsp");
rd.forward(req, resp);
}
그런 다음 나머지 후열 코드로 인해 게시글페이지로 다시 이동하여 조회수가 오르는게 반영되는걸 뷰를 통해 보여준다.