상품등록 구현

최주영·2023년 7월 2일
0

세미프로젝트

목록 보기
10/11

✅ 상품등록


  • 상품등록의 UI는 다음과 같이 구현하였다

위의 UI 부분에서 가장 어려웠던 사진등록 UI부분 + 카테고리 + 검색태그등록 UI + 가격 정규표현식
에 대해서만 작성해보겠다

  • 이미지 안에 input type file을 넣고 그 부분을 안보이게 설정한 작업
  • 이미지를 클릭시 -> 파일을 선택할 수 있는 UI
<img src="<%=request.getContextPath()%>/images/productregist/imgregist.png" 
class="upload" width="250px" height="250px"> 
<input type="file" id="inputFile" class="real-upload" 
accept="image/*" required multiple style="display: none;"> 
<!-- multiple -> 사용자가 둘 이상의 값을 입력할 수 있음을 명시 -->
<!--  email, file 타입에만 적용 가능함 -->
  • 대표카테고리의 값을 선택시 -> 그 하위 서브카테고리들이 출력되는 작업
		<div class="cate">
			<h4 class="h4Size">카테고리 *</h4>
				<select class="mainCate" onchange="chageSubCate(this.value);">
					<!-- this.value -> 선택된 option의 밸류값을 매개변수로 넣음 -->
					<%
                       <!-- categorys는 getAttribute를 통해서 디비안의 값들을 갖고온다 -->
					if (!categorys.isEmpty()) {
						for (int i = 0; i < categorys.size(); i++) {
					%>
					<option value="<%=categorys.get(i).getCategoryId()%>"><%=categorys.get(i).getCategoryName()%></option>
					<%
					}
				}
					%>
                      
                </select> 
                <select class="middleCate" name="subCate">
				<!-- 이안의 내용은 서브카테고리의 내용들이 출력된다 -->
				</select>
// 카테고리 선택하는 작업

$(() => {
	$(".mainCate").trigger("change", $(".mainCate:selected").val());  // 페이지로딩되었을때, 자동으로 change 함수 실행
	//	대상값은 현재 그 select에 선택된 값
})

function chageSubCate(value) {
	console.log(value);
	$.ajax({
		url: "findSubCate",
		data: { "cateId": value },
		success: function(result) {

			const subCate = result.split(","); // 문자열로 넘어온 값들을 ,를 구분자로 배열을 만듬

			$(".middleCate option").remove();   // 메인카테고리 선택할때마다 옵션들 다 삭제(초기화)
			for (let i = 0; i < subCate.length; i++) { // 배열에 있는 서브카테고리들을 option value로 만들어줌
				var option = $("<option value=" + subCate[i] + ">" + subCate[i] + "</option>");
				$(".middleCate").append(option);
			}
		},
		error: function() {
			console.log("카테고리 선택 오류발생");
		}
	})
}

컨트롤러

// findSubCate 서블릿
String cateId = request.getParameter("cateId");
List<String> subCategorys = new ProductRegistService().selectSubCate(cateId); // 서브카테고리들이 리스트로 나옴
// 리스트안에 문자열 형식으로 받아야함 

String result = subCategorys.stream().map(n->String.valueOf(n)).collect(Collectors.joining(","));
// 리스트를 -> 문자열로 만들어줌 (문자열로 만들어줄때 앞뒤로 공백을 없애고 ,로 구분해줌)
// [컴퓨터, 노트북, 스마트폰, 소프트웨어, 기타 주변기기]  -> 컴퓨터,노트북,스마트폰,소프트웨어,기타 주변기기

Mapper

	<select id="selectSubCate" resultType="string" parameterType="string">
		 SELECT SUBCATEGORY_NAME FROM SUBCATEGORY WHERE CATEGORY_ID = #{cateId}
	</select>   <!--반환타입 : 문자열,  매개변수 타입 : 문자열 -->
    <!-- 타입을 정수형으로 할 경우 -> _int -->
  • 가격입력시 숫자들만 입력가능하며, 중간중간에 , 로 숫자를 구별해줌
<input type="text" id="priceId" oninput="inputNumberFormat(this);" 
 placeholder="숫자만 입력해주세요." name="price">
<!-- oninput 속성을 통해 정규표현식을 적용하였다 -->
// ==== 가격 입력했을 때, 숫자만입력되고, 3자리수마다 ,로 구분해주는 작업
function comma(str) {
	str = String(str);
	return str.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, "$1,");
}

function uncomma(str) {
	str = String(str);
	return str.replace(/[^\d]+/g, "");
}

function inputNumberFormat(obj) {
	obj.value = comma(uncomma(obj.value));
}


// 가격이 입력될때마다 예외처리
const priceValue = document.getElementById("priceId")
const spanPrice = $("#spanPrice");
priceValue.addEventListener("keyup", function() {

	if (priceValue.value.length == 0) {
		spanPrice.text("");
		checkProductRegist.productPrice=false;
	}else{
		replacePrice = priceValue.value.replace(",","");
		if(replacePrice <= 0){
			spanPrice.text("0원보다 크게 입력하세요").css("color","red");
			checkProductRegist.productPrice=false;
		}else{
			spanPrice.text("○").css("color","green");
			checkProductRegist.productPrice=true;
		}
	}
});
  • 단어입력시, 해당 포함하는 연관 단어들을 선택 또는 엔터를 통해서 태그를 등록 가능
		<div class="relativeTag">
			<h4 class="h4Size">
				상품태그
			</h4>
			<input type="text" id="searchTag" placeholder="연관 태그를 입력해주세요" autocomplete="on">
			<div id="relativeTagDiv">
				<!-- 태그들이 등록되는 곳 -->
			</div>
		</div>

		<div class="autocomplete"><!-- 자동완성 검색어들이 나오는 곳 --></div>
// 상품태그 검색 관련 js
const dataList = ["넣을값"];
 
let registTagList = [];

const $searchTag = document.querySelector("#searchTag");
const $autoComplete = document.querySelector(".autocomplete");

let nowIndex = 0;
let matchDataList;
$searchTag.onkeyup = (event) => {
	// 검색어
	const value = $searchTag.value.trim();

	// 자동완성 필터링
	matchDataList = value
		? dataList.filter((label) => label.includes(value))
		: [];

	switch (event.keyCode) {
		// UP KEY
		case 38:
			nowIndex = Math.max(nowIndex - 1, 0);
			break;

		// DOWN KEY
		case 40:
			nowIndex = Math.min(nowIndex + 1, matchDataList.length - 1);
			// document.querySelector("#searchTag").value = matchDataList[nowIndex];
			break;

		// 관련검색어에 엔터키 눌렀을 때 (태그만들어지는 과정)
		case 13:
			document.querySelector("#searchTag").value = matchDataList[nowIndex] || "";

			const key = document.getElementById("searchTag").value;

			const aa = document.getElementById("registTag>li label");


			if ((key != "") && (!registTagList.includes(key))) { // 검색키가 없거나, 중복값이 있으면 추가 x

				if (registTagList.length >= 5) {
					alert("태그는 최대 5개까지만 추가 가능합니다.");
					document.querySelector("#searchTag").value = "";  // 연관검색창 닫기
					break;
				}

				registTagList.push(key); // 태그가 5개미만일경우 추가
				const $li = document.createElement("li");

				document.getElementById("relativeTagDiv").appendChild($li);
				const $button1 = document.createElement("label");
				const $button2 = document.createElement("button");

				const $img = document.createElement("img"); // img 태그 생성
				$img.height = "15";
				$img.width = "15";
				$img.src = context + "/images/productregist/xbtn.png";

				$img.addEventListener("click", e => {  // 해당 이미지 클릭시

					for (let i = 0; i < registTagList.length; i++) { // 저장해놓은 키워드배열에서 값 삭제하고 개수 줄임
						if (registTagList[i] == e.target.parentElement.previousElementSibling.innerHTML) {
							registTagList.splice(i, 1);
							break;
						}
					}
					$(e.target).parent().parent().remove(); // li밑 label+button 밑 img까지 삭제
				});

				$button2.appendChild($img);
				$button1.innerHTML = key;

				$li.appendChild($button1);
				$li.appendChild($button2);

				var input1 = document.createElement('input');
				input1.setAttribute("type", "hidden");
				input1.setAttribute("name", "data1");
				input1.setAttribute("value", key);

				$li.appendChild(input1);
			}

			// 초기화
			nowIndex = 0;
			matchDataList.length = 0;
			document.querySelector("#searchTag").value = ""; // 연관검색창 닫기
			break;

		case 27: // esc 눌렀을때 입력창 초기화 및 관련검색어 창 닫기
			document.querySelector("#searchTag").value = "";
			matchDataList.length = 0;

		// 그외 다시 초기화
		default:
			nowIndex = 0;
			break;

	}

	// 리스트 보여주기
	showList(matchDataList, value, nowIndex);
};

const showList = (data, value, nowIndex) => {
	// 정규식으로 변환
	const regex = new RegExp(`(${value})`, "g");

	$autoComplete.innerHTML = data
		.map(
			(label, index) => `
      <div class='${nowIndex === index ? "active" : ""}'>
        ${label.replace(regex, "<label>$1</label>")}
      </div>
    `
		)
		.join("");
};


$autoComplete.addEventListener("click", e => {  // 관련검색어 클릭했을경우

	let clickAnswer = e.target.innerHTML.trim();

	for (let i = 0; i <= 10; i++) {
		clickAnswer = clickAnswer.replace("<label>", "");
		clickAnswer = clickAnswer.replace("</label>", "");
	}

	document.querySelector("#searchTag").value = clickAnswer;

	const key = document.getElementById("searchTag").value;

	const aa = document.getElementById("registTag>li label");

	if (registTagList.length >= 5) {
		alert("태그는 최대 5개까지만 추가 가능합니다.");
		document.querySelector("#searchTag").value = "";  // 연관검색창 닫기
		return;
	}

	if ((key != "") && (!registTagList.includes(key))) {
		
		registTagList.push(key);
		const $li = document.createElement("li");

		document.getElementById("relativeTagDiv").appendChild($li);
		const $button1 = document.createElement("label");
		const $button2 = document.createElement("button");
		/*const $button2 = $("<button>").css({"border":"none", "background-color":"transparent"});
		*/
		const $img = document.createElement("img");
		$img.height = "15";
		$img.width = "15";
		$img.src = context + "/images/productregist/xbtn.png";

		$button2.appendChild($img);

		$button1.innerHTML = key;

		$li.appendChild($button1);
		$li.appendChild($button2);


		var input1 = document.createElement('input');
		input1.setAttribute("type", "hidden");
		input1.setAttribute("name", "data1");
		input1.setAttribute("value", key);

		$li.appendChild(input1);


		$img.addEventListener("click", e => {  // 해당 이미지 클릭시
			$(e.target).parent().parent().remove(); // li밑 label+button 밑 img까지 삭제

			for (let i = 0; i < registTagList.length; i++) { // 저장해놓은 키워드배열에서 값 삭제하고 개수 줄임
				if (registTagList[i] == e.target.parentElement.previousElementSibling.innerHTML) {
					registTagList.splice(i, 1);
					break;
				}
			}
		});

		document.querySelector("#searchTag").dispatchEvent(new KeyboardEvent("keyup", { keyCode: 13 })); 
      // 관련검색어 마우스로 클릭했을 때 엔터효과 한번 발생
		$("#searchTag").val("");   // 그 후 input창 비워줌 document.querySelector("#searchTag").value =""; 
		$("#searchTag").focus();   // 그 input창에 focus 줌 document.getElementById("searchTag").focus(); 
	}
})


/* 관련검색어에 #키 입력 못하도록 설정*/
$(document).ready(function() {
	$("#searchTag").keypress(function(e) {
		if (event.key == '#') {
			e.preventDefault();
			e.returnValue = false;
		}
	});
});
/*=============================*/
function productRegist() {  // 상품등록 버튼 클릭됬을 때,
	
	if(checkProductRegist.productTitle && checkProductRegist.productPrice && checkProductRegist.productExplan 
		&& checkProductRegist.productImg){ // 입력칸들이 다 true여야만 상품등록가능함
	}else{
			console.log("다 입력해라")
			return;
	}

	const form = new FormData();  // form 객체에 입력한 값들을 먼저 다 추가함
	form.append("title", $(".inputTitle").val());
	form.append("subCate", $(".middleCate").val());
	form.append("place", $("#sample6_address").val());
	form.append("state", $("input[name=state]:checked").val());
	form.append("price", $("#priceId").val())
	form.append("explan", $("#explanId").val())
	let tag="";
	$("input[name=data1]").each((i,element)=>{ 
      // jquery로 해당 선택자로 값을 가져옴 .each(i,elemnet) 번호와, 
      // -> 해당 데이터들의 인덱스 해당 값을 가져옴 (상품태그들)
		if(i!=0) tag+=",";
		tag+=element.value;	
	})
	form.append("tag",tag); 
	
	const files= dataTransfer.files; // 올린 이미지 파일값들

	$.each(files,(index,file)=>{
		form.append("upfile"+index,file);
	});

	$.ajax({
		url: "productRegistEnd.do", // 해당 서블릿으로 ajax로 요청
		data: form,   // 저정한 form 객체를 데이터로 보냄
		processData:false, // 멀티파트폼으로 보내기위해서 설정
		contentType:false, // 멀티파트폼으로 보내기위해서 설정
		type:"post",
		success: function(result) {
			if(result==1) { // db는 결과값이 정수로 나옴 // 입력성공
					alert("등록 성공");
					location.replace("http://localhost:9090/semi-hifive/");
			}else{ 
					alert("등록 실패");
					location.replace("http://localhost:9090/semi-hifive/"+"productRegist.do");
			}
		},
		error: function() {
			alert("오류발생");
			location.replace("http://localhost:9090/semi-hifive/"+"productRegist.do");
		}
	})
}
  • 상품등록 하는 서블릿
		HttpSession session = request.getSession();
		String path=getServletContext().getRealPath("/upload/productRegist");  // ->  /upload/productRegist 안에다 업로드되는 이미지 넣음
		System.out.println(path);
		MultipartRequest mr= new MultipartRequest(request, path, 1024*1024*60,"UTF-8",new DefaultFileRenamePolicy()); 
		// MultipartRequest 객체 사용하려면, 이 서블릿을 요청시킨 form태그에 enctype="multipart/form-data" 를 넣어야함
		
		String replacePrice = mr.getParameter("price"); 
		replacePrice = replacePrice.replace(",",""); // ,있는 돈 문자열을 ,를 ""로 대체함
		
		Member m = (Member) session.getAttribute("loginMember");// 세션에서 현재로그인한 정보 갖고옴
		String productId = mr.getParameter("productId");
		String userId = m.getUserId();
		String title = mr.getParameter("title");
		String subCate = mr.getParameter("subCate");
		String place = mr.getParameter("place");
		String state = mr.getParameter("state");
		int price = Integer.parseInt(replacePrice); // 정수일경우 앞에 Integer.parseInt 로 형변환해야함
		String explan = mr.getParameter("explan");
		String tag = mr.getParameter("tag");
		
		List<ProductFile> files= new ArrayList();
		
		Enumeration<String> names=mr.getFileNames(); // 해당 파일객체들의 키값을 하나씩 출력 (Enumeration -> 열거객체 순환) 
		while(names.hasMoreElements()) {  // 다중 파일들의 이미지를 접근 가능
			String key=names.nextElement();  // key-> 해당 파일은 객체로 저장되잇음 각각 파일의 키를 저장함
//			mr.getFilesystemName(key));  new 파일
//			mr.getOriginalFileName(key)); ori파일

			files.add(ProductFile.builder().imageName(mr.getFilesystemName(key)).build()); 
			// ProductFile 객체 안에 멤버변수들을 builder를 통해서 각각 넣어줌 (파일이름, 등등)
			// builder 함수 사용시 마지막에 .build() 해줘야함
		}
		
		 Product p = Product.builder().title(title).productStatus(state).price(price).explanation(explan)
				 .keyword(tag).areaName(place).subCategoryName(subCate).files(files).build();
		 // Product 객체 안에 멤버변수들을 builder를 통해서 넣어줌 (마지막에는 files 멤버변수도 builder를 통해서 해당맞는 타입의 값을 넣어줌)
		 // Product 클래스안에 결국 ProudctFile 값들도 들어있는것임 
		 // 그러기 때문에 Product만 객체만 넣어줘도 됨
		 
		 int result = new ProductRegistService().insertProduct(p,userId); // 상품등록 및 상품이미지첨부파일 데이터 추가 하는 작업
		 
		 response.getWriter().print(result); // 해당 반환되는 0 또는 1의 값을 다시 js로 반환됨 (ajax이기 때문에 해줘야함)-> js에서 정수 값을 통해 분기처리
profile
우측 상단 햇님모양 클릭하셔서 무조건 야간모드로 봐주세요!!

0개의 댓글