본문 바로가기

개발/Web

[Java] Sevlet/JSP - 일반게시판 - 게시글 작성 (첨부파일 1개 업로드)

일반게시판

 

< 게시글 작성 (첨부파일 1개) >

 

1) 게시글 작성 화면 요청

2) 게시글 작성 요청

 

* BOARD 테이블

* CATEGORY 테이블

* ATTACHMENT 테이블

 

 

1) 게시글 작성 화면 요청

1. 게시글 작성 화면 요청 

- 로그인한 회원만 보이는 게시글 작성 버튼 작성

<% if (loginUser != null) { %>
    <a href="<%= contextPath %>/enrollForm.bo" class="btn btn-info btn-sm">글작성</a>        
<% } %>

 

2. BoardEnrollForm.java (Servlet)

- 게시글 작성 화면 응답

- DB의 CATEGORY테이블을 조회해서 option태그로 띄워줌

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 카테고리 목록 조회
    ArrayList<Category> list = new BoardService().selectCategoryList();

    request.setAttribute("list", list);
    request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);
}

 

3. BoardService.java (자바클래스)

- selectCategoryList() : 카테고리가 담긴 ArrayList 반환

public ArrayList<Category> selectCategoryList() {
    Connection conn = getConnection();

    ArrayList<Category> list = new BoardDao().selectCategoryList(conn);

    close(conn);

    return list;
}

 

4. BoardDao.java (자바클래스)

- selectCategoryList() : 카테고리가 담긴 ArrayList 반환

public ArrayList<Category> selectCategoryList(Connection conn) {
    // SELECT문 => ResultSet => 여러행 => List
    ArrayList<Category> list = new ArrayList();
    PreparedStatement pstmt = null;
    ResultSet rset = null;

    String sql = prop.getProperty("selectCategoryList");

    try {
        pstmt = conn.prepareStatement(sql);
        rset = pstmt.executeQuery();

        while(rset.next()) {
            list.add(new Category(rset.getInt("CATEGORY_NO"), 
                                  rset.getString("CATEGORY_NAME")));
        }			

    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rset);
        close(pstmt);
    }

    return list;
}

- SQL문 (SELECT)

<entry key="selectCategoryList">
    SELECT CATEGORY_NO, CATEGORY_NAME
      FROM CATEGORY
</entry>

 

2) 게시글 작성 요청

 

1. 게시글 작성 화면

- 넘겨받은 카테고리 목록을 변수로 저장

<%@ page import="java.util.ArrayList, com.kh.board.model.vo.Category" %>
<%
    ArrayList<Category> list = (ArrayList<Category>)request.getAttribute("list");
%>

- 게시글 입력할 form태그 작성. 파일을 첨부하는 경우 enctype속성을 multipart/form-data로 설정

- 내용이 많을 수 있으므로 post방식으로 요청

<form action="<%= contextPath %>/insert.bo" method="post" enctype="multipart/form-data">
    <!-- 제목, 내용, 카테고리, 제출버튼, 글쓴이, 첨부파일 -->
    <!-- 작성자의 회원번호를 hidden으로 같이 넘겨서 board 테이블에 INSERT하게 만들 것 -->
    <input type="hidden" name="userNo" value="<%= loginUser.getUserNo() %>">

    <table align="center">
        <tr>
            <th width="150">카테고리</th>
            <td width="600">
                <select name="category">
                    <% for(Category c : list) { %>
                        <option value="<%= c.getCategoryNo() %>"><%= c.getCategoryName() %></option>
                    <% } %>
                </select>
            </td>
        </tr>
        <tr>
            <th>제목</th>
            <td><input type="text" name="title" required></td>
        </tr>
        <tr>
            <th>내용</th>
            <td><textarea name="content" style="resize: none;" rows="10" required></textarea></td>
        </tr>
        <tr>
            <th>첨부파일</th>
            <td><input type="file" name="upfile"></td>
        </tr>
    </table>
    
    <br>
    <div align="center">
        <button type="reset">취소하기</button>
        <button type="submit">작성하기</button>
    </div>

</form>

 

2. BoardImsertController.java (Servlet)

- 매핑값 : /insert.bo

- enctype을 multipart/form-data로 전송했기때문에 MultipartRequest객체로 값을 다뤄야 함

- MultipartRequest 객체 생성자에 매개변수로 전달할 값 : request, savePath, maxSize, 인코딩, 파일명을 수정시켜주는 객체

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

    // 1) 인코딩 설정 (POST)
    request.setCharacterEncoding("UTF-8");

    // 2) 값 뽑기
  
    // 스텝 0. 조건문 : enctype이 multipart/form-data로 잘 전송되었을 경우 전반적인 내용들이 수정되도록 조건을 걸어줌
    if(ServletFileUpload.isMultipartContent(request)) {
   
        // 스텝 1. 전송되는 파일을 처리할 작업
        // 1-1. 전송파일 용량 제한
        int maxSize = 10 * 1024 * 1024; // 10MByte

        // 1-2. 전달된 파일을 저장할 서버의 폴더 경로 알아내기
        // 메소드를 써서 알아냄 - getRealPath() 호출 => 인자값으로 WebContent부터 board_upfiles폴더까지의 경로를 제시
        HttpSession session = request.getSession();
        ServletContext application = session.getServletContext();
        String savePath = application.getRealPath("/resources/board_upfiles/");

        // 스텝 2. 서버에 업로드 작업(파일명 수정)
        MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());

        // 2) 값 뽑기
        // 카테고리 번호, 제목, 내용, 게시글을 작성한 회원번호 Board객체로 가공
        String category = multiRequest.getParameter("category");
        String title = multiRequest.getParameter("title");
        String content = multiRequest.getParameter("content");
        String userNo = multiRequest.getParameter("userNo");

        // 3) VO객체로 가공 => 첫번째 INSERT문에 해당(BOARD테이블)
        Board b = new Board();
        b.setCategory(category);
        b.setBoardTitle(title);
        b.setBoardContent(content);
        b.setBoardWriter(userNo);

        // 두번째 INSERT -> 선택적(첨부파일이 있을 경우에만 INSERT)
        Attachment at = null;

        // => 첨부파일이 있으면 "원본파일명" / 첨부파일이 없으면 null 리턴
        if(multiRequest.getOriginalFileName("upfile") != null) {
            // 첨부파일이 있다 => VO객체로 가공
            at = new Attachment();
            // 원본파일명
            at.setOriginName(multiRequest.getOriginalFileName("upfile"));
            // 수정파일명
            at.setChangeName(multiRequest.getFilesystemName("upfile"));
            // 파일 경로
            at.setFilePath("resources/board_upfiles");
        }

        // 4) 서비스 요청
        int result = new BoardService().insertBoard(b, at);

        // 5) 응답페이지 지정			
        if (result > 0) { // 성공
            request.getSession().setAttribute("alertMsg", "게시글 작성에 성공했습니다.");
            response.sendRedirect(request.getContextPath() + "/list.bo?cpage=1");
        } else { // 실패

            // 첨부파일이 있을 때만(at != null), delete() 호출해서 파일 삭제
            if(at != null) {
                new File(savePath + at.getChangeName()).delete();					
            }

            request.setAttribute("errorMsg", "게시글 작성 실패");
            request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
        }
    }
}

- MyFileRenamePolicy.java (자바클래스)

- 인터페이스를 구현 (implements FileRenamePolicy)

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.oreilly.servlet.multipart.FileRenamePolicy;

// 인터페이스를 구현
public class MyFileRenamePolicy implements FileRenamePolicy {
	
	@Override
	public File rename(File originFile) {
		
		// 원본 파일명 뽑기 => 매개변수로 전달받은 원본 파일로부터 
		String originName = originFile.getName();
		
		// 수정파일명 만들기 (규칙)
		// 파일이 업로드된 시간 (년월일시분초) + 5자리 랜덤값(10000 ~ 99999) + 확장자

		// 1. 파일이 업로드된 시간 추출
		String currentTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
		
		// 2. 5자리 랜덤값
		int ranNum = (int)(Math.random() * 99999 + 10000);
		
		// 3. 확장자 뽑기 
		String ext = originName.substring(originName.lastIndexOf("."));
		
		// 4. 1+2+3 조합해서 수정파일명을 변수에 담기
		String changeName = "kakao_" + currentTime + "_" + ranNum + ext;
		
		// 기존 파일을 수정된 파일명으로 적용시켜서 리턴
		return new File(originFile.getParent(), changeName);
	}
}

 

3. BoardService.java (자바클래스)

- insertBoard() : 게시글내용을 담은 Board객체와 첨부파일 내용을 담은 Attachment객체 전달

- DAO에 두 번 요청 (게시글 등록 요청 / 첨부파일이 있을 경우)

- 두 등록이 모두 성공해야 성공 → 트랜잭션처리

public int insertBoard(Board b, Attachment at) {

    Connection conn = getConnection();

    // 1) BOARD테이블에 INSERT
    int result1 = new BoardDao().insertBoard(conn, b);

    // 2) ATTACHMENT테이블에 INSERT
    int result2 = 1; // 0이 돌아오면 실패로
    if (at != null) {
        result2 = new BoardDao().insertAttachment(conn, at);
    }

    // 3) 트랜잭션 처리
    if((result1 * result2) > 0) commit(conn);
    else rollback(conn);

    close(conn);

    return (result1 * result2);
}

 

4. BoardDao.java (자바 클래스)

- 두 개의 메소드 작성

- insertBoard() : BOARD게시판에 INSERT문 실행

public int insertBoard(Connection conn, Board b) {

    int result = 0;
    PreparedStatement pstmt = null;

    String sql = prop.getProperty("insertBoard");

    try {
        pstmt = conn.prepareStatement(sql);

        pstmt.setInt(1, Integer.parseInt(b.getCategory()));
        pstmt.setString(2, b.getBoardTitle());
        pstmt.setString(3, b.getBoardContent());
        pstmt.setInt(4, Integer.parseInt(b.getBoardWriter()));

        result = pstmt.executeUpdate();			

    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(pstmt);
    }

    return result;
}

- SQL문

<entry key="insertBoard">
    INSERT
      INTO
           BOARD
           (
           BOARD_NO,
           BOARD_TYPE,
           CATEGORY_NO,
           BOARD_TITLE,
           BOARD_CONTENT,
           BOARD_WRITER
           )
    VALUES
           (
           SEQ_BNO.NEXTVAL,
           1,
           ?,
           ?,
           ?,
           ?
           )
</entry>

- insertAttachment() : ATTACHMENT테이블에 INSERT문 실행

public int insertAttachment(Connection conn, Attachment at) {
    int result = 0;
    PreparedStatement pstmt = null;

    String sql = prop.getProperty("insertAttachment");

    try {
        pstmt = conn.prepareStatement(sql);

        pstmt.setString(1, at.getOriginName());
        pstmt.setString(2, at.getChangeName());
        pstmt.setString(3, at.getFilePath());

        result = pstmt.executeUpdate();		
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(pstmt);
    }		

    return result;
}

- SQL문 

- 게시글 번호로 직전에 실행되는 insertBoard에서 발생한 시퀀스의 번호값을 가져옴

<entry key="insertAttachment">
    INSERT
      INTO
           ATTACHMENT
           (
           FILE_NO,
           REF_BNO,
           ORIGIN_NAME,
           CHANGE_NAME,
           FILE_PATH
           )
    VALUES
           (
           SEQ_FNO.NEXTVAL,
           SEQ_BNO.CURRVAL,
           ?,
           ?,
           ?
           )
</entry>