프로젝트

[ Spring Boot 프로젝트 ] 멀티셀렉트 구현 / vo list 를 컨트롤러로 넘기기 ++ 난관과 해결

sian han 2022. 7. 12. 16:03

7주짜리 파이널 프로젝트 주제는 비대면 세탁 서비스

내가 처음 맡게 된건 '주문하기' 구현하는 것

 

※ 첫번째 난관 멀티셀렉트 구현

 

 

리액트를 배우기 전이었으나 멀티셀렉트 기능은 무조건 리액트로 구현하는게 편할거라는 정보를 얻었음

인도사람의 유튜브를 열심히 시청하여 리액트로 어찌저찌 작동하는 멀티셀렉트를 만들어내는데에는 성공

 

 

 

문제점 1. 한 상품당 1개밖에 선택을 못함

 

https://react-select.com/home

  - 위 링크를 통해 사용할 수 있는 select 패키지를 사용하면 멀티셀렉트가 구현되긴 하나

    한 항목당 한번씩만 선택할 수 있는 멀티셀렉트가 구현됨. 

 

문제점 2. 스프링부트에 적용하는 방법을 모르겠음

 

  - 한 상품당 한개씩만 주문할 수 있도록 (..) 구현을 한다쳐도

   만든 jsx 를 스프링부트 프로젝트에 어떻게 가져와서 어떻게     

   사용해야하는지 모르겠음. 이틀정도 모든걸 stop 하고 이것만 찾았었는데 알아내는데 실패함

 

 


 

최종결과

 

여러 시도를 해봤고 3일 정도를 허비함

새로운 시도들을 했을 때 1단계인 구현은 되나 2,3 단계인 응용, 문제해결에서 시간을 크게 잡아먹혔음

 

일단 내가 맡은 부분은 시간내로 완성을 해야하니, 좋은 코드는 아닐지라도 의도대로 움직이게만 구현해보자 라는 결론.

jquery 와 ajax 로 구현함

 

 

 

orderMake.jsp

<div class="orderInfo-order-select-wrapper">
    <div class="title-select">세탁물 선택</div>
    <c:import url="/laundryService/order/orderMakeSelect">
        <c:param name="categoryGroup" value="1"></c:param>
    </c:import>

    <div id="categoryDiv">
        <div id="categoryNameDiv" class="categoryDiv-div">상품</div>
        <div id="categoryPriceDiv" class="categoryDiv-div">금액</div>
        <div id="categoryQTYDiv" class="categoryDiv-div">수량</div>
        <div id="categoryTotalDiv" class="categoryDiv-div">총금액</div>
    </div>

    <div id="order-item-form-div">

        <div class="order-item-Div"></div>
    </div>
    <div id="totalPriceDiv">
        총 : <input type="text" name="totalPriceMake" id="totalPriceMake"
            value=""></input>원
    </div>

</div>

 

 

orderMakeSelect.jsp

<div class="orderInfo-order">
    <div class="order-select-wrap">
        <select class="mulit-select" name="order-item" id="order-item">
            <option value="" selected>선택하세요</option>
            <c:forEach var = "vo" items ="${list }">
            <option value="${vo.no} ">${vo.categoryName} </option>
            </c:forEach>
        </select> 
        <div class="addBtnWrap"><button class="addBtn" id="orderAddBtn">추가</button>&nbsp;<i class="fa-solid fa-circle-plus" id="plusI"></i></div>
    </div>
</div>

 

 

 

 

js

  - select option value 에 상품 번호를 넣어놓고

    '추가' 버튼을 눌렀을 때 no에 해당하는 vo 를 가져와서 (이때 ajax 를 처음으로 사용해봄. ajax 배우기 전)

    appendTo 하여 사용자가 선택한 상품의 정보를 볼 수 있도록 한다 

 

  - 이 때 배열에에 선택한 상품의 번호들을 넣어놓아 같은 상품이 중복으로 선택되는 것을 방지한다

 

  - 새로운 상품이 추가될때마다 input 태그의 class 가 .priceByQty 인 것의 value 를 배열에 넣고,

    반복문으로 배열 안의 값들을 모두 더해 사용자가 총 가격을 볼 수 있도록한다

$('#orderAddBtn').click(function () {
            var itemNo = $('#order-item').val();

            var itemInfo = {"no": itemNo};

            $.ajax({
                url: '/launer/laundryService/order/optionInfo',
                type: 'GET',
                data: itemInfo,

                success: function (vo) {

                    //모든 input 태그의 value를 배열에 저장
                    //이미 배열에 값이 있다면 return false
                    //없다면 새로운 input 태그 생성
                    const list = new Array();
                    $("input[name=no]").each(function (index, item) {
                        list.push($(item).val());
                    });

                    var cNo = (vo.no).toString();
                    const isInArray = list.includes(cNo);

                    if (isInArray) {
                        alert("이미 선택된 상품입니다");
                        return false;
                    }

                    var tagAdd = "<div class ='testForm'><input type = 'hidden' name = 'no' class ='tagInputAdd' value=" + vo.no + ">"
                        + "<input type ='text' name = 'itemName' class ='tagAddDiv' readonly value=" + vo.categoryName + ">"
                        + "<input type = 'text' name = 'itemPrice' class ='tagAddDiv' readonly value=" + vo.price + ">"
                        + "<select class='mulit-select' name='order-num' id='order-num'>"
                        + "<c:forEach var ='cnt' begin ='1' end='10' step='1'>"
                        + "<option >${cnt}</option>"
                        + "</c:forEach>"
                        + "</select>"
                        + "<input type ='text' name='priceByQty' class = 'priceByQty' readonly style='border:none' value=" + vo.price + " >"
                        + "<div id ='xWrap' style='display:inline-block'><a href=# onclick='event.preventDefault()' id='delBtn'><i class='fa-solid fa-xmark' id ='XDel'></i></a></div>"
                        + "</div>";
                    $(tagAdd).appendTo(".order-item-Div");

                    const totalPriceMake = new Array();
                    let sum = 0;
                    $(".priceByQty").each(function (index, item) {
                        totalPriceMake.push($(item).val());
                    });
                    for (let i = 0; i < totalPriceMake.length; i++) {
                        const intTotalPrice = Number(totalPriceMake[i]);
                        sum += intTotalPrice;
                    }
                    $("#totalPriceMake").attr("value", sum);
                },
                error: function () {
                    alert("상품을 선택하세요");
                }
            });
        });

 

 

나의 계획 조건에 만족하는 멀티셀렉트를 구현했다.

여기서 두번째 난관

 

그래서 주문정보를 어떻게 넘길건데 ? 

 

바로 생각나는 방법이있었으나.. 별로 사용하고 싶지않은 방법이었음

뭐 결론만 말하자면 결국 처음에 떠올린 그 방법을 사용했음

 

 

 

 


 

 

※ 두번째 난관 vo list 를 컨트롤러로 넘기기

 

상품 추가 버튼을 누르면 아래 testForm 이 .order-item-div 안에 생성된다

주문하기 버튼을 눌렀을때 아래의 방식으로 생성된 여러개의 testForm 데이터를 컨트롤러로 넘겨야하는것

var tagAdd = "<div class ='testForm'>
	<input type = 'hidden' name = 'no' class ='tagInputAdd' value=" + vo.no + ">"
    + "<input type ='text' name = 'itemName' class ='tagAddDiv' readonly value=" + vo.categoryName + ">"
    + "<input type = 'text' name = 'itemPrice' class ='tagAddDiv' readonly value=" + vo.price + ">"
    + "<select class='mulit-select' name='order-num' id='order-num'>"
    + "<c:forEach var ='cnt' begin ='1' end='10' step='1'>"
    + "<option >${cnt}</option>"
    + "</c:forEach>"
    + "</select>"
    + "<input type ='text' name='priceByQty' class = 'priceByQty' readonly style='border:none' value=" + vo.price + " >"
    + "</div>";
$(tagAdd).appendTo(".order-item-Div");

 

 

방법은 뻔하다

각 input 의 value 를 변수에 저장하고 이를 연결하여 문자열로 컨트롤러에 보내는 것

 

상품번호, 상품명, 상품가격, 수량, 총가격 | 상품번호, 상품명, 상품가격, 수량, 총가격 | 상품번호, 상품명, 상품가격, 수량, 총가격 ... 

 

이런 모양으로 컨트롤러에 보내게 되는 것이다 (.. ㅠ )

 

js

$('#orderBtn').click(function () {
            if ($("input[name=itemName]").length < 1) {
                alert("상품을 선택해주세요");
                event.preventDefault();
                return false;
            }
			let dataArr = [];

			let dataList = $(".testForm");
			console.log(dataList);

			var param_string ="";
			$.each(dataList, (idx, item) => {

				let no = item.childNodes[0].defaultValue;
				let name = item.childNodes[1].defaultValue;
				let price = item.childNodes[2].defaultValue;
				let qty = item.childNodes[3].value;
				let sum = item.childNodes[4].defaultValue;
				param_string+=
					no +","+name+","+price+","+ qty + "," + sum + "|"
			});
			console.log("param_string = "+param_string);

			$('#param').val(param_string);

			$('form[name=frm]').attr('action',"<c:url value='/laundryService/order/orderConfirm'/>");
			$('form[name=frm]').attr('method','post');
        });

 

 

 

orderController 컨트롤러

  - (,), (|) 기준으로 문자열(String param) 을 split 하여 map 에 저장하고, 각 map 을 list 에 add 하여

    뷰 (결제전 최종확인 창) 로 내려보냄

@PostMapping("/orderConfirm")
	public String orderConfirm_post(@RequestParam String param, Model model, HttpSession session) {
		logger.info("결제전 최종확인 화면, param_string 파라미터 = {}",param);
		//회원정보 불러오기
		int no = (int) session.getAttribute("no");
		logger.info("결제전 최종확인 화면, 파라미터 userid ={}", no);
		
		UserVO vo = userService.selectById(no);
		
		logger.info("회원정보조회 vo={}",vo);

		//회원 주소 정보 불러오기
		HashMap<String,Object> mapAddress = userService.selectByIdAddress(no);
		logger.info("회원주소조회={}",mapAddress);

		//주문정보확인
		List<Map<String, Object>> list= new ArrayList<>();
		
		logger.info("파라미터 param={}", param);
		
		String paramString[] = param.split("[|]");
		String setParamString[];
		
		for(String str : paramString) logger.info("분리 후 str={}", str);
		
		int paramPrice = 0;
		for(int i=0;i<paramString.length;i++) {
			Map<String, Object> map = new HashMap<>();
			setParamString = paramString[i].split(",");

			map.put("categoryNo", setParamString[0]);
			map.put("name", setParamString[1]);
			map.put("price", setParamString[2]);
			map.put("quan", setParamString[3]);
			map.put("sum", setParamString[4]);
			
			//총 결제금액 int 로 실어보내기
			paramPrice += Integer.parseInt( setParamString[4]);

			list.add(map);
		}
		
		logger.info("최종금액 ={}",paramPrice);

		model.addAttribute("userVo", vo);
		model.addAttribute("list", list);
		model.addAttribute("paramPrice", paramPrice);
		model.addAttribute("param", param);
		model.addAttribute("map", mapAddress);
		return "/laundryService/order/orderConfirm";
	}

 

 

 

이후 주문정보를 db에 넣는 과정은 어려움 없이 진행했다.

분명 .. 더좋은 방법이있을꺼다 json 을 이용한다던지 하는 ..

사실 json 사용 방법을 알게된 지금도 명확하게 떠오르는 방법은 없다

개인적으로 토이프로젝트를 만들어볼 계획인데 같은걸 더 좋은 방법으로 시도해볼꺼다. 

 

 

 

 => 지금 같은 문제를 마주친다면 ? 

 

해결 1 ) DataType 을 json 로 설정해서 ajax 로 넘긴다 !  ==> XX 

  - ajax 로 넘길 시 json 객체로 만들어야 하는데, 컨트롤러에서 요청을 받고 다른 뷰페이지로 넘길 수 없음

    (방법있나 .. ? 현재로서는 모르겠음) 인줄 알았는데 이게 되네

 

 

 

 

해결 2 )  vo list 에 데이터들을 저장 후 post 방식으로 컨트롤러에 전달 ! ==> OO

 

1. 프로퍼티를 저장하는 VO 객체를 만든다. (해당 객체를 VO vo)

2  List<vo>를 담고있는 VO 객체를 만든다. (해당 객체를 VOs vos)

3. Controller에 넘겨줄 input의 name을 'vos[i].프로퍼티명' 형태로 정한다.

4. submit이 될 경우 Controller에서는 'ModelAtturibete VOs ~~' 로 받아준다.

 

clear !

 

 

해결 3 ) view 에서 view 로 데이터를 넘기기

  - 지금까지 view 에서 view 로 데이터를 넘길 때 항상

    view => controller => view 방식으로 데이터를 넘겼었다.

    컨트롤러에서 데이터를 가공하는것도 아닌데 view => view 방법을 몰라서 ! 

    

        빛권순         

 :   localStorage 를 사용하면 해결됨

 

 

sessionStorage : 브라우저 상에 데이터를 저장할 수 있는 웹 스토리지.                            

 - String 형식만 지원함

 - 세션 스토리지는 웹페이지의 세션이 끝날 때 저장된 데이터가 지워짐

 참조 : https://www.daleseo.com/js-web-storage/

 

$("#resultbtn").click(() => {
                let formData = [];
                document.querySelectorAll("#park div").forEach(item => {
                    let tempData = {
                        name: $(item).find("input[name=name]").val(),
                        price: $(item).find("input[name=price]").val(),
                        sum: $(item).find("select[name=sum]").val()
                    } 
                    console.log(tempData);

                    formDate.push(tempData);
                });

                console.log(formData);

                console.log(JSON.stringify(formDate));
                
                //
                sessionStorage.setItem("json", JSON.stringify(formData)); //객체를 문자열로 변환, 그 후 저장
                location.href = "/test/2";
                // $("#park form").submit();
            });

 

 

1. input 파라미터를 JSON 형태로 만들어준다 (tempData) -- sessionStorage는 문자열만 저장하기 때문

2. 문자열로 변환 후 sessionStorage에 저장한다 

3. location.href 사용하여 페이지 리다이렉트

4. test/2 페이지에서 이런형식으로 받으면 json 형식으로 view <=> view 데이터를 전달할수있게된다

    let temp = JSON.parse(sessionStorage.getItem("json"));
    console.log(temp);