Uncaught ReferenceError: kakao is not defined 오류

https://kevinjuniors.github.io/my-project/webapp/index.html

카카오 지도 API를 이용하여 직접 약국 지도 웹 앱을 구현 해본 것입니다.

그 중 카카오 맵 API를 불러오는 JavaScript 파일의 코드들인데 그 중
진한 부분의 코드가 Uncaught ReferenceError: kakao is not defined 오류를 내 뱉는데 어디서 엃힌 것 일까요… 이 오류 때문에 기존에 잘 보이던 지도도 브라우저 상에서도 보이지 않고 있습니다 ㅠㅠ;;;;;;

// // 마커를 클릭했을 때 해당 장소의 상세정보를 보여 줄 커스텀 오버레이
/// var placeOverlay = new kakao.maps.CustomOverlay({zindex: 1}), ///
contentNode = document.createElement(‘div’),
markers = [],
currCategory = ‘’;

// 지도를 웹 브라우저 상에 표시할 DOM Element를 지정
const mapContainer = document.getElementById(“map”);

mapOption = {
center: new kakao.maps.LatLng(37.20993410884578, 127.05867025427891), // 지도의 중심좌표
level: 3 // 지도의 확대 레벨
};

// 지도를 생성하고 객체(Object)를 DOM에 반환하여 출력
const map = new kakao.maps.Map(mapContainer, mapOption);

// 장소 검색 객체를 생성
var ps = new kakao.maps.services.Places(map);

// 지도에 idle Event를 등록
kakao.maps.event.addListener(map, ‘idle’, searchPlaces);

// 커스텀 오버레이의 Contents Node에 css class를 추가
contentNode.className = ‘placeinfo_wrap’;

/* 커스텀 오버레이의 컨텐츠 노드에 mousedown, touchstart 이벤트가 발생했을때
지도 객체에 이벤트가 전달되지 않도록 이벤트 핸들러로 kakao.maps.event.preventMap 메소드를 등록 */
addEventHandle(contentNode, ‘mousedown’, kakao.maps.event.preventMap);
addEventHandle(contentNode, ‘touchstart’, kakao.maps.event.preventMap);

// 커스텀 오버레이 컨텐츠를 설정합니다
placeOverlay.setContent(contentNode);

addCategoryClickEvent();

// Element에 이벤트 핸들러를 추가시키는 함수
function addEventHandle(target, type, callback) {
if (target.addEventListener) {
target.addEventListener(type, callback);
} else {
target.attachEvent(‘on’ + type, callback);
}
}

// 카테고리 검색을 요청하는 함수입니다
function searchPlaces() {
if (!currCategory) {
return;
}

placeOverlay.setMap(null);

removeMarker();

ps.categorySearch(currCategory, placesSearchCB, {useMapBounds:true}); 

}

// 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
function placesSearchCB(data, status, pagination) {
if (status === kakao.maps.services.Status.OK) {

    // 정상적으로 검색이 완료됐으면 지도에 마커를 표출합니다
    displayPlaces(data);

} else if (status === kakao.maps.services.Status.ZERO_RESULT) {
    // 검색결과가 없는경우
    alert("해당 지역의 약국 정보 조회 결과가 존재하지 않습니다.");

} else if (status === kakao.maps.services.Status.ERROR) {
    // 에러로 인해 검색결과가 나오지 않은 경우
    alert("예기지 않은 오류 발생으로 인해 약국 정보 조회 결과를 정상적으로 출력 하는데 실패 하였습니다.");
}

}

// 지도에 마커를 표출하는 함수
function displayPlaces(places) {
var order = document.getElementById(currCategory).getAttribute(‘data-order’);

for ( var i=0; i<places.length; i++ ) {

        // 마커를 생성하고 지도에 표시
        var marker = addMarker(new kakao.maps.LatLng(places[i].y, places[i].x), order);

        /* 마커와 검색결과 항목을 클릭 했을 때
        장소정보를 표출하도록 클릭 이벤트를 추가 */
        (function(marker, place) {
            kakao.maps.event.addListener(marker, 'click', function() {
                displayPlaceInfo(place);
            });
        })(marker, places[i]);
}

}

// 마커를 생성하고 지도 위에 마커를 표시하는 함수
function addMarker(position, order) {
var imageSrc = ‘https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/places_category.png’, // 마커 이미지 url, 스프라이트 이미지를 씁니다
imageSize = new kakao.maps.Size(27, 27), // 마커 이미지의 크기
imgOptions = {
spriteSize : new kakao.maps.Size(72, 206), // 스프라이트 이미지의 크기
spriteOrigin : new kakao.maps.Point(46, (order*36)), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
offset: new kakao.maps.Point(11, 28) // 마커 좌표에 일치시킬 이미지 내에서의 좌표
},
markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imgOptions),
marker = new kakao.maps.Marker({
position: position, // 마커의 위치
image: markerImage
});

marker.setMap(map);
markers.push(marker);

return marker;

}

// 지도 위에 표시되고 있는 마커를 모두 제거합니다
function removeMarker() {
for ( var i = 0; i < markers.length; i++ ) {
markers[i].setMap(null);
}
markers = [];
}

// 클릭한 마커에 대한 장소 상세정보를 커스텀 오버레이로 표시하는 함수
function displayPlaceInfo (place) {
var content = ‘

’ +
’ + place.place_name + ‘’;

if (place.road_address_name) {
    content += '    <span title="' + place.road_address_name + '">' + place.road_address_name + '</span>' +
                '  <span class="jibun" title="' + place.address_name + '">(지번 : ' + place.address_name + ')</span>';
}  else {
    content += '    <span title="' + place.address_name + '">' + place.address_name + '</span>';
}                

content += '    <span class="tel">' + place.phone + '</span>' + 
            '</div>' + 
            '<div class="after"></div>';

contentNode.innerHTML = content;
placeOverlay.setPosition(new kakao.maps.LatLng(place.y, place.x));
placeOverlay.setMap(map);  

}

// 각 카테고리에 클릭 이벤트를 등록
function addCategoryClickEvent() {
var category = document.getElementById(‘category’),
children = category.children;

for (var i=0; i<children.length; i++) {
    children[i].onclick = onClickCategory;
}

}

// 카테고리를 클릭했을 때 호출되는 함수입
function onClickCategory() {
var id = this.id,
className = this.className;

placeOverlay.setMap(null);

if (className === 'on') {
    currCategory = '';
    changeCategoryClass();
    removeMarker();
} else {
    currCategory = id;
    changeCategoryClass(this);
    searchPlaces();
}

}

// 클릭된 카테고리만 클릭된 스타일을 적용하는 함수
function changeCategoryClass(el) {
var category = document.getElementById(‘category’),
children = category.children,
i;

for ( i=0; i<children.length; i++ ) {
    children[i].className = '';
}

if (el) {
    el.className = 'on';
} 

}

/* 지도상 컨트롤 메뉴 삽입
일반 지도와 스카이 뷰 지도 타입으로 변경 할 수 있는 지도 타입 컨트롤 추가 */
const mapTypeControl = new kakao.maps.MapTypeControl();

// 컨트롤을 추가하고자 하는 지도를 지정
map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);

// 지도 Zoom-In, Zoom-Out 기능을 지원하는 Zoom 컨트롤 추가
const zoomControl = new kakao.maps.ZoomControl();
map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);

// 좌표 정보를 주소로 변환
// 좌표 정보를 주소로 변환하는 객체를 생성
var geocoder = new kakao.maps.services.Geocoder();
var marker = new kakao.maps.Marker();
infowindow = new kakao.maps.InfoWindow({zindex: 2});

// 현재 지도 중심좌표로 주소를 검색해서 지도 좌측 상단에 표시
searchAddrFromCoords(map.getCenter(), displayCenterInfo);

/* 지도 클릭시 클릭한 지점의 위치 자표를 주소 정보롤 변환시켜
브라우저 상에 보여지도록 하는 이벤트를 추가 */
kakao.maps.event.addListener(map, ‘click’, function(mouseEvent) {
searchDetailAddrFromCoords(mouseEvent.latLng, function(result, status) {
if (status === kakao.maps.services.Status.OK) {
var detailAddr = !!result[0].road_address ? '

도로명주소 : ’ + result[0].road_address.address_name + ‘
’ : ‘’;
detailAddr += '
지번 주소 : ’ + result[0].address.address_name + ‘
’;

        var content = '<div class="bAddr">' +
                        '<span class="title">법정동 주소정보</span>' + 
                        detailAddr + 
                    '</div>';

        // 마커를 클릭한 위치에 표시합니다 
        marker.setPosition(mouseEvent.latLng);
        marker.setMap(map);

        // 인포윈도우에 클릭한 위치에 대한 법정동 상세 주소정보를 표시합니다
        infowindow.setContent(content);
        infowindow.open(map, marker);
    }
});

});

// 중심 좌표나 확대 수준이 변경됐을 때 지도 중심 좌표에 대한 주소 정보를 표시하도록 이벤트를 등록합니다
kakao.maps.event.addListener(map, ‘idle’, function() {
searchAddrFromCoords(map.getCenter(), displayCenterInfo);
});

function searchAddrFromCoords(coords, callback) {
// 좌표로 행정동 주소 정보를 요청합니다
geocoder.coord2RegionCode(coords.getLng(), coords.getLat(), callback);
}

function searchDetailAddrFromCoords(coords, callback) {
// 좌표로 법정동 상세 주소 정보를 요청합니다
geocoder.coord2Address(coords.getLng(), coords.getLat(), callback);
}

// 지도 좌측상단에 지도 중심좌표에 대한 주소정보를 표출하는 함수입니다
function displayCenterInfo(result, status) {
if (status === kakao.maps.services.Status.OK) {
var infoDiv = document.getElementById(‘centerAddr’);

    for(var i = 0; i < result.length; i++) {
        // 행정동의 region_type 값은 'H' 이므로
        if (result[i].region_type === 'H') {
            infoDiv.innerHTML = result[i].address_name;
            break;
        }
    }
}    

}

401 에러는 앱 인증 에러입니다.
프로토콜을 포함한 접속 도메인 주소가 사이트 도메인에 등록되어 있는지,
스크립트 앱 키가 올바른지 확인해주세요.