Skip to content

Instantly share code, notes, and snippets.

@no-pla
Last active July 21, 2023 07:39
Show Gist options
  • Save no-pla/38644a460caf6951aa73bdd3d3446a2c to your computer and use it in GitHub Desktop.
Save no-pla/38644a460caf6951aa73bdd3d3446a2c to your computer and use it in GitHub Desktop.
다른 개발자가 작성한 카카오 지도 API 가져오는 리팩토링 이전 코드입니다.. 프로젝트는 Next.js 프레임워크지만 자바스크립트 코드로 작성되어 있습니다.
useEffect(() => {
const script = document.createElement("script");
script.src = `https://dapi.kakao.com/v2/maps/sdk.js?appkey=${KAKAO_API_KEY}&libraries=services&autoload=false`;
document.head.appendChild(script);
if (router.query.target !== undefined) {
// 메인 화면에서 타고 들어왔을 떄
setSearch(target);
document.getElementById("form")?.focus();
setIsOpen1(true);
}
script.onload = () => {
window.kakao.maps.load(function () {
let markers: any[] = [];
const container = document.getElementById("map");
const options = {
center: new window.kakao.maps.LatLng(37.566826, 126.9786567),
level: 8,
};
const map = new window.kakao.maps.Map(container, options);
const markerPosition = new window.kakao.maps.LatLng(
37.566826,
126.9786567,
);
const marker = new window.kakao.maps.Marker({
position: markerPosition,
});
marker.setMap(map);
//--------------------------------------------------
// 일반 지도와 스카이뷰로 지도 타입을 전환할 수 있는 지도타입 컨트롤을 생성합니다
const mapTypeControl = new window.kakao.maps.MapTypeControl();
// 지도에 컨트롤을 추가해야 지도위에 표시됩니다
// kakao.maps.ControlPosition은 컨트롤이 표시될 위치를 정의하는데 TOPRIGHT는 오른쪽 위를 의미합니다
map.addControl(
mapTypeControl,
window.kakao.maps.ControlPosition.TOPRIGHT,
);
// 지도 확대 축소를 제어할 수 있는 줌 컨트롤을 생성합니다
const zoomControl = new window.kakao.maps.ZoomControl();
map.addControl(zoomControl, window.kakao.maps.ControlPosition.RIGHT);
//-----------------------------------------------------
// HTML5의 geolocation으로 사용할 수 있는지 확인합니다
if (navigator.geolocation) {
// GeoLocation을 이용해서 접속 위치를 얻어옵니다
navigator.geolocation.getCurrentPosition(function (position) {
const lat = position.coords.latitude, // 위도
lon = position.coords.longitude; // 경도
const locPosition = new window.kakao.maps.LatLng(lat, lon), // 마커가 표시될 위치를 geolocation으로 얻어온 좌표로 생성합니다
message = '<div style="padding:5px;">여기에 계신가요?!</div>'; // 인포윈도우에 표시될 내용입니다
// 마커와 인포윈도우를 표시합니다
displayMarkers(locPosition, message);
});
} else {
// HTML5의 GeoLocation을 사용할 수 없을때 마커 표시 위치와 인포윈도우 내용을 설정합니다
const locPosition = new window.kakao.maps.LatLng(
33.450701,
126.570667,
),
message = "geolocation을 사용할수 없어요..";
displayMarkers(locPosition, message);
}
// 지도에 마커와 인포윈도우를 표시하는 함수입니다
function displayMarkers(locPosition: any, message: any) {
// 마커를 생성합니다
const marker = new window.kakao.maps.Marker({
map: map,
position: locPosition,
});
const iwContent = message, // 인포윈도우에 표시할 내용
iwRemoveable = true;
// 인포윈도우를 생성합니다
const infowindow = new window.kakao.maps.InfoWindow({
content: iwContent,
removable: iwRemoveable,
});
// 인포윈도우를 마커위에 표시합니다
infowindow.open(map, marker);
// 지도 중심좌표를 접속위치로 변경합니다
map.setCenter(locPosition);
}
//-----------------------------------------------------
const panTo = () => {
// 이동할 위도 경도 위치를 생성합니다
const moveLatLon = navigator.geolocation;
// 지도 중심을 부드럽게 이동시킵니다
// 만약 이동할 거리가 지도 화면보다 크면 부드러운 효과 없이 이동합니다
map.panTo(moveLatLon);
};
//----------------------------------
const ps = new window.kakao.maps.services.Places();
const infowindow = new window.kakao.maps.InfoWindow({ zIndex: 1 });
const searchForm = document.getElementById("submit_btn");
searchForm?.addEventListener("click", function (e) {
e.preventDefault();
searchPlaces(search);
});
const searchForm1 = document.getElementById("form");
searchForm1?.addEventListener("enter", function (e) {
e.preventDefault();
});
function searchPlaces(target: any) {
const keyword = (
document.getElementById("keyword") as HTMLInputElement
).value;
if (!keyword.replace(/^\s+|\s+$/g, "")) {
alert("키워드를 입력해주세요!");
return false;
}
setNewSearch(keyword);
ps.keywordSearch(keyword + " 근처의 동물병원", placesSearchCB);
}
if (target) {
searchPlaces(target);
}
function placesSearchCB(data: any, status: any, pagination: any) {
if (status === window.kakao.maps.services.Status.OK) {
// 정상적으로 검색이 완료됐으면
// 검색 목록과 마커를 표출합니다
displayPlaces(data);
// 페이지 번호를 표출합니다
displayPagination(pagination);
// 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
// LatLngBounds 객체에 좌표를 추가합니다
const bounds = new window.kakao.maps.LatLngBounds();
for (let i = 0; i < data.length; i++) {
displayMarker(data[i]);
bounds.extend(new window.kakao.maps.LatLng(data[i].y, data[i].x));
}
// 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
map.setBounds(bounds);
} else if (status === window.kakao.maps.services.Status.ZERO_RESULT) {
alert("검색 결과가 존재하지 않습니다.");
return;
} else if (status === window.kakao.maps.services.Status.ERROR) {
alert("검색 결과 중 오류가 발생했습니다.");
return;
}
}
function displayMarker(place: any) {
// 마커를 생성하고 지도에 표시합니다
const marker = new window.kakao.maps.Marker({
map: map,
position: new window.kakao.maps.LatLng(place.y, place.x),
});
// 마커에 클릭이벤트를 등록합니다
window.kakao.maps.event.addListener(marker, "click", function () {
router.push(place.place_url);
// 마커를 클릭하면 장소명이 인포윈도우에 표출됩니다
infowindow.setContent(
'<div style="padding:5px;font-size:12px;">' +
place.place_name +
"</div>",
);
infowindow.open(map, marker);
});
}
function displayPlaces(places: any) {
const listEl = document.getElementById("placesList"),
menuEl = document.getElementById("menu_wrap"),
fragment = document.createDocumentFragment(),
bounds = new window.kakao.maps.LatLngBounds(),
listStr = "";
// 검색 결과 목록에 추가된 항목들을 제거합니다
removeAllChildNods(listEl);
// 지도에 표시되고 있는 마커를 제거합니다
removeMarker();
for (let i = 0; i < places.length; i++) {
// 마커를 생성하고 지도에 표시합니다
const placePosition = new window.kakao.maps.LatLng(
places[i].y,
places[i].x,
),
marker = addMarker(placePosition, i),
itemEl = getListItem(i, places[i]); // 검색 결과 항목 Element를 생성합니다
// 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
// LatLngBounds 객체에 좌표를 추가합니다
bounds.extend(placePosition);
// 마커와 검색결과 항목에 click 했을때
// 해당 장소에 인포윈도우에 장소명을 표시합니다
// mouseout 했을 때는 인포윈도우를 닫습니다
(function (marker, title) {
window.kakao.maps.event.addListener(marker, "click", function () {
displayInfowindow(marker, title, places[i]);
});
window.kakao.maps.event.addListener(
marker,
"mouseout",
function () {
infowindow.close();
},
);
itemEl.onclick = function () {
displayInfowindow(marker, title, places[i]);
// router.push('/searchMap/${places.id}')
};
itemEl.onmouseout = function () {
infowindow.close();
};
})(marker, places[i].place_name);
fragment.appendChild(itemEl);
}
// 검색결과 항목들을 검색결과 목록 Element에 추가합니다
listEl?.appendChild(fragment);
if (menuEl) menuEl.scrollTop = 0;
// 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
map.setBounds(bounds);
}
function getListItem(index: any, places: any) {
let el = document.createElement("li"),
itemStr =
'<span class="markerbg marker_' +
(index + 1) +
'"></span>' +
'<div class="info">' +
"<h5>" +
places.place_name +
"</h5>";
if (places.road_address_name) {
itemStr +=
"<span>" +
places.road_address_name +
"</span>" +
'<span class="jibun gray">' +
places.address_name +
"</span>";
} else {
itemStr += "<span>" + places.address_name + "</span>";
}
itemStr += '<span class="tel">' + places.phone + "</span>" + "</div>";
el.innerHTML = itemStr;
el.className = "item";
// placesData(places);
return el;
}
// localStorage.setItem("places", JSON.stringify(places));
// 마커를 생성하고 지도 위에 마커를 표시하는 함수입니다
function addMarker(position: any, idx: any) {
const imageSrc =
"https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png", // 마커 이미지 url, 스프라이트 이미지를 씁니다
imageSize = new window.kakao.maps.Size(36, 37), // 마커 이미지의 크기
imgOptions = {
spriteSize: new window.kakao.maps.Size(36, 691), // 스프라이트 이미지의 크기
spriteOrigin: new window.kakao.maps.Point(0, idx * 46 + 10), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
offset: new window.kakao.maps.Point(13, 37), // 마커 좌표에 일치시킬 이미지 내에서의 좌표
},
markerImage = new window.kakao.maps.MarkerImage(
imageSrc,
imageSize,
imgOptions,
),
marker = new window.kakao.maps.Marker({
position: position, // 마커의 위치
image: markerImage,
});
marker.setMap(map); // 지도 위에 마커를 표출합니다
markers.push(marker); // 배열에 생성된 마커를 추가합니다
return marker;
}
// 지도 위에 표시되고 있는 마커를 모두 제거합니다
function removeMarker() {
for (let i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
markers = [];
}
// 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
function displayPagination(pagination: any) {
let paginationEl = document.getElementById("pagination"),
fragment = document.createDocumentFragment(),
i;
// 기존에 추가된 페이지번호를 삭제합니다
while (paginationEl?.hasChildNodes()) {
if (paginationEl.lastChild)
paginationEl.removeChild(paginationEl.lastChild);
}
for (i = 1; i <= pagination.last; i++) {
const el = document.createElement("a");
el.href = "#";
String(i);
if (i === pagination.current) {
el.className = "on";
} else {
el.onclick = (function (i) {
return function () {
pagination.gotoPage(i);
};
})(i);
}
fragment.appendChild(el);
}
if (paginationEl) paginationEl.appendChild(fragment);
}
// 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다
// 인포윈도우에 장소명을 표시합니다
async function displayInfowindow(marker: any, title: any, places: any) {
placesData(places);
const content1 = `<div style="padding:10px;min-width:200px">${title}</div>`;
const content = `
<div class="item">
<div id="roadview"></div>
<div style="margin-left:10px">
<h2>${title}</h2>
<span class="tel">${places.phone}</span>
<div class="info">
<span class="gray">${places.road_address_name}</span>
<br>
<span> ${places.address_name}</span>
</div>
<br>
<p>
<a href="${places.place_url}" target="_blank"
class="detailButton" >상세정보 및 공유, 데이터 보기</a>
</p>
<p style="margin-top:40px">
<a class="reviewButton" href="/posts/createPost">리뷰 참여하기</a>
</p>
<div id="reviewList"></div>
</div>
</div>
`;
const menuWrap = document.getElementById("menu_wrap1");
if (menuWrap) menuWrap.innerHTML = content;
const { x, y } = places;
const roadview = document.getElementById("roadview"); // 로드뷰를 표시할 HTML 요소
if (roadview) {
const root1 = createRoot(roadview);
root1.render(
<Roadview
position={{
lat: y,
lng: x,
radius: 50,
}}
style={{ width: "100%", height: "200px" }}
/>,
);
}
const reviewList = document.getElementById("reviewList");
if (reviewList) {
const root2 = createRoot(reviewList);
root2.render(
<ReviewList>
{!isLoading &&
recentlyReview?.data
.map((review) => {
if (places.id == review.hospitalId) {
return (
<Review key={review.id}>
<ReviewImg src={review.downloadUrl} alt="" />
<ReviewInfo>
<UserContainer>
<ProfileImg src={review.profileImage} />
<ProfileName>{review.displayName}</ProfileName>
<StarRating>
<FaStar size="15" color="#ffc107" />
{review.rating}/5
</StarRating>
</UserContainer>
<ContentsWrap>
<ReviewTitle>{review.title}</ReviewTitle>
<ReviewContents>
{review.contents}
</ReviewContents>
</ContentsWrap>
<PetpitalPrice>
<PetpitalHighPrice>
{review.totalCost}원
</PetpitalHighPrice>
</PetpitalPrice>
<TagWrap>
<TagTop>
{review.selectedColors?.map((c) => {
if (c === "깨끗해요") {
return <TagFirst key={c}>{c}</TagFirst>;
} else if (c === "시설이좋아요") {
return <TagSecond key={c}>{c}</TagSecond>;
} else if (c === "친절해요") {
return <TagThird key={c}>{c}</TagThird>;
}
})}
</TagTop>
<TagBottom>
{review.selectedColors?.map((c) => {
if (c === "꼼꼼해요") {
return <TagFourth key={c}>{c}</TagFourth>;
} else if (c === "저렴해요") {
return <TagFifth key={c}>{c}</TagFifth>;
}
})}
<DateWrap>{review.date}</DateWrap>
</TagBottom>
</TagWrap>
</ReviewInfo>
</Review>
);
}
})
.reverse()}
</ReviewList>,
);
}
console.log(places.id);
setIsOpen1(!isOpen1);
infowindow.setContent(content1);
infowindow.open(map, marker);
}
// 검색결과 목록의 자식 Element를 제거하는 함수입니다
function removeAllChildNods(el: any) {
while (el?.hasChildNodes()) {
el.removeChild(el.lastChild);
}
}
});
};
}, [recentlyReview]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment