본문 바로가기
ICIA 수업일지

2021.10.14 수업일지(Spring Framework, Fileupload)

by 주성씨 2021. 10. 14.

- 어제와 마찬가지로 게시글 등록, 파일 업로드 등을 해보도록 하겠다.

- 테이블에서 DB와 user 생성을 확인한다.

- go to mysql

# 2021.10.14
create user 'product'@'localhost' identified by 'pass';
create database productdb;
grant all privileges on productdb.* to 'product'@'localhost';

 

- productDB에 아래와 같은 테이블을 만들어준다.

#2021.10.14
create table tbl_product(
	pcode char(4) primary key not null,
    pname nvarchar(200) not null,
    price int default 0,
    image nvarchar(200)
);

desc tbl_product;

insert into tbl_product(pcode, pname, price, image)
values('P110', '[14k Gold] 스테디 원터치 링귀걸이', 99900, 'img01.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P111', '[14kGold] 델리아 원터치 링귀걸이', 69900, 'img02.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P112', '[925실버] 샤인모션 원터치 링귀걸이', 20900, 'img03.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P113', '[925실버] 볼앤하트 원터치 링귀걸이', 17900, 'img04.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P114', '[14kGold] 셔링 귀걸이', 49900, 'img05.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P115', '[14kGold] 메르시 목걸이', 15900, 'img06.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P116', '[925실버] 트윈 원터치 링귀걸이', 20800, 'img07.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P117', '[14kGold] 한쪽 베이직 금볼 귀걸이', 7900, 'img08.jpg');

create table tbl_attach(
	image nvarchar(200) primary key not null,
    pcode char(4),
    foreign key(pcode) references tbl_product(pcode)
);

 

다이어그램 확인

 

- C:\data\product 해당 경로에 테이블에서 insert한 이미지가 들어가 있는지 확인하고 없으면 넣어주자. 

확인

 

- 스프링과 DB연결 유저 설정 및 파일 업로드 경로 확인

/ex08/src/main/webapp/WEB-INF/spring/root-context.xml

.....
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="url" value="jdbc:log4jdbc:mysql://127.0.0.1:3306/productdb"></property>
		<property name="username" value="product"></property>
		<property name="password" value="pass"></property>
	</bean>
.....

 

/ex08/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml

.....
	<!-- 파일 업로드 루트 설정 -->
	<beans:bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<beans:property name="maxUploadSize" value="10485760" />
	</beans:bean>
	<beans:bean id="uploadPath" class="java.lang.String">
		<beans:constructor-arg value="c:/data/product" />
	</beans:bean>
</beans:beans>

 

정상구동 확인

 

 

- SQL 문 실행을 위한 mapper 생성하겠다.

/ex08/src/main/resources/mapper/ProductMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.ProductMapper">
	<select id="list" resultType="com.example.domain.ProductVO">
		select * from tbl_product order by pcode desc limit 0,8
	</select>
	<insert id="insert">
		insert into tbl_product(pcode, pname, image, price)
		values(#{pcode},#{pname},#{image},#{price})
	</insert>
	<select id="read" resultType="com.example.domain.ProductVO">
		select * from tbl_product where pcode=#{pcode}
	</select>
	<update id="update">
		update tbl_product set pname=#{pname},image=#{image},price=#{price}
		where pcode=#{pcode}
	</update>
	<delete id="delete">
		delete from tbl_product where pcode=#{pcode}
	</delete>
</mapper>

 

- 출력 타입인 VO를 생성하겠다.

/ex08/src/main/java/com/example/domain/ProductVO.java

package com.example.domain;

import java.util.ArrayList;

import com.fasterxml.jackson.annotation.JsonFormat;

public class ProductVO {
	private String pcode;
	private String pname;
	private int price;
	private String image;

	// 멀티 이미지 
	private ArrayList<String> images;
    ... getter, setter, tostring

 

- 인터페이스 생성한다.

/ex08/src/main/java/com/example/mapper/ProductDAO.java

package com.example.mapper;

import java.util.List;

import com.example.domain.ProductVO;

public interface ProductDAO {
	public List<ProductVO> list();
	public void insert(ProductVO vo);
	public void update(ProductVO vo);
	public void delete(String pcode);
	public ProductVO read(String pcode);
}

 

- 인터페이스를 메서드를 오버라이드하라 임플리먼트를 생성한다.

/ex08/src/main/java/com/example/mapper/ProductDAOImpl.java

package com.example.mapper;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.example.domain.ProductVO;

@Repository
public class ProductDAOImpl implements ProductDAO {
	@Autowired
	SqlSession session;

	// mapper와의 연결을 위한 변수
	String namespace = "com.example.mapper.ProductMapper";

	@Override
	public List<ProductVO> list() {
		return session.selectList(namespace + ".list");
	}

	@Override
	public void insert(ProductVO vo) {
		session.insert(namespace + ".insert", vo);
	}

	@Override
	public void update(ProductVO vo) {
		session.update(namespace + ".update", vo);
	}

	@Override
	public void delete(String pcode) {
		session.delete(namespace + ".delete", pcode);
	}

	@Override
	public ProductVO read(String pcode) {
		return session.selectOne(namespace+".read", pcode);
	}

}

 

- 파일 업로드할 sql 문을 작성하겠다.

/ex08/src/main/resources/mapper/AttachMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.AttachMapper">
	<select id="list" resultType="string">
		select * from tbl_attach where pcode=#{pcode}
	</select>
	<insert id="insert">
		insert into tbl_attach(pcode,image)
		values(#{pcode},#{image})
	</insert>
	<delete id="delete">
		delete from tbl_attach
		where image=#{image}
	</delete>
</mapper>

 

/ex08/src/main/java/com/example/mapper/AttachDAO.java

package com.example.mapper;

import java.util.List;

public interface AttachDAO {
	public List<String> list(String pcode);
	public void insert(String pcode, String image);
	public void delete(String image);
}

 

/ex08/src/main/java/com/example/mapper/AttachDAOImpl.java

package com.example.mapper;

import java.util.HashMap;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class AttachDAOImpl implements AttachDAO {

	@Autowired
	SqlSession session;

	String namespace = "com.example.mapper.AttachMapper";

	@Override
	public List<String> list(String pcode) {
		return session.selectList(namespace + ".list", pcode);
	}

	@Override
	public void insert(String pcode, String image) {
		HashMap<String, Object> map = new HashMap<>();
		map.put("image", image);
		map.put("pcode", pcode);
		session.insert(namespace + ".insert", map);
	}

	@Override
	public void delete(String image) {
		session.delete(namespace + ".delete", image);
	}

}

 

db 연결 확인

 

- 메인페이지를 만들겠다.

- 이전에 사용했던 아래의 데이터를 해당 프로젝트에 넣어준다.

 

/ex08/src/main/webapp/WEB-INF/views/home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page session="false"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<html>
<head>
<title>Home & Shopping</title>
<link rel="stylesheet" href="/resources/home.css" />
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
	src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
</head>
<body>
	<div id="page">
		<div id="header">
			<a href="/" title="홈으로"><img src="/resources/back.png" width=960 /></a>
		</div>
		<div id="center">
			<div id="menu">
				<a href="/insert" title="상품등록">상품등록</a>
			</div>
			<div id="content"></div>
		</div>
		<div id="footer">
			<h3>Copyright H&S. All rights Reserved.</h3>
		</div>
	</div>
</body>
</html>

 

확인

 

- 이제 #content에 넣을 페이지를 만들겠다.

- 우선 컨트롤러를 생성하겠다.

/ex08/src/main/java/com/example/controller/ProductController.java

package com.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.domain.ProductVO;
import com.example.mapper.ProductDAO;

@Controller
public class ProductController {
	@Autowired
	ProductDAO pdao;
	
	@RequestMapping("/list.json")
	@ResponseBody
	public List<ProductVO> list(){
		return pdao.list();
	}
}

 

- 이 json 을 list page에 출력하겠다.

/ex08/src/main/webapp/WEB-INF/views/list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<style>
#products {
	margin : 0px auto;
	width: 900px;
	overflow: hidden;
}

.box {
	width: 200px;
	height:200px;
	float: left;
	border: 1px solid rgb(224, 224, 235);
	margin: 5px;
	padding: 5px;
	box-shadow:5px 5px 5px gray;
}
.pname{
	text-overflow:ellipsis;
	white-space:nowrap;
	overflow: hidden;
}
img{
	cursor: pointer;
}
</style>
<h1>[상품 목록]</h1>
<div id="products"></div>
<script id="temp" type="text/x-handlebars-template">
	{{#each .}}
	<div class="box">
		<img src="/display?fileName={{image}}" width=150 
		onClick="location.href='read?pcode={{pcode}}'"/>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
<script>
	getList();
	function getList() {
		$.ajax({
			type : 'get',
			url : '/list.json',
			dataType : 'json',
			success : function(data) {
				var temp = Handlebars.compile($('#temp').html());
				$('#products').html(temp(data));
			}
		});
	}
</script>

 

/ex08/src/main/java/com/example/controller/HomeController.java

package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		model.addAttribute("pageName", "list.jsp");
		return "home";
	}

}

 

/ex08/src/main/webapp/WEB-INF/views/home.jsp

....
			<div id="content">
			<jsp:include page="${pageName}"></jsp:include>
			</div>
....

 

/ex07/src/main/java/com/example/controller/ProductController.java

package com.example.controller;

import java.io.File;
import java.nio.file.Files;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.domain.ProductVO;
import com.example.mapper.AttachDAO;
import com.example.mapper.ProductDAO;

@Controller
public class ProductController {
	@Autowired
	ProductDAO pdao;

	@Autowired
	AttachDAO adao;

	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	@RequestMapping("/list.json")
	@ResponseBody
	public List<ProductVO> list() {
		return pdao.list();
	}

	// 이미지파일 브라우저에 출력
	@RequestMapping("/display")
	@ResponseBody
	public ResponseEntity<byte[]> display(String fileName) throws Exception {
		ResponseEntity<byte[]> result = null;
		// display fileName이 있는 경우
		if (!fileName.equals("")) {
			File file = new File(path + File.separator + fileName);
			HttpHeaders header = new HttpHeaders();
			header.add("Content-Type", Files.probeContentType(file.toPath()));
			result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
		}
		return result;
	}
}

 

확인

 

- 이제 컨트롤러에 입력 페이지를 연결해주도록 하겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// insert
	@RequestMapping("/insert")
	public String insert(Model model){
		model.addAttribute("pageName","insert.jsp");
		return "home";
	}
.....

 

/ex08/src/main/webapp/WEB-INF/views/insert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<h1>[상품 등록]</h1>
<style>
form {
	overflow: hidden;
}
input[type=text]{
	width: 100%;
	margin: 10px 0px 10px 0px;
	border: none;
	border-bottom: 1px solid gray;
	font-size: 20px;
}
input[type=file]{
	margin: 10px 0px 10px 0px;
	display: none;
}
</style>
<form name="frm" enctype="multipart/form-data">
	<div>
		<img src="http://placehold.it/350x300" id="image" width=300 />
	</div>
	<div>
		<input type="text" name="pcode" readonly/> 
		<input type="text" name="pname" placeholder="상품명"/>
		<input type="text" name="price" value="0" />
		<input type="file" name="file"/>
	</div>
	<div>
		<input type="submit" value="상품등록" />
		<input type="reset" value="등록취소" />
	</div>
</form>

 

확인

 

- 상품코드를 테이블의 pcode 최대값을 구하여 넣어보겠다.

/ex07/src/main/resources/mapper/ProductMapper.xml

.....
	<select id="maxCode" resultType="string">
		select max(pcode) from tbl_product
	</select>
.....

 

....

	public String maxCode();
}

 

.....
	@Override
	public String maxCode() {
		return session.selectOne(namespace+".maxCode");
	}

}

 

/ex08/src/main/java/com/example/controller/ProductController.java

.....
	// insert
	@RequestMapping("/insert")
	public String insert(Model model){
		String maxCode=pdao.maxCode();
		String pcode = "P"+(Integer.parseInt(maxCode.substring(1))+1);
		model.addAttribute("pcode",pcode);
		model.addAttribute("pageName","insert.jsp");
		return "home";
	}
....

 

/ex08/src/main/webapp/WEB-INF/views/insert.jsp

.....
	<div>
		<input type="text" name="pcode" value="${pcode}" readonly/> 
		<input type="text" name="pname" placeholder="상품명"/>
		<input type="text" name="price" value="0" />
		<input type="file" name="file"/>
	</div>
......

 

확인

 

- 이미지 클릭시 해당 이미지 태그가 변경될 수 있도록 하겠다.

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

....
<script>
	// 이미지 tag 클릭시
	$('#image').on("click", function() {
		$(frm.file).click();
	})
	
	// 이미지를 삽입하거나 변경시
	$(frm.file).on("change", function(e) {
		var file = $(this)[0].files[0];
		$("#image").attr("src", URL.createObjectURL(file));
	})
</script>

 

확인

 

 

- 데이터 입력시 유효성 체크를 해보도록 하겠다.

/ex08/src/main/webapp/WEB-INF/views/insert.jsp

.....
<script>
	// submit시 유효성 체크
	$(frm).on("submit",function(e){
		e.preventDefault();
		var pcode = $(frm.pcode).val();
		var pname = $(frm.pname).val();
		var price = $(frm.price).val();
		var file = $(frm.file).val();
		if(pcode==""||pname==""||price==""||file==""){
			alert("입력란을 확인해주세요.");
			return;
		}
		if (price == '' || price.replace(/[0-9]/g, '')) {
			alert('가격을 숫자로 입력하세요.');
			$(frm.price).focus();
			return;
		}
		if(!confirm("상품을 등록하시겠습니까?"))return;
		frm.action="insert"
		frm.method="post"
		frm.submit();
	});
.....

 

- 컨트롤러에서 post해줄 수 있도록 하겠다.

/ex08/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// insert data
	@RequestMapping(value="/insert", method=RequestMethod.POST)
	public String insertPost(ProductVO vo, MultipartHttpServletRequest multi) throws Exception{
		System.out.println(vo.toString());
		// 대표이미지 업로드
		MultipartFile file=multi.getFile("file");
		String image = System.currentTimeMillis()+"_"+file.getOriginalFilename();
		file.transferTo(new File(path+"/"+image));
		vo.setImage(image);
		
		// 데이터 입력
		pdao.insert(vo);
		return "redirect:/";
	}
.....

 

확인

 

 

- 이제 read page를 만들어 update할 수 있도록 하겠다.

- insert.jsp를 복사해서 read.jsp를 만들겠다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<h1>[상품 정보]</h1>
<style>
form {
	overflow: hidden;
}
input[type=text]{
	width: 100%;
	margin: 10px 0px 10px 0px;
	border: none;
	border-bottom: 1px solid gray;
	font-size: 20px;
}
input[type=file]{
	margin: 10px 0px 10px 0px;
	display: none;
}
</style>
<form name="frm" enctype="multipart/form-data">
	<div>
		<img src="/display?fileName=${vo.image}" id="image" width=300 />
	</div>
	<div>
		<input type="text" name="pcode" value="${vo.pcode}" readonly/> 
		<input type="text" name="pname" value="${vo.pname}"/>
		<input type="text" name="price" value="${vo.price}" />
		<input type="file" name="file" accept="image/*"/>
	</div>
	<div>
		<input type="submit" value="상품수정" />
		<input type="reset" value="수정취소" />
	</div>
</form>
<script>
	// submit시 유효성 체크
	$(frm).on("submit",function(e){
		e.preventDefault();
		var pcode = $(frm.pcode).val();
		var pname = $(frm.pname).val();
		var price = $(frm.price).val();
		var file = $(frm.file).val();
		if(pcode==""||pname==""||price==""||file==""){
			alert("입력란을 확인해주세요.");
			return;
		}
		if (price == '' || price.replace(/[0-9]/g, '')) {
			alert('가격을 숫자로 입력하세요.');
			$(frm.price).focus();
			return;
		}
		if(!confirm("상품을 등록하시겠습니까?"))return;
		frm.action="insert"
		frm.method="post"
		frm.submit();
	});

	// 이미지 tag 클릭시
	$('#image').on("click", function() {
		$(frm.file).click();
	})

	// 이미지를 삽입하거나 변경시
	$(frm.file).on("change", function(e) {
		var file = $(this)[0].files[0];
		$("#image").attr("src", URL.createObjectURL(file));
	})
</script>

 

/ex08/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// read page con // new
	@RequestMapping("/read")
	public String read(String pcode, Model model){
		model.addAttribute("vo",pdao.read(pcode));
		model.addAttribute("pageName","read.jsp");
		return "home";
	}
.....

 

확인

 

 

- 수정해보자.

- 기존에 이미지를 바꾸지 않으면 기존 이미지가 그대로 들어가고 바꾼다면 바꾼 이미지 이름이 DB에 저장될 수 있도록 하겠다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<h1>[상품 정보]</h1>
<style>
form {
	overflow: hidden;
}
input[type=text]{
	width: 100%;
	margin: 10px 0px 10px 0px;
	border: none;
	border-bottom: 1px solid gray;
	font-size: 20px;
}
input[type=file]{
	margin: 10px 0px 10px 0px;
	display: none;
}
</style>
<form name="frm" enctype="multipart/form-data">
	<div>
		<img src="/display?fileName=${vo.image}" id="image" width=300 />
	</div>
	<div>		
		<input type="hidden" name="image" value="${vo.image}" style="display:none"/> 
		<input type="text" name="pcode" value="${vo.pcode}" readonly/> 
		<input type="text" name="pname" value="${vo.pname}"/>
		<input type="text" name="price" value="${vo.price}" />
		<input type="file" name="file" accept="image/*"/>
	</div>
	<div>
		<input type="submit" value="상품수정" />
		<input type="reset" value="수정취소" />
	</div>
</form>
<script>
	// submit시 유효성 체크
	$(frm).on("submit",function(e){
		e.preventDefault();
		var pcode = $(frm.pcode).val();
		var pname = $(frm.pname).val();
		var price = $(frm.price).val();
		var file = $(frm.file).val();
		if(pcode==""||pname==""||price==""){
			alert("입력란을 확인해주세요.");
			return;
		}
		if (price == '' || price.replace(/[0-9]/g, '')) {
			alert('가격을 숫자로 입력하세요.');
			$(frm.price).focus();
			return;
		}
		if(!confirm("상품을 수정하시겠습니까?"))return;
		frm.action="update"
		frm.method="post"
		frm.submit();
	});

	// 이미지 tag 클릭시
	$('#image').on("click", function() {
		$(frm.file).click();
	})

	// 이미지를 삽입하거나 변경시
	$(frm.file).on("change", function(e) {
		var file = $(this)[0].files[0];
		$("#image").attr("src", URL.createObjectURL(file));
	})
</script>

 

/ex08/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// update data // new
	@RequestMapping(value="update", method=RequestMethod.POST)
	public String update(ProductVO vo,MultipartHttpServletRequest multi) throws Exception{
		MultipartFile file = multi.getFile("file");
		// 이미지가 바뀐 경우 기존 이미지 삭제 후 바뀐 이미지 입력
		if(!file.isEmpty()){
			new File(path+"/"+vo.getImage()).delete();
			String image = System.currentTimeMillis()+"_"+file.getOriginalFilename();
			file.transferTo(new File(path+"/"+image));
			vo.setImage(image);
		}
		// 아니면 update
		pdao.update(vo);
		return "redirect:/";
	}
.....

 

확인

 

- 첨부이미지 보여주도록 하겠다.

/ex08/src/main/java/com/example/controller/ProductController.java

.....

.....
	// read page con
	@RequestMapping("/read")
	public String read(String pcode, Model model){
		model.addAttribute("vo",pdao.read(pcode));
		model.addAttribute("attList",adao.list(pcode));
		model.addAttribute("pageName","read.jsp");
		return "home";
	}
.....

 

/ex08/src/main/webapp/WEB-INF/views/read.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
.....
<form name="frm" enctype="multipart/form-data">
	<div>
		<img src="/display?fileName=${vo.image}" id="image" width=300 />
	</div>
	<div>		
		<input type="hidden" name="image" value="${vo.image}" style="display:none"/> 
		<input type="text" name="pcode" value="${vo.pcode}" readonly/> 
		<input type="text" name="pname" value="${vo.pname}"/>
		<input type="text" name="price" value="${vo.price}" />
		<input type="file" name="file" accept="image/*"/>
	</div>
	<div>
		<input type="submit" value="상품수정" />
		<input type="reset" value="수정취소" />
	</div>
	<hr/>
	<div>
		첨부파일 추가 <input type="file" name="attFile" style="display: inline ;"/>	
	</div>
	<h4>첨부 이미지</h4>
	<div id="images">
		<c:forEach items="${attList}" var="image">
			<div class="box">
				<img src="/display?fileName=${image}"/>
				<a href="">삭제</a>
			</div>
		</c:forEach>
	</div>
</form>
.....

 

첨부 파일 이미지 확인

 

 

- 첨부파일을 추가할 경우 tbl_attach에 insert할 수 있도록 하겠다. ajax처리 하겠다.

/ex08/src/main/webapp/WEB-INF/views/read.jsp

.....
<script>
	// 어떤 제품에 첨부파일을 넣을 것인지
	var pcode = $(frm.pcode).val();
	// 첨부파일을 추가할 경우
	$(frm.attFile).on('change',function(){
		var file=$(this)[0].files[0];
		if(file==null) return;
		if(!confirm("파일을 첨부하시겠습니까?")) return;
		var formData = new FormData();
		formData.append("file",file);
		formData.append("pcode",pcode);
		$.ajax({
			type:'post',
			url:'/attInsert',
			data:formData,
			processData:false,
			contentType:false,
			success:function(data){
				alert(data);
			}
		})
	});
.......

 

- 컨트롤러에서 insert할 수 있도록 하겠다.

/ex08/src/main/java/com/example/controller/ProductController.java

..
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// 첨부 파일 추가
	@RequestMapping(value="attInsert", method=RequestMethod.POST)
	@ResponseBody
	public String attInsert(String pcode, MultipartFile file) throws Exception{
		// 첨부파일 업로드
		File attPath = new File(path+"/"+pcode);
		if(!attPath.exists()) {
			attPath.mkdir(); // .mkdir ; 디렉토리 생성 
		}
		String image = pcode+"/"+System.currentTimeMillis()+"_"+file.getOriginalFilename();
		file.transferTo(new File(path+"/"+image));
		
		// 첨부데이터 입력
		adao.insert(pcode, image);
		return image;
	}
.....

 

- insert한 이미지가 바로 보일 수 있도록 하겠다.

/ex08/src/main/webapp/WEB-INF/views/read.jsp

...
	// 어떤 제품에 첨부파일을 넣을 것인지
	var pcode = $(frm.pcode).val();
	// 첨부파일을 추가할 경우
	$(frm.attFile).on('change',function(){
		var file=$(this)[0].files[0];
		if(file==null) return;
		if(!confirm("파일을 첨부하시겠습니까?")) return;
		var formData = new FormData();
		formData.append("file",file);
		formData.append("pcode",pcode);
		$.ajax({
			type:'post',
			url:'/attInsert',
			data:formData,
			processData:false,
			contentType:false,
			success:function(data){
				var str='<div class="box">'
				str+='<img src="/display?fileName='+data+'"/>'
				str+='<a href="'+data+'">삭제</a>'
				str+='</div>'
				$('#images').append(str);
			}
		})
	});
...

 

확인

 

 

- 이제 첨부파일을 삭제해보도록 하겠다.

.....
	<div id="images">
		<c:forEach items="${attList}" var="image">
			<div class="box">
				<img src="/display?fileName=${image}"/>
				<a href="${image}">삭제</a>
			</div>
		</c:forEach>
	</div>
</form>
<script>
	// 어떤 제품에 첨부파일을 넣을 것인지
	var pcode = $(frm.pcode).val();
	
	// 첨부파일 삭제
	$("#images").on('click','.box a',function(e){
		e.preventDefault();
		if(!confirm("첨부파일을 삭제하시겠습니까?"))return;
		var image=$(this).attr("href");
		$.ajax({
			type:'post',
			url:'/attDelete',
			data:{image:image},
			success:function(){
				alert("삭제되었습니다.");
			}
		})
	});
.....

 

image 값 확인

 

 

/ex08/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// 첨부 파일 삭제
	@RequestMapping(value="attDelete", method=RequestMethod.POST)
	@ResponseBody
	public void attDelete(String image){
		adao.delete(image); // 테이블에서 삭제
		new File(path+"/"+image).delete(); // 디스크에서 삭제
	}
.....

 

- 삭제되면 view에서도 삭제되게 하겠다.

/ex08/src/main/webapp/WEB-INF/views/read.jsp

....
	// 어떤 제품에 첨부파일을 넣을 것인지
	var pcode = $(frm.pcode).val();
	
	// 첨부파일 삭제
	$("#images").on('click','.box a',function(e){
		e.preventDefault();
		if(!confirm("첨부파일을 삭제하시겠습니까?"))return;
		var box=$(this).parent(); // new 삭제 경로
		var image=$(this).attr("href");
		$.ajax({
			type:'post',
			url:'/attDelete',
			data:{image:image},
			success:function(){
				alert("삭제되었습니다.");
				box.remove(); // new
			}
		})
	});
....

 

페이지에서 삭제 확인

 

 

- 이제 목록 페이징을 해보겠다.

/ex08/src/main/resources/mapper/ProductMapper.xml

....
	<select id="list" resultType="com.example.domain.ProductVO">
		select * from tbl_product
		order by pcode desc limit #{pageStart},#{perPageNum}
	</select>
....

 

/ex08/src/main/java/com/example/controller/ProductController.java

....
	@RequestMapping("/list.json")
	@ResponseBody
	public List<ProductVO> list(Criteria cri) {
		return pdao.list(cri);
	}
....

 

/ex08/src/main/java/com/example/mapper/ProductDAO.java

....
public interface ProductDAO {
	public List<ProductVO> list(Criteria cri);
....

 

/ex08/src/main/java/com/example/mapper/ProductDAOImpl.java

....
	@Override
	public List<ProductVO> list(Criteria cri) {
		return session.selectList(namespace + ".list",cri);
	}
....

 

/ex08/src/main/java/com/example/controller/ProductController.java

....
	@RequestMapping("/list.json")
	@ResponseBody
	public HashMap<String, Object> list(Criteria cri) {
		HashMap<String, Object> map = new HashMap<>();
		cri.setPerPageNum(8);
		map.put("cri", cri);
		map.put("list", pdao.list(cri));
		
		PageMaker pm = new PageMaker();
		pm.setCri(cri);
		pm.setTotalCount(pdao.totCount());
		map.put("pm", pm);
		return map;
	}
....

 

/ex08/src/main/webapp/WEB-INF/views/list.jsp

....
<script id="temp" type="text/x-handlebars-template">
	{{#each list}} <<<-------------------------------------------------------------
	<div class="box">
		<img src="/display?fileName={{image}}" width=150 height=150
		onClick="location.href='read?pcode={{pcode}}'"/>
		<div class="pcode">{{pcode}}</div>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
....

 

- 토탈 카운터 및 조건검색하겠다.

/ex08/src/main/resources/mapper/ProductMapper.xml

...
	<select id="totCount" resultType="int">
		select count(*) from tbl_product
	</select>
...

 

 

/ex08/src/main/java/com/example/mapper/ProductDAO.java

	public int totCount();
}

 

/ex08/src/main/java/com/example/mapper/ProductDAOImpl.java

	@Override
	public int totCount() {
		return session.selectOne(namespace+".totCount");
	}

}

 

/ex08/src/main/java/com/example/controller/ProductController.java

	@RequestMapping("/list.json")
	@ResponseBody
	public HashMap<String, Object> list(Criteria cri) {
		HashMap<String, Object> map = new HashMap<>();
		cri.setPerPageNum(8);
		map.put("cri", cri);
		map.put("list", pdao.list(cri));
		
		PageMaker pm = new PageMaker();
		pm.setCri(cri);
		pm.setTotalCount(pdao.totCount());
		map.put("pm", pm);
		return map;
	}

 

/ex08/src/main/webapp/WEB-INF/views/list.jsp

....
<h1>[상품 목록]</h1>
<div id="condition">
	<select name="" id="searchType">
		<option value="pcode">상품코드</option>
		<option value="pname">상품명</option>
	</select> <input type="text" id="keyword" placeholder="검색어" /> 검색수 : <span
		id="totCount"></span>
	<hr />
</div>
<div id="products"></div>
<script id="temp" type="text/x-handlebars-template">
	{{#each list}}
	<div class="box">
		<img src="/display?fileName={{image}}" width=150 height=150
		onClick="location.href='read?pcode={{pcode}}'"/>
		<div class="pcode">{{pcode}}</div>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
<div id="pagination" class="pagination"></div>
<script src="/resources/pagination.js"></script>
<script>
	var page = 1;
	getList();
	function getList() {
		var keyword = $('#keyword').val();
		var searchType = $('#searchType').val();
		$.ajax({
			type : 'get',
			url : '/list.json',
			dataType : 'json',
			data : {
				page : page,
				keyword : keyword,
				searchType : searchType
			},
			success : function(data) {
				var temp = Handlebars.compile($('#temp').html());
				$('#products').html(temp(data));
				$("#pagination").html(getPagination(data));
				$("#totCount").html(data.pm.totalCount);
			}
		});
	}
....

 

- 조건 검색을 위해 if tag 지정

/ex08/src/main/resources/mapper/ProductMapper.xml

...
	<select id="list" resultType="com.example.domain.ProductVO">
		select * from tbl_product
		<if test="searchType=='pcode'">
			where pcode like concat('%',#{keyword},'%')
		</if>
		<if test="searchType=='pname'">
			where pname like concat('%',#{keyword},'%')
		</if>
		order by pcode desc
		limit #{pageStart},#{perPageNum}
	</select>

....

 

- 조건 검색에 따른 검색갯수 변하게 하겠다.

/ex08/src/main/resources/mapper/ProductMapper.xml

...
	<select id="totCount" resultType="int">
		select count(*) from tbl_product
		<if test="searchType=='pcode'">
			where pcode like concat('%',#{keyword},'%')
		</if>
		<if test="searchType=='pname'">
			where pname like concat('%',#{keyword},'%')
		</if>
	</select>
...

 

/ex08/src/main/java/com/example/mapper/ProductDAO.java

	public int totCount(Criteria cri);
}

 

/ex08/src/main/java/com/example/mapper/ProductDAOImpl.java

	@Override
	public int totCount(Criteria cri) {
		return session.selectOne(namespace+".totCount",cri);
	}

}

 

/ex08/src/main/java/com/example/controller/ProductController.java

...
	@RequestMapping("/list.json")
	@ResponseBody
	public HashMap<String, Object> list(Criteria cri) {
		HashMap<String, Object> map = new HashMap<>();
		cri.setPerPageNum(8);
		map.put("cri", cri);
		map.put("list", pdao.list(cri));
		
		PageMaker pm = new PageMaker();
		pm.setCri(cri);
		pm.setTotalCount(pdao.totCount(cri));<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<----
		map.put("pm", pm);
		return map;
	}
...

 

조건 검색 및 검색에 따르느 검색수 변경 확인

 

- 조회수 카운터 해보겠다.

- go to mysql

# 뷰 카운터 컬럼 추가
alter table tbl_product add viewcnt int default 0;

alter table tbl_product add attachcnt int default 0;

 

- list.jsp 에 출력해보겠다.

/ex08/src/main/webapp/WEB-INF/views/list.jsp

...
<script id="temp" type="text/x-handlebars-template">
	{{#each list}}
	<div class="box">
		<img src="/display?fileName={{image}}" width=150 height=150
		onClick="location.href='read?pcode={{pcode}}'"/>
		<div class="pcode">
			<b>{{pcode}}</b>
			<span class="viewcnt">조회수:{{viewcnt}}</span>
		</div>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
...

 

/ex08/src/main/resources/mapper/ProductMapper.xml

	<update id="viewcnt">
		update tbl_product set viewcnt=viewcnt+1
		where pcode=#{pcode}
	</update>

 

	public void viewcnt(String pcode);
}

 

	@Override
	public void viewcnt(String pcode) {
		session.update(namespace+".viewcnt",pcode);
	}

}

 

- 해당 작업은 두개 이상이니 서비스를 이용하겠다.

/ex08/src/main/java/com/example/service/ProductService.java

package com.example.service;

import com.example.domain.ProductVO;

public interface ProductService {
	public ProductVO read(String pcode);
}

 

/ex08/src/main/java/com/example/service/ProductServiceImpl.java

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.domain.ProductVO;
import com.example.mapper.ProductDAO;

@Service
public class ProductServiceImpl implements ProductService {

	@Autowired
	ProductDAO pdao;
	
	@Transactional
	@Override
	public ProductVO read(String pcode) {
		pdao.viewcnt(pcode);
		return pdao.read(pcode);
	}

}

 

- 이제 정보 read 시 service의 read를 이용하면 된다.

...
	@Autowired
	ProductService service;
...
	// read page con
	@RequestMapping("/read")
	public String read(String pcode, Model model){
		model.addAttribute("vo",service.read(pcode));
		model.addAttribute("attList",adao.list(pcode));
		model.addAttribute("pageName","read.jsp");
		return "home";
	}
...

 

- VO에 추가해주지 않고 viewcnt를 가지고 오기 위해서 list 반환타입을 hashmap으로 한다.

/ex08/src/main/resources/mapper/ProductMapper.xml

...
	<select id="list" resultType="hashmap">
		select * from tbl_product
		<if test="searchType=='pcode'">
			where pcode like concat('%',#{keyword},'%')
		</if>
		<if test="searchType=='pname'">
			where pname like concat('%',#{keyword},'%')
		</if>
		order by pcode desc
		limit #{pageStart},#{perPageNum}
	</select>
...

 

확인

 

 

- 이제 조건 정렬 해보겠다.

/ex08/src/main/webapp/WEB-INF/views/list.jsp

...
<div id="condition">
	<select name="" id="searchType">
		<option value="pcode">상품코드</option>
		<option value="pname">상품명</option>
	</select> <input type="text" id="keyword" placeholder="검색어" /> 
	검색수 : <span id="totCount"></span>
	<select id="orderType">
		<option value="order1">인기상품순</option>
		<option value="order2">최신상품순</option>
		<option value="order3">높은가격순</option>
		<option value="order4">낮은가격순</option>
	</select>
	<hr />
</div>
...
	function getList() {
		var keyword = $('#keyword').val();
		var searchType = $('#searchType').val();
		var orderType = $('#orderType').val();
		$.ajax({
			type : 'get',
			url : '/list.json',
			dataType : 'json',
			data : {
				page : page,
				keyword : keyword,
				searchType : searchType,
				orderType : orderType
			},
			success : function(data) {
				var temp = Handlebars.compile($('#temp').html());
				$('#products').html(temp(data));
				$("#pagination").html(getPagination(data));
				$("#totCount").html(data.pm.totalCount);
			}
		});
	}
    ...

 

/ex08/src/main/java/com/example/controller/ProductController.java

...
	@RequestMapping("/list.json")
	@ResponseBody
	public HashMap<String, Object> list(Criteria cri, String orderType) {
		HashMap<String, Object> map = new HashMap<>();
		cri.setPerPageNum(8);
		map.put("cri", cri);
		map.put("list", pdao.list(cri, orderType));
		
		PageMaker pm = new PageMaker();
		pm.setCri(cri);
		pm.setTotalCount(pdao.totCount(cri));
		map.put("pm", pm);
		return map;
	}
...

 

/ex08/src/main/java/com/example/mapper/ProductDAO.java

public interface ProductDAO {
	public List<ProductVO> list(Criteria cri, String orderType);

 

/ex08/src/main/java/com/example/mapper/ProductDAOImpl.java

	@Override
	public List<ProductVO> list(Criteria cri, String orderType) {
		HashMap<String, Object> map = new HashMap<>();
		map.put("cri", cri);
		map.put("orderType", orderType);		
		return session.selectList(namespace + ".list",cri);
	}

 

/ex08/src/main/resources/mapper/ProductMapper.xml

...
	<select id="list" resultType="hashmap">
		select * from tbl_product
		<if test="cri.searchType=='pcode'">
			where pcode like concat('%',#{cri.keyword},'%')
		</if>
		<if test="cri.searchType=='pname'">
			where pname like concat('%',#{cri.keyword},'%')
		</if>
		<!-- 조건 정렬 -->
		<if test="orderType=='order1'">
			order by viewcnt desc
		</if>
		<if test="orderType=='order2'">
			order by pcode desc
		</if>
		<if test="orderType=='order3'">
			order by price desc
		</if>
		<if test="orderType=='order4'">
			order by price
		</if>
		limit #{cri.pageStart},#{cri.perPageNum}
	</select>
...

 

/ex08/src/main/webapp/WEB-INF/views/list.jsp

	$('#orderType').on('change',function(){
		page=1;
		getList();
	})

 

확인