[Spring]오늘의 직방 - 카카오맵 커스텀오버레이 클릭이벤트

dh·2022년 10월 15일
0
post-thumbnail

커스텀오버레이 클릭

지도에 나온 매물 커스텀오버레이 클릭시 선택된 마커를 표시하고 매물의 정보를 ajax를 이용해 가져오기로 했습니다.
매물커스텀오버레이를 지도에 찍을때 addEventHandle함수를 이용해 클릭이벤트를 적용하기로 했습니다.
addEventHandle함수에서 클릭시 클래스이름을 변경하여 선택된 이미지로 변경하고 도로명주소와 현재 페이지 번호를 넘겨 getMaemulList함수를 호출하여 ajax로 매물 정보를 받아와 테이블 형태로 보여줍니다. 한 페이지에 매물 5개씩 보여주며 전체 데이터 수가 5개이상이면 더보기가 활성화 되어 클릭시 다음 페이지의 데이터를 추가하고 더이상 데이터가 없으면 더보기를 비활성화 하여 클릭을 못하게 했습니다.

코드

map.js

function addMarker(address_result){

  //-----------생략-----------------------
  
  // 주소로 좌표를 검색합니다
            geocoder.addressSearch(address_result[i].roadName, function(result, status) {
                // 정상적으로 검색이 완료됐으면 
                if (status === kakao.maps.services.Status.OK) {
                    var coords = new kakao.maps.LatLng(result[0].y, result[0].x);

                    // 아파트명 커스텀 오버레이를 생성합니다
                    var customOverlay = new kakao.maps.CustomOverlay({
                        //map: map,
                        position: coords,
                        content: content,
                        clickable: true,
                        yAnchor: 1
                    });
                    
                    customOverlay.setMap(map);
                  
					//저번에 했던 addMarker함수 안에서 호출
                    addEventHandle(content,'click')
                } 
            }); 
}

var selectedMarker = "";

function addEventHandle(target,type) {

    if (target.addEventListener) {
        target.addEventListener(type, function(e){
         
            // 클릭된 마커가 없고, click 마커가 클릭된 마커가 아니면
            // 마커의 이미지를 클릭 이미지로 변경합니다
            if (!selectedMarker || selectedMarker !== target) {
                // 클릭된 마커 객체가 null이 아니면
                // 클릭된 마커의 이미지를 기본 이미지로 변경하고
                selectedMarker.className="building";
                if(selectedMarker){
                    selectedMarker.parentNode.style.zIndex=0
                }

                // 현재 클릭된 마커의 이미지는 클릭 이미지로 변경하고 맨앞으로 표시합니다.
                target.className="building-select"
                target.firstChild.className="building-top-select"
                target.firstChild.nextSibling.className="building-bot-select"
                target.parentNode.style.zIndex=10  
            }

            // 클릭된 마커를 현재 클릭된 마커 객체로 설정합니다
            selectedMarker = target;

            var roadName = e.target.getAttribute("data-roadName");
            var sigungu = e.target.getAttribute("data-sigungu");

            realEstateList.innerHTML='' //매물리스트 초기화

            //매물리스트 테이블헤더 설정
            let thead = document.createElement("thead")
            thead.className="table-warning"
            let tr = document.createElement("tr")
            let th = document.createElement("th")
            let thText = document.createTextNode("등록일")
            th.appendChild(thText)
            tr.appendChild(th)
            th = document.createElement("th")
            thText = document.createTextNode("거래가격")
            th.appendChild(thText)
            tr.appendChild(th)
            th = document.createElement("th")
            thText = document.createTextNode("면적")
            th.appendChild(thText)
            tr.appendChild(th)
            th = document.createElement("th")
            thText = document.createTextNode("층수")
            th.appendChild(thText)
            tr.appendChild(th)
            th = document.createElement("th")
            thText = document.createTextNode("관심")
            th.appendChild(thText)
            tr.appendChild(th)

            thead.appendChild(tr)
            realEstateList.append(thead)

            page=1;
            reviewPage=1;
          //타겟의 도로명주소와 현재페이지 번호로 매물리스트 호출
            getMaemulList(roadName,page) 
        });
    } else {
        target.attachEvent('on' + type, callback);
    }
}

function getMaemulList(roadName,p){
    buildingInfo.style.display="block"
 
    let user = interestedCheck(roadName);

    let xhttp = new XMLHttpRequest();
    xhttp.open("GET","./getList?roadName="+roadName+"&page="+p);
    xhttp.send();
    xhttp.addEventListener("readystatechange", function(){
        if(xhttp.status==200 && xhttp.readyState==4){
            
            var result = JSON.parse(xhttp.responseText.trim());
            let list= result.list
            let pager=result.maemulPager

            for(let i=0; i<list.length; i++){   
                let tr = document.createElement("tr")
                tr.className = "tr"
                let td = document.createElement("td")
                let tdText = document.createTextNode(list[i].dealDay)
                td.appendChild(tdText)
                tr.appendChild(td)

                td = document.createElement("td")
                if(list[i].dealType=="매매"){
                    if(list[i].deal.toString().length>=5){
                        let million = parseInt(list[i].deal /10000); 
                        let remain = list[i].deal %10000 ==0 ? '': list[i].deal %10000
                        tdText = document.createTextNode(list[i].dealType + million + "억" + remain)
                    }else{
                        tdText = document.createTextNode(list[i].dealType + list[i].deal)
                    }
                }else if(list[i].dealType=="전세"){
                    if(list[i].deposit.toString().length>=5){
                        let million = parseInt(list[i].deposit /10000); 
                        let remain = list[i].deposit %10000 ==0 ? '': list[i].deposit %10000 
                        tdText = document.createTextNode(list[i].dealType + million + "억" + remain)
                    }else{
                        tdText = document.createTextNode(list[i].dealType + list[i].deposit)
                    }
                }else{
                    if(list[i].wdeposit.toString().length>=5){
                        let million = parseInt(list[i].wdeposit /10000); 
                        let remain = list[i].wdeposit %10000 ==0 ? '': list[i].wdeposit %10000 
                        tdText = document.createTextNode(list[i].dealType + million + "억" + remain + '/' + list[i].monthly)
                    }else{
                        tdText = document.createTextNode(list[i].dealType + list[i].wdeposit + '/'+ list[i].monthly )
                    }
                } 
                
                td.appendChild(tdText)
                tr.appendChild(td)

                td = document.createElement("td")
                tdText = document.createTextNode(list[i].area)
                td.appendChild(tdText)
                tr.appendChild(td)

                td = document.createElement("td")
                tdText = document.createTextNode(list[i].floor)
                td.appendChild(tdText)
                tr.appendChild(td)

                td=document.createElement("td")
                tdText = document.createTextNode("🤍")
                let tdAttr = document.createAttribute("class")
                tdAttr.value="interested"
                td.setAttributeNode(tdAttr)
                td.appendChild(tdText)
                td.setAttribute("data-num",list[i].num)
                td.setAttribute("data-userId",userId.value)
                tr.appendChild(td)
        
                realEstateList.append(tr)

                //페이지 없으면 더보기버튼 비활성화
                if(page >= pager.totalPage){
                    more.disabled= true;
                    more.className='disabled'
                }else{
                    more.disabled= false;
                    more.className='more'
                }
                
                more.setAttribute("data-name",list[i].roadName)
                buildingName.innerText=list[i].buildingNm

            }
        }
    })
}

//매물리스트 더보기버튼 클릭 이벤트
more.addEventListener("click", function(){
    page++	//다음페이지
    let roadName = more.getAttribute("data-name");
    getMaemulList(roadName,page)
});

AptController.java

	@GetMapping("getList")
	@ResponseBody
	public Map<String, Object> getList(MaemulPager maemulPager) throws Exception{
		ModelAndView mv = new ModelAndView();
		List<AptDTO> list = aptService.getList(maemulPager);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("list", list);
		map.put("maemulPager", maemulPager);
		
		return map;
	}

AptService

@Service
public class AptService {
	@Autowired
	private AptDAO aptDAO;
	
	public List<AptDTO> getList(MaemulPager maemulPager)throws Exception{
		maemulPager.getRowNum();	//현재 페이지에서 시작번호,끝번호 계산

		Long totalCount = aptDAO.getTotalCount(maemulPager);	//전체 데이터의 개수
		maemulPager.makePage(totalCount); //전체 데이터 수로 총 페이지 계산
		
		return aptDAO.getList(maemulPager);
	}
}

결과

더보기 클릭시 다음페이지의 정보 밑에 추가,다음 페이지가 없으면 더보기 비활성화

관심매물 체크

매물 정보의 하트모양 클릭시 Interested테이블에 userId와 해당매물의 num을 insert하고 하트를 빨간색으로 칠해준다. 다시 하트를 클릭시 색을 없애고 Interested테이블에서 delete를 했습니다.

REALESTATE 테이블과 INTERESTED 테이블의 관계는 위와 같고 매물 마커 클릭시 관심매물의 정보도 받아오려면 ROADNAME이 필요합니다. 그래서 두 테이블을 조인하여 데이터를 가져왔습니다.

InterestedMapper.xml

<mapper namespace="com.goodee.home.interested.InterestedDAO">
      <select id="getInterestedUser" parameterType="InterestedDTO" resultType="InterestedDTO">
          SELECT NUM, I.USERID
          FROM REALESTATE R
             INNER JOIN
             INTERESTED I
             USING(NUM)
           WHERE ROADNAME=#{roadName} AND USERID=#{userId} ORDER BY DEALDAY
      </select>
    
      <insert id="setInterested" parameterType="InterestedDTO">
          INSERT INTO INTERESTED VALUES(INTERESTED_SEQ.NEXTVAL, #{userId}, #{num})
      </insert>
      
      <delete id="setDeleteInterested" parameterType="InterestedDTO">
          DELETE FROM INTERESTED WHERE USERID=#{userId} AND NUM=#{num}
      </delete>
  </mapper>

map.js

//매물 좋아요 클릭 이벤트처리
realEstateList.addEventListener("click",function(event){
    let num =event.target.getAttribute("data-num");
    let userId = event.target.getAttribute("data-userId");
    if(event.target.className=="interested"){
        if(userId==""){
            alert("로그인이 필요합니다.")  
        }else{ //관심매물 insert
            event.target.classList.add("interested_select")
    
            let xhttp = new XMLHttpRequest();
            xhttp.open("POST","./setInterested");
            xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            xhttp.send("userId="+userId+"&num="+num);
            xhttp.addEventListener("readystatechange",function(){
                if(xhttp.status==200 && xhttp.readyState==4){
                    let result = xhttp.responseText
                }
            })
        }
    }else if(event.target.classList.contains("interested_select")){	//관심매물 delete
        event.target.classList.toggle("interested_select")

        let xhttp = new XMLHttpRequest();
        xhttp.open("POST","./setDeleteInterested");
        xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhttp.send("userId="+userId+"&num="+num);
        xhttp.addEventListener("readystatechange",function(){
            if(xhttp.status==200 && xhttp.readyState==4){
                let result = xhttp.responseText
                console.log(result)
            }
        })
    }
})

//유저별 좋아요클릭한 매물 받아오기
function interestedCheck(roadname){

    let result

    $.ajax({
        type: "post",
        url: "./getInterestedUser",
        async: false,     //값을 리턴시 해당코드를 추가하여 동기로 변경
        data: { userId: userId.value,
                roadName: roadname},
        success: function (data) {
            result = data;
        }
    });
    return result
}

function getMaemulList(roadName,p){
	let user = interestedCheck(roadName);

  //----------------생략------------
  
    td=document.createElement("td")
    tdText = document.createTextNode("🤍")
    let tdAttr = document.createAttribute("class")
    tdAttr.value="interested"
    td.setAttributeNode(tdAttr)
    td.appendChild(tdText)  
    td.setAttribute("data-num",list[i].num)
    td.setAttribute("data-userId",userId.value)
    tr.appendChild(td)
  
    if(user.length>0){
      for(let j =0; j<user.length; j++){
        if(user[j].num==list[i].num){
          td.classList.add("interested_select")
          user.splice(j, 1);
        }
      }
    }
  //------------생략---------------------
}

관심매물을 등록한 유저와 매물번호를 받아와서 그 유저의 관심매물 번호가 매물리스트의 매물번호가 같으면 interested_select 클래스를 추가해 하트를 빨간색으로 보여줍니다.
관심매물을 등록한 유저를 먼저 ajax로 가져오고 매물을 ajax로 가져와서 유저가 관심등록을 했는지 비교를 하려했는데 비동기식으로 진행되다보니 문제가 있었습니다.
그래서 관심매물 유저 ajax를 async: false로 동기식으로 처리하여 유저정보를 먼저 가져오고 이 유저정보로 나중에 가져온 매물과 비교를 진행했으나 이런식으로 하니 최종 사용자 환경에 부정적인 영향을 미치므로 기본 스레드의 동기식 XMLHttpRequest가 지원 중단되었습니다. 추가 지원이 필요한 경우 다음 페이지를 참고하세요.라는 경고가 떴습니다. 권장하지 않는 방식 이었던 것이다.

지금 생각해보면 관심을 등록한유저 따로 매물 따로 select 하지말고 조인을 이용해 매물정보를 가져올때 관심을 등록한 유저도 같이 가져오는 방법을 사용했으면 해결됬을 문제였던것 같습니다.

SELECT * 
FROM (SELECT ROWNUM R, A.*
    FROM (SELECT X.*, Y.USERID 
          FROM REALESTATE X
                LEFT OUTER JOIN
                (SELECT * FROM INTERESTED WHERE USERID='ㅇㅇㅇ') Y
                ON X.NUM=Y.NUM
            WHERE ROADNAME='ㅇㅇㅇ' AND BUILDTYPE='아파트' ORDER BY DEALDAY) A)
WHERE R BETWEEN 1 AND 10;

left outer join을 사용해 특정 아파트의 등록된 모든 매물을 가져오되 로그인한 유저의 id로 관심등록한 정보가 있으면 표시하고 없으면 null로 처리하는 방식이 있다.

결과

0개의 댓글