카테고리 없음

#59. JSP/Servlet 사진게시판(작성, 조회)

열하나요 2023. 9. 22. 11:52

59-1. 사진게시판 

작성 및 조회

<sementic>

모든 요소들이 div로 이루어져 있다보니 구분하기가 어렵다. => sementic 태그 사용!

header/footer/section/nacv.aside/ article 를 써서 태그로 쓸 수 있음 (직관적구조)

 

1. menubar.jsp

사진게시판 버튼 누르면 이동하게끔

<div class="menu"><a href="<%= contextPath %>/list.th">사진게시판</a></div>

 

2. thumbnailListForm.jsp

사진게시판 화면

 

 

 

3. ThumbnailListController.java(Servlet)

package com.kh.jsp.board.controller;

import java.io.IOException;
import java.util.ArrayList;

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.kh.jsp.board.model.sevice.BoardService;
import com.kh.jsp.board.model.vo.Board;

/**
 * Servlet implementation class ThumbnailListController
 */
@WebServlet("/list.th")
public class ThumbnailListController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ThumbnailListController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// 화면을 띄우기 전 => 테이블로부터 조회
		ArrayList<Board> list = new BoardService().selectThumbnailList();
		
		request.setAttribute("list", list);
		
		request.getRequestDispatcher("views/board/thumbnailListView.jsp").forward(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

4. thumbnailListView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.jsp.board.model.vo.Board" %>
<%
	ArrayList<Board> list = (ArrayList<Board>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사진아 나와라 얍</title>
<style>
    .list-area{
        text-align : center;
        border : 1px solid white;
    }

    .thumbnail{
        border : 1px solid white;
        width : 300px;
        display : inline-block;
        margin : 7px;
        background-color : mediumpurple;
    }

    .thumbnail > img{
        width : 250px;
        height : 200px;
        padding : 10px;
    }

    .thumbnail:hover{
        cursor : pointer;
        opacity : 0.9;
    }
</style>
</head>
<body>

	<%@ include file="../common/menubar.jsp" %>
	
    <div class="outer">

        <br>
        <h2 align="center">사진 게시판</h2>
        <br>

        <% if(loginUser != null) { %>
            <div style="width:870px;" align="right">
                <a href="<%= contextPath %>/enrollForm.th" class="btn btn-sm btn-primary">글작성</a>
            </div>
        
        <% } %>

        <div class="list-area">
        
        
        	<% if(list.isEmpty()) { %>
            <!-- 등록된 게시글이 없을 경우 -->
            	등록된 게시글이 없습니다. <br>
        	<% } else { %>
            <!-- 게시글이 존재할 경우 -->
            <% for(Board b : list) { %>
	            <div class="thumbnail" align="center">
	                <img src="<%= b.getTitleImg() %>">
	                <p>
	                    No. <%= b.getBoardNo() %> / <%= b.getBoardTitle() %> <br>
	                   	조회수 : <%= b.getCount() %>
	                </p>
	            </div>
        		<% } %>
        	<% } %>
            <!-- 
            <div class="thumbnail" align="center">
                <img src="https://i.namu.wiki/i/WkPZlLjf0g5i_o4r5j08iC9l29cT-RDp6jc1oQsheTjeVW_hJ31K2Qt_JV6PzSqR0DhsCWkWm8IxM7e-HNKBdQ.webp" alt="페페">
                <p>
                    No. 2 / 슬픈개구리페페 <br>
                    조회수 : 220
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="https://i.namu.wiki/i/WkPZlLjf0g5i_o4r5j08iC9l29cT-RDp6jc1oQsheTjeVW_hJ31K2Qt_JV6PzSqR0DhsCWkWm8IxM7e-HNKBdQ.webp" alt="페페">
                <p>
                    No. 3 / 슬픈개구리페페 <br>
                    조회수 : 330
                </p>
            </div>
             -->
        </div>

    </div>



</body>
</html>

 

5. ThumbnailEnrollFormController.java(Servlet)

글작성

package com.kh.jsp.board.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;

/**
 * Servlet implementation class ThumbnailEnrollFormController
 */
@WebServlet("/enrollForm.th")
public class ThumbnailEnrollFormController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ThumbnailEnrollFormController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.getRequestDispatcher("views/board/thumbnailEnrollForm.jsp").forward(request, response);
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

 

6. thumbnailEnrollForm.jsp

글작성 화면

input type="file"은 '파일선택'은 숨기고, 사진은 미리보기로 보여줄거임

 

inputFile : 현재 변화가 생긴 <input type="file"> 요소객체
inputFile.files.length : 파일을 첨부 1, 선택 취소 0

files속성 : 업로드된 파일의 정보들을 배열형식으로 여러개 묶어서 가지고 있음

 

let reader = new FileReader();  FileReader객체로부터 파일을 읽어들이는 메소드를 호출
reader.readAsDataURL(inputFile.files[0]); 해당 파일을 읽어들이는 순간 파일만의 고유한 겁나긴 url이 부여됨
reader.onload = function(e){

Servlet가기전에 작성자를 hidden으로 넘겨주어야 한다.

<%@ 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>
<style>
	#enroll-form > table{
		border : 1px solid white;
	}

	#enroll-form input, #enroll-form textarea{
		width : 100%;
	}
</style>
</head>
<body>

	<%@ include file="../common/menubar.jsp" %>
	
	<div class="outer">
		<br>
		<h2 align="center">글써~</h2>
		<br>
		
		<form action="<%= contextPath%>/insert.th" id="enroll-form" method="post" enctype="multipart/form-data">

			<input type="hidden" name="userNo" value="<%= loginUser.getUserNo() %>">

			<table align="center" border="1">
				<tr>
					<th width="150">제목</th>
					<td colspan="3"><input type="text" name="title" required></td>
				</tr>
				<tr>
					<th>내용</th>
					<td colspan="3">
						<textarea name="content" style="resize : none" rows="10"></textarea>
					</td>
				</tr>
				<!-- 미리보기 영역 -->
				<tr>
					<th>대표이미지</th>
					<td colspan="3" align="center">
						<img src="https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png" alt="대표이미지" id="titleImg" width="250" height="180">
					</td>
				</tr>
				<tr>
					<th>상세이미지</th>
					<td><img src="https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png" alt="상세이미지1" id="contentImg1" width="150" height="110"></td>
					<td><img src="https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png" alt="상세이미지2" id="contentImg2" width="150" height="110"></td>
					<td><img src="https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png" alt="상세이미지3" id="contentImg3" width="150" height="110"></td>
				</tr>
			</table>
			<br>

			<div id="file-area">
				<input type="file" name="file1" id="file1" required onchange="loadImg(this, 1);">
				<input type="file" name="file2" id="file2" onchange="loadImg(this, 2);">
				<input type="file" name="file3" id="file3" onchange="loadImg(this, 3);">
				<input type="file" name="file4" id="file4" onchange="loadImg(this, 4);">
			</div>

			<!-- onchange : input태그(뿐 아니라 select등..)의 내용물이 변경되었을 때 발생하는 이벤트 속성-->

			<script>
				function loadImg(inputFile, num){
					
					// inputFile : 현재 변화가 생긴 <input type="file"> 요소객체
					// num : 몇 번째 input요소인지 확인 후 해당 영역에 미리보기를 하기위해 받아줌
					// console.log(inputFile);

					// console.log(inputFile.files);
					// inputFile.files.length : 파일을 첨부 1, 선택 취소 0
					// => 파일의 존재 유무를 알 수 있다. inputFiel.files[0]에 선택된 파일이 담겨있음

					// files속성 : 업로드된 파일의 정보들을 배열형식으로 여러개 묶어서 가지고 있음

					if(inputFile.files.length == 1) {// 파일이 첨부
						
						// 선택된 파일을 읽어서 영역에 맞는 미리보기

						// 파일을 읽어들일 FileReader객체 생성
						let reader = new FileReader();

						// console.log(inputFile.files[0]);
						// FileReader객체로부터 파일을 읽어들이는 메소드를 호출
						reader.readAsDataURL(inputFile.files[0]);
						// 해당 파일을 읽어들이는 순간 파일만의 고유한 겁나긴 url이 부여됨
						// 해당 url을 src속성의 값으로 부여할 것(attr)

						// 파일 읽기가 완료되면 실행할 익명함수를 정의
						reader.onload = function(e){
							// e의 target => e.target => 이벤트 발생한 친구
							// console.log(e.target);

							// e.target.result에 파일의 url이 담긴다.

							// 각 영역에 맞춰서 이미지 미리보기
							switch(num){
								case 1 : $('#titleImg').attr('src', e.target.result); break;
								case 2 : $('#contentImg1').attr('src', e.target.result); break;
								case 3 : $('#contentImg2').attr('src', e.target.result); break;
								case 4 : $('#contentImg3').attr('src', e.target.result); break;
							}
						}
					} else {
						const str = 'https://usagi-post.com/wp-content/uploads/2020/05/no-image-found-360x250-1.png';
						switch(num) {
							case 1 : $('#titleImg').attr('src', str); break;
							case 2 : $('#contentImg1').attr('src', str); break;
							case 3 : $('#contentImg2').attr('src', str); break;
							case 4 : $('#contentImg3').attr('src', str); break;
						}
					}

				};

				$(function(){

					$('#file-area').hide();

					$('#titleImg').click(function(){
						$('#file1').click();
					});

					$('#contentImg1').click(function(){
						$('#file2').click();
					});
					
					$('#contentImg2').click(function(){
						$('#file3').click();
					});
					$('#contentImg3').click(function(){
						$('#file4').click();
					});
				});

			</script>

			<br>
			<div align="center">
				<button type="submit" class="btn btn-sm btn-primary">작성하기</button>
				<button type="reset" class="btn btn-sm btn-info">다시쓰기</button>
			</div>

			<br><br><br>


		</form>

	</div>
	

</body>
</html>



7. ThumbnailInsertController.java(Servlet)

파일에 properties를 보면 경로를 볼 수 있다.

package com.kh.jsp.board.controller;

import java.io.IOException;
import java.util.ArrayList;

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 org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;

import com.kh.jsp.board.model.sevice.BoardService;
import com.kh.jsp.board.model.vo.Attachment;
import com.kh.jsp.board.model.vo.Board;
import com.kh.jsp.common.MyFileRenamePolicy;
import com.oreilly.servlet.MultipartRequest;

/**
 * Servlet implementation class ThumbnailInsertController
 */
@WebServlet("/insert.th")
public class ThumbnailInsertController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ThumbnailInsertController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// POST 방식 -> 인코딩
		
		// 1) 인코딩
		request.setCharacterEncoding("UTF-8");
		
		// 2) "첨부파일" => multipart/form-data => 조건제시 => 서버로 파일을 올려주자
		if(ServletFileUpload.isMultipartContent(request)) {
			
			// 1) MultipartRequest객체 생성
			// 1_1. 전송 용량 제한(10Mbyte)
			int maxSize = 1024 * 1024 * 10;
			
			// 1_2. 저장할 경로를 구해야함!
			String savePath = request.getServletContext().getRealPath("/resources/thumbnail_upfiles/");
			
			// 2. MultipartRequest객체 생성하면서 파일의 이름을 수정하면서 업로드! 
			MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
			
			// 2) multiRequest로부터 값 뽑기 => getParameter메소드 이용
			String boardTitle = multiRequest.getParameter("title");
			String boardContent = multiRequest.getParameter("content");
			String userNo = multiRequest.getParameter("userNo");
			
			// 3) VO로 가공
			// BOARD
			Board b = new Board();
			b.setBoardTitle(boardTitle);
			b.setBoardContent(boardContent);
			b.setBoardWriter(userNo);
			
			// Attachment => 사진게시판 작성 폼에 메인이미지 required
			// => 최소한 게시글 한 개당 한 개의 첨부파일은 존재한다!!
			// 여러개의 VO를 묶어서 다룰 경우 ArrayList를 쓰면 편하지 않을까?
			ArrayList<Attachment> list = new ArrayList();
			
			// 키값 : file1 ~ file4
			
			for(int i = 1; i <= 4; i++) {
				
				// 키값만 미리 변수!
				String key = "file" + i;
				
				// 현재 반복하고 있는 키값으로 파일을 업로드 했는지 파악!
				if(multiRequest.getOriginalFileName(key) != null) { // 파일이 존재한다!!
					
					// 첨부파일이 존재한다! Attachment객체 생성
					// 필드 : 원본명, 수정명, 파일경로
					Attachment at = new Attachment();
					at.setOriginName(multiRequest.getOriginalFileName(key)); // 원본명
					at.setChangeName(multiRequest.getFilesystemName(key)); // 수정명
					at.setFilePath("resources/thumbnail_upfiles");
					
					// 대표이미지를 FileLevel을 1로 설정
					// 파일레벨
					if(i == 1) {
						// 대표이미지
						at.setFileLevel(1);
					} else {
						at.setFileLevel(2);
					}
					list.add(at);
				}
			}
			// 4) 서비스 요청
			int result = new BoardService().insertThumbnailBoard(b, list);
			
			// 5) 결과에 따른 응답 뷰 지정
			if(result > 0) { // 성공 => list.th로 요청
				request.getSession().setAttribute("alertMsg", "게시글 작성 성공 ~ !");
				response.sendRedirect(request.getContextPath() + "/list.th");
			}
		}
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

8. BoardService.java

// 사진게시판 작성
public int insertThumbnailBoard(Board b, ArrayList<Attachment> list) {

    Connection conn = getConnection();

    // 1개의 트랜잭션에서 최소 2개 최대 5개의 INSERT가 필요함!
    int result1 = new BoardDao().insertThumbnailBoard(conn, b);

    int result2 = new BoardDao().insertAttachmentList(conn, list);

    if((result1 * result2) > 0) {
        commit(conn);
    } else {
        rollback(conn);
    }
    close(conn);
    return (result1 * result2);
}

// 사진게시판 조회
public ArrayList<Board> selectThumbnailList(){

    Connection conn = getConnection();

    ArrayList<Board> list = new BoardDao().selectThumbnailList(conn);

    close(conn);

    return list;
}

 

9. BoardDao.java

// 사진게시판
// 작성
public int insertThumbnailBoard(Connection conn, Board b) {

    int result = 0;
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("insertThumbnailBoard");

    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, b.getBoardTitle());
        pstmt.setString(2, b.getBoardContent());
        pstmt.setInt(3, Integer.parseInt(b.getBoardWriter()));

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

public int insertAttachmentList(Connection conn, ArrayList<Attachment> list) {

    int result = 1;
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("insertAttachmentList");

    try {
        // 리스트의 요소 개수만큼 Attachment 테이블에 행을 추가
        for(Attachment at : list) {
            // 반복할 때마다 미완성된 SQL문을 담은 pstmt객체 생성
            pstmt = conn.prepareStatement(sql);

            // 완성형태로 만들기 = > at에서 뽑아서
            pstmt.setString(1, at.getOriginName());
            pstmt.setString(2, at.getChangeName());
            pstmt.setString(3, at.getFilePath());
            pstmt.setInt(4, at.getFileLevel());

            // 실행
            result *= pstmt.executeUpdate();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(pstmt);
    }
    return result;
    // 혹은 int result = 0세팅해놀고 return list.size() == result ? 1 : 0; 
}

// 사진게시판 조회
public ArrayList<Board> selectThumbnailList(Connection conn){

    ArrayList<Board> list = new ArrayList();
    PreparedStatement pstmt = null;
    ResultSet rset = null;

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

    try {
        pstmt = conn.prepareStatement(sql);

        rset = pstmt.executeQuery();

        while(rset.next()) {
            Board b = new Board();
            b.setBoardNo(rset.getInt("BOARD_NO"));
            b.setBoardTitle(rset.getString("BOARD_TITLE"));
            b.setCount(rset.getInt("COUNT"));
            // 번호, 제목, 조회수, 저장경로, 바뀐이름

            // String img = rset.getString("FILE_PATH") + "/" + rset.getString("CHANGE_NAME");
            // b.setTitleImg(img);
            // String은 불변객체 => String 만들고 붙이고.. 힘듦 => DB에서 가져오는 건 어떨까(||로 붙여버려서 가져오기)
            b.setTitleImg(rset.getString("TITLEIMG"));

            list.add(b);
        }

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

10. sql

	<entry key="insertThumbnailBoard">
		INSERT
		  INTO 
		       BOARD
		       (
		       BOARD_NO,
		       BOARD_TYPE,
		       BOARD_TITLE,
		       BOARD_CONTENT,
		       BOARD_WRITER
		       )
		VALUES
			   (
			   SEQ_BNO.NEXTVAL,
			   2,
			   ?,
			   ?,
			   ?
			   )
	</entry>
	
	<entry key="insertAttachmentList">
		INSERT
		  INTO
		       ATTACHMENT
		       (
		       FILE_NO,
		       REF_BNO,
		       ORIGIN_NAME,
		       CHANGE_NAME,
		       FILE_PATH,
		       FILE_LEVEL
		       )
		VALUES
		       (
		       SEQ_FNO.NEXTVAL,
		       SEQ_BNO.CURRVAL,
		       ?,
		       ?,
		       ?,
		       ?
		       )
	</entry>
    
    	<entry key="selectThumbnailList">
		SELECT 
			   BOARD_NO,
			   BOARD_TITLE,
			   COUNT,
			   FILE_PATH||'/'||CHANGE_NAME TITLEIMG
		  FROM 
		       BOARD
		  JOIN 
		       ATTACHMENT ON (BOARD_NO = REF_BNO)
		 WHERE 
		 	   BOARD_TYPE = 2
		   AND 
		       FILE_LEVEL = 1
		   AND 
		       BOARD.STATUS = 'Y'
		 ORDER 
		    BY 
		       BOARD_NO DESC		
	</entry>

 

11. Board.java

// 추가
private String titleImg;


public String getTitleImg() {
    return titleImg;
}
public void setTitleImg(String titleImg) {
    this.titleImg = titleImg;
}