본문 바로가기
05_Server (04. JSP 프로그래밍 구현)

[2-4] 공지사항서비스 수정요청

by moca7 2024. 9. 10.

 

 

 

ㅁ 공지사항서비스
- 공지사항서비스_목록조회요청
- 공지사항서비스_작성요청
- 공지사항서비스_수정요청
- 공지사항서비스_삭제요청

 

 

 

 

 

ㅁ 공지사항 수정페이지

 

 

 

- 공지사항 상세페이지는 만들지 않고, 하단에 바로 보여지게끔 구성했다. 

(부트스트랩 collapse를 이용해서 제목행을 클릭하면 내용행이 보여지게)

- 대신 loginUser != null && loginUser.getUserId().equals(n.getNoticeWriter()) 이런 조건으로

로그인 되어있고, 로그인한 유저아이디가 글 작성자 아이디와 같으면

'수정하기', '삭제하기' 버튼이 보여지게끔 noticeList.jsp를 구성했었다.

 

 

 

 

 

- '수정하기', '삭제하기' 버튼에 접근하려면 이미 조건문이 걸려있어서 updateNotice, deleteNotice 쿼리는 간단하다. 

 

 

 

 

ㅁ src > main > webapp > views > notice의 noticeList.jsp에 '수정하기' 버튼에 요청 url을 작성한다.

 

 
<%@ page import="java.util.List" %>
<%@ page import="com.br.web.notice.model.vo.Notice" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%
  List<Notice> list = (List<Notice>)request.getAttribute("list");
%>
 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

   <!-- Header, Nav start -->
   <%@ include file="/views/common/header.jsp" %>
   <!-- Header, Nav end -->

   <!-- Section start -->
   <section class="row m-3" style="min-height: 500px">

    <div class="container border p-5 m-4 rounded">

      <h2 class="m-4">공지사항 목록</h2>

      <% if(loginUser != null && loginUser.getStatus().equals("A")) { %>
      <div align="right">
        <a href="<%= contextPath %>/write.no" class="btn btn-secondary btn-sm">등록하기</a>
      </div>
      <% } %>

      <br>
      <table id="notice-list" class="table">
        <thead>
          <tr>
            <th width="100px">번호</th>
            <th width="600px">글제목</th>
            <th width="120px">작성자</th>
            <th>작성일</th>
          </tr>
        </thead>
        <tbody>
          <% if(list.isEmpty()) { %>
          <!-- case1. 조회된 공지글이 없을 경우 -->
          <tr>
            <td colspan="4" style="text-align:center">존재하는 공지사항글이 없습니다.</td>
          </tr>
          <% } else { %>
            <!-- case2. 조회된 공지글이 있을 경우 -->
            <% for(Notice n : list) { %>
            <tr class="board-title" data-toggle="collapse" data-target="#notice<%=n.getNoticeNo()%>">
              <td><%= n.getNoticeNo() %></td>
              <td><%= n.getNoticeTitle() %></td>
              <td><%= n.getNoticeWriter() %></td>
              <td><%= n.getRegistDt() %></td>
            </tr>
            <tr class="board-content collapse" id="notice<%=n.getNoticeNo()%>">
              <td colspan="4">
                <p class="border rounded p-3 w-75 mx-auto" style="min-height: 150px; white-space:pre;"><%= n.getNoticeContent() %></p>
               
                <% if(loginUser != null && loginUser.getUserId().equals(n.getNoticeWriter())) { %>
                <div align="center">
                  <a href="<%= contextPath %>/modify.no?no=<%= n.getNoticeNo() %>" class="btn btn-secondary btn-sm">수정하기</a>
                  <a href="#" class="btn btn-danger btn-sm">삭제하기</a>
                </div>
                <%--
                  * 공지사항 삭제하기 과제 *
                    삭제하기 버튼 클릭시 /delete.no 요청하도록
                    이때 삭제할 글번호 넘기기
                   
                    미리작성해둔 쿼리 실행 후
                   
                    성공일 경우 다시 목록페이지가 보여지도록, alert메세지로 성공메세지
                    실패일 경우 에러페이지 보여지도록, 에러메세지 출력되도록
                --%>
               
                <% } %>
              </td>
            </tr>
            <% } %>
          <% } %>

        </tbody>
      </table>
     
     
    </div>

   </section>
   <!-- Section end -->

   <!-- Footer start -->
   <%@ include file="/views/common/footer.jsp" %>
   <!-- Footer end -->

 </div>

</body>
</html>
 

 

 

- 공지사항 목록페이지에서 '수정하기' 버튼 클릭시 "/modify.no"라는 url mapping 값을 가지는 서블릿 클래스를 호출한다.

- '수정하기' 버튼은 원래 <button type="button" class="btn btn-secondary btn-sm">수정하기</button>이었다.

이걸 script 구문으로 요소를 선택해서 클릭이벤트를 부여해서 location.href 속성을 이용해도 되지만,

여기서는 a 태그로 바꾸고 부트스트랩을 이용해서 class에 btn을 줬다. 이러면 href 속성을 쓸 수 있다.

 

 

- '글번호' 데이터를 요청할 때 서블릿으로 넘겨줘야 한다.

공지사항 게시글마다 '수정하기' 버튼이 여러개가 있다.

공지사항 수정페이지에서 특정 공지사항 게시글의 글제목과 글내용이 보여지려면

특정 게시글의 '글번호'로 db로부터 글제목과 글내용을 조회해 와야 한다.

- 서블릿에서 응답페이지인 "공지사항 수정페이지"로 '글번호', '글제목', '글내용'을 보내줘야 한다.

- 사용자로부터 입력받는 값은 글제목과 글내용인데,

update 쿼리를 수행하려면 특정 글만 수정해야하므로 '글번호'가 필요하다. 

 

 

- 공지사항 목록페이지는 db로부터 조회해온 Notice 객체가 담긴 list를 반복문으로 순차 접근하면서 공지사항 목록을 만든다.

- <a href="<%= contextPath %>/modify.no?no=<%= n.getNoticeNo() %>" class="btn btn-secondary btn-sm">수정하기</a>

이때 각 Notice 객체마다 제목행과 내용행을 만드는데, 내용행의 '수정하기' 버튼 요청시 

서블릿 클래스를 호출하면서 이 Notice 객체의 '글번호'를 같이 넘기게 작성한다.

 

 

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/controller에 NoticeModifyController.java라는 서블릿을 만든다.

 

 
package com.br.web.notice.controller;

import java.io.IOException;

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 com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/modify.no")
public class NoticeModifyController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public NoticeModifyController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        int noticeNo = Integer.parseInt(request.getParameter("no")); // 수정할 글 번호
       
        Notice n = new NoticeService().selectNoticeByNo(noticeNo);
        // 글번호, 글제목, 글내용
 
 
       
        // 2. 응답
        //    ㄴ 응답페이지 : 수정페이지 (/views/notice/noticeModify.jsp)
        //    ㄴ 응답데이터 : 수정할 해당 게시글의 제목, 내용 (db로 부터 조회)
   
    }


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

- 요청시 전달된 값인 '글번호'를 request.getParameter() 메소드로 뽑는다.

무조건 문자열로 반환되기 때문에 int에 담으려면 형변환 과정이 필요하다.

- 이 '글번호'를 db로 넘겨서 이에 해당하는 '글제목', '글내용', '글번호'를 뽑아온다.

 

- 응답페이지인 공지사항 수정페이지에서는 '글번호'(update 쿼리용), '글제목'과 '글내용'이 필요하다.

 

 

 

 

 

 

ㅁ src > main > webapp > views > notice 폴더 우클릭 - new - Jsp File로 "noticeModify.jsp"를 만든다.

 

 

- src > main > webapp > views > notice 폴더의 "공지사항수정페이지.html"의 body 태그를 복붙해온다.

 

 

 

 

 

 

ㅁ NoticeService.java에 selectNoticeByNo()라는 메소드를 만든다.

 

 
package com.br.web.notice.model.service;

import java.sql.Connection;
import java.util.List;

import com.br.web.notice.model.dao.NoticeDao;
import com.br.web.notice.model.vo.Notice;

import static com.br.web.common.template.JDBCTemplate.getConnection;
import static com.br.web.common.template.JDBCTemplate.close;
import static com.br.web.common.template.JDBCTemplate.commit;
import static com.br.web.common.template.JDBCTemplate.rollback;

public class NoticeService {
   
    private NoticeDao nDao = new NoticeDao();
   
    public List<Notice> selectNoticeList(){
 
        Connection conn = getConnection();
        List<Notice> list = nDao.selectNoticeList(conn);
        close(conn);
        return list;
    }
   
    public int insertNotice(Notice n) {
 
        Connection conn = getConnection();
        int result = nDao.insertNotice(conn, n);
        if(result > 0) {
            commit(conn);
        }else {
            rollback(conn);
        }
        close(conn);
        return result;
    }
 
   
    public Notice selectNoticeByNo(int noticeNo) {
 
        Connection conn = getConnection();
        Notice n = nDao.selectNoticeByNo(conn, noticeNo);
        close(conn);
        return n;
 
    }

}

 

 

- dao로 Connection 객체와 '글번호'를 그대로 넘긴다.

- db에서 '글번호'는 PK라 유일하다. '글번호'로 조회하면 한 행(한 객체)이 조회된다.

'글번호', '글제목', '글내용'이 Notice 객체에 담겨서 돌아올 것이다.

 

 

 

 

 

 

ㅁ NoticeDao.java에 selectNoticeByNo()라는 메소드를 만든다.

 

 
package com.br.web.notice.model.dao;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.br.web.notice.model.vo.Notice;

import static com.br.web.common.template.JDBCTemplate.close;

public class NoticeDao {
 
   
    private Properties prop = new Properties();
   
    public NoticeDao() {
        try {
            prop.loadFromXML(new FileInputStream(NoticeDao.class.getResource("/db/mappers/notice-mapper.xml").getPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
   
    public List<Notice> selectNoticeList(Connection conn){

        // select => ResultSet (여러행, 여러Notice객체) => List<Notice>
        List<Notice> list = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rset = null;
        String sql = prop.getProperty("selectNoticeList");
       
        try {
            pstmt = conn.prepareStatement(sql);
            rset = pstmt.executeQuery();
           
            while(rset.next()) {
                list.add(new Notice(rset.getInt("notice_no")
                                  , rset.getString("notice_title")
                                  , rset.getString("notice_content")
                                  , rset.getString("user_id")
                                  , rset.getDate("regist_date")));
            }
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rset);
            close(pstmt);
        }
       
        return list;
       
    }
   
    public int insertNotice(Connection conn, Notice n) {

        // insert => 처리된 행수
        int result = 0;
        PreparedStatement pstmt = null;
        String sql = prop.getProperty("insertNotice");
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, n.getNoticeTitle());
            pstmt.setString(2, n.getNoticeContent());
            pstmt.setString(3, n.getNoticeWriter());
           
            result = pstmt.executeUpdate();
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pstmt);
        }
       
        return result;
       
    }
 
   
    public Notice selectNoticeByNo(Connection conn, int noticeNo) {

        // select => ResultSet (한행, 한개의 공지사항) => Notice
        Notice n = null;
        PreparedStatement pstmt = null;
        ResultSet rset = null;
 
        String sql = prop.getProperty("selectNoticeByNo");
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, noticeNo);
            rset = pstmt.executeQuery();
           
            if(rset.next()) {
                n = new Notice();
                n.setNoticeNo(rset.getInt("notice_no"));
                n.setNoticeTitle(rset.getString("notice_title"));
                n.setNoticeContent(rset.getString("notice_content"));
            }
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rset);
            close(pstmt);
        }
       
        return n;
       
    }
   
}

 

 

- Notice에 매개변수 3개짜리 생성자를 만들어도 되고, 기본생성자로 생성하고 setXXX 메소드로 담아서 보내도 된다.

 

 

 

 

 

 

ㅁ NoticeModifyController.java로 돌아온다.

 

 
package com.br.web.notice.controller;

import java.io.IOException;

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 com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/modify.no")
public class NoticeModifyController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public NoticeModifyController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        int noticeNo = Integer.parseInt(request.getParameter("no")); // 수정할 글 번호
       
        Notice n = new NoticeService().selectNoticeByNo(noticeNo); // 글번호, 글제목, 글내용
 
       
        // 2. 응답
        //    ㄴ 응답페이지 : 수정페이지 (/views/notice/noticeModify.jsp)
        //    ㄴ 응답데이터 : 수정할 해당 게시글의 제목, 내용 (db로 부터 조회)
 
        request.setAttribute("n", n);
        request.getRequestDispatcher("/views/notice/noticeModify.jsp").forward(request, response);
   
    }


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

 

 

- 공지사항 수정페이지로 이동은 처음이다. redirect로 이동할 수 없다.

- 단순 페이지 이동도 forward로 이동한다.

- forward로 이동시 응답데이터(응답페이지에서 필요한 데이터)는 request에 담아서 보낸다. 

request에 '글번호', '글제목', '글내용'이 담겨있는 Notice 객체를 담는다.

(따로 빼서 각각 request에 담아도 된다)

 

- 여기서 응답은 실패를 가정하지 않는다.

 

 

 

 

 

 

ㅁ /webApp/src/main/webapp/views/notice의 noticeModify.jsp(공지사항 수정페이지)를 아래와 같이 완성한다.

 

 

- 아래는 noticeModify.jsp다.

 
<%@ page import="com.br.web.notice.model.vo.Notice" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%
  Notice n = (Notice)request.getAttribute("n"); // 글번호, 글제목, 글내용
%>
 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

    <!-- Header, Nav start -->
    <%@ include file="/views/common/header.jsp" %>
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">공지사항 수정</h2>
       
        <form action="<%=contextPath %>/update.no" method="post" class="m-4">
          <input type="hidden" name="no" value="<%=n.getNoticeNo()%>">
          <table class="table">
            <tr>
              <th>제목</th>
              <td><input type="text" class="form-control" required name="title" value="<%= n.getNoticeTitle() %>"></td>
            </tr>
            <tr>
              <th>내용</th>
              <td><textarea rows="10" class="form-control" style="resize:none" name="content" required><%= n.getNoticeContent() %></textarea></td>
            </tr>
          </table>

          <br>

          <div align="center">
            <button type="submit" class="btn btn-primary btn-sm">수정하기</button>
            <button type="reset" class="btn btn-danger btn-sm">초기화</button>
          </div>

        </form>

     
      </div>

    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <%@ include file="/views/common/footer.jsp" %>
    <!-- Footer end -->

  </div>

</body>
</html>
 

 

 

- header, footer를 include한다.

- section 태그의 form 요소에 method 속성으로 "post"를, action 속성으로 "<%=contextPath %>/update.no"를 준다.

- 상단에 "Notice n = (Notice)request.getAttribute("n");  // 글번호, 글제목, 글내용"을 작성해 놓는다.

데이터를 꺼내는 구문은 상단에 작성하는 것이 좋다.

그리고 항상 db에서 조회해왔을 때 이 객체에 어떤 필드들에 값이 담겨있는지를 작성해야 좋다.

- Notice 클래스도 import 한다.

 

 

- 수정페이지이므로 이미 작성되어있던 글제목과 글내용이 보여져야 한다.

요청시 request에 담아서 보냈던 Notice 객체로부터 '글제목', '글내용'을 뽑는다.

 

 

update 쿼리를 실행하려면 글번호도 필요한데 사용자의 입력값으로는 글제목과 글내용만 있다.

- "/update.no"라는 url mapping 값을 가지는 서블릿을 호출하면서

hidden 타입의 input 요소로 '글번호'를 담아서 넘겨야 한다.

- '글번호'는 session의 loginUser에 담겨있지 않아서 서블릿에서 session에서 뽑아서 쓸 수 없다. 보내줘야 한다.

 

 

- 공지사항 수정페이지에서 '수정하기' 버튼은 form 요소 내에 위치하므로 submit 타입으로 작성했다.

 

 

 

 

 

 

 

 

- 서버 재시작, 브라우저 새로고침, 관리자로 로그인 후 

공지사항 목록페이지에서 내가 쓴 글에서 '수정하기' 버튼을 누르면 공지사항 수정페이지로 이동된다.

- 글제목과 글내용이 텍스트상자와 textarea에 이미 작성되어있다.

글제목, 글내용을 수정하거나 아니면 수정하지 않고 그대로도 '수정하기' 요청을 보낼 수 있다.

어찌됐든 각각의 텍스트 상자에 쓰여진 값이 넘어간다.

 

- SQL SYNTAX ERROR는 sql 부분만 보면 된다. 

 

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/controller에 NoticeUpdateController.java라는 서블릿 클래스를 만든다. 

 

- NoticeModifyController.java는 '공지사항 수정페이지'로 이동시키는 서블릿이었다.

- NoticeUpdateController.java는 db에 update 시키고 다시 공지사항 목록페이지를 띄워주는 서블릿이다.

- 한 서비스(기능)에서 2개의 dao 메소드를 호출하지 않고,

'공지사항 수정페이지로 이동'시키는 요청과 '공지사항 테이블에 update'하는 요청은 따로 나누어져 있다.

(한 요청이 아니고 각각 다른 요청이다)

 

 

 
package com.br.web.notice.controller;

import java.io.IOException;

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 com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/update.no")
public class NoticeUpdateController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public NoticeUpdateController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        request.setCharacterEncoding("UTF-8");
       
        Notice n = new Notice();
        n.setNoticeTitle(request.getParameter("title"));
        n.setNoticeContent(request.getParameter("content"));
        n.setNoticeNo(Integer.parseInt(request.getParameter("no")));
       
        int result = new NoticeService().updateNotice(n);
 
       
        // 2. 응답
        if(result > 0) { // 성공
           
        }else { // 실패
           
        }
   
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

 

 

- Notice 객체에 noticeNo 필드는 int로 선언되어 있다.

- post 방식으로 글제목, 글내용, 글번호를 넘겼다. 한글 데이터여서 인코딩을 해준다.

- request 객체에서 글제목, 글내용, 글번호를 뽑아서 Notice 객체에 담아서 service로 넘긴다. 

 

 

 

 

 

ㅁ NoticeService.java에 updateNotice() 메소드를 만든다.

 

 
package com.br.web.notice.model.service;

import java.sql.Connection;
import java.util.List;

import com.br.web.notice.model.dao.NoticeDao;
import com.br.web.notice.model.vo.Notice;

import static com.br.web.common.template.JDBCTemplate.getConnection;
import static com.br.web.common.template.JDBCTemplate.close;
import static com.br.web.common.template.JDBCTemplate.commit;
import static com.br.web.common.template.JDBCTemplate.rollback;

public class NoticeService {
   
    private NoticeDao nDao = new NoticeDao();
   
    public List<Notice> selectNoticeList(){
        Connection conn = getConnection();
        List<Notice> list = nDao.selectNoticeList(conn);
        close(conn);
        return list;
    }
   
    public int insertNotice(Notice n) {
        Connection conn = getConnection();
        int result = nDao.insertNotice(conn, n);
        if(result > 0) {
            commit(conn);
        }else {
            rollback(conn);
        }
        close(conn);
        return result;
    }
   
    public Notice selectNoticeByNo(int noticeNo) {
        Connection conn = getConnection();
        Notice n = nDao.selectNoticeByNo(conn, noticeNo);
        close(conn);
        return n;
    }
   
    public int updateNotice(Notice n) {
 
        Connection conn = getConnection();
        int result = nDao.updateNotice(conn, n);
 
        if(result > 0) {
            commit(conn);
        }else {
            rollback(conn);
        }
 
        close(conn);
        return result;
    }


}

 

 

 

 

 

 

 

ㅁ NoticeDao.java에 updateNotice() 메소드를 만든다.

 

 
package com.br.web.notice.model.dao;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.br.web.notice.model.vo.Notice;

import static com.br.web.common.template.JDBCTemplate.close;
 

public class NoticeDao {
   
    private Properties prop = new Properties();
   
    public NoticeDao() {
        try {
            prop.loadFromXML(new FileInputStream(NoticeDao.class.getResource("/db/mappers/notice-mapper.xml").getPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
   
    public List<Notice> selectNoticeList(Connection conn){

        // select => ResultSet (여러행, 여러Notice객체) => List<Notice>
        List<Notice> list = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rset = null;
        String sql = prop.getProperty("selectNoticeList");
       
        try {
            pstmt = conn.prepareStatement(sql);
            rset = pstmt.executeQuery();
           
            while(rset.next()) {
                list.add(new Notice(rset.getInt("notice_no")
                                  , rset.getString("notice_title")
                                  , rset.getString("notice_content")
                                  , rset.getString("user_id")
                                  , rset.getDate("regist_date")));
            }
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rset);
            close(pstmt);
        }
       
        return list;
       
    }
   
    public int insertNotice(Connection conn, Notice n) {

        // insert => 처리된 행수
        int result = 0;
        PreparedStatement pstmt = null;
        String sql = prop.getProperty("insertNotice");
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, n.getNoticeTitle());
            pstmt.setString(2, n.getNoticeContent());
            pstmt.setString(3, n.getNoticeWriter());
           
            result = pstmt.executeUpdate();
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pstmt);
        }
       
        return result;
       
    }
   
    public Notice selectNoticeByNo(Connection conn, int noticeNo) {

        // select => ResultSet (한행, 한개의 공지사항) => Notice
        Notice n = null;
        PreparedStatement pstmt = null;
        ResultSet rset = null;
        String sql = prop.getProperty("selectNoticeByNo");
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, noticeNo);
           
            rset = pstmt.executeQuery();
           
            if(rset.next()) {
                n = new Notice();
                n.setNoticeNo(rset.getInt("notice_no"));
                n.setNoticeTitle(rset.getString("notice_title"));
                n.setNoticeContent(rset.getString("notice_content"));
            }
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rset);
            close(pstmt);
        }
       
        return n;
       
    }
   
    public int updateNotice(Connection conn, Notice n) {

        // update => 처리된 행수
        int result = 0;
        PreparedStatement pstmt = null;
 
        String sql = prop.getProperty("updateNotice");
 
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, n.getNoticeTitle());
            pstmt.setString(2, n.getNoticeContent());
            pstmt.setInt(3, n.getNoticeNo());
            result = pstmt.executeUpdate();
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pstmt);
        }
       
        return result;
    }


}
 

 

 

 

 

 

 

ㅁ 다시 NoticeUpdateController.java로 돌아온다.

 

 
package com.br.web.notice.controller;

import java.io.IOException;

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 com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/update.no")
public class NoticeUpdateController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public NoticeUpdateController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        request.setCharacterEncoding("UTF-8");
       
        Notice n = new Notice();
        n.setNoticeTitle(request.getParameter("title"));
        n.setNoticeContent(request.getParameter("content"));
        n.setNoticeNo(Integer.parseInt(request.getParameter("no")));
       
        int result = new NoticeService().updateNotice(n);
 
       
        // 2. 응답
        if(result > 0) { // 성공
 
            // 응답페이지 : 다시 목록페이지
            // 응답데이터 : "성공적으로 공지사항이 수정되었습니다" alert메세지
 
            request.getSession().setAttribute("alertMsg", "성공적으로 공지사항이 수정되었습니다.");
            response.sendRedirect(request.getContextPath() + "/list.no");
           
        }else { // 실패
 
            // 응답페이지 : 에러페이지 (/web/views/common/errorPage.jsp")
            // 응답데이터 : "공지사항 변경 실패" 메세지
           
            request.setAttribute("msg", "공지사항 변경 실패");
            request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
        }
   
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

- 요청이 다 끝난 후에는 응답페이지는 뭘지, 거기서 필요한 응답데이터는 뭔지 생각할 수 있어야 한다.

- 수정페이지에서 '수정' 버튼을 누르면 다시 목록페이지가 뜨게끔한다. alert도 띄워준다.

공지사항 목록페이지는 이미 띄우는 서블릿("/list.no")이 있으므로 redirect로 이동한다.

 

- 실패부분은 직접 작성했다.

그런데 사실 실패할 일이 없다. 이미 존재하는 공지사항 게시글에서 '글번호'를 뽑는거라 실패할 일이 없다.

 

 

 

 

ㅁ 서버 재시작, 브라우저 새로고침, 관리자로 로그인 후 공지사항 목록페이지에서 내가 쓴 글을 '수정하기' 누르고 

공지사항 수정페이지에서 '수정하기'를 눌러서 요청을 보내본다.

 

 

 

- '부적합한 열 인덱스' 에러는 notice-mapper.xml의 한글 부분들을 물음표로 바꾸고 저장을 안해서 그렇다.

물음표에 값이 덜채워졌거나 물음표가 없을 때의 에러다.

 

 

- 저장하고 다시 해보면 alert가 뜨고, 공지사항 목록페이지가 띄워진다.

수정된 제목과 내용이 화면과 db에 보여진다.

 

 

 

 

 

 

ㅁ 그런데 글내용을 두줄로 입력하고 '수정하기'를 눌러도 한줄로 보여진다.

 

 

 

- 공지사항 수정페이지에서 textarea 안에 내용을 두 줄로 작성했다.

 

 

 

 

 

 

 

 

- 그러나 공지사항 목록페이지에는 두 줄이 아닌 한 줄로 보여진다.

db에서 데이터를 조회해서 뿌렸을 때 여러줄로 안보일 수 있다.

 

 

 

 

- 다시 수정하기를 누르면 두 줄로 보여진다.

- textarea에는 두 줄로 보여지지만, 공지사항 목록페이지에서는 글 내용을 <p>태그로 데이터를 뿌리고 있다.

<p> 태그 내에서는 개행이 안먹힌다. 줄바꿈이 적용 안된다.

 

 

 

 

 

- db에서 방금 수정한 데이터를 더블클릭해서 '연필 모양 아이콘'을 누르면 실제 저장된 데이터가 보여진다.

 

 

 

 

 

- DB에는 textarea에 쓰여진 개행까지 들어간 채로 데이터가 저장된다. <p> 태그의 문제다.

 

 

 

 

 

 

 

- f12로 Elements 탭을 열고, 요소를 찾아서 아래 Styles 탭에서 바꿔본다.

- <p> 태그에 white-space라는 속성이 있다.

 

 

 

 

 

 

- 글내용이 보여지는 <p>태그를 선택한다.

인라인 스타일로 "min-height: 150px;"만 있는데, "white-space: pre;"를 여기서 작성한다.

- white-space를 pre로 주면 줄바꿈이 적용된다. 

 

- <pre> 태그 안에 작성하면 개행이든 공백이든 다 적용된다.

<p> 태그 안에서는 개행과 공백이 안먹힌다. '<br>' 태그나 '&nbsp' 등을 써야한다.

- <p> 태그에 "white-space: pre;" 스타일을 적용하면,

<p> 태그의 텍스트가 <pre> 태그처럼 공백과 줄바꿈을 그대로 유지하여 표시된다.

 

 

 

 

 

white-space

 

- white-space: pre는 CSS의 white-space 속성 중 하나로, 텍스트의 공백과 줄바꿈을 그대로 유지하여 표시하도록 하는 속성 값입니다.

- white-space: normal (기본값): 여러 개의 공백은 하나의 공백으로 처리되며, 줄바꿈은 무시됩니다.

 

- white-space 속성은 모든 HTML 요소에 사용할 수 있는 CSS 속성입니다.

- 모든 요소가 기본적으로 white-space 속성을 가지고 있지만, 각 요소마다 이 속성의 기본값이 다를 수 있습니다.

(1) 일반 블록 요소 (예: <div>, <p> 등): 기본값은 white-space: normal입니다.

(2) <pre> 요소: 기본값은 white-space: pre입니다.

 

 

 

 

 

ㅁ /webApp/src/main/webapp/views/notice의 noticeList.jsp

 

 
<%@ page import="java.util.List" %>
<%@ page import="com.br.web.notice.model.vo.Notice" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%
  List<Notice> list = (List<Notice>)request.getAttribute("list");
%>
 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

   <!-- Header, Nav start -->
   <%@ include file="/views/common/header.jsp" %>
   <!-- Header, Nav end -->

   <!-- Section start -->
   <section class="row m-3" style="min-height: 500px">

    <div class="container border p-5 m-4 rounded">

      <h2 class="m-4">공지사항 목록</h2>

      <% if(loginUser != null && loginUser.getStatus().equals("A")) { %>
      <div align="right">
        <a href="<%= contextPath %>/write.no" class="btn btn-secondary btn-sm">등록하기</a>
      </div>
      <% } %>

      <br>
      <table id="notice-list" class="table">
        <thead>
          <tr>
            <th width="100px">번호</th>
            <th width="600px">글제목</th>
            <th width="120px">작성자</th>
            <th>작성일</th>
          </tr>
        </thead>
        <tbody>
          <% if(list.isEmpty()) { %>
          <!-- case1. 조회된 공지글이 없을 경우 -->
          <tr>
            <td colspan="4" style="text-align:center">존재하는 공지사항글이 없습니다.</td>
          </tr>
          <% } else { %>
            <!-- case2. 조회된 공지글이 있을 경우 -->
            <% for(Notice n : list) { %>
            <tr class="board-title" data-toggle="collapse" data-target="#notice<%=n.getNoticeNo()%>">
              <td><%= n.getNoticeNo() %></td>
              <td><%= n.getNoticeTitle() %></td>
              <td><%= n.getNoticeWriter() %></td>
              <td><%= n.getRegistDt() %></td>
            </tr>
            <tr class="board-content collapse" id="notice<%=n.getNoticeNo()%>">
              <td colspan="4">
                <p class="border rounded p-3 w-75 mx-auto" style="min-height: 150px; white-space:pre;"><%= n.getNoticeContent() %></p>
               
                <% if(loginUser != null && loginUser.getUserId().equals(n.getNoticeWriter())) { %>
                <div align="center">
                  <a href="<%= contextPath %>/modify.no?no=<%= n.getNoticeNo() %>" class="btn btn-secondary btn-sm">수정하기</a>
                  <a href="#" class="btn btn-danger btn-sm">삭제하기</a>
                </div>
               
                <% } %>
              </td>
            </tr>
            <% } %>
          <% } %>

        </tbody>
      </table>
     
     
    </div>

   </section>
   <!-- Section end -->

   <!-- Footer start -->
   <%@ include file="/views/common/footer.jsp" %>
   <!-- Footer end -->

 </div>

</body>
</html>
 

 

 

- 공지사항 글내용을 보여주는 <p> 태그에 인라인 스타일로 "white-space: pre;"를 준다.

 

 

 

 

 

 

- 다시 목록페이지에서 새로고침하고 글제목을 눌러보면 입력된 데이터대로 여러줄로 보여진다.