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

[2-3] 공지사항서비스 작성요청

by moca7 2024. 9. 10.

 

 

 

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

 

 

 

 

 

ㅁ 공지사항 목록 페이지에서 '등록하기' 버튼을 누르면 공지사항 작성 페이지로 이동한다.

 

 

 

 

 

 

- 아래는 /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>
 

 

 

- 원래 '등록하기' 버튼은 <button type="button" class="btn btn-secondary btn-sm">등록하기</button>으로 되어 있었다.

이러면 a 태그가 아니기 때문에 href 속성을 사용할 수 없다.

별도로 script문으로 클릭 이벤트를 걸어서 location.href = "이동할 경로"로 url을 요청 했어야 했다.

 

- button 태그를 a 태그로 바꾸고 class에 btn을 줘서 버튼으로 만들었다.

이러면 href 속성을 사용할 수 있다.

'등록하기' 버튼의 href 속성으로 "<%= contextPath %>/write.no"를 준다. 

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/controller에 NoticeWriteController.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;


@WebServlet("/write.no")
public class NoticeWriteController extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
       
    public NoticeWriteController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 응답페이지 : 작성페이지 (/web/views/notice/noticeWrite.jsp)
        request.getRequestDispatcher("/views/notice/noticeWrite.jsp").forward(request, response);
   
    }

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

}
 

 

 

- "/write.no"라는 url mapping 값을 가지는 서블릿 클래스를 만든다.

- 요청하며 보낸 값도 없고, 실행할 쿼리도 없다. 응답페이지인 작성페이지로 이동만 하면 된다.

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

(작성페이지로 이동하는 서블릿 클래스는 이게 처음이다)

 

 

 

 

 

- 공지사항 작성페이지 이동 요청 기능도 스프레드 시트에 기록해 놓는다. 

 

 

 

 

 

 

ㅁ /webApp/src/main/webapp/views/notice에 noticeWrite.jsp를 만든다. 

 

 

 

 

 

 

 

- 화면구현하며 만들었던 "공지사항작성페이지.html"에서 body 태그 안을 복붙한다.

 

 

 

 

- 아래는 noticeWrite.jsp다.

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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 %>/insert.no" method="post" class="m-4">
          <table class="table">
            <tr>
              <th>제목</th>
              <td><input type="text" class="form-control" name="title" required></td>
            </tr>
            <tr>
              <th>내용</th>
              <td><textarea rows="10" class="form-control" name="content" style="resize:none" required></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 한다.

- 공지사항 등록 페이지의 form 요소에 method 속성에 "post"를, action 속성에 "<%= contextPath %>/insert.no"을 준다.

- 게시글은 방대한 데이터가 넘어갈 수 있기 때문에 post 방식으로 요청한다.

 

 

 

 

 

 

- 서버 재시작, 브라우저 새로고침 후 관리자로 로그인하고 공지사항 목록페이지에서 '등록하기' 버튼을 누르면

위와 같이 공지사항 작성페이지가 뜬다.

- url은 "http://localhost:8888/web/write.no"이다.

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/controller에 NoticeInsertController.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 javax.servlet.http.HttpSession;

import com.br.web.member.model.vo.Member;
import com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/insert.no")
public class NoticeInsertController extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
       
    public NoticeInsertController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        request.setCharacterEncoding("UTF-8");
        String noticeTitle = request.getParameter("title");
        String noticeContent = request.getParameter("content");
       
        HttpSession session = request.getSession();
        int noticeWriter = ((Member)session.getAttribute("loginUser")).getUserNo();
       
        Notice n = new Notice();
        n.setNoticeTitle(noticeTitle);
        n.setNoticeContent(noticeContent);
        n.setNoticeWriter(String.valueOf(noticeWriter)); // 1 => "1"
       
        int result = new NoticeService().insertNotice(n);
 
       
        // 2. 응답
        if(result > 0) { // 성공
 
        }else { // 실패
 
        }
   
   
    }

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

}
 

 

 

- '등록하기' 요청시 전달값이 2개 있다. 글제목과 글내용. request 객체로부터 뽑는다.

post 방식이고 한글 데이터이기에 인코딩을 먼저 한다.

 

 

 

 

- 공지게시판에 insert하려면 글제목과 글내용, 글번호(시퀀스), 작성일(기본값), 상태(기본값) 외에도

NOTICE_WRITER 컬럼에 들어갈 '글 작성자 번호'가 필요하다. (회원 아이디 아님)

- '글 작성자 번호'는 session으로부터 현재 로그인한 회원의 회원번호를 알아낼 수 있다.

공지사항 작성페이지에 왔다는 것은 관리자 아이디로 로그인했다는 의미이기 때문이다.

 

 

 

 

 

- 이렇게 session으로부터 현재 로그인한 회원의 회원번호를 알아내고, 글제목과 글내용과 함께 Notice 객체에 setXXX 메소드로 담아서 service로 전달한다.

- VO 객체 'Member'에 userNo 필드는 int로 선언되어있다. 그래서 데이터를 뽑으면 int형이다.

그러나 'Notice' 객체의 noticeWriter 필드는 String으로 선어되어 있다.

(이 필드에 insert 시에는 회원번호, select 시에는 회원id를 담음)

 

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/model/service/NoticeService.java에 insertNotice() 메소드를 만든다.

 

 
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;
    }

}
 

 

 

- import static 구문을 작성한다.

 

 

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/notice/model/dao/NoticeDao.java에 insertNotice() 메소드를 만든다.

 

 
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;
       
    }
   

}
 

 

 

- Notice 객체인 n의 noticeWriter 필드에 문자열로 대입을 했다.

- DB의 NOTICE 테이블의 NOTICE_WRITER 컬럼은 NUMBER 타입이다.

pstmt.setInt(3, Integer.parseInt(n.getNoticeWriter()); 이렇게 써야 한다.

 

- 그런데 사실 NUMBER 타입 컬럼에 '1' 이렇게 insert해도 자동으로 형변환이 된다. 

그래서 그냥 setString()을 써도 된다.

 

 

 

 

 

ㅁ NoticeInsertController로 돌아온다.

 

 
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 javax.servlet.http.HttpSession;

import com.br.web.member.model.vo.Member;
import com.br.web.notice.model.service.NoticeService;
import com.br.web.notice.model.vo.Notice;


@WebServlet("/insert.no")
public class NoticeInsertController extends HttpServlet {
 
    private static final long serialVersionUID = 1L;

    public NoticeInsertController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        // 1. 요청
        request.setCharacterEncoding("UTF-8");
        String noticeTitle = request.getParameter("title");
        String noticeContent = request.getParameter("content");
       
        HttpSession session = request.getSession();
        int noticeWriter = ((Member)session.getAttribute("loginUser")).getUserNo();
       
        Notice n = new Notice();
        n.setNoticeTitle(noticeTitle);
        n.setNoticeContent(noticeContent);
        n.setNoticeWriter(String.valueOf(noticeWriter)); // 1 => "1"
       
        int result = new NoticeService().insertNotice(n);
 
       
        // 2. 응답
        if(result > 0) { // 성공
 
            // 응답페이지 : 다시 목록페이지
            // 응답데이터 : "성공적으로 추가" alert 메세지
            session.setAttribute("alertMsg", "성공적으로 공지사항이 등록되었습니다.");
            response.sendRedirect(request.getContextPath() + "/list.no");
           
        }else { // 실패
 
            // 응답페이지 : 에러페이지
            // 응답데이터 : "실패" 메세지
            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 메세지를 띄워준다.

- 공지사항 등록에 실패했다면 에러페이지로 이동시킨다. 포워딩 한다.

 

- 공지사항 목록페이지는 header.jsp를 include하고 있다.

header.jsp에서는 session으로부터 "alertMsg"라는 key값에 데이터가 있는지 검사하여 있다면 alert 메세지를 띄우고 session에서 "alertMsg"라는 key값의 key-value 세트를 지운다.

 

- 에러페이지는 section에서 <%= request.getAttribute("msg") %>으로 msg를 화면에 띄우고 있다.

 

 

 

 

- 공지사항 목록페이지로 이동할 때, 이미 공지사항 목록페이지로 이동하는 서블릿 클래스가 있기도 하지만,

곧바로 포워딩을 하게 되면 null pointer exception이 발생한다. 그래서 반드시 redirect로 url을 재요청해야 한다.

- noticeList.jsp의 테이블에서 List<Notice> list를 사용하고 있다.

 

- noticeList.jsp는 DB로부터 select해서 공지사항 데이터를 list에 담아서, request로 보내면서 forwarding 하게끔 작성되어 있다.

이걸 이미 "/list.no"라는 url mapping 값을 가지는 NoticeListController 서블릿에서 하고 있어서 이걸 재요청(redirect)한다.

 

 

 

 

 

 

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

공지사항 목록 페이지에 띄워줄 공지사항 글 목록들을 db로부터 조회해와서 list에 담아서 noticeList에 request로 넘긴다.

 

 

 


 이래서 개발할 때 이왕이면 앞 페이지에서부터 차근차근 타고 들어가면서 기능 구현하는게 좋다. 

 

 

 

ㅁ 서버를 재시작하고, 브라우저 새로고침하고, 로그인 다시하고, 공지사항을 등록하면 공지사항 목록에 보여진다.

- db에서도 확인할 수 있다.

- 서버 재시작 안하고 그냥 그대로 브라우저 켜진 상태로 했더니 오류 발생했었다.