[til_068] semiproject중

김동현·2023년 11월 3일
0

til

목록 보기
51/53

세미프로젝트 발표까지 약 2주일이 남았다.

그동안 수업진도와 프로젝트 진행이 겹쳐 til작성에 좀 소흘했다.

회의를 하다가 직방같은 사이트를 만들어 보기로 했다.

담당하고 싶었던 지도 들어간 부분을 맡아서 진행했다.

안그래도 한명 적은 팀으로 시작하고 한명이 탈주하는바람에 초기에 나눴던 분량보다 더 맡아서 진행하게 되었는데 오히려 재미있게 진행할수있는것 같아 좋았다.

아직 해야될 부분이 많지만 지금까지 진행했던 부분에서 어려웠던 점 몇가지 서술해 보겠다.

먼저 카카오맵 api접근.

카카오 디벨로퍼에서 카카오맵 키를 받아서 사용하는 부분부터 난항을 겪었다. 아무리해도 지도가 제대로 표시가 안되었던것.


(이부분에서도 끝에 /가 들어가있으면 등록이 되지 않는다.)


(포트번호를 잘 확인해야한다)

vscode로 html 작업했을때는 포트 고정이 안되어있어서 됬던것이 안되곤 했었는데 포트를 고정함으로서 해결이 되었다.

대부분 카카오 맵 api샘플에서 기능확인이 가능했는데 몇가지 문제점이 있었다.

  1. 주소검색으로 위치표시까진 했는데 새로운 주소를 입력하면 이전 마커가 남아있던것.

  2. 직방처럼 범위 안쪽에 있는물건들만 왼쪽이나 오른쪽에 표시해주고싶었던것.

1번은 이전마커와 인포윈도우를 없에주는 함수를 중간에 추가해 해결하였다.

var previousMarker = null;
var previousInfowindow = null;

document.getElementById("adress-btn").addEventListener("click", function(){
const textbox = document.getElementById("productAddress")

// 주소로 좌표를 검색합니다
geocoder.addressSearch(textbox.value, function(result, status) {

    // 정상적으로 검색이 완료됐으면 
     if (status === kakao.maps.services.Status.OK) {
		removePreviousMarker();
        var coords = new kakao.maps.LatLng(result[0].y, result[0].x);

        // 결과값으로 받은 위치를 마커로 표시합니다
        var marker = new kakao.maps.Marker({
            map: map,
            position: coords
        });

        // 인포윈도우로 장소에 대한 설명을 표시합니다
        var infowindow = new kakao.maps.InfoWindow({
            content: '<div style="width:150px;text-align:center;padding:6px 0;">검색주소</div>'
        });
        infowindow.open(map, marker);

        // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
        map.setCenter(coords);
        previousMarker = marker;
        previousInfowindow = infowindow;
    } 
});    

});

// 이전 마커와 인포윈도를 삭제하는 함수
function removePreviousMarker() {
if (previousMarker !== null) {
previousInfowindow.close();

    previousMarker.setMap(null);
    
   
}

}

2번은 fetch api를 통해 카카오맵에서 제공하는

지도영역을 반영하는 getBounds() 와 bounds_changed를 이용해서 해결하였다.
getBounds() 는 현재 지도의 모서리 좌표값들을 얻어올수있는 펑션이었고
bounds_changed는 지도가 이동할때마다 이벤트를 줄수있는 기능이었다. 이를이용하여
패치 api를 통해 지도가 이동할때마다 getBouds값을 얻어와서
현재 현재 가지고온 매물목록의 주소를 가지고와서
geocoder를통해 좌표값으로 변환해준뒤 getbounds 범위안에 들어온 값들만 간단보기로 만들어주어 해결하였다.
이후 생긴 문제점으로는 맵이동과동시에 계속해서 범위안의 값을 빠르게 가지고오다보니 같은주소값들이 계속 불러져오는 문제점이 생겼다.

이를 해결하기위해 타이머를주고 맵이동후 일정시간 후에 좌표값들을 불러오는 방식으로 해결하였다.

var debounceTimer;

var searchParams;

document.getElementById("option").addEventListener("submit", function(event) {
event.preventDefault(); // 폼의 기본 제출 행동을 막습니다.

const formData = new FormData(event.target); // 폼의 데이터를 가져옵니다.
searchParams = new URLSearchParams(formData).toString(); // 폼의 데이터를 URL 파라미터 형식으로 변환합니다.

// AJAX 요청을 사용하여 데이터를 가져옵니다.
fetch("/link/getProducts?" + searchParams)
    .then(response => response.json())
    .then(products => {
        displayMarkersWithinBounds(products);
    })
    .catch(error => {
        console.error("Error in fetch:", error);
    });

});

function displayMarkersWithinBounds(products) {
if (debounceTimer) {
clearTimeout(debounceTimer);
}

debounceTimer = setTimeout(() => {
    var bounds = map.getBounds();
    const moveProducts = [];
    
    clusterer.clear();
	
	
    // 각 주소 검색에 대한 Promise를 저장할 배열
    let promises = products.map(product => {
        return new Promise((resolve, reject) => {
            let address = product.productAddress;
            geocoder.addressSearch(address, (result, status) => {
                if (status === kakao.maps.services.Status.OK) {
                    var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
                    if (bounds.contain(coords)) {
                        var marker = new kakao.maps.Marker({
                            position: coords
                        });
                        clusterer.addMarker(marker);
                        moveProducts.push(product);
                    }
                } else {
                    console.error("Failed to get coords for address:", address);
                }
                resolve();  // 주소 검색 완료
            });
        });
    });

    // 모든 주소 검색이 완료되면 DOM 업데이트
    Promise.all(promises).then(() => {
        var mainLeftElem = document.getElementById("main-left");
        mainLeftElem.innerHTML = '';

        if (moveProducts.length === 0) {
            var emptyMsgElem = document.createElement("p");
            emptyMsgElem.textContent = "앗! 이 주변에는 방이 없어요!";
            mainLeftElem.appendChild(emptyMsgElem);
        } else {
            moveProducts.forEach(product => {
            	
            	var linkElem = document.createElement("a");
            	linkElem.href = "/productDetail/" + product.productNo;
            	
                var productContainer = document.createElement("div");
                productContainer.className = "preview-container";
                
                var productImgElem = document.createElement("div");
				productImgElem.className = "preview-img";
				// 이미지 요소를 생성합니다.
				var imgElem = document.createElement("img");
				imgElem.src = product.thumbnail; // 이미지 주소 설정
				productImgElem.appendChild(imgElem); // div 요소에 이미지 요소를 추가
				
				productContainer.appendChild(productImgElem);

                var productTextContainer = document.createElement("div");
                productTextContainer.className = "preview-text-container";

                var rentTypeElem = document.createElement("p");
                rentTypeElem.textContent = product.roomType + " " + product.productRentType;
                productTextContainer.appendChild(rentTypeElem);

                var depositElem = document.createElement("p");
                depositElem.textContent = "보증금 : " + product.productDeposit + " 만원";
                productTextContainer.appendChild(depositElem);

                if (product.productMonthlyRent != 0) {
                    var monthlyRentElem = document.createElement("p");
                    monthlyRentElem.textContent = "월세 : " + product.productMonthlyRent + " 만원";
                    productTextContainer.appendChild(monthlyRentElem);
                }

                if (product.productMaintenace) {
                    var maintenanceElem = document.createElement("p");
                    maintenanceElem.textContent = "관리비 : " + product.productMaintenace + " 만원";
                    productTextContainer.appendChild(maintenanceElem);
                }

				
                productContainer.appendChild(productTextContainer);
                linkElem.appendChild(productContainer);
                mainLeftElem.appendChild(linkElem);
            });
        }
    });

}, 300);

}

// 지도의 범위 변경 이벤트에 대한 리스너 추가
kakao.maps.event.addListener(map, 'bounds_changed', function() {
fetch("/link/getProducts?" + searchParams)
.then(response => response.json())
.then(products => {
displayMarkersWithinBounds(products);
})
.catch(error => {
console.error("Error in fetchMapCluster:", error);
});
});

// 초기 로딩 시 마커 표시

fetch("/link/getProducts")
.then(response => response.json())
.then(products => {
displayMarkersWithinBounds(products);
})
.catch(error => {
console.error("Error in fetchMapCluster:", error);
});

var previousMarker = null;
var previousInfowindow = null;

document.getElementById("adress-btn").addEventListener("click", function(){
const textbox = document.getElementById("input-adress")

geocoder.addressSearch(textbox.value, function(result, status) {
    
   

     
     if (status === kakao.maps.services.Status.OK) {
        console.log("hi");
        removePreviousMarker(); 
        var coords = new kakao.maps.LatLng(result[0].y, result[0].x);

        
        var marker = new kakao.maps.Marker({
            map: map,
            position: coords
        });

         var infowindow = new kakao.maps.InfoWindow({
             content: '<div id="div1" style="width:150px;text-align:center;padding:6px 0;">현재주소</div>'
         });
         infowindow.open(map, marker);

        map.setCenter(coords);
        previousMarker = marker;
        previousInfowindow = infowindow;
    } else {
        console.log("nohi");
    }
    
});

console.log((textbox.value));

});

사진올리기에서도 문제점들을 직면했다.

방사진에서 중요한 정보량은 최소 5장이고 원,투룸만 대상이기 때문에 5장으로도 충분할듯하여 무조건 5장을 올리도록 하고 싶었다.

js 에서
var selectedFiles = [];

document.getElementById('fileInput').addEventListener('change', function(event) {
var newFiles = Array.from(event.target.files); // 새로 선택된 파일들
// 선택된 파일과 현재 선택된 파일을 합칩니다.
selectedFiles = selectedFiles.concat(newFiles);

if (selectedFiles.length  > 5) {
    alert("사진은 5장까지만 올려주세요");
    selectedFiles = selectedFiles.slice(0, selectedFiles.length - newFiles.length); // 마지막 선택 무시
    event.target.value = ""; // input 필드 초기화
    return;
}

이렇게 작성했을때는

5장이 올라갔음에도 선택된 파일이 없다고 떴는데 인풋필드가 초기화되어서 그랬다.

인풋필드 초기화를 없엔 이후에는

3개 선택후 2개만 선택하면 두개만 담기기도했고

var selectedFiles = [];

document.getElementById('fileInput').addEventListener('change', function(event) {
var files = event.target.files;
var maxFiles = 5;

// 새로 선택된 파일들을 selectedFiles 배열로 임시 저장
var tempSelectedFiles = [];
for (var i = 0; i < files.length; i++) {
    tempSelectedFiles.push(files[i]);
}

// 선택된 파일의 총 개수가 5개일 경우
if (tempSelectedFiles.length === maxFiles) {
    selectedFiles = tempSelectedFiles;
    updatePreviews();
} else {
    if (tempSelectedFiles.length > maxFiles) {
        alert("사진은 5개를 초과할 수 없습니다.");
    } else {
        alert("사진은 총 5개 선택해야 합니다.");
        resetPreviews();  // 프리뷰 초기화
        resetFileInput();  // 파일 선택 초기화
    }
}

});

이렇게 해결하였다.

재미있는 작업이었다.

0개의 댓글

관련 채용 정보