빅데이터 Java 개발자 교육 - 49일차[JSP 실습]

Jun_Gyu·2023년 4월 10일
0
post-thumbnail

오늘은 게시글 이미지 첨부기능을 추가할 예정이다.

물품별로 사진 한개등록 및 일괄등록 기능을 추가하였다.

사진이 추가되면 default 이미지가 현재 등록하려는 이미지로 변경되도록 코드를 구성해주었다.

구성된 코드는 아래와 같다.

물품조회하기 이미지등록 기능 추가

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>물품목록 조회하기</title>
<!-- Include stylesheet -->
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css"
	rel="stylesheet">
<!-- Bootstrap -->
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
	rel="stylesheet"
	integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
	crossorigin="anonymous">
</head>
<body style="color: rgba(123, 123, 102, 0.5);">
	<div id="layoutAuthentication">
		<div id="layoutAuthentication_content">
			<main>
				<div class="container">
					<div class="row justify-content-center">
						<div class="col-lg">
							<div class="card shadow-lg border-0 rounded-lg mt-5 mb-5">
								<div class="card-header">
									<h3 class="text-left font-weight-light my-4">물품 조회하기</h3>
								</div>
								<div class="card-body">
									<form class="form-inline d-flex justify-content-first"
										method="GET" th:action="@{/item/itemList}"
										th:value="${param.searchText}">
										<div class="col-auto">
											<input type="text" class="form-control" id="searchText"
												name="searchText" placeholder="검색어">
										</div>
										<div class="col-auto">
											<button type="submit" class="btn btn-success mb-2">검색</button>
										</div>
									</form>
									<table class="table">
										<thead>
											<tr>
												<th scope="col">no</th>
												<th scope="col">name</th>
												<th scope="col">content</th>
												<th scope="col">quantity</th>
												<th scope="col">price</th>
												<th scope="col">date</th>
												<th scope="col">image</th>
											</tr>
										</thead>
										<tbody>
											<c:forEach var="obj" items="${list}">
												<tr>
													<td scope="row"><a href="#">${obj.no}</td>
													<td>${obj.name}</a></td>
													<td>${obj.content}</td>
													<td>${obj.quantity}</td>
													<td>${obj.price}</td>
													<td>${obj.regdate}</td>
													<td><a href="imagewrite.do?ino=${obj.no}" 
														   class="btn btn-warning">이미지등록</a></td>
												</tr>
											</c:forEach>
										</tbody>
									</table>
									<hr />

									<div class="col-auto">
										<a type="button" class="btn btn-success"
											href="/web01/item/write.do">물품등록하기</a>
									</div>
									<br>
									<div class="col-auto pull-end">
										<ul id="pagination-demo"
											class="pagination-sm d-flex justify-content-center"></ul>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</main>
		</div>
	</div>
	<!-- axios CDN -->
	<script
		src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.5/axios.min.js"
		integrity="sha512-nnNHpffPSgINrsR8ZAIgFUIMexORL5tPwsfktOTxVYSv+AUAILuFYWES8IHl+hhIhpFGlKvWFiz9ZEusrPcSBQ=="
		crossorigin="anonymous" referrerpolicy="no-referrer"></script>
	<!-- jQuery CDN -->
	<script src="https://code.jquery.com/jquery-3.6.4.min.js"
		integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8="
		crossorigin="anonymous"></script>
	<!-- twbsPagination CDN -->
	<script
		src="https://cdnjs.cloudflare.com/ajax/libs/twbs-pagination/1.4.2/jquery.twbsPagination.min.js"
		integrity="sha512-frFP3ZxLshB4CErXkPVEXnd5ingvYYtYhE5qllGdZmcOlRKNEPbufyupfdSTNmoF5ICaQNO6SenXzOZvoGkiIA=="
		crossorigin="anonymous" referrerpolicy="no-referrer"></script>
	<script>
	
	
		$(function() {
			$('#pagination-demo').twbsPagination({
				totalPages : Number('${pages}'),
				visiblePages : 10,
				first : '◀',
				last : '▶',
				next : '▷',
				prev : '◁',
				initiateStartPageClick : false,
				// 페이지 활성화 (주소창에 있는 페이지값을 받아오는 역할)
				startPage : Number('${param.page}'),
				onPageClick : function(event, page) {
					/*$('#page-content').text('Page ' + page);*/
					window.location.href = "select.do?page=" + page;
				}
			});
		});
	</script>
</body>
</html>

개별 물품 사진등록 및 일괄등록

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>물품이미지 등록하기</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<!-- Include stylesheet -->
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css"
	rel="stylesheet">
<!-- Bootstrap -->
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
	rel="stylesheet"
	integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
	crossorigin="anonymous">
</head>
<body style="color: rgba(123, 123, 102, 0.5);">
	<div id="layoutAuthentication">
		<div id="layoutAuthentication_content">
			<main>
				<div class="container">
					<div class="row justify-content-center">
						<div class="col-lg">
							<div class="card shadow-lg border-0 rounded-lg mt-5">
								<div class="card-header">
									<h3 class="text-left font-weight-light my-4">🖼️ 물품이미지 등록</h3>
								</div>

								<form id="form"
									action="${pageContext.request.contextPath}/item/imagewrite.do"
									method="post" enctype="multipart/form-data">
									<div class="card-body">
										<div class="form-floating mt-3 mb-3">
											<input type="text" name="ino" class="form-control" id="ino"
												for="floatingInput" value="${ino}" readonly> <label
												for="ino" class="form-label">물품번호</label>
										</div>
										<c:forEach var='tmp' items="${no}">
											<img src="${pageContext.request.contextPath}/item/image?no=${tmp}"
												style="width: 70px; height: 70px">
										</c:forEach>
										<hr />
										<div class="form-floating mt-3 mb-3">
											<img
												src="${pageContext.request.contextPath}/resources/images.png"
												id="img"
												style="width: 250px; height: 200px; cursor: pointer;"
												onclick="clickImage()"> <input type="file" name="file"
												id="file" class="form-control" style="display: none"
												onchange="imageChange(this)" />
										</div>

										<input type="button" value="등록" id="commit" name="commit"
											class="btn btn-success btn-right" onclick="insertItemImage()"></input>
										<a class="btn btn-secondary" href="select.do">목록으로</a>
									</div>
								</form>
								<form id="form1" action="imagewritebatch.do?ino=${ino}"
									method="post" enctype="multipart/form-data">
									<div class="card-body">
										<hr />
										<div class="form-floating mt-3 mb-3">
											<c:forEach var="idx" begin="1" end="3" step="1">
												<img
													src="${pageContext.request.contextPath}/resources/images.png"
													class="imgs"
													style="width: 50px; height: 50px; cursor: pointer;">
												<input type="file" name="file[]"
													onchange="imageChangeBatch(this, '${idx-1}')" />
												<br />
											</c:forEach>
										</div>
										<input type="button" value="일괄등록"
											class="btn btn-success btn-right"
											onclick="insertImageBatch()"></input> <a
											class="btn btn-secondary" href="select.do">목록으로</a>
									</div>
								</form>
							</div>
						</div>
					</div>
				</div>
			</main>
		</div>
	</div>
	<!-- sweetalert2 -->
	<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
	<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
	<script>
		// document.getElementById("아이디"); id가 일치하는 1개 찾기
		// document.getElementsByName("name값"); name값이 일치하는 n개 찾기
		// document.getElementsByClassName("class값"); class값이 일치하는 n개 찾기
		// document.getElementsByTagName("img"); 태그가 img인 n개 찾기

		const imgs = document.getElementsByClassName("imgs");
		const files = document.getElementsByName("file[]")
		function imageChangeBatch(e, idx) {
			if (e.files.length > 0) {
				imgs[Number(idx)].src = URL.createObjectURL(e.files[0]); // 가상의 url 정보를 생성해 추가. 
			} else {
				imgs[Number(idx)].src = "${pageContext.request.contextPath}/resources/images.png";
			}
		}

		function insertImageBatch() { // no image 이미지를 클릭하면 "file"이라는 id의 기능을 실행시킨다. (이미지 추가하기 기능. 현재 감춰둔 상태)
			console.log(imgs);
			console.log(imgs.length);
			console.log(e.files);
			console.log(idx);
		}

		function clickImage() {
			document.getElementById("file").click();
		}

		function clickImages() {
			document.getElementById("file[]").click();
		}

		function imageChange(e) {
			console.log(e.files);
			if (e.files.length > 0) { // 첨부
				// 파일을 첨부하면 크롬에서 "blob:http://127.0.0.1:8080/...."라고 콘솔창에 띄운다.
				console.log(URL.createObjectURL(e.files[0])); // 확인용

				img.src = URL.createObjectURL(e.files[0]); // 가상의 url 정보를 생성해서 추가함. 
			} else { // 취소
				img.src = "${pageContext.request.contextPath}/resources/images.png";
			}
		}

		function insertItemImage() {
			// 유효성 검사 한 후 
			const file = document.getElementById("file");
			if (file.value.length <= 0) {
				Swal.fire('첨부된 파일이 없어요!', '이미지가 없으면 등록할 수 없어요.', 'error')
				return false;
			}
			// form 태그 전송
			document.getElementById("form").submit();

			Swal.fire({
				icon : 'success',
				title : '이미지등록 완료!',
				showConfirmButton : false,
				timer : 2000
			})
		}

		function insertImageBatch() {
			// 유효성 검사		
			var chk = 0;
			for (let i = 0; i < imgs.length; i++) { // 반복문으로 files 전체 유효성 검사
				if (files[i].value.length <= 0) {
					Swal.fire('첨부된 파일이 없어요!', '이미지가 없으면 등록할 수 없어요.', 'error')
					chk = 1;
					break; // 반복문 종료
				}
			}
			if (chk === 0) { // if문을 한번도 수행하지 않았다면,
				//form 전송
				document.getElementById("form1").submit();

				Swal.fire({
					icon : 'success',
					title : '이미지등록 완료!',
					showConfirmButton : false,
					timer : 2000
				})
			}
		}
	</script>
</body>
</html>

등록하려는 이미지 파일을 표시하기 위해서는 이미지의 URL 정보가 필요한데, 가상으로 url의 정보를 생성하여 이를 기본이미지와 바꿔 출력시키는 방법으로 코드를 구성하였다.

등록된 이미지들은 각각 fk키인 물품넘버, 이미지 파일명, 크기, 파일종류, 파일 데이터 등을 토대로 mapper를 통해 DB로 전송해주었다.

아래는 3개의 이미지를 일괄 등록해주는 Controller(servlet)이다.

일괄등록 Controller

package jejuWebController;

import java.io.IOException;
import java.util.Collection;

import config.MyBatisContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import webdto.ItemImage;
import webmapper.ItemImageMapper;

@WebServlet(urlPatterns = { "/item/imagewritebatch.do" })
@MultipartConfig
public class ItemImageWriteBatch extends HttpServlet {
	private static final long serialVersionUID = 1L;

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

		long ino = Long.parseLong(request.getParameter("ino"));
		request.setAttribute("ino", ino);
		request.getRequestDispatcher("/WEB-INF/item_img_write.jsp").forward(request, response);

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		long ino = Long.parseLong(request.getParameter("ino"));
		Collection<Part> partlist = request.getParts();

		for (Part part : partlist) {
			if (part.getName().equals("file[]")) {
				ItemImage obj = new ItemImage();
				obj.setItemno(ino);
				obj.setFilename(part.getSubmittedFileName()); // 첨부한 파일명
				obj.setFilesize(part.getSize()); // 첨부한 파일크기
				obj.setFiletype(part.getContentType()); // 첨부한 파일의 종류 (gif, jpg, png ...)
				obj.setFiledata(part.getInputStream().readAllBytes()); // 첨부한 파일 실제 데이터

				System.out.println(obj);

				int ret = MyBatisContext.getSqlSession().getMapper(ItemImageMapper.class).insertItemImage(obj);
			}
		}
		response.sendRedirect("imagewrite.do?ino=" + ino);

	}
}

form 태그 만들어서 사용해보기

<form action="write.do" method="post">
<form action="/write.do" method="post">는 완전히 다른 형태이다.
첫번째의 경우에는 이미 지정된 write.do의 주소로 보내는 형태라면, 두번째의 경우에는 /앞에 오는 주소지에 따라서 아예 다른곳으로 보낼 수 있다는 것이다.

유효성 검사

위의 코드들에서도 참고하였듯, 유효성 검사의 경우에는 각 업로드된 파일들의 value 길이를 기반으로 0 이하면 업로드 자체가 되지 않았다는 것이므로, 이 조건의 경우에 alert창을 띄워 저장이 되지 않도록 하였다.

function insertImageBatch() {
			// 유효성 검사		
			var chk = 0;
			for (let i = 0; i < imgs.length; i++) { // 반복문으로 files 전체 유효성 검사
				if (files[i].value.length <= 0) {
					Swal.fire('첨부된 파일이 없어요!', '이미지가 없으면 등록할 수 없어요.', 'error')
					chk = 1;
					break; // 반복문 종료
				}
			}
			if (chk === 0) { // if문을 한번도 수행하지 않았다면,
				//form 전송
				document.getElementById("form1").submit();

				Swal.fire({
					icon : 'success',
					title : '이미지등록 완료!',
					showConfirmButton : false,
					timer : 2000
				})
			}
		}

절대경로 & 상대경로

// 절대 경로를 이용한 페이지 이동
response.sendRedirect(request.getContextPath() + "/item" + select.do"); 
// 상대 결로를 이용한 페이지 이동
response.sendRedirect("select.do");
profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글