이번 프로젝트는 남성의류 쇼핑몰이다. 스프링과 오라클 , jsp를 사용해 구현했다.
저번 프로젝트에서 구현했던 회원이나 게시판등의 기능들도 구현했지만
쇼핑몰 기능 부분만 작성해보려고 한다.
먼저 로직의 설명이다.
기본정보에서 상품페이지에 표시 될 정보들을 입력한다 ( 카테고리 , 제목 , 내용 등...)
기본정보를 입력 후 , 옵션설정에서 옵션추가를 눌러서 원하는 만큼의 옵션과 수량을 입력 할 수 있다.
상품자체의 수량이 아닌, 상품의 옵션 ( 95, red / 100,blue ) 등 같은 제품이라도 옵션에 따라 수량이 다를 수 있기 때문
에, 쇼핑몰 재고관리의 편의성을 높이기 위해 이런 방식으로 구현했다.
다음은 이미지 설정이다.
메인이미지와 서브이미지를 올릴 수 있다.
로직순서는
상품테이블에 insert -
옵션테이블에 옵션의 개수[i] 만큼 insert -
multipart 라이브러리를 이용한 파일테이블에 insert
총 3번의 insert가 들어가서 상품정보가 등록된다.
3개의 테이블에 대한 VO가 필요한데,
파일 테이블의 VO는 필요없고 상품과 옵션의 VO다.
/* */ public class ItemVO
/* */ {
/* */ private int item_no;
/* */ private String item_admin;
/* */ private String item_name;
/* */ private String item_price;
/* */ private String item_size;
/* */ private String item_color;
/* */ private int item_disc;
/* */ private String item_content;
/* */ private String item_imgsub;
/* */ private Date item_date;
/* */ private int item_star;
/* */ private String item_catemain;
/* */ private String item_catesub;
/* */ private String item_subcontent;
/* */ private String item_model;
/* */ private String item_imgmain;
/* */ private String[] item_option;
/* */ private int[] item_vol;
/* */ private String option_content;
/* */ private String option_item_no;
/* */ private int option_vol;
/* */ private int option_no;
public class OptionVO {
private int option_no;
private String option_content;
private int option_item_no;
private int option_vol;
item_no ( 상품PR ) 을 기준한다.
mapper
<insert id="itemInsert" parameterType="kr.co.vo.ItemVO"
useGeneratedKeys="true" keyProperty="item_no">
<selectKey keyProperty="item_no" resultType="int" order="BEFORE">
SELECT
ITEM_SEQ.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO ITEM (ITEM_NO , ITEM_NAME , ITEM_PRICE , ITEM_SIZE, ITEM_COLOR,
ITEM_CONTENT, ITEM_CATEMAIN, ITEM_CATESUB, ITEM_SUBCONTENT, ITEM_MODEL
) VALUES (
#{item_no} ,
#{item_name},
#{item_price},
#{item_size},
#{item_color},
#{item_content},
#{item_catemain},
#{item_catesub},
#{item_subcontent},
#{item_model}
)
</insert>
<insert id="itemOptionInsert" >
INSERT INTO ITEMOPTION (OPTION_NO, OPTION_CONTENT, OPTION_ITEM_NO , OPTION_VOL
)VALUES(
(SELECT NVL(MAX(OPTION_NO), 0) + 1 FROM ITEMOPTION) , #{item_option} , #{item_no} , #{item_vol}
)
</insert>
<insert id="itemMainFile">
insert into IMG (IMG_NO , IMG_ORGNAME , IMG_STRNAME , IMG_SIZE , IMG_ITEM_NO
)VALUES(
(SELECT NVL(MAX(IMG_NO), 0) + 1 FROM IMG), #{IMG_ORGNAME}, #{IMG_STRNAME} , #{IMG_SIZE} , #{ITEM_NO}
)
</insert>
<update id="itemMainImg">
update item set ITEM_IMGMAIN = #{IMG_STRNAME} where ITEM_NO = #{ITEM_NO}
</update>
<update id="itemSubImg">
update item set ITEM_IMGSUB = #{IMG_STRNAME} where ITEM_NO = #{ITEM_NO}
</update>
상품등록 , 옵션등록 , 파일등록 후 상품테이블에 파일명을 update 시켜준다.
item_no가 필요한데 , 그러려면 먼저 아이템이 insert되어야 하기 때문에 이런 방식으로 구현했다.
select key를 사용해서 item_no가 등록되고 VO에 set되도록 했으며,
VO에 set 된 item_no를 기준으로 옵션을 등록한다.
service
/* */ @Transactional
/* */ public void itemInsert(ItemVO itemVO, MultipartHttpServletRequest multipart) throws Exception {
/* 29 */ this.masterDAO.itemInsert(itemVO);
/* */
/* 31 */ Map<String, Object> itemMainImg = this.fileUtils.itemMainImg(itemVO, multipart);
/* 32 */ this.masterDAO.itemMainImg(itemMainImg);
/* 33 */ this.masterDAO.itemMainFile(itemMainImg);
/* */
/* 35 */ Map<String, Object> itemSubImg = this.fileUtils.itemSubImg(itemVO, multipart);
/* 36 */ this.masterDAO.itemSubImg(itemSubImg);
/* 37 */ this.masterDAO.itemMainFile(itemSubImg);
/* */
/* 39 */ for (int i = 0; i < (itemVO.getItem_option()).length; i++) {
/* 40 */ String itemOptionContent = itemVO.getItem_option()[i];
/* 41 */ int itemOptionVol = itemVO.getItem_vol()[i];
/* 42 */ Map<String, Object> map = new HashMap<String, Object>();
/* 43 */ map.put("item_option", itemOptionContent);
/* 44 */ map.put("item_no", Integer.valueOf(itemVO.getItem_no()));
/* 45 */ map.put("item_vol", Integer.valueOf(itemOptionVol));
/* 46 */ this.masterDAO.itemOptionInsert(map);
/* */ }
/* */ }
먼저 , 상품을 insert하는 itemInsert 이다.
그 다음 , 메인이미지와 서브이미지를 insert하고 , 로컬에 저장하는 itemMainImg 와 itemSubImg 이다.
multipart를 사용해서 파일의 값을 불러오고 , 로컬에 저장 후 정보를 map에 담아서 return 해야하는데
해당 부분은 너무 길어질 것 같아, fileUtils라는 클래스를 만들어 처리했다.
그에 대한 내용은 전 글에 설명했다.
[스프링]게시판 첨부파일 업로드 구현 설명(insert) (1) :: 간편 웹프로그래밍 (tistory.com)
그 후 , option에 대한 내용을 옵션 수 만큼 insert하는 로직이다.
for문을 사용해서 배열의 수 만큼, 배열의 값을 담아 insert 한다.
뷰에서 같은 name값의 파라미터가 들어오면 , 컨트롤러는 자동으로 파라미터를 배열로 받아준다.
때문에, 배열로 받아서 처리했다.
Controller
@RequestMapping(value = {"/itemInsert"}, method = {RequestMethod.POST})
public String ItemInsert(ItemVO itemVO, MultipartHttpServletRequest multipart) throws Exception {
logger.info("itemInsert=");
this.masterService.itemInsert(itemVO, multipart);
return "/master/ItemInsertView";
}
컨트롤러에선 아이템VO로 상품 기본정보와 option의 정보를 배열로 받아온다. ( VO에 배열로 된 객체가 있다)
파일은 multipartHttpServletRequest로 받아서,
multipartFile을 이용해 name값으로 main인지 , sub인지 구별하여 처리했다.
View 부분이다.
<form action="/master/itemInsert" method="post"
name="itemInsertForm" id="itemInsertForm"
enctype="multipart/form-data">
<div class="tab-content">
<!-- 상품등록 -->
<div class="tab-pane active" id="itemInsert_1">
<div class="form-group" style="">
<h3>대분류</h3>
<select class="form-control" name="item_catemain"
id="item_catemain" title="상품 대분류">
<option value="item">일반상품</option>
<option value="mainpage">메인페이지</option>
<option value="popularitem">인기상품</option>
</select>
<h3>소분류</h3>
<select class="form-control" name="item_catesub"
id="item_catesub" title="상품 소분류">
<option value="jumper">Jumper</option>
<option value="coat">Coat</option>
<option value="jacket">Jacket</option>
<option value="padding">Padding</option>
<option value="shirts">Shirts</option>
</select>
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>상품명</h3>
<input class="form-control input-lg" type="text"
placeholder="상품명" name="item_name" id="item_name" />
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>판매가</h3>
<input class="form-control input-lg" type="number"
placeholder="판매가 / 단위 : 원" name="item_price" id="item_price" />
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>사이즈</h3>
<input class="form-control input-lg" type="text"
placeholder="사이즈" name="item_size" id="item_size" />
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>색상</h3>
<input class="form-control input-lg" type="text"
placeholder="색상" name="item_color" id="item_color" />
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>상품설명</h3>
<input class="form-control input-lg" type="text"
placeholder="상품설명" name="item_subcontent"
id="item_subcontent" />
</div>
<hr class="divider-w mt-10 mb-20">
<div class="form-group">
<h3>모델정보</h3>
<input class="form-control input-lg" type="text"
placeholder="모델정보" name="item_model" id="item_model" />
</div>
<hr class="divider-w mt-10 mb-20">
<!-- 상품내용 에디터 -->
<div class="form-group">
<h3>상품내용</h3>
<textarea class="form-control input-lg"
placeholder="내용을 입력하세요" name="item_content" id="item_content">
</textarea>
</div>
<!-- 상품등록 끝-->
<!-- 상품옵션 -->
<div class="tab-pane" id="itemInsert_2">
<button id="optionAdd_btn" class="btn btn-primary mb-3"
type="button">옵션추가</button>
<div id="optionIndex"></div>
<br />
<div class="form-group mt-3">
<input class="form-control input-lg " type="text"
placeholder="최소 1개의 옵션이 필요합니다. ex) 100 , red /// 95 , blue "
readonly />
</div>
</div>
<!-- 상품옵션 끝 -->
<!-- 상품이미지 -->
<div class="tab-pane" id="itemInsert_3">
<div class="form-group">
<h3>메인이미지 ( 썸네일 )</h3>
<input class="form-control input-lg" type="file"
name="itemMainImg" id="itemMainImg" />
</div>
<div class="form-group">
<h3>서브이미지 ( 썸네일에 마우스 올릴시 )</h3>
<input class="form-control input-lg" type="file"
name="itemSubImg" id="itemSubImg" />
</div>
<button class="btn btn-d btn-circle" type="button"
onclick="fnSubmit(); return false;">상품등록</button>
</div>
</form>
<!-- 상품이미지 끝 -->
</div>
$(document).ready(function() {
console.log('ready');
optionAdd();
});
function optionAdd() {
console.log('optionadd');
var optionIndex = 1;
//$("#fileIndex").append("<div><input type='file' style='float:left;' name='file_"+(fileIndex++)+"'>"+"<button type='button' style='float:right;' id='fileAddBtn'>"+"추가"+"</button></div>");
$("#optionAdd_btn")
.on(
"click",
function() {
console.log('옵션추가');
$("#optionIndex")
.append(
" <div class='form-group'><input placeholder='옵션' class='form-control input-lg' type='text' style='float:left;' name='item_option"
+ "' id='item_option'>"
+ "<input type='number' name='item_vol' id='item_vol' placeholder='수량'>"
+ "<button type='button' style='float:right;' id='optionDelBtn' class='btn-btn dark'>"
+ "삭제" + "</button></div>"
);
});
$(document).on("click", "#optionDelBtn", function() {
$(this).parent().remove();
});
};
function fnSubmit() {
var email_rule = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
// var tel_rule = /^\d{2,3}-\d{3,4}-\d{4}$/; 전화번호용
if ($("#item_name").val() == null || $("#item_name").val() == "") {
alert("상품명을 입력해주세요.");
$("#item_name").focus();
return false;
}
if ($("#item_price").val() == null || $("#item_price").val() == "") {
alert("상품가격를 입력해주세요.");
$("#item_price").focus();
return false;
}
if ($("#item_size").val() == null || $("#item_size").val() == "") {
alert("상품사이즈를 입력해주세요.");
$("#item_size").focus();
return false;
}
if ($("#item_color").val() == null || $("#item_color").val() == "") {
alert("상품색상을 입력해주세요.");
$("#item_color").focus();
return false;
}
if ($("#item_vol").val() == null || $("#item_vol").val() == "") {
alert("상품수량을 입력해주세요.");
$("#item_vol").focus();
return false;
}
if ($("#item_content").val() == null || $("#item_content").val() == "") {
alert("상품내용을 입력해주세요.");
$("#item_content").focus();
return false;
}
if ($("#item_subcontent").val() == null
|| $("#item_subcontent").val() == "") {
alert("상품설명을 입력해주세요.");
$("#item_subcontent").focus();
return false;
}
if ($("#item_model").val() == null || $("#item_model").val() == "") {
alert("모델정보를 입력해주세요.");
$("#item_model").focus();
return false;
}
if ($("#item_option").val() == null || $("#item_option").val() == "") {
alert("상품옵션을 입력해주세요.");
$("#item_option").focus();
return false;
}
if ($("#itemMainImg").val() == null || $("#itemMainImg").val() == "") {
alert("상품썸네일사진 등록해주세요.");
$("#itemMainImg").focus();
return false;
}
if ($("#itemSubImg").val() == null || $("#itemSubImg").val() == "") {
alert("상품썸네일보조사진 등록해주세요.");
$("#itemSubImg").focus();
return false;
}
if (confirm("등록하시겠습니까?")) {
$("#itemInsertForm").submit();
return false;
}
}
옵션추가 및 상품등록 전 공백검사 script 문이다.
'SPRING > Homme Shop' 카테고리의 다른 글
[스프링] 쇼핑몰 - 상품 주문 [ 2 ] (0) | 2021.12.09 |
---|---|
[스프링] 쇼핑몰 - 상품 주문 [ 1 ] (0) | 2021.12.08 |
[스프링] 쇼핑몰 - 비동기 장바구니 [3] (0) | 2021.12.08 |
[스프링] 쇼핑몰 - 비동기 장바구니 [2] (1) | 2021.12.08 |
[스프링] 쇼핑몰 - 비동기 장바구니 [1] (6) | 2021.12.08 |