쇼핑몰 페이지를 만들어 보자.
로그인 기능은... 우선 뺐다. member를 따로 정리해야 하는데 시간이 너무 없음.
lib 파일 넣는 과정은 너무 많이 넣어서 생략한다. 기억할 수 있지 나 자신아?
자바 파일들은 구분하기 쉽게 admin용 controller (shop), client 용 controller 로 구분한다. dto와 dao 를 나눠 한 눈에 구분하기 쉽게 한다.
나는 client와 admin 으로 아예 나누어서 패키지가 6개 정도 생겼었는데 다음 버전에서 수정했다. 구성 요소를 설정할 때 처음부터 큰 그림을 보고 나눠 놔야 나중에 고생을 안 한다.
jsp 파일의 구성은 이렇다. prefix 를 shop 까지 설정해 주고 나머지를 나누어 구분해 접근의 용이성을 챙기려고 한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>springShop</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springShop</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>EUC-KR</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
문제는 이렇게 한글 처리를 하면... 한글이 넘어가질 않는다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
<bean id="viewResolver"
// DAO 의존성 주입
// 접두사 & 접미사 결정
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/shop/"/>
<property name="suffix" value=".jsp"/>
</bean>
// 데이터 소스 주입하기
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="fin01" />
<property name="password" value="fin01" />
</bean>
// jdbcTemplate 사용하기
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
// dao 사용하기
<bean id="categoryDAO" class="shop.dao.CategoryDAOImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="productDAO" class="shop.dao.ProductDAOImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
// controller 등록하기
<bean class="shop.controller.CategoryController"/>
<bean class="shop.controller.ProductController"/>
<bean class="mall.controller.MallController"/>
// Spring 제공 파일 업로더 사용하기
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
</beans>
주석으로 각 코드의 기능을 달아보았다.
여기까지 오면 드디어 사전 작업은 끝이다. DAO 는 한번에 작업했지만 기능마다 나누어 업로드하려고 한다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- main.jsp -->
<%@ include file="top.jsp" %>
<img src="img/main.png" width="100%" height="100%">
<%@ include file="bottom.jsp"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- main.jsp -->
<%@ include file="top.jsp" %>
<img src="img/main.png" width="100%" height="100%">
<%@ include file="bottom.jsp"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- bottom.jsp -->
</td>
</tr>
<tr height="10%">
<td colspan="5" align="center">
Gahyun's shop
</td>
</tr>
</table>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- cate_input.jsp -->
<%@ include file="top.jsp" %>
<div align="center">
<form name="f" action="cate_input.do" method="post">
<table border="1" width="40%">
<caption><h3>카테고리 등록</h3></caption>
<tr>
<th width="40%" bgcolor="yellow">카테고리 코드</th>
<td><input type="text" name="code" class="box"></td>
</tr>
<tr>
<th width="40%" bgcolor="yellow">카테고리 이름</th>
<td><input type="text" name="cname" class="box"></td>
</tr>
<tr bgcolor="orange">
<td align="center" colspan="2">
<input type="submit" value="등록">
<input type="reset" value="취소">
</td>
</tr>
</table>
</form>
</div>
<%@ include file="bottom.jsp"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- cate_list.jsp -->
<%@ include file="top.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div align="center">
<table border="1" width="50%">
<caption><h3>카테고리목록</h3></caption>
<tr bgcolor="yellow">
<th width="10%">번호</th>
<th width="30%">카테고리코드</th>
<th width="50%">카테고리명</th>
<th width="10%">삭제</th>
</tr>
<c:if test="${empty listCate}">
<tr>
<td colspan="4">등록된 카테고리가 없습니다.</td>
</tr>
</c:if>
<c:forEach var="dto" items="${listCate}">
<tr>
<td align="right">${dto.cnum}</td>
<td align="center">${dto.code}</td>
<td align="center">${dto.cname}</td>
<td align="center">
<a href="cate_delete.do?cnum=${dto.cnum}">삭제</a>
</td>
</tr>
</c:forEach>
</table>
</div>
<%@ include file="bottom.jsp"%>
등록은 이렇게 된다.
package shop.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import shop.dto.CategoryDTO;
public class CategoryDAOImpl implements CategoryDAO {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int insertCate(CategoryDTO dto) {
String sql = "insert into category values(cate_seq.nextval, ?, ?)";
Object[] values = new Object[] {dto.getCode(), dto.getCname()};
return jdbcTemplate.update(sql, values);
}
@Override
public int deleteCate(int cnum) {
String sql = "delete from category where cnum = ?";
return jdbcTemplate.update(sql, cnum);
}
@Override
public List<CategoryDTO> listCate() {
String sql = "select * from category";
RowMapper mapper = new RowMapper<CategoryDTO>() {
@Override
public CategoryDTO mapRow(ResultSet rs, int co) throws SQLException {
CategoryDTO dto = new CategoryDTO();
dto.setCnum(rs.getInt("cnum"));
dto.setCode(rs.getString("code"));
dto.setCname(rs.getString("cname"));
return dto;
}
};
return jdbcTemplate.query(sql, mapper);
}
}
DAO 메서드를 통해 구현된다.
package shop.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import shop.dao.CategoryDAO;
import shop.dto.CategoryDTO;
@Controller
public class CategoryController {
@Autowired
private CategoryDAO categoryDAO;
@RequestMapping("/shop.do")
public String index_shop() {
return "admin/main";
}
@RequestMapping("/index.do")
public String index() {
return "redirect:index.jsp";
}
@RequestMapping(value="/cate_input.do", method=RequestMethod.GET)
public String cate_input() {
return "admin/cate_input";
}
@RequestMapping(value="/cate_input.do", method=RequestMethod.POST)
public String cate_input_ok(@ModelAttribute CategoryDTO dto) {
int res = categoryDAO.insertCate(dto);
return "redirect:cate_list.do";
}
@RequestMapping("/cate_list.do")
public String cate_list(HttpServletRequest req) {
List<CategoryDTO> list = categoryDAO.listCate();
req.setAttribute("listCate", list);
return "admin/cate_list";
}
@RequestMapping("/cate_delete.do")
public String cate_delete(@RequestParam int cnum) {
int res = categoryDAO.deleteCate(cnum);
return "redirect:cate_list.do";
}
}
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- prod_input.jsp -->
<%@ include file="top.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div align="center">
<form name="f" action="prod_input.do"
method="post" enctype="multipart/form-data">
<table border="0" class="outline" width="60%">
<caption>상품등록카테고리</caption>
<tr>
<th class="m2">카테고리</th>
<td>
<select name="pcategory_fk">
<c:forEach var="dto" items="${listCate}">
<option value="${dto.code}">
${dto.cname}[${dto.code}]
</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<th class="m2">상품명</th>
<td><input type="text" name="pname" class="box"></td>
</tr>
<tr>
<th class="m2">상품코드</th>
<td><input type="text" name="pcode" class="box"></td>
</tr>
<tr>
<th class="m2">제조회사</th>
<td><input type="text" name="pcompany" class="box"></td>
</tr>
<tr>
<th class="m2">상품이미지</th>
<td><input type="file" name="pimage" class="box"></td>
</tr>
<tr>
<th class="m2">상품수량</th>
<td><input type="text" name="pqty" class="box"></td>
</tr>
<tr>
<th class="m2">상품가격</th>
<td><input type="text" name="price" class="box"></td>
</tr>
<tr>
<th class="m2">상품스팩</th>
<td>
<select name="pspec">
<option value="normal">NORMAL</option>
<option value="hit">HIT</option>
<option value="new">NEW</option>
<option value="best">BEST</option>
</select>
</td>
</tr>
<tr>
<th class="m2">상품소개</th>
<td>
<textarea name="pcontents" rows="5" cols="30"></textarea>
</td>
</tr>
<tr>
<th class="m2">상품포인트</th>
<td><input type="text" name="point" class="box"></td>
</tr>
<tr>
<td align="center">
<input type="submit" value="상품등록">
<input type="reset" value="취소">
</td>
</tr>
</table>
</form>
</div>
<%@ include file="bottom.jsp"%>
@RequestMapping(value="/prod_input.do", method=RequestMethod.GET)
public String prod_input(HttpServletRequest req) {
List<CategoryDTO> list = categoryDAO.listCate();
if (list == null || list.size() == 0) {
req.setAttribute("msg", "등록된 카테고리가 없습니다.");
req.setAttribute("url", "cate_input.do");
return "forward:message.jsp";
}
req.setAttribute("listCate", list);
return "admin/prod_input";
}
@RequestMapping(value="/prod_input.do", method=RequestMethod.POST)
public String prod_input(HttpServletRequest req,
@ModelAttribute ProductDTO dto, BindingResult result) {
if (result.hasErrors()) {
dto.setPimage("");
}
MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
MultipartFile mf = mr.getFile("pimage");
String filename = mf.getOriginalFilename();
String path = req.getServletContext().getRealPath("/files");
File file = new File(path, filename);
try {
mf.transferTo(file);
}catch(IOException e) {
req.setAttribute("msg", "파일 업로드 중 오류발생!! 관리자에게 문의해 주세요");
req.setAttribute("url", "shop.do");
return "forward:message.jsp";
}
dto.setPimage(filename);
String pcode = req.getParameter("pcode");
dto.setPcategory_fk(dto.getPcategory_fk()+pcode);
int res = productDAO.insertProduct(dto);
return "redirect:prod_list.do";
}
코드를 보면 GET 방식일 때는 카테고리 리스트를 전송하는 기능만 하고, Post 방식일 때는 MultipartHttpServletRequest 를 거쳐 이미지 파일을 받고, dto를 받아서 나중에 pimage만 따로 설정을 해 준다.
이후에는 리스트에 디비 변경이 있고 난 뒤의 연결인 redirect 를 해 준다.
@RequestMapping("/prod_delete.do")
public String prod_delete(HttpServletRequest req, @RequestParam Map<String, String> params) {
int res = productDAO.deleteProduct(Integer.parseInt(params.get("pnum")));
if (res>0) {
String upPath = req.getServletContext().getRealPath("/files");
File file = new File(upPath, params.get("pimage"));
if (file.exists()) {
file.delete();
}
}
return "redirect:prod_list.do";
}
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<!-- prod_list.jsp -->
<%@ include file="top.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<script type="text/javascript">
function checkDel(pnum, pimage){
var isDel = window.confirm("정말로 삭제하시겠습니까?")
if (isDel){
document.del.pnum.value = pnum
document.del.pimage.value = pimage
document.del.submit()
}
}
</script>
<div align="center">
<table border="0" width="80%" class="outline">
<caption>상품목록</caption>
<tr class="m2">
<th>번호</th>
<th>상품코드</th>
<th>상품명</th>
<th>이미지</th>
<th>가격</th>
<th>제조사</th>
<th>수량</th>
<th>수정|삭제</th>
</tr>
<c:if test="${empty listProd}">
<tr>
<td colspan="7">등록된 상품이 없습니다.</td>
</tr>
</c:if>
<c:forEach var="dto" items="${listProd}">
<tr>
<td>${dto.pnum}</td>
<td>${dto.pcategory_fk}</td>
<td>${dto.pname}</td>
<td>
<a href="prod_view.do?pnum=${dto.pnum}">
<img src="${upPath}/${dto.pimage}" width="40" height="40">
</a>
</td>
<td align="right"><fmt:formatNumber value="${dto.price}" pattern="###,###"/>원</td>
<td>${dto.pcompany}</td>
<td align="right">${dto.pqty}개</td>
<td>
<a href="prod_update.do?pnum=${dto.pnum}">수정</a> |
<a href="javascript:checkDel('${dto.pnum}','${dto.pimage}')">삭제</a>
</td>
</tr>
</c:forEach>
</table>
</div>
<form name="del" action="prod_delete.do" method="post">
<input type="hidden" name="pnum">
<input type="hidden" name="pimage">
</form>
<%@ include file="bottom.jsp"%>
@RequestMapping("/prod_list.do")
public String prod_list(HttpServletRequest req) {
List<ProductDTO> list = productDAO.listProduct();
req.setAttribute("upPath", req.getServletContext().getRealPath("/files"));
req.setAttribute("listProd", list);
return "admin/prod_list";
}
이렇게 하면 리스트로 들어갔을 때 DAO에서 리스트를 가져와서 쭉 뽑아준다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR" import="shop.*"%>
<!-- prod_view.jsp -->
<%@ include file="top.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<div align="center">
<form name="f" action="prod_list.shop" method="post">
<table border="0" class="outline" width="70%">
<tr>
<th width="15%" class="m2">카테고리</th>
<td width="35%" align="center">${getProduct.pcategory_fk}</td>
<th width="15%" class="m2">상품번호</th>
<td width="35%" align="center">${getProduct.pnum}</td>
</tr>
<tr>
<th width="15%" class="m2">상품명</th>
<td width="35%" align="center">${getProduct.pname}</td>
<th width="15%" class="m2">제조회사</th>
<td width="35%" align="center">${getProduct.pcompany}</td>
</tr>
<tr>
<th width="15%" class="m2">상품이미지</th>
<td align="center" colspan="3">
<img src="${upPath}/${getProduct.pimage}"
width="150" height="150">
</td>
</tr>
<tr>
<th width="15%" class="m2">상품수량</th>
<td width="35%" align="center">${getProduct.pqty}</td>
<th width="15%" class="m2">상품가격</th>
<td width="35%" align="center">
<fmt:formatNumber value="${getProduct.price}" pattern="###,###"/>원
</td>
</tr>
<tr>
<th width="15%" class="m2">스펙</th>
<td width="35%" align="center">${getProduct.pspec}</td>
<th width="15%" class="m2">상품포인트</th>
<td width="35%" align="center">
<fmt:formatNumber value="${getProduct.point}" pattern="###,###"/>point
</td>
</tr>
<tr>
<th width="15%" class="m2">상품소개</th>
<td colspan="3">
<textarea name="pcontents" rows="5" cols="50" readOnly>${getProduct.pcotents}</textarea>
</td>
</tr>
<tr>
<td colspan="4" align="center" class="m1">
<input type="submit" value="돌아가기">
</td>
</tr>
</table>
</form>
</div>
<%@ include file="bottom.jsp"%>
@RequestMapping("/prod_view.do")
public String prod_view(HttpServletRequest req, @RequestParam int pnum) {
ProductDTO dto = productDAO.getProduct(pnum);
req.setAttribute("upPath", req.getServletContext().getRealPath("/files"));
req.setAttribute("getProduct", dto);
return "admin/prod_view";
}
이러면 받아온 pnum 으로 product 를 가져올 수 있고, 그 친구를 그대로 productview 에 보내 준다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR" import="shop.*"%>
<!-- prod_update.jsp -->
<%@ include file="top.jsp" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<div align="center">
<form name="f" action="prod_update.do"
method="post" enctype="multipart/form-data">
<table border="0" class="outline" width="60%">
<caption>상품수정</caption>
<tr>
<th class="m2">카테고리</th>
<td>
<input type="text" name="pcategory_fk"
value="${getProduct.pcategory_fk}" readOnly>
</td>
</tr>
<tr>
<th class="m2">상품번호</th>
<td>
${getProduct.pnum}
<input type="hidden" name="pnum" value="${getProduct.pnum}">
</td>
</tr>
<tr>
<th class="m2">상품명</th>
<td><input type="text" name="pname" class="box" value="${getProduct.pname}"></td>
</tr>
<tr>
<th class="m2">제조회사</th>
<td><input type="text" name="pcompany" class="box" value="${getProduct.pcompany}"></td>
</tr>
<tr>
<th class="m2">상품이미지</th>
<td>
<img src="${upPath}/${getProduct.pimage}" width="80" height="80">
<input type="file" name="pimage" class="box">
<input type="hidden" name="pimage2" value="${getProduct.pimage}"/>
</td>
</tr>
<tr>
<th class="m2">상품수량</th>
<td><input type="text" name="pqty" class="box" value="${getProduct.pqty}"></td>
</tr>
<tr>
<th class="m2">상품가격</th>
<td><input type="text" name="price" class="box" value="${getProduct.price}"></td>
</tr>
<tr>
<th class="m2">상품스팩</th>
<td>
<select name="pspec">
<c:forTokens var="spec" items="normal,hit,new,best" delims=",">
<c:if test="${spec == getProduct.pspec}">
<option value="${spec}" selected>"${fn:toUpperCase(spec)}"</option>
</c:if>
<c:if test="${spec != getProduct.pspec}">
<option value="${spec}">"${fn:toUpperCase(spec)}"</option>
</c:if>
</c:forTokens>
</select>
</td>
</tr>
<tr>
<th class="m2">상품소개</th>
<td>
<textarea name="pcotnents" rows="5" cols="30">${getProduct.pcontents}</textarea>
</td>
</tr>
<tr>
<th class="m2">상품포인트</th>
<td><input type="text" name="point" class="box" value="${getProduct.point}"></td>
</tr>
<tr>
<td align="center">
<input type="submit" value="상품수정">
<input type="reset" value="취소">
</td>
</tr>
</table>
</form>
</div>
<%@ include file="bottom.jsp"%>
@RequestMapping(value="/prod_update.do", method=RequestMethod.GET)
public String prod_update(HttpServletRequest req, @RequestParam int pnum) {
ProductDTO dto = productDAO.getProduct(pnum);
req.setAttribute("upPath", req.getServletContext().getRealPath("/files"));
req.setAttribute("getProduct", dto);
return "admin/prod_update";
}
@RequestMapping(value="/prod_update.do", method=RequestMethod.POST)
public String prod_update(HttpServletRequest req,
@ModelAttribute ProductDTO dto, BindingResult result) {
if (result.hasErrors()) {
dto.setPimage("");
}
MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
MultipartFile mf = mr.getFile("pimage");
String filename = mf.getOriginalFilename();
if (filename == null || filename.trim().equals("")) {
dto.setPimage(req.getParameter("pimage2"));
}else {
String path = req.getServletContext().getRealPath("/files");
File file = new File(path, filename);
try {
mf.transferTo(file);
}catch(IOException e) {
req.setAttribute("msg", "파일 업로드 중 오류발생!! 관리자에게 문의해 주세요");
req.setAttribute("url", "shop.do");
return "forward:message.jsp";
}
dto.setPimage(filename);
}
int res = productDAO.updateProduct(dto);
return "redirect:prod_list.do";
}
GET 메서드로 받아왔을 때는 pnum 으로 개체 값 불러와서 주고, post 값으로 받아왔을 때는 수정된 값 받아와서 DB를 업데이트 해 준다. 역시 db 작업이 들어갔으므로 redirect 해 준다.