지도에 relayout()과 setCenter()를 실행해도 맵이 센터에 오지 않는 문제 입니다

안녕하세요 웹크롤링을 활용해서 사이트 만드는 연습을 하다가

지도에 마커를 찍는 작업을 하는 중 제 짧은 지식으로는 해결이 안되는 문제가 있어서 올립니다.

<!--카카오 지도 API-->
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=appkey&libraries=services"></script>
 /* 지도 영역 */

    // 카카오 지도 생성
    var mapContainer = document.getElementById('map'), // 지도를 표시할 div
        mapOption = {
            center: new kakao.maps.LatLng(33.4509, 126.6493), // 지도의 중심좌표
            level: 3 // 지도의 확대 레벨
        };

    // 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
    var map = new kakao.maps.Map(mapContainer, mapOption);

    // 주소를 담을 객체 생성
    var viewAddr = null;

    // #collapseTwo 의 주소 ul의 정보를 가져오는 jQuery
    $("#collapseTwo ul:eq(2)>li").each(function(index,element){
        // 객체에 주소 담기
        viewAddr=$(this).text();
        console.log($(this).text());
    });

    // 주소-좌표 변환 객체를 생성합니다
    var geocoder = new kakao.maps.services.Geocoder();
    // 변환된 좌표를 담을 변수 생성
    var changedCoor= (33.4509, 126.6493);
    // 주소로 좌표를 검색합니다
    geocoder.addressSearch( viewAddr, function (result, status) {

        // 정상적으로 검색이 완료됐으면
        if (status === kakao.maps.services.Status.OK) {

            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;font-size: 5px">[[${result.name}]]</div>'
            });
            infowindow.open(map, marker);

            map.setCenter(coords);

            changedCoor=coords;
            console.log('changedCoor : '+changedCoor)
            // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다


        }
    });

    // 아코디언이 실행 된 후 실행
    $('#mapOn').click(function () {
        console.log('relayout실행1')
       map.relayout();
    })

    function relayout() {
        console.log('relayout버튼 실행');
        map.relayout();
        map.setCenter(new kakao.maps.LatLng(changedCoor));
    }

위와 같은 코드로 진행 하고 있는데 최초 지도 화면이


위와같이 나옵니다
마커는 좌측 상단에 찍혀 있어 지도를 옮기면 나옵니다.

map.setCenter(coords);
이게 안먹는 것같아 버튼을 눌러보면


이런식으로 나옵니다.

해결방안이 있을까요?

밑에는 콘솔에 찍힌 내용입니다.
콘솔

첨부 사진으로 보면 좌표를 잘못설정하고 있는 걸로 보입니다.
지도 중심좌표를 설정할 때 changeCoor가 괄호를 포함한 문자열이라면 오류가 나기 때문에
아래와 같이 number 타입으로 위경도를 설정해서 중심좌표를 재설정해주세요.

map.setCenter(new kakao.maps.LatLng(37.454754, 126.635321));

답변 감사합니다. 우선 말씀해주신대로 고쳐서 아코디언 클릭시 반응해 중앙으로 오는 것까지 확인되었습니다.
고친 코드는 아래입니다.

/* 지도 영역 */

    // 카카오 지도 생성
    var mapContainer = document.getElementById('map'), // 지도를 표시할 div
        mapOption = {
            center: new kakao.maps.LatLng(33.4509, 126.6493), // 지도의 중심좌표
            level: 3 // 지도의 확대 레벨
        };

    // 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
    var map = new kakao.maps.Map(mapContainer, mapOption);

    // 주소를 담을 객체 생성
    var viewAddr = null;

    // #collapseTwo 의 주소 ul의 정보를 가져오는 jQuery
    $("#collapseTwo ul:eq(2)>li").each(function(index,element){
        // 객체에 주소 담기
        viewAddr=$(this).text();
        console.log($(this).text());
    });

    // 주소-좌표 변환 객체를 생성합니다
    var geocoder = new kakao.maps.services.Geocoder();
    // 변환된 좌표를 담을 변수 생성
    var changedCoor_y= 33.4509;
    var changedCoor_x= 126.6493;
    // 주소로 좌표를 검색합니다
    geocoder.addressSearch( viewAddr, function (result, status) {

        // 정상적으로 검색이 완료됐으면
        if (status === kakao.maps.services.Status.OK) {

            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;font-size: 5px">[[${result.name}]]</div>'
            });
            infowindow.open(map, marker);

            console.log('setCenter() 실행전 relayout() 실행')
            map.relayout();

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

            changedCoor_y=result[0].y;
            changedCoor_x=result[0].x;
            console.log('changedCoor_y : '+changedCoor_y);
            console.log('changedCoor_x : '+changedCoor_x);

        }
    });

    // 아코디언이 실행 된 후 실행
    $('#mapOn').click(function () {
        console.log('relayout실행1')
        relayout();
    })

    function relayout() {
        console.log('relayout버튼 실행');
        // 지도를 표시하는 div 크기를 변경한 이후 지도가 정상적으로 표출되지 않을 수도 있습니다
        // 크기를 변경한 이후에는 반드시  map.relayout 함수를 호출해야 합니다
        // window의 resize 이벤트에 의한 크기변경은 map.relayout 함수가 자동으로 호출됩니다
        map.relayout();
        map.setCenter(new kakao.maps.LatLng(changedCoor_y,changedCoor_x));
    }

위와 같은 코드로 실행 했을때 작동은 잘되었습니다.
여기서 궁금한 점은
map.setCenter(coords); 이 코드의 실행 유무 입니다. 이게 실행이 되었다면 ‘#mapOn’ 클릭이벤트와 그 아래부분을 호출않았어도 되어야 하는 것 아닌지 궁금합니다.

 // 아코디언이 실행 된 후 실행
    $('#mapOn').click(function () {
        console.log('relayout실행1')
        relayout();
    })

    function relayout() {
        console.log('relayout버튼 실행');
        // 지도를 표시하는 div 크기를 변경한 이후 지도가 정상적으로 표출되지 않을 수도 있습니다
        // 크기를 변경한 이후에는 반드시  map.relayout 함수를 호출해야 합니다
        // window의 resize 이벤트에 의한 크기변경은 map.relayout 함수가 자동으로 호출됩니다
        map.relayout();
        map.setCenter(new kakao.maps.LatLng(changedCoor_y,changedCoor_x));
    }

이부분을 주석처리하고 실행하게 되면


위형식으로 나오게 되어 질문드립니다.

로직이 실행되면서 changedCoor 변수는 LatLng 클래스의 객체를 할당 받게 됩니다.

그래서

map.setCenter(new kakao.maps.LatLng(changedCoor));

위과 같이 실행하면 setCenter에 들어가는 좌표는 NaN값이 들어가게 됩니다.
LatLng 클래스 명세 참고 부탁드려요(Kakao 지도 Web API Documentation)

map.setCenter(new kakao.maps.LatLng(changedCoor.getLat(), changedCoor.getLng()));
map.setCenter(changedCoor);

수정을 이 두개 중에 하나로 하시면 동작할거에요~

참고로 이런 런타임 오류의 경우엔, 크롬 개발자도구에서 브레이크 포인트 걸고 테스트를 해보면 바로 알 수 있는 부분인점 참고 바랍니다.

검색 전후에; 지도를 그리는 DOM 엘리먼트의 크기가 변화가 되는 구조일까요?

먼저 친절한 답변 감사합니다! 크기는 고정되어 있고 아코디언에 의해 나타나는 구조 입니다!

addressSearch API로 응답을 받은 이후에,
지도는 마커를 만들고,
relayout을 실행하고,
응답데이터의 좌표를 통해 setCenter()가 정상적으로 잘 수행이 됩니다. 즉 지금의 코드 흐름으로는 딱히 문제가 없습니다.

(주소 데이터를 가져오는 부분 말고, 올려주신 코드 복사해서 실행해 보니 지도 중심에 마커 잘 찍힙니다)

다만, 두번째 질문의 스샷을 보면, 지도가 그려지는 DOM의 사이즈가 변하는 것 같은데;
이게 변하는 시점이 relayout 이후라면, 지도가 스샷 처럼 나올 수 있습니다.

그럼 해결방법은,

  1. 지도가 그려질 DOM 엘리먼트의 크기를 먼저 키운후에, 지도를 렌더링 하거나
  2. DOM엘리먼트의 사이즈를 변화시키는 로직에서 콜백함수나 이벤트를 제공한다면, DOM의 크기가 변한 후에 map.relayout과 setCenter를 다시 해주면 될것 같습니다.
  3. 이것도 안된다면, setTimout과 같은 함수를 통해서 일정 시간 후에 relayout을 시켜주는 것도 방법입니다.

이해하기 쉽게 말씀해 주셔서 감사합니다.
html 부분은

<div class="accordion-item">
                                    <h2 class="accordion-header" id="headingThree">
                                        <button class="accordion-button collapsed" type="button"
                                                data-bs-toggle="collapse" data-bs-target="#collapseThree"
                                                aria-expanded="false" aria-controls="collapseThree" id="mapOn">
                                            숙소위치
                                        </button>
                                    </h2>
                                    <div id="collapseThree" class="accordion-collapse collapse"
                                         aria-labelledby="headingThree" data-bs-parent="#accordionExample">
                                        <div class="accordion-body">
                                            <!-- 지도를 표시할 div 입니다 -->
                                            <div id="map" style="width:100%;height:600px;"></div>
                                            <button onclick="relayout()">ralayout함수 발동!</button>
                                        </div>
                                    </div>
                                </div>

이렇게 적용되어 있습니다. 제가 이해한 부분이 맞다면 아코디언 형식이라 버튼 반응에따라 나타나고 사라지는 것 때문에 relayout()을 재호출해야 한다는 말씀이신가요?

넵 사라지고 생기는 방식이 어떤방식인지 모르겠으나,
지도가 그려질 DOM의 사이즈가 변한다면,
그냥 명확히,

  • 사이즈의 변환이 종료된 후에 → relayout()을 호출한다

로 생각하시면 됩니다.
사이즈 변환전에 실행해봤자 의미없고, 변환후에 실행해야 합니다.

1개의 좋아요

정말감사합니다. 남은 하루 좋은 하루 보내세요!!
공부 더 열심히해서 다른분들게 답변 남길수 있도록 하겠습니다!

1개의 좋아요