2020년 01월 06일

업데이트:

게시글

게시글 상세조회


boardList.jsp
한 행의 어느 곳을 눌러도 게시글 상세조회가 진행된다.

<script>
// 게시글 상세보기 기능 (jquery를 통해 작업)

$("#list-table td").on("click",function(){

// 게시글 번호 얻어오기
var boardNo = $(this).parent().children().eq(0).text();
//console.log(boardNo);
// 클릭이 되는지 테스트

var url = "${contextPath}/board/view.do?cp=${pInfo.currentPage}&no="+boardNo;
                                            
location.href = url;
    
});
</script>



게시글 상세조회 초기 화면

게시글 상세조회화면

게시글 상세조회 화면 구성 후 게시글 상세조회 화면구성 이후

게시글 조회수 증가 조회수 증가
조회수 증가2


BoardController

// 게시글 상세조회 Controller *********************************************
else if(command.contentEquals("/view.do")) {
    errorMsg = "게시글 상세 조회 과정에서 오류 발생";
    
    int boardNo = Integer.parseInt(request.getParameter("no"));
    
    // 상세조회 비즈니스 로직 수행 후 결과 반환 받기
    Board board = service.selectBoard(boardNo);
    
    if(board!=null) {	// 상세조회 성공 시
        path = "/WEB-INF/views/board/boardView.jsp";
        request.setAttribute("board", board);
        view = request.getRequestDispatcher(path);
        view.forward(request, response);
        
    }else {
        request.getSession().setAttribute("swalIcon", "error");
        request.getSession().setAttribute("swalTitle", "게시글 상세 조회 실패");
        response.sendRedirect("list.do");
    }
}


BoardService.java
상세 조회에 성공할 때마다 조회 수가 1씩 증가해야 되기 때문에 같이 작성해 준다.

/** 게시글 상세조회 service
* @param boardNo
* @return board
* @throws Exception
*/
public Board selectBoard(int boardNo) throws Exception {
Connection conn = getConnection();

Board board = dao.selectBoard(conn,boardNo);

if(board != null) { // DB에서 조회 성공 시
    
    // 조회수 증가
    int result = dao.increaseReadCount(conn,boardNo);
    
    if(result>0) {
        commit(conn);
        
        // 반환되는 Board 데이터에는 조회수가 증가되어 있지 않기 때문에
        // 조회수를 1 증가 시켜줌
        board.setReadCount(board.getReadCount() + 1);
    }
    else   rollback(conn);
}
close(conn);

return board;
}


BoardDao.java

/** 게시글 상세조회 DAO
* @param conn
* @param boardNo
* @return board
* @throws Exception
*/
public Board selectBoard(Connection conn, int boardNo) throws Exception {
Board board = null;

String query = prop.getProperty("selectBoard");
try {
    pstmt = conn.prepareStatement(query);
    pstmt.setInt(1,boardNo);
    
    rset = pstmt.executeQuery();
    
    if(rset.next()) {
        board = new Board();
        board.setBoardNo(rset.getInt("BOARD_NO"));
        board.setBoardTitle(rset.getString("BOARD_TITLE"));
        board.setBoardContent(rset.getString("BOARD_CONTENT"));
        board.setMemberId(rset.getString("MEMBER_ID"));
        board.setReadCount(rset.getInt("READ_COUNT"));
        board.setBoardCreateDate(rset.getTimestamp("BOARD_CREATE_DT"));
        board.setBoardModifyDate(rset.getTimestamp("BOARD_MODIFY_DT"));
        board.setCategoryName(rset.getString("CATEGORY_NM"));
    }
}finally {
    close(rset);
    close(pstmt);
}
return board;
}




/** 조회수 증가 DAO
* @param conn
* @param boardNo
* @return	result
* @throws Exception
*/
public int increaseReadCount(Connection conn, int boardNo) throws Exception {
int result =0;

String query = prop.getProperty("increaseReadCount");

try {
    
    pstmt = conn.prepareStatement(query);
    pstmt.setInt(1,boardNo);
    
    result = pstmt.executeUpdate();
}finally {
    close(pstmt);
}

return result;
}


SQL

<!-- 게시글 상세 조회  -->
<entry key="selectBoard">
SELECT * FROM V_BOARD
WHERE BOARD_NO=?
AND BOARD_STATUS='Y'
</entry>


<!-- 조회수 증가  -->
<entry key="increaseReadCount">
UPDATE BOARD SET
READ_COUNT = READ_COUNT+1
WHERE BOARD_NO =?
</entry>




본인이 작성한 글에만 수정/삭제 버튼이 노출된다.


로그인 안 했을때 로그인 안 했을 때

글 작성자로 로그인을 했을 때 본인이 작성한 글 수정삭제


boardView.jsp

<%-- 로그인된 회원과 해당  작성자가 같은 경우--%>
<c:if test="${!empty loginMember && (board.memberId == loginMember.memberId) }">
    <button id="deleteBtn" class="btn btn-primary float-right">삭제</button> 
    <a href="#" class="btn btn-primary float-right ml-1 mr-1">수정</a>
</c:if>




게시글 검색


boardList.jsp

<div class="my-5">
<form action="${contextPath }/search.do" method="GET" class="text-center" id="searchForm">
    <select name="sk" class="form-control" style="width: 100px; display: inline-block;">
        <option value="title">글제목</option>
        <option value="content">내용</option>
        <option value="titcont">제목+내용</option>
        <option value="writer">작성자</option>
    </select>
    <input type="text" name="sv" class="form-control" style="width: 25%; display: inline-block;">
    <button class="form-control btn btn-primary" style="width: 100px; display: inline-block;">검색</button>
</form>
</div>



목록으로 버튼을 누르면 기존 페이지가 유지된다.


검색과 관련된 기능들을 모아서 관리 할 Controller생성.

SearchController.java

package com.kh.wsp.search.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
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.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;
import com.kh.wsp.search.model.service.SearchService;

@WebServlet("/search.do")
public class SearchController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		
		String searchKey = request.getParameter("sk");
		String searchValue = request.getParameter("sv");
		String cp = request.getParameter("cp");
	
		try {
			SearchService service = new SearchService();
			
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("searchKey", searchKey);
			map.put("searchValue", searchValue);
			map.put("currentPage", cp);



검색 내용이 포함된 페이징 처리


SearchController.java

// 페이징 처리를 위한 데이터를 계산하고 저장하는 객체 PageInfo 얻어오기
PageInfo pInfo = service.getPageInfo(map);


SearchService.java

package com.kh.wsp.search.model.service;

import static com.kh.wsp.common.JDBCTemplate.*;

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

import com.kh.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;
import com.kh.wsp.search.model.dao.SearchDAO;

public class SearchService {
private SearchDAO dao = new SearchDAO();



/** 검색 내용이 포함된 페이징 처리 정보 생성 service
* @param map
* @return pInfo
* @throws Exception
*/
public PageInfo getPageInfo(Map<String, Object> map) throws Exception {
    Connection conn = getConnection();
    
    // 얻어온 파라미트 cp 가  null이면 1, 아니면 int형으로 파싱
    map.put("currentPage", (map.get("currentPage") == null )? 1 : Integer.parseInt((String)map.get("currentPage")) );
    
    // 검색 조건에 따른 SQL 조건문을 조합하는 메소드 호출
    String condition = createCondition(map);
    
    // SQL 조건문을 map에 추가
    map.put("condition", condition);
    
    // DB에서 조건을 만족하는 게시글의 수를 조회하기
    int listCount = dao.getListCount(conn, condition);
    
    close(conn);
    
    
    // PageInfo 객체를 생성하여 반환
    return new PageInfo((int)map.get("currentPage"), listCount) ;
}


SeachDAO.java

package com.kh.wsp.search.model.dao;

import static com.kh.wsp.common.JDBCTemplate.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.kh.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;

public class SearchDAO {
	
	private Statement stmt = null;
	private PreparedStatement pstmt = null;
	private ResultSet rset = null;
	
	
	
	/** 조건을 만족하는 게시글 수 조회 DAO
	 * @param conn
	 * @param condition
	 * @return listCount
	 * @throws Exception
	 */
	public int getListCount(Connection conn, String condition) throws Exception {
		int listCount = 0;
		
		String query = "SELECT COUNT(*) FROM V_BOARD WHERE BOARD_STATUS = 'Y' AND " + condition;
		
		try {
			stmt = conn.createStatement();
			
			rset = stmt.executeQuery(query);
			
			if(rset.next()) {
				listCount = rset.getInt(1);
			}
		}finally {
			close(rset);
			close(stmt);
		}
		return listCount;
	}



검색 전/후 주소 조건으로 나눠주기


검색을 하지 않았을 때 검색 안 했을 때의 주소

검색을 했을 때 검색했을 때의 주소


boardList.jsp

<%---------------------- Pagination ----------------------%>
<%-- 페이징 처리 주소를 쉽게 사용할  있도록 미리 변수에 저장 --%>
<c:choose>
    <%-- 검색 내용이 파라미터에 존재할 때 == 검색을 통해 만들어진 페이지인가? --%>        
<c:when test="${!empty param.sk && !empty param.sv }">
        <c:url var="pageUrl" value="/search.do"/>
        
        <%-- 쿼리스트링으로 사용할 내용을 변수에 저장함. --%>
        <c:set var="searchStr" value="&sk=${param.sk }&sv=${param.sv }"/>
</c:when>
<c:otherwise>
            <c:url var="pageUrl" value="/board/list.do"/>
</c:otherwise>
</c:choose>


<!-- 화살표에 들어갈 주소를 변수로 생성 -->
<%-- 검색을  했을  : /board/list.do?cp=1 
    검색을 했을  : /search.do?cp=1&sk=title&sc=49   --%>
<c:set var="firstPage" value="${pageUrl}?cp=1${searchStr }"/>
<c:set var="lastPage" value="${pageUrl}?cp=${pInfo.maxPage}${searchStr }"/>

<%-- EL을 이용한 숫자 연산의 단점 : 연산이 자료형에 영향을 받지 않는다. --%>
<%-- <fmt:parseNumber> : 숫자 형태를 지정하여 변수 선언 
integerOnly="true" : 정수로만 숫자 표현 (소수점 버림)
--%>


<fmt:parseNumber var="c1" value="${(pInfo.currentPage - 1)/10}" integerOnly="true"/>
<fmt:parseNumber var="prev" value="${c1 * 10}" integerOnly="true"/>
<c:set var="prevPage" value="${pageUrl}?cp=${prev}${searchStr }"/> <!-- /board/list/do?cp=10  -->

<fmt:parseNumber var="c2" value="${(pInfo.currentPage + 9)/10}" integerOnly="true"/>
<fmt:parseNumber var="next" value="${c2 * 10 + 1}" integerOnly="true"/>
<c:set var="nextPage" value="${pageUrl}?cp=${next}${searchStr }"/>

        
<div class="my-5">
<ul class="pagination">

<%-- 현재 페이지가 10페이지 초과인 경우 --%>
<c:if test="${pInfo.currentPage>10}">
    <li><!-- 첫 페이지로 이동(<<) -->
        <a class="page-link" href="${firstPage}">&lt;&lt;</a>
    </li>
    
    <li> <!-- 이전 페이지로 이동(<) -->
        <a class="page-link" href="${prevPage}">&lt;</a>
    </li>
</c:if>

        
        <!-- 페이지 목록 -->
<c:forEach var="page" begin="${pInfo.startPage}" end="${pInfo.endPage}">
    <!-- 1부터 10까지  1씩(step설정이 안되어있다면) 순차적으로 증가해라 -->
    <c:choose>
        <c:when test="${pInfo.currentPage == page}">
        <li>
            <a class="page-link">${page}</a> <!-- a태그의 href를 없애서 클릭이 안되게 한다.  -->
        </li>
        </c:when>
        
        <c:otherwise>
        <li>
        
            <a class="page-link" href="${pageUrl}?cp=${page}${searchStr }">${page}</a>
        </li>
        </c:otherwise>
    </c:choose>
</c:forEach>
        
        
        <%-- 현재 페이지가 마지막 페이지 미만인 경우 --%>
<c:if test="${next < pInfo.maxPage}">
    <li> <!-- 다음 페이지로 이동(>) -->
        <a class="page-link" href="${nextPage}">&gt;</a>
    </li>
    <li><!-- 마지막 페이지로 이동(>>) -->
        <a class="page-link" href="${lastPage}">&gt;&gt;</a>
    </li>
    
</c:if>

</ul>
</div>


검색 내용이 있을 경우 검색창에 해당 내용을 작성해두는 기능


검색어 유지 검색어 유지




boardList.jsp

<script>
// 검색 내용이 있을 경우 검색창에 해당 내용을 작성해두는 기능
(function(){
    var searchKey = "${param.sk}";
    // 파라미터 중 sk가 있을 경우  ex ) "49"
    // 파라미터 중 sk가 없을 경우 ex ) 빈문자열
    
    var searchValue = "${param.sv}";
    
    // 검색창 select의 option을 반복 접근
    $("select[name=sk]>option").each(function(index,item){
        // index : 현재 접근중인 요소의 인덱스
        // item : 현재 접근중인 요소
        
        if($(item).val() == searchKey){
            $(item).prop("selected",true);
        }
    });
    
    // 검색어 입력창에 searchValue 값 출력
    $("input[name=sv]").val(searchValue);
    
})();
</script>


게시글 상세보기 기능과 목록으로 버튼에 검색여부 조건 추가해주기


boardList.jsp

게시글 상세보기 기능에도 쿼리스트링 변수를 추가해 준다.

<script>
// 게시글 상세보기 기능 (jquery를 통해 작업)

$("#list-table td").on("click",function(){
    
    // 게시글 번호 얻어오기
    var boardNo = $(this).parent().children().eq(0).text();
    //console.log(boardNo);
    // 클릭이 되는지 테스트
    
    var url = 
    "${contextPath}/board/view.do?cp=${pInfo.currentPage}&no="+boardNo + "${searchStr}";
                                                                         // 검색
    location.href = url;
    });
</script>


boardView.jsp

<c:choose>
    <c:when test="${!empty param.sk && !empty param.sv }">
        <c:url var="goToList" value="../search.do">
            <%-- .. == 상위 주소로 이동 --%>
            <c:param name="cp">${param.cp }</c:param>
            <c:param name="sk">${param.sk }</c:param>
            <c:param name="sv">${param.sv }</c:param>
        </c:url>
    </c:when>
    <c:otherwise>
        <c:url var="goToList" value="list.do">
        <!-- url를 사용하면 쿼리스트링을 만들 수 있다.  -->
        <c:param name="cp">${param.cp }</c:param>
        </c:url>
        <!-- 이부분이 원래 바깥에 있었음  -->
    </c:otherwise>
</c:choose>

<a href="${goToList}" class="btn btn-primary float-right">목록으로</a>




로그인을 했을 때에만 글쓰기 버튼이 보임


로그인 안 했을 때 로그인안했을때 글쓰기버튼



로그인 했을 때 로그인했을때 글쓰기 버튼


boardList.jsp

<%-- 로그인이 되어있는 경우 --%>
<c:if test="${!empty loginMember }">
    <button type="button" class="btn btn-primary float-right" id="insertBtn" 
    onclick="location.href = '${contextPath}/board/insertForm.do'">글쓰기</button>
</c:if>


글쓰기


글쓰기 화면 구성

글쓰기 초기 화면 글쓰기 초기화면




boardInsert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판</title>
<style>
    .insert-label {
      display: inline-block;
      width: 80px;
      line-height: 40px
    }
    
    .boardImg{
    	cursor : pointer;
    }
</style>
</head>
<body>
    <jsp:include page="../common/header.jsp"></jsp:include>

    <div class="container my-5">

    <h3>게시글 등록</h3>
    <hr>
    <!-- 파일 업로드를 위한 라이브러리 cos.jar 라이브러리 다운로드(http://www.servlets.com/) -->
    
    <!-- 
        - enctype : form 태그 데이터가 서버로 제출 될 때 인코딩 되는 방법을 지정. (POST 방식일 때만 사용 가능)
        - application/x-www-form-urlencoded : 모든 문자를 서버로 전송하기 전에 인코딩 (form태그 기본값)
        - multipart/form-data : 모든 문자를 인코딩 하지 않음.(원본 데이터가 유지되어 이미지, 파일등을 서버로 전송 할 수 있음.) 
    -->
    <form action="${contextPath }/board/insert.do" method="post" 
            enctype="multipart/form-data" role="form" onsubmit="return boardValidate();">
        <div class="mb-2">
            <label class="input-group-addon mr-3 insert-label">카테고리</label> 
            <select	class="custom-select" id="categoryCode" name="categoryCode" style="width: 150px;">
                <option value="10">운동</option>
                <option value="20">영화</option>
                <option value="30">음악</option>
                <option value="40">요리</option>
                <option value="50">게임</option>
                <option value="60">기타</option>
            </select>
        </div>
        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">제목</label> 
            <input type="text" class="form-control" id="boardTitle" name="boardTitle" size="70">
        </div>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">작성자</label>
            <h5 class="my-0" id="writer">${loginMember.memberId }</h5>
        </div>


        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">작성일</label>
            <h5 class="my-0" id="today"></h5>
        </div>

        <hr>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">썸네일</label>
            <div class="boardImg" id="titleImgArea">
                <img id="titleImg" width="200" height="200">
            </div>
        </div>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">업로드<br>이미지</label>
            <div class="mr-2 boardImg" id="contentImgArea1">
                <img id="contentImg1" width="150" height="150">
            </div>

            <div class="mr-2 boardImg" id="contentImgArea2">
                <img id="contentImg2" width="150" height="150">
            </div>

            <div class="mr-2 boardImg" id="contentImgArea3">
                <img id="contentImg3" width="150" height="150">
            </div>
        </div>

        <!-- 파일 업로드 하는 부분 -->
        <div id="fileArea">
            <input type="file" id="img0" name="img0" onchange="LoadImg(this,0)" multiple="multiple">
            <!-- multiple 속성 = 사진 여러개 선택 가능  --> 
            <input type="file" id="img1" name="img1" onchange="LoadImg(this,1)"> 
            <input type="file" id="img2" name="img2" onchange="LoadImg(this,2)"> 
            <input type="file" id="img3" name="img3" onchange="LoadImg(this,3)">
        </div>

        <div class="form-group">
            <div>
                <label for="content">내용</label>
            </div>
            <textarea class="form-control" id="boardContent" name="boardContent" rows="15" style="resize: none;"></textarea>
        </div>
        <hr class="mb-4">
        <div class="text-center">
            <button type="submit" class="btn btn-primary">등록</button>
            <button type="button" class="btn btn-primary">목록으로</button>
        </div>

			</form>
		</div>

		<jsp:include page="../common/footer.jsp"></jsp:include>
		
		
	<script>
		(function printToday(){
			// 오늘 날짜 출력 
	 		var today = new Date();
			var month = (today.getMonth()+1);
			var date = today.getDate();
	
	  	var str = today.getFullYear() + "-"
	        		+ (month < 10 ? "0"+month : month) + "-"
	        		+ (date < 10 ? "0"+date : date);
			$("#today").html(str);
		})();

		// 유효성 검사 
		function boardValidate() {
			if ($("#boardTitle").val().trim().length == 0) {
				alert("제목을 입력해 주세요.");
				$("#title").focus();
				return false;
			}

			if ($("#content").val().trim().length == 0) {
				alert("내용을 입력해 주세요.");
				$("#content").focus();
				return false;
			}
		}
	</script>
</body>
</html>


파일 업로드

  • COS 코스 다운로드 후, 라이브러리 추가하기
    라이브러리 추가
    라이브러리 추가


  • 이미지 업로드와 관련된 스크립트 작성

boardInsert.jsp

<script>
// 이미지 영역을 클릭할 때 파일 첨부 창이 뜨도록 설정하는 함수
// 페이지 로딩이 끝나고나면 #fileArea 요소를 숨김.
$(function(){
    $("#fileArea").hide(); 
    // 파일 선택 버튼이 사라진다.

    
    $(".boardImg").on("click",function(){// 이미지 영역이 클릭 되었을 때
        // 클릭한 이미지 영역 인덱스 얻어오기
        var index = $(".boardImg").index(this);
        // 클릭된 요소가 .boardImg 중 몇 번째 인덱스인지 반환
        
        // console.log(index);

        // 클릭된 영역 인덱스에 맞는 input file 태그 클릭
        $("#img" + index).click();
        
    
    });
});


    
// 각각의 영역에 파일을 첨부 했을 경우 미리 보기가 가능하도록 하는 함수
function LoadImg(value, num) {
    // value.files == input태그에 파일이 업로드되어 있으면 true
    // value.files[0] : 여러 파일 중 첫번째 파일이 업로드 되어있으면 true
    if(value.files && value.files[0] ){ // 해당 요소에 업로드된 파일이 있을 경우
        var reader = new FileReader();
        // 자바스크립트 FileReader
    // 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 
    // 읽을 파일을 가리키는 File 혹은 Blob객체를 이용해 파일의 내용을 읽고 
    // 사용자의 컴퓨터에 저장하는 것을 가능하게 해주는 객체
    
    reader.readAsDataURL(value.files[0]);
        // FileReader.readAsDataURL()
    // 지정된의 내용을 읽기 시작합니다. 
    // Blob완료되면 result속성 data:에 파일 데이터를 나타내는 URL이 포함 됨.
    
    reader.onload = function(e){
        // FileReader.onload
            // load 이벤트의 핸들러. 
            // 이 이벤트는 읽기 동작이 성공적으로 완료 되었을 때마다 발생함.
            
            
            // 읽어들인 내용(이미지 파일)을 화면에 출력
            $(".boardImg").eq(num).children("img").attr("src", e.target.result);
        // e.target.result : 이벤트가 발생한 요소의 결과 , 파일 읽기 동작을 성공한 요소가 읽어들인 파일 내용
    
        }
    }
}
</script>



전체 boardView.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글</title>
<style>
	#board-area{ height: 700px; }
	#board-content{ padding-bottom:150px;}

	.boardImgArea{
		height: 300px;
	}

	.boardImg{
		width : 100%;
		height: 100%;
		
		max-width : 300px;
		max-height: 300px;
		
		margin : auto;
	}
	
	/* 이미지 화살표 색 조정
	-> fill='%23000' 부분의 000을
	   RGB 16진수 값을 작성하여 변경 가능 
	 */
	.carousel-control-prev-icon {
 		background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E") !important;
	}
	
	.carousel-control-next-icon {
  		background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E") !important;
	}
	
	.replyWrite > table{
		width: 90%;
		align: center;
	}
	
	#replyContentArea{ width: 90%; }
	
	#replyContentArea > textarea{
	    resize: none;
    	width: 100%;
	}
	
	#replyBtnArea{
	    width: 100px;
	    text-align: center;
	}
	
	.rWriter{ margin-right: 30px;}
	.rDate{
		font-size: 0.7em;
		color : gray;
	}
	
	#replyListArea{
		list-style-type: none;
	}
	
	.board-dateArea{
		font-size: 14px;
	}
</style>
</head>
<body>
	<jsp:include page="../common/header.jsp"></jsp:include>
	<div class="container  my-5">

    <div>
        <div id="board-area">
            <!-- Category -->
        <h6 class="mt-4">카테고리 : [${board.categoryName}]</h6>
        
        <!-- Title -->
        <h3 class="mt-4">${board.boardTitle}</h3>

        <!-- Writer -->
        <p class="lead">
                    작성자 : ${board.memberId}
        </p>
        <hr>
        <!-- Date -->
        <p>
            <span class="board-dateArea">
                작성일 : <fmt:formatDate value="${board.boardCreateDate}" pattern="yyyy년 MM월 DD일 HH:mm:ss"/>
                <br>
                마지막 수정일 : <fmt:formatDate value="${board.boardModifyDate}" pattern="yyyy년 MM월 DD일 HH:mm:ss"/>
            </span>
            <span class="float-right">조회수 ${board.readCount} </span>
        </p>

        <hr>
        <!-- 이미지 출력 -->

        <!-- Content -->
        <div id="board-content">${board.boardContent}</div>

            <hr>
            <div>
                <%-- 로그인된 회원과 해당  작성자가 같은 경우--%>
                <c:if test="${!empty loginMember && (board.memberId == loginMember.memberId) }">
                    <button id="deleteBtn" class="btn btn-primary float-right">삭제</button> 
                    <a href="#" class="btn btn-primary float-right ml-1 mr-1">수정</a>
                </c:if>
                
                <%--
                    상대 경로 작성법
                    - 앞에 아무것도 없음 : 현재 위치(주소 제일 마지막  /뒷부분)
                    - ../ : 현재 위치에서 한단계 상위 (주소 제일 마지막 /보다 왼쪽으로 한칸  /)
                    --%>
            <c:choose>
                    <c:when test="${!empty param.sk && !empty param.sv }">
                            <c:url var="goToList" value="../search.do">
                                        <%-- .. == 상위 주소로 이동 --%>
                                    <c:param name="cp">${param.cp }</c:param>
                                    <c:param name="sk">${param.sk }</c:param>
                                    <c:param name="sv">${param.sv }</c:param>
                            </c:url>
                    </c:when>
                    <c:otherwise>
                            <c:url var="goToList" value="list.do">
                            <!-- url를 사용하면 쿼리스트링을 만들 수 있다.  -->
                            <c:param name="cp">${param.cp }</c:param>
                            </c:url>
                            <!-- 이부분이 원래 바깥에 있었음  -->
                    </c:otherwise>
            </c:choose>
                
            <a href="${goToList}" class="btn btn-primary float-right">목록으로</a>
                
            </div>
        </div>

    </div>
</div>
<jsp:include page="../common/footer.jsp"></jsp:include>
	<script>
	</script>
</body>
</html>



전체 boardList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판</title>
<style>
.pagination {
	justify-content: center;
}

#searchForm {
	position: relative;
}

#searchForm>* {
	top: 0;
}

.boardTitle>img {
	width: 50px;
	height: 50px;
}

#list-table th {
	text-align: center;
}

#list-table td:not(:nth-of-type(3)) {
	text-align: center;
}

.list-wrapper{
	height: 540px;
}

#list-table td:hover{
	cursor : pointer;
}



</style>

</head>
<body>
	<jsp:include page="../common/header.jsp"></jsp:include>
	<div class="container my-5">
		
<h1>게시판</h1>

    <div class="list-wrapper">
        <table class="table table-hover table-striped my-5" id="list-table">
            <thead>
                <tr>
                    <th>글번호</th>
                    <th>카테고리</th>
                    <th>제목</th>
                    <th>작성자</th>
                    <th>조회수</th>
                    <th>작성일</th>
                </tr>
            </thead>
            
            <%-- 게시글 목록 출력 --%>
            <tbody>
                <c:choose>
                    <c:when test="${empty bList }">
                    <!-- bList가 비어있을 때 : 게시글 목록 조회에서 조회되지 않았을 때  -->
                            <tr>
                                    <td colspan="6">존재하는 게시글이 없습니다.</td>
                            </tr> 
                    </c:when>
                    
                    <c:otherwise> <!-- 조회된 게시글 목록이 있을 때  -->
                            <c:forEach var="board" items="${bList }"> <!-- 이때 bList에는 최소 1개, 최대 10개의 데이터가 들어있다.  -->
                            <!-- for문으로 반복접근해서 모든 데이터를 출력 해준다. 
                                한 바퀴마다 bList에서 하나씩 꺼내와 board에 담는다 -->
                                    <tr>
                                            <td>${board.boardNo }</td> <!--글번호 -->
                                            <td>${board.categoryName }</td> <!--카테고리명 -->
                                            <td class="boardTitle">
                                                    ${board.boardTitle }
                                            </td> <!--제목 -->
                                            <td>${board.memberId }</td> <!--작성자 -->
                                            <td>${board.readCount }</td> <!-- 조회수 -->
                                            
                                            <td>
                                                    <%-- 날짜 출력 모양 지정 --%>
                            <fmt:formatDate var="createDate" value="${board.boardCreateDate}" pattern="yyyy-MM-dd"/>
                            <fmt:formatDate var="today" value="<%= new java.util.Date() %>" pattern="yyyy-MM-dd"/>
                            
                            <c:choose>
                            <%--  작성일이 오늘이 아닐 경우 --%>
                            <c:when test="${createDate != today}">
                                ${createDate }
                            </c:when>
                            
                            <%--  작성일이 오늘일 경우 --%>
                            <c:otherwise>
                                <fmt:formatDate value="${board.boardCreateDate}" pattern="HH:mm"/>
                                                            </c:otherwise>
                                                        </c:choose>
                                            </td> <!-- 날짜 출력 -->
                                    
                                    </tr>	 
                            </c:forEach>
                    </c:otherwise>
                </c:choose>
            </tbody>
        </table>
    </div>


    <%-- 로그인이 되어있는 경우 --%>
    <c:if test="${!empty loginMember }">
            <button type="button" class="btn btn-primary float-right" id="insertBtn" 
                    onclick="location.href = '${contextPath}/board/insertForm.do'">글쓰기</button>
    </c:if>
    
    
        <%---------------------- Pagination ----------------------%>
    <%-- 페이징 처리 주소를 쉽게 사용할  있도록 미리 변수에 저장 --%>
    <c:choose>
            <%-- 검색 내용이 파라미터에 존재할 때 == 검색을 통해 만들어진 페이지인가? --%>        
        <c:when test="${!empty param.sk && !empty param.sv }">
                <c:url var="pageUrl" value="/search.do"/>
                
                <%-- 쿼리스트링으로 사용할 내용을 변수에 저장함. --%>
                <c:set var="searchStr" value="&sk=${param.sk }&sv=${param.sv }"/>
        </c:when>
        <c:otherwise>
                    <c:url var="pageUrl" value="/board/list.do"/>
        </c:otherwise>
    </c:choose>
    
    
    <!-- 화살표에 들어갈 주소를 변수로 생성 -->
    <%-- 검색을  했을  : /board/list.do?cp=1 
            검색을 했을  : /search.do?cp=1&sk=title&sc=49   --%>
    <c:set var="firstPage" value="${pageUrl}?cp=1${searchStr }"/>
    <c:set var="lastPage" value="${pageUrl}?cp=${pInfo.maxPage}${searchStr }"/>
    
    <%-- EL을 이용한 숫자 연산의 단점 : 연산이 자료형에 영향을 받지 않는다. --%>
    <%-- <fmt:parseNumber> : 숫자 형태를 지정하여 변수 선언 
    integerOnly="true" : 정수로만 숫자 표현 (소수점 버림)
    --%>

        
        <fmt:parseNumber var="c1" value="${(pInfo.currentPage - 1)/10}" integerOnly="true"/>
    <fmt:parseNumber var="prev" value="${c1 * 10}" integerOnly="true"/>
    <c:set var="prevPage" value="${pageUrl}?cp=${prev}${searchStr }"/> <!-- /board/list/do?cp=10  -->
    
    <fmt:parseNumber var="c2" value="${(pInfo.currentPage + 9)/10}" integerOnly="true"/>
    <fmt:parseNumber var="next" value="${c2 * 10 + 1}" integerOnly="true"/>
    <c:set var="nextPage" value="${pageUrl}?cp=${next}${searchStr }"/>

                
        <div class="my-5">
    <ul class="pagination">
    
        <%-- 현재 페이지가 10페이지 초과인 경우 --%>
        <c:if test="${pInfo.currentPage>10}">
            <li><!-- 첫 페이지로 이동(<<) -->
                <a class="page-link" href="${firstPage}">&lt;&lt;</a>
            </li>
            
            <li> <!-- 이전 페이지로 이동(<) -->
                <a class="page-link" href="${prevPage}">&lt;</a>
            </li>
        </c:if>

                
                <!-- 페이지 목록 -->
        <c:forEach var="page" begin="${pInfo.startPage}" end="${pInfo.endPage}">
        <!-- 1부터 10까지  1씩(step설정이 안되어있다면) 순차적으로 증가해라 -->
            <c:choose>
                <c:when test="${pInfo.currentPage == page}">
                <li>
                    <a class="page-link">${page}</a> 
                    <!-- a태그의 href를 없애서 클릭이 안되게 한다.  -->
                </li>
                </c:when>
                
                <c:otherwise>
                <li>
                    <a class="page-link" href="${pageUrl}?cp=${page}${searchStr }">${page}</a>
                </li>
                </c:otherwise>
            </c:choose>
        </c:forEach>
                <%-- 현재 페이지가 마지막 페이지 미만인 경우 --%>
        <c:if test="${next < pInfo.maxPage}">
            <li> <!-- 다음 페이지로 이동(>) -->
                <a class="page-link" href="${nextPage}">&gt;</a>
            </li>
            <li><!-- 마지막 페이지로 이동(>>) -->
                <a class="page-link" href="${lastPage}">&gt;&gt;</a>
            </li>
            
        </c:if>

    </ul>
    </div>
        <!-- 검색창 -->
    <div class="my-5">
        <form action="${contextPath }/search.do" method="GET" class="text-center" id="searchForm">
            <select name="sk" class="form-control" style="width: 100px; display: inline-block;">
                <option value="title">글제목</option>
                <option value="content">내용</option>
                <option value="titcont">제목+내용</option>
                <option value="writer">작성자</option>
            </select>
            <input type="text" name="sv" class="form-control" style="width: 25%; display: inline-block;">
            <button class="form-control btn btn-primary" style="width: 100px; display: inline-block;">검색</button>
        </form>
			</div>
	</div>
	<jsp:include page="../common/footer.jsp"></jsp:include>


	<script>
    // 게시글 상세보기 기능 (jquery를 통해 작업)
    
    $("#list-table td").on("click",function(){
       
       // 게시글 번호 얻어오기
       var boardNo = $(this).parent().children().eq(0).text();
       //console.log(boardNo);
       // 클릭이 되는지 테스트
       
       var url = "${contextPath}/board/view.do?cp=${pInfo.currentPage}&no="+boardNo + "${searchStr}";
       	// 검색
       location.href = url;
       
			
		});
    
    
    // 검색 내용이 있을 경우 검색창에 해당 내용을 작성해두는 기능
    (function(){
    	var searchKey = "${param.sk}";
    	// 파라미터 중 sk가 있을 경우  ex ) "49"
    	// 파라미터 중 sk가 없을 경우 ex ) 빈문자열
    	
    	var searchValue = "${param.sv}";
    	
    	// 검색창 select의 option을 반복 접근
    	$("select[name=sk]>option").each(function(index,item){
    		// index : 현재 접근중인 요소의 인덱스
    		// item : 현재 접근중인 요소
    		
    		if($(item).val() == searchKey){
    			$(item).prop("selected",true);
    		}
    	});
    	
    	// 검색어 입력창에 searchValue 값 출력
    	$("input[name=sv]").val(searchValue);
    })();
	</script>
</body>
</html>



전체 SearchController.java

package com.kh.wsp.search.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
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.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;
import com.kh.wsp.search.model.service.SearchService;

@WebServlet("/search.do")
public class SearchController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		
		String searchKey = request.getParameter("sk");
		String searchValue = request.getParameter("sv");
		String cp = request.getParameter("cp");
	
		try {
			SearchService service = new SearchService();
			
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("searchKey", searchKey);
			map.put("searchValue", searchValue);
			map.put("currentPage", cp);
			
			// 페이징 처리를 위한 데이터를 계산하고 저장하는 객체 PageInfo 얻어오기
			PageInfo pInfo = service.getPageInfo(map);
			
			// 검색 게시글 목록 조회
			List<Board> bList = service.searchBoardList(map, pInfo);
			
			// 결과 확인
//			System.out.println(pInfo);
//			for(Board b:bList) {
//				System.out.println(b);
//			}
			
			// 조회된 내용과 PageInfo 객체를 request객체에 담아서 요청 위임
			String path = "/WEB-INF/views/board/boardList.jsp";
			
			request.setAttribute("bList", bList);
			request.setAttribute("pInfo", pInfo);
			
			RequestDispatcher view = request.getRequestDispatcher(path);
			view.forward(request, response);
			
		}catch(Exception e) {
			e.printStackTrace();
			String path = "/WEB-INF/views/common/errorPage.jsp";
			request.setAttribute("errorMsg", "검색 과정에서 오류 발생");
			RequestDispatcher view = request.getRequestDispatcher(path);
			view.forward(request, response);
		}
	
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		doGet(request, response);
	}
}



전체 SearchService.java

package com.kh.wsp.search.model.service;

import static com.kh.wsp.common.JDBCTemplate.*;

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

import com.kh.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;
import com.kh.wsp.search.model.dao.SearchDAO;

public class SearchService {
	private SearchDAO dao = new SearchDAO();

	
	
	/** 검색 내용이 포함된 페이징 처리 정보 생성 service
	 * @param map
	 * @return pInfo
	 * @throws Exception
	 */
	public PageInfo getPageInfo(Map<String, Object> map) throws Exception {
		Connection conn = getConnection();
		
		// 얻어온 파라미트 cp 가  null이면 1, 아니면 int형으로 파싱
		map.put("currentPage", (map.get("currentPage") == null )? 1 : Integer.parseInt((String)map.get("currentPage")) );
		
		// 검색 조건에 따른 SQL 조건문을 조합하는 메소드 호출
		String condition = createCondition(map);
		
		// SQL 조건문을 map에 추가
		map.put("condition", condition);
		
		// DB에서 조건을 만족하는 게시글의 수를 조회하기
		int listCount = dao.getListCount(conn, condition);
		
		close(conn);
		
		
		// PageInfo 객체를 생성하여 반환
		return new PageInfo((int)map.get("currentPage"), listCount) ;
	}


	
	/** 검색 조건에 따라 SQL에 사용될 조건문을 조합하는 메소드
	 * @param map
	 * @return 
	 */
	private String createCondition(Map<String, Object> map) {
		String condition = null;
		
		String searchKey = (String)map.get("searchKey");
		String searchValue = (String)map.get("searchValue");
		
		
		// 검색 조건(searchKey)에 따라 SQL 조합
    switch(searchKey) {
                                // "BOARD_TITLE LIKE '%' || '49' || '%'"
    case "title" : condition = " BOARD_TITLE LIKE '%' || '" + searchValue + "' || '%' "; break;
    case "content" : condition = " BOARD_CONTENT LIKE '%' || '" + searchValue + "' || '%' "; break;
    case "titcont" : condition = " (BOARD_TITLE LIKE '%' || '" + searchValue + "' || '%' "
                                    + "OR BOARD_CONTENT LIKE '%' || ' " + searchValue + "' || '%') "; break;
    case "writer" : condition = " MEMBER_ID LIKE '%' || '" + searchValue + "' || '%' "; break;
    }
		return condition;
	}



	/** 검색 게시글 목록 리스트 조회 Service
	 * @param map
	 * @param pInfo 
	 * @return bList
	 * @throws Exception
	 */
	public List<Board> searchBoardList(Map<String, Object> map, PageInfo pInfo) throws Exception {
		Connection conn = getConnection();
		
		String condition = createCondition(map);
		
		List<Board> bList = dao.searchBoardList(conn, pInfo, condition);
		
		close(conn);
		
		return bList;
	}

}



전체 SearchDAO.java

package com.kh.wsp.search.model.dao;

import static com.kh.wsp.common.JDBCTemplate.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.kh.wsp.board.model.vo.Board;
import com.kh.wsp.board.model.vo.PageInfo;

public class SearchDAO {
	
	private Statement stmt = null;
	private PreparedStatement pstmt = null;
	private ResultSet rset = null;
	
	
	
	/** 조건을 만족하는 게시글 수 조회 DAO
	 * @param conn
	 * @param condition
	 * @return listCount
	 * @throws Exception
	 */
	public int getListCount(Connection conn, String condition) throws Exception {
		int listCount = 0;
		
		String query = "SELECT COUNT(*) FROM V_BOARD WHERE BOARD_STATUS = 'Y' AND " + condition;
		
		try {
			stmt = conn.createStatement();
			
			rset = stmt.executeQuery(query);
			
			if(rset.next()) {
				listCount = rset.getInt(1);
			}
		}finally {
			close(rset);
			close(stmt);
		}
		return listCount;
	}



	/** 검색 게시글 목록 조회 DAO
	 * @param conn
	 * @param pInfo
	 * @param condition
	 * @return
	 * @throws Exception
	 */
	public List<Board> searchBoardList(Connection conn, PageInfo pInfo, String condition) throws Exception {
		List<Board> bList = null;
		
		String query  = "SELECT * FROM" + 
			            "    (SELECT ROWNUM RNUM , V.*" + 
			            "    FROM" + 
			            "        (SELECT * FROM V_BOARD " + 
			            "        WHERE " + condition +
			            "        AND BOARD_STATUS = 'Y' ORDER BY BOARD_NO DESC) V )" + 
			            "WHERE RNUM BETWEEN ? AND ?";
		
		try {
			// SQL 구문 조건절에 대입할 변수 생성
			int startRow = (pInfo.getCurrentPage()-1) * pInfo.getLimit() +1;
			int endRow = startRow + pInfo.getLimit()-1;
			// 500개의 글 중에서 1페이지에 해당하는 글을 가져옴 : 500~491번째 글만 가져오게 됨.
			
			pstmt = conn.prepareStatement(query);
			pstmt.setInt(1, startRow);
			pstmt.setInt(2, endRow);
			
			rset = pstmt.executeQuery();
			
			bList = new ArrayList<Board>();
			
			while(rset.next()) {
				Board board = new Board(rset.getInt("BOARD_NO"), 
						rset.getString("BOARD_TITLE"), rset.getString("MEMBER_ID"), 
						rset.getInt("READ_COUNT"), 
						rset.getString("CATEGORY_NM"), rset.getTimestamp("BOARD_CREATE_DT"));
				bList.add(board);
			}
			
		}finally {
			close(rset);
			close(pstmt);
		}
		return bList;
	}

}



전체 boardInsert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판</title>
<style>
    .insert-label {
      display: inline-block;
      width: 80px;
      line-height: 40px
    }
    
    .boardImg{
    	cursor : pointer;
    }
</style>
</head>
<body>
    <jsp:include page="../common/header.jsp"></jsp:include>

    <div class="container my-5">

    <h3>게시글 등록</h3>
    <hr>
    <!-- 파일 업로드를 위한 라이브러리 cos.jar 라이브러리 다운로드(http://www.servlets.com/) -->
    
    <!-- 
        - enctype : form 태그 데이터가 서버로 제출 될 때 인코딩 되는 방법을 지정. (POST 방식일 때만 사용 가능)
        - application/x-www-form-urlencoded : 모든 문자를 서버로 전송하기 전에 인코딩 (form태그 기본값)
        - multipart/form-data : 모든 문자를 인코딩 하지 않음.(원본 데이터가 유지되어 이미지, 파일등을 서버로 전송 할 수 있음.) 
    -->
    <form action="${contextPath }/board/insert.do" method="post" 
            enctype="multipart/form-data" role="form" onsubmit="return boardValidate();">
        <div class="mb-2">
            <label class="input-group-addon mr-3 insert-label">카테고리</label> 
            <select	class="custom-select" id="categoryCode" name="categoryCode" style="width: 150px;">
                <option value="10">운동</option>
                <option value="20">영화</option>
                <option value="30">음악</option>
                <option value="40">요리</option>
                <option value="50">게임</option>
                <option value="60">기타</option>
            </select>
        </div>
        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">제목</label> 
            <input type="text" class="form-control" id="boardTitle" name="boardTitle" size="70">
        </div>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">작성자</label>
            <h5 class="my-0" id="writer">${loginMember.memberId }</h5>
        </div>


        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">작성일</label>
            <h5 class="my-0" id="today"></h5>
        </div>

        <hr>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">썸네일</label>
            <div class="boardImg" id="titleImgArea">
                <img id="titleImg" width="200" height="200">
            </div>
        </div>

        <div class="form-inline mb-2">
            <label class="input-group-addon mr-3 insert-label">업로드<br>이미지</label>
            <div class="mr-2 boardImg" id="contentImgArea1">
                <img id="contentImg1" width="150" height="150">
            </div>

            <div class="mr-2 boardImg" id="contentImgArea2">
                <img id="contentImg2" width="150" height="150">
            </div>

            <div class="mr-2 boardImg" id="contentImgArea3">
                <img id="contentImg3" width="150" height="150">
            </div>
        </div>


        <!-- 파일 업로드 하는 부분 -->
        <div id="fileArea">
            <input type="file" id="img0" name="img0" onchange="LoadImg(this,0)" multiple="multiple">
            <!-- multiple 속성 = 사진 여러개 선택 가능  --> 
            <input type="file" id="img1" name="img1" onchange="LoadImg(this,1)"> 
            <input type="file" id="img2" name="img2" onchange="LoadImg(this,2)"> 
            <input type="file" id="img3" name="img3" onchange="LoadImg(this,3)">
        </div>

        <div class="form-group">
            <div>
                <label for="content">내용</label>
            </div>
            <textarea class="form-control" id="boardContent" name="boardContent" rows="15" style="resize: none;"></textarea>
        </div>


        <hr class="mb-4">

        <div class="text-center">
            <button type="submit" class="btn btn-primary">등록</button>
            <button type="button" class="btn btn-primary">목록으로</button>
        </div>

    </form>
</div>

		<jsp:include page="../common/footer.jsp"></jsp:include>
		
		
	<script>
		(function printToday(){
			// 오늘 날짜 출력 
	 		var today = new Date();
			var month = (today.getMonth()+1);
			var date = today.getDate();
	
	  	var str = today.getFullYear() + "-"
	        		+ (month < 10 ? "0"+month : month) + "-"
	        		+ (date < 10 ? "0"+date : date);
			$("#today").html(str);
		})();

		// 유효성 검사 
		function boardValidate() {
			if ($("#boardTitle").val().trim().length == 0) {
				alert("제목을 입력해 주세요.");
				$("#title").focus();
				return false;
			}

			if ($("#content").val().trim().length == 0) {
				alert("내용을 입력해 주세요.");
				$("#content").focus();
				return false;
			}
		}
		
		// 이미지 영역을 클릭할 때 파일 첨부 창이 뜨도록 설정하는 함수
		// 페이지 로딩이 끝나고나면 #fileArea 요소를 숨김.
		$(function(){
			$("#fileArea").hide(); 
			
			$(".boardImg").on("click",function(){// 이미지 영역이 클릭 되었을 때
				// 클릭한 이미지 영역 인덱스 얻어오기
				var index = $(".boardImg").index(this);
				// 클릭된 요소가 .boardImg 중 몇 번째 인덱스인지 반환
				
				// console.log(index);
		
				// 클릭된 영역 인덱스에 맞는 input file 태그 클릭
				$("#img" + index).click();
			});
		});
		
	  // 각각의 영역에 파일을 첨부 했을 경우 미리 보기가 가능하도록 하는 함수
	  function LoadImg(value, num) {
		  // value.files == input태그에 파일이 업로드되어 있으면 true
		  // value.files[0] : 여러 파일 중 첫번째 파일이 업로드 되어있으면 true
		  if(value.files && value.files[0] ){ // 해당 요소에 업로드된 파일이 있을 경우
			  var reader = new FileReader();
            // 자바스크립트 FileReader
		    // 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 
		    // 읽을 파일을 가리키는 File 혹은 Blob객체를 이용해 파일의 내용을 읽고 
		    // 사용자의 컴퓨터에 저장하는 것을 가능하게 해주는 객체
		    
		    reader.readAsDataURL(value.files[0]);
            // FileReader.readAsDataURL()
            // 지정된의 내용을 읽기 시작합니다. 
            // Blob완료되면 result속성 data:에 파일 데이터를 나타내는 URL이 포함 됨.
	      
	      reader.onload = function(e){
            // FileReader.onload
            // load 이벤트의 핸들러. 
            // 이 이벤트는 읽기 동작이 성공적으로 완료 되었을 때마다 발생함.
            
            
            // 읽어들인 내용(이미지 파일)을 화면에 출력
            $(".boardImg").eq(num).children("img").attr("src", e.target.result);
            // e.target.result : 이벤트가 발생한 요소의 결과 , 파일 읽기 동작을 성공한 요소가 읽어들인 파일 내용
     		 }
		  }
		}
	</script>
</body>
</html>






댓글남기기