📗 self-study/📗 KH정보교육원 당산지원

[Servlet & JSP] 기본적인 기능이 구현된 동적 웹 페이지 만들기 (Board ver. - 사진 게시판)

천재강쥐 2022. 10. 24. 14:37

 

더보기

Servlet/JSP의 사진 게시판(Board)를 만들어 보자

 

 

 

사진 게시판

✔️ 게시글 작성, 게시판 전체 조회, 게시판 상세 조회, 수정, 삭제

 

 

 

 

 

*게시글 등록

menubar.jsp

👉🏻 페이징 처리는 일반 게시판 참고하세요!

    <div class="nav-area" align="center">
        <!-- (div.menu>a)*4 + Enter -->
        <div class="menu"><a href="<%= contextPath %>">HOME</a></div>
        <div class="menu"><a href="<%= contextPath %>/list.no">공지사항</a></div>
        <div class="menu"><a href="<%= contextPath %>/list.bo?currentPage=1">일반게시판</a></div>
        <div class="menu"><a href="<%= contextPath %>/list.th">사진게시판</a></div>
    </div>

 

thumbnailListView.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>
<style>
    .outer {
        background-color: black;
        color : white;
        width : 1000px;
        height : 1000px;
        margin : auto;
        margin-top : 50px;
    }

    .list-area {
        width : 760px;
        margin : auto;
    }

    .thumbnail {
        border : 1px solid white;
        width : 220px;
        display : inline-block; /* 가로로 배치, 그냥 inline은 속성 안 먹힘 */
        margin : 14px;
    }

    .thumbnail:hover {
        cursor : pointer;
        opacity : 0.7; /* 투명도 조절해서 마우스 올렸을 때 살짝 흐려지게 보이도록 */
    }

</style>
</head>
<body>

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

    <div class="outer">


        <br>
        <h2 align="center">사진 게시판</h2>
        <br>
		
		<% if(loginUser != null) { %>
		<!--
			 로그인한 회원만 보이는 버튼
			 a href 태그 뒤에 절대경로로 <%= contextPath %>/enrollForm.th 기재해도 무방!
	  	-->
	        <div style="width:850px" align="right">
	            <a href="enrollForm.th" class="btn btn-secondary">글작성</a>
	        </div>
		<% } %>
        <br><br>

        <div class="list-area">
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>


        </div>
</div>
</body>
</html>

 

ThumbnailListController 서블릿 생성

👉🏻 url mapping: /list.th

package com.kh.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 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 {
		
	    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);
	}

}

 


사진 게시판 폼 만들고 서블릿으로 포워딩까지 함!


 

ThumbnailEnrollFormController 서블릿 생성

👉🏻 url mapping: /enrollForm.th

package com.kh.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);
	}

}

 

thumbnailEnrollForm.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>
<style>
    .outer {
        background-color: black;
        color : white;
        width : 1000px;
        height : 700px;
        margin : auto;
        margin-top : 50px;
    }

    #enroll-form>table {border : 1px solid white; }

    #enroll-form input, #enroll-form textarea {
        width : 100%;
        box-sizing : border-box; /* 내가 지정한 길이는 border 포함 길이임을 알려 줌 */
    }


</style>
</head>
<body>

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

    <div class="outer">
        
        <br>
        <h2 align="center">사진 게시판 작성하기</h2>
        <br>
        <!-- 
            길이가 얼마나 될지 모르는 게시판은 post 형식이 좋음
            사진 게시판에는 적어도 1개 이상의 첨부파일이 꼭 들어가야 함! enctype 속성 넣어 주기
        -->
        <form id="enroll-form" action="<%= contextPath %>/insert.th" method="post" enctype="multipart/form-data">

			<!-- 작성자의 회원번호도 같이 넘기기 -->
			<input type="hidden" name="userNo" value="<%= loginUser.getUserNo() %>">
		
            <table align="center">

                <!--
                    (tr>th+td+td+td)*4 + Enter
                    name 속성은 추후 키값이 됨!
                    테이블 확인 후 not null이라면 required 속성 걸어 주기
                -->
                <tr>
                    <th width="100"> 제목</th>
                    <td colspan="3"><input type="text" name="title" required></td>
                </tr>
                <tr>
                    <th>내용</th>
                    <td colspan="3"><textarea name="content" rows="5" style="resize:none;" required></textarea></td>
                </tr>

                <!-- 첨부파일 이미지 미리 보기 폼 -->
                <tr>
                    <th>대표 이미지</th> <!-- 필수 -->
                    <td colspan="3">
                        <img id="titleImg" width="250" height="170">
                    </td>
                </tr>
                <tr>
                    <th>상세이미지</th> <!-- 선택 -->
                    <td><img id="contentImg1" width="150" height="120"></td>
                    <td><img id="contentImg2" width="150" height="120"></td>
                    <td><img id="contentImg3" width="150" height="120"></td>
                </tr>

            </table>

            <!--
                img는 단순히 사진을 보여 주는 용도
                원하는 사진 파일을 서버로 보내려면 input type="file"이 필요함 (form 내부에)
            -->

            <div id="file-area">
                <!-- (input[type=file id=file$ name=file$])*4 -->
                <input type="file" id="file1" name="file1" onchange="loadImg(this, 1);" required> <!-- 대표 이미지 업로드용(대표이미지인 썸네일은 필수)  -->
                <input type="file" id="file2" name="file2" onchange="loadImg(this, 2);"> <!-- 상세 이미지 업로드용 -->
                <input type="file" id="file3" name="file3" onchange="loadImg(this, 3);">
                <input type="file" id="file4" name="file4" onchange="loadImg(this, 4);">
                <!-- onchange 메소드: input 태그의 내용물이 변경될 시 발생하는 이벤트 속성(change 이벤트) -->
                <!-- loadImg() : 내가 직접 만든 선언적 함수, 매개변수 this: 이벤트 당한 요소, 1: input 태그의 위치값 (구분 용도)-->
            </div>

            <br><br>

            <script>

                $(function() {
                    $("#file-area").hide();

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

                    $("#contentImg1").click(function () {
                        $("#file2").click();
                    });

                    $("#contentImg2").click(function () {
                        $("#file3").click();
                    });

                    $("#contentImg3").click(function () {
                        $("#file4").click();
                    });

                });

                function loadImg(inputFile, num) {
                    // inputfile: 현재 변화가 생긴 input type="file"요소 객체
                    // num : 몇 번째 input 요소인지 확인 후 해당 그 영역에 미리보기 하기 위한 변수

                    // input type="file" 요소 객체는 내부적으로 files라는 속성을 가지고 있음
                    // => 현재 이 input 태그로 선택된 파일들의 정보를 배열 형식으로 가지고 있음
                    console.log(inputFile.files.length);
                    // 파일 선택 시 1, 파일 취소 시 0이 출력됨
                    // => 즉, 파일의 존재 유무를 알 수 있음

                    if(inputFile.files.length == 1) { // 선택된 파일이 있을 경우

                        // 선택된 파일을 읽어들여서 그 영역에 맞는 곳에 미리보기 기능 추가

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

                        // 파일을 읽어들이는 메소드 속성을 호출
                        // => 어느 파일을 읽어들일 건지 그 파일의 정보 자체를 매개변수로 제시해야 함
                        // => inputFile.files라는 배열의 0번째 인덱스에 파일 정보가 담겨 있음
                        reader.readAsDataURL(inputFile.files[0]);
                        // => 해당 파일을 읽어들이는 순간 그 파일만의 고유한 url 주소가 하나 부여됨(FileReader 객체의 result 속성에)
                        //    == 해당 이미지의 고유 url 주소(이미지 주소 복사할 때 뜨는 url)가 하나 만들어지는 꼴임
                        // => 이 고유한 url 주소를 각 img 태그의 src 속성으로 부여

                        // 파일 읽기가 완료되었을 때 실행할 함수를 정의
                        reader.onload = function(e) {
                            
                            // e: 현재 발생한 이벤트의 정보(이벤트 객체)
                            // e.target: 현재 이벤트가 발생된 요소(이벤트를 당한 요소객체)

                            // e.target == reader == this
                           //  $("#titleImg").attr("src", e.target.result);

                            // 각 영역에 맞춰서 이미지 미리보기
                            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 { // 선택된 파일이 사라졌을 경우

                        // 미리보기 이미지를 사라지게 하기 => src 속성에 null 대입
                        switch(num) {
                                case 1 : $("#titleImg").attr("src", null); break;
                                case 2 : $("#contentImg1").attr("src", null); break;
                                case 3 : $("#contentImg2").attr("src", null); break;
                                case 4 : $("#contentImg3").attr("src", null); break;
                            }
                    }

                }
            </script>

            <div align="center">
                <button type="submit">등록하기</button>
            </div>

        </form>

    </div>

</body>
</html>

 

ThumbnailInsertController 서블릿 생성

👉🏻 url mapping: /insert.th

package com.kh.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.board.model.service.BoardService;
import com.kh.board.model.vo.Attachment;
import com.kh.board.model.vo.Board;
import com.kh.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 {
		
	    // 인코딩 설정
	    request.setCharacterEncoding("UTF-8");
	    
	    // 이 요청이 multipart/form-data 형식인지 먼저 검사
	    if(ServletFileUpload.isMultipartContent(request)) {
	        
	        // 1. 전달된 파일에 대한 정보 먼저 지정 (전송파일 용량 제한, 지정할 파일의 물리적인 경로)
	        // 1_1. 용량 제한
	        int maxSize = 10 * 1024 * 1024;
	        
	        // 1_2. 저장할 파일의 물리적인 경로
	       String savePath = request.getSession().getServletContext().getRealPath("/resources/thumbnail_upfiles/");
	       
	       // 2. 전달된 파일명 수정 작업 후 서버에 업로드 + MultipartRequest 타입으로 변환
	       MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
	       // MultipartRequest로 바꿀 request 넣어 주고, 사진 저장할 경로 savePath 넣어 주고, 최대 용량 넣어 주고, 인코딩, 파일명 수정작업
	       
	       // 3. DB에 전달할 값 뽑기
	       
	       // Board에 Insert => userNo, title, content
	       Board b = new Board();
	       b.setBoardWriter(multiRequest.getParameter("userNo"));
	       b.setBoardTitle(multiRequest.getParameter("title"));
	       b.setBoardContent(multiRequest.getParameter("content"));
	       
	       // Attachment에 Insert
	       // 단, 여러 개의 첨부파일이 있을 예정!
	       // => ArrayList<Attachment>에 담기 (최소 1개~ 최대 4개)
	       ArrayList<Attachment> list = new ArrayList<>();
	       
	       for(int i = 1; i <= 4; i++) { // i: 1, 2, 3, 4
	           
	           // 키값 먼저 세팅
	           String key = "file" + i; // key: "file1", "file2", "file3", "file4"
	           
	           // 해당 키값에 대한 첨부파일이 있다면 처리!
	           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/");
	               
	               if(i == 1) { // 대표 이미지일 경우 => 1
	                   at.setFileLevel(1);     
	               } else { // 상세 이미지일 경우  => 2
	                   at.setFileLevel(2);
	               }
	               
	               list.add(at);
	           }
	       }
	       
	       // 현재 list에는 첨부파일이 차곡차곡 담긴 상태임
	       // Service 단으로 요청 후 결과 받기
	       int result = new BoardService().insertThumbnailBoard(b, list);
	       
	       // 결과에 따른 응답 페이지 지정
	       if(result > 0) { // 성공 => 사진 게시판 리스트 url(list.th)로 재요청
	           
	           request.getSession().setAttribute("alertMsg", "성공적으로 사진 게시글이 업로드되었습니다");
	           response.sendRedirect(request.getContextPath() + "/list.th");
	           
	       } else { // 실패 => 에러 문구 담아서 에러페이지 포워딩
	           
	           request.setAttribute("errorMsg", "사진 게시판 업로드 실패");
	           request.getRequestDispatcher("views/common/errorPage.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);
	}

}

 

thumbnail_upfiles 폴더 생성

👉🏻 사진게시판에 사람들이 올리는 파일들만 저장할 수 있게끔

 

BoardService

    public int insertThumbnailBoard(Board b, ArrayList<Attachment> list) {
        
        Connection conn = getConnection();
        
        // 각각 b와 list를 insert 할 수 있는 요청 보내기
        // list는 bno을 참조해야 하기 때문에 "b부터 진행"해야 함!!
        int result1 = new BoardDao().insertThumbnailBoard(conn, b);
        
        int result2 = new BoardDao().insertAttachmentList(conn, list);
        
        if(result1 > 0 && result2 > 0) {
            commit(conn);
        } else {
            rollback(conn);
        }
        
        close(conn);
        
        return result1 * result2;
    }

 

BoardDao

    public int insertThumbnailBoard(Connection conn, Board b) {
        
        // INSERT문 => int (처리된 행의 개수)
        
        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) {
        
        // INSERT문 여러 번 => int (처리된 행의 개수)
        
        int result = 1;
        PreparedStatement pstmt = null;
        
        String sql = prop.getProperty("insertAttachmentList");
        
        try {
        // DAO 메소드 하나로 동일한 쿼리문을 여러 번 실행해야 함
        // => 쿼리문을 실행할 때마다 pstmt를 생성해야 함
        // => list에 담긴 Attachment의 개수만큼 반복 돌리기
        
        // list에서 하나씩 뽑아서 at에 담겠다
        for(Attachment at : list) {
            
            pstmt = conn.prepareStatement(sql);
            
            // 쿼리문 완성시키기
            pstmt.setString(1,  at.getOriginName());
            pstmt.setString(2, at.getChangeName());
            pstmt.setString(3, at.getFilePath());
            pstmt.setInt(4,  at.getFileLevel());
            
            // 쿼리문 실행 후 결과 받기(결과를 누적곱으로 => 하나라도 실패하면 0, 시작(초기화)값은 1이 되어야 함!)
            result *= pstmt.executeUpdate();

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

 

board-mapper.xml

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

 

 

 

 

 

*게시판 전체 조회

Board

👉🏻 titleimg 필드, getter/setter 메소드 추가

    // 필드부
    private String titleImg;

    // 메소드부
    public String getTitleImg() {
            return titleImg;
        }

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

 

ThumbnailListController

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

 

BoardService

    public ArrayList<Board> selectThumbnailList() {
        
        Connection conn = getConnection();
        
        ArrayList<Board> list = new BoardDao().selectThumbnailList(conn);
     
        close(conn);
        
        return list;
    }

 

BoardDao

    public ArrayList<Board> selectThumbnailList(Connection conn) {
        
        // SELECT문 => ResultSet 타입의 객체로 리턴 (여러 행 조회)
        
        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"));
                b.setTitleImg(rset.getString("TitleImg"));
                
                list.add(b);
                
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            close(rset);
            close(pstmt);
        }
        
        return list;

    }

 

board-mapper.xml

	<entry key="selectThumbnailList">
		SELECT BOARD_NO
		     , BOARD_TITLE
		     , COUNT
		     , FILE_PATH || CHANGE_NAME "TITLEIMG"
		FROM BOARD B
		JOIN ATTACHMENT ON (BOARD_NO = REF_BNO)
		WHERE BOARD_TYPE = 2
		    AND B.STATUS = 'Y'
		    AND FILE_LEVEL = 1
		ORDER BY BOARD_NO DESC
	</entry>

 

thumbnailListView

👉🏻 상단 부분 Board 타입 ArrayList 선언, import

👉🏻 list-area 부분 정적 코딩을 동적 코딩으로 바꿔 줌 (반복문 돌리기)

// 페이지 정보 (상단)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.ArrayList, com.kh.board.model.vo.Board" %>
<%
	ArrayList<Board> list = (ArrayList<Board>)request.getAttribute("list");
%>

	// list-area 부분 정적 코딩을 동적 코딩으로 바꿔 줌
    <div class="outer">


        <br>
        <h2 align="center">사진 게시판</h2>
        <br>
		
		<% if(loginUser != null) { %>
		<!--
			 로그인한 회원만 보이는 버튼
			 a href 태그 뒤에 절대경로로 <%= contextPath %>/enrollForm.th 기재해도 무방!
	  	-->
	        <div style="width:850px" align="right">
	            <a href="enrollForm.th" class="btn btn-secondary">글작성</a>
	        </div>
		<% } %>
        <br><br>


        <div class="list-area">
       		<!--
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
            <div class="thumbnail" align="center">
                <img src="" width="200px" height="150px">
                <p>
                    No.123 제목입니다. <br>
                    조회수: 230
                </p>
            </div>
		-->
		
		<% if(!list.isEmpty()) { %>

			<% for(Board b : list) { %>
				
				<div class="thumbnail" align="center">
					<input type="hidden" value="<%= b.getBoardNo() %>">
					<img src="<%= contextPath %>/<%= b.getTitleImg() %>" width="200px" height="150px">
					<p>
						No.<%= b.getBoardNo() %> <%= b.getBoardTitle() %> <br>
						조회수 : <%= b.getCount() %>
					</p>
				</div>
				
			<% } %>

		<% } else { %>
			등록된 게시글이 없습니다
		<% } %>
        </div>
        
        <script>
        
        	$(function() {
        		$(".thumbnail").click(function() {
        		
        			location.href = "<%= contextPath %>/detail.th?bno=" + $(this).children().eq(0).val();
        			/*
        				p 태그 안에 있던 No. 뒤 글 번호를 span 태그로 감싼 뒤 탐색 메소드 활용하여 불러와도 되고,
        			    div에 input type="hidden"으로 글 번호를 따로 한 번 더 만들어서 숨겨 줘도 됨
        			        이번은 input type="hiddne"을 사용함!
        			    "class값 thumbnail인  div 클릭 시, 해당 영역 자식 중 첫 번째의 값의 value(== b.getBoardNo())을 가지고 와라"
        			    => 이 방법으로 글 번호를 구할 수 있음!
        			*/
        			
        		});
        	});
        	
        </script>
</div>

 

 

 

 

 

*게시글 상세 조회

 

thumbnailDetailView.jsp 생성

👉🏻 경로: WebContent\views\board\thumbnailDetailView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.ArrayList, com.kh.board.model.vo.*"%>
    
<%
	Board b = (Board)request.getAttribute("b");
	ArrayList<Attachment> list = (ArrayList<Attachment>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
    .outer {
        background-color : black;
        color : white;
        width : 1000px;
        height : 800px;
        margin : auto;
        margin-top : 50px;
    }

    .detail-area td {
        border : 1px solid white;
        text-align : center;
    }
</style>
</head>
<body>

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

    <div class="outer">

        <br>
        <h2 align="center">사진게시판 상세보기</h2>
        <br>

        <table class="detail-area" align="center">

            <!-- (tr>td*4)*5 + Enter -->
            <tr>
                <td width="70">제목</td>
                <td colspan="3" width="600"><%= b.getBoardTitle() %></td>
            </tr>
            <tr>
                <td>작성자</td>
                <td><%= b.getBoardWriter() %></td>
                <td>작성일</td>
                <td><%= b.getCreateDate() %></td>
            </tr>
            <tr>
                <td>내용</td>
                <td colspan="3">
                    <p style="height:50px;">
                        <%= b.getBoardContent() %>
                    </p>
                </td>
            </tr>
            <tr>
                <td>대표사진</td>
                <td colspan="3">
                    <div>
                        <img src="<%= contextPath %>/<%= list.get(0).getFilePath() + list.get(0).getChangeName() %>" width="500" height="300">
                    </div>
                </td>
            </tr>
            <tr>
                <td>상세사진</td>
                <td colspan="3">
                
                	<% for (int i = 1; i < list.size(); i++) { %>
                    	<img src="<%= contextPath %>/<%= list.get(i).getFilePath() + list.get(i).getChangeName() %>" width="200" height="150">
                    <% } %>
                </td>
            </tr>

        </table>

    </div>

</body>
</html>

 

ThumbnailDetailController 서블릿 생성

👉🏻 url mapping : /detail.th

package com.kh.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.board.model.service.BoardService;
import com.kh.board.model.vo.Attachment;
import com.kh.board.model.vo.Board;

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

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	    int boardNo = Integer.parseInt(request.getParameter("bno"));
	    
	    // 조회 수 증가용 서비스 요청 후 성공 시 상세 조회 요청 => increaseCount 메소드 재활용
	    int result = new BoardService().increaseCount(boardNo);
	    
	    if(result > 0) { // 성공 => 게시글 정보, 첨부파일들 정보 조회
	        
	        // Board 테이블로부터 해당 게시글 정보만 뽑아오기
	        // 일반 게시판용 selectBoard 쿼리 활용 => 기본 내부 조인에서 left outer 조인으로 변경
	        // 내부 조인인 경우 일치하는 컬럼만을 가져오는 구조였는데,
	        // 사진 게시판의 경우 카테고리가 null이기 때문에 일치하는 것이 없어 반환되는 것이 없었던 것!
	        // 카테고리 컬럼을 기준으로 일치하는 컬럼, 일치하지 않는 컬럼도 가지고 오려면 outer join(외부조인) 해 줘야 함!
	        // 어찌되었든 간에 Board 테이블의 내용물을 조회하고 싶기 때문에 Board 테이블을 기준으로 left outer 조인으로 변경
	        Board b = new BoardService().selectBoard(boardNo);
	        
	        // Attachment 테이블로부터 해당 게시글에 딸린 첨부파일들을 모두 조회
	        ArrayList<Attachment> list = new BoardService().selectAttachmentList(boardNo);
	        
	        // 수화물 싣기
	        request.setAttribute("b", b);
	        request.setAttribute("list", list);
	        
	        request.getRequestDispatcher("views/board/thumbnailDetailView.jsp").forward(request, response);
	        
	    } else { // 실패 => 에러 문구 담아서 에러 페이지로 포워딩 
	        
	        request.setAttribute("errorMsg", "사진 게시글 조회 실패");
	        request.getRequestDispatcher("views/common/errorPage.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);
	}

}

 

BoardService

    public ArrayList<Attachment> selectAttachmentList(int boardNo) {
        
        Connection conn = getConnection();
        
        ArrayList<Attachment> list = new BoardDao().selectAttachmentList(conn, boardNo);
        
        close(conn);
        
        return list;
    }

 

BoardDao

    public ArrayList<Attachment> selectAttachmentList(Connection conn, int boardNo) {
        
        // SELECT문 => ResultSet 객체 (여러 행 조회)
        
        ArrayList<Attachment> list = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rset = null;
        
        // 일반 게시판 상세조회 시 썼던 쿼리문 재활용
        String sql = prop.getProperty("selectAttachment");
        
        try {
            pstmt = conn.prepareStatement(sql);
            
            pstmt.setInt(1,  boardNo);
            
            rset = pstmt.executeQuery();
            
            while(rset.next()) {
                
                Attachment at = new Attachment();
                
                at.setFileNo(rset.getInt("FILE_NO"));
                at.setOriginName(rset.getString("ORIGIN_NAME"));
                at.setChangeName(rset.getString("CHANGE_NAME"));
                at.setFilePath(rset.getString("FILE_PATH"));
                
                list.add(at);
                
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            
            close(rset);
            close(pstmt);
        }
        
        return list;
    }
}

 

board-mapper.xml

👉🏻 selectAttachment 재활용!

	<entry key="selectAttachment">
		SELECT FILE_NO
			 , ORIGIN_NAME
			 , CHANGE_NAME
			 , FILE_PATH
		FROM ATTACHMENT
		WHERE REF_BNO = ?
			AND STATUS = 'Y'
	</entry>

 

 

 

 

 

*게시글 수정 (숙제)

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

 

 

 

 

 

*게시글 삭제(숙제)

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻 

 

화면

👉🏻