19주차 (2)
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>홈 | PinkTopia</title>
<link rel="stylesheet" href="./styles.css">
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ff69b4;
padding: 15px;
color: white;
font-weight: bold;
position: relative;
height: 50px;
/* 네비게이션 바 높이 고정 */
}
/* 햄버거 메뉴 아이콘 스타일 */
.menu-icon {
position: absolute;
left: 15px;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
z-index: 2;
}
.menu-icon .hamburger {
width: 25px;
height: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.menu-icon .hamburger span {
display: block;
height: 3px;
width: 100%;
background-color: white;
border-radius: 3px;
transition: all 0.3s ease;
}
/* 로고 컨테이너 위치 수정 */
.nav .logo-container {
position: absolute;
left: 50px;
/* 햄버거 메뉴 옆으로 이동 */
height: 100%;
display: flex;
align-items: center;
}
.nav img {
width: 45px;
/* 이미지 크기 증가 */
height: 45px;
border-radius: 0;
/* 네모 모양으로 변경 */
object-fit: cover;
}
/* 로고 텍스트 */
.nav .logo-text {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 24px;
}
#authMenu {
margin-left: auto;
z-index: 1;
/* 로고 텍스트 위에 표시되도록 */
}
.nav a {
color: white;
text-decoration: none;
margin: 0 10px;
}
.menu {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-top: 20px;
}
.menu a {
display: block;
width: calc(33.333% - 20px);
padding: 20px;
text-align: center;
background: white;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-decoration: none;
color: black;
font-weight: bold;
}
.nav>div:first-child {
display: flex;
align-items: center;
gap: 8px;
}
/* 사이드바 스타일 */
.sidebar {
height: 100%;
width: 0;
position: fixed;
z-index: 1000;
top: 0;
left: 0;
background-color: #ff69b4;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
box-shadow: 3px 0 10px rgba(0, 0, 0, 0.2);
}
.sidebar a {
padding: 15px 25px;
text-decoration: none;
font-size: 18px;
color: white;
display: block;
transition: 0.3s;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar a:hover {
background-color: #ff4da6;
}
.close-sidebar {
position: absolute;
top: 15px;
right: 25px;
font-size: 36px;
cursor: pointer;
color: white;
}
.sidebar-header {
padding: 20px;
color: white;
font-size: 24px;
font-weight: bold;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
/* 지도 컨테이너 스타일 */
#map {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border: 2px solid #ff69b4;
}
/* 길찾기 폼 스타일 */
.route-finder input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
.route-finder label {
display: block;
margin-bottom: 5px;
color: #666;
}
.route-button:hover {
background: #ff4da6 !important;
}
/* 반응형 디자인을 위한 미디어 쿼리 */
@media (max-width: 768px) {
.map-section {
flex-direction: column;
}
#map {
height: 300px;
}
.route-finder {
width: 100%;
}
}
#overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
}
#popup {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background-color: #ff84c1;
border: 1px solid #ff84c1;
z-index: 1000;
text-align: center;
}
#userProfileBox {
display: flex;
align-items: center;
background-color: white;
/* 배경을 흰색으로 설정 */
padding: 10px 15px;
border-radius: 20px;
/* 둥근 네모 */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
color: #ff5faf;
/* 기본 글자색 */
font-weight: bold;
font-size: 16px;
}
#userProfileBox img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
border: 2px solid #ff7fbf;
/* 핑크 테두리 */
}
#userProfileBox .welcome-text {
color: #ff78bc;
/* 핑크색 */
font-weight: bold;
}
</style>
<!-- "https://cdn.socket.io/4.3.2/socket.io.min.js" -->
<script src="https://cdn.socket.io/4.3.2/socket.io.min.js"></script>
</head>
<body>
<!-- 카카오맵 API 스크립트 추가 -->
<script
src="https://dapi.kakao.com/v2/maps/sdk.js?appkey=5dc1bc6ffb574e60204390643bad8557&libraries=services,clusterer,drawing"></script>
<div id="overlay"></div>
<div id="popup">
<p id="popupMessage"></p>
<button id="confirmButton">확인</button>
<button id="cancelButton">취소</button>
</div>
<div class="nav">
<!-- 햄버거 메뉴 아이콘 추가 -->
<div class="menu-icon" onclick="openSidebar()">
<div class="hamburger">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div class="logo-container">
<img src="./images/123.jpg" alt="PinkTopia Logo" />
</div>
<div class="logo-text">
<a href="/public/home.html" style="text-decoration: none; color: white">PinkTopia</a>
</div>
<!-- 로그인 상태에 따라 변경될 영역 -->
<div id="authMenu" style="margin-left: auto; display: flex; align-items: center;"></div>
</div>
<div class="container">
<h1>홈</h1>
<!-- 지도와 길찾기 섹션을 감싸는 컨테이너 -->
<div class="map-section" style="display: flex; gap: 20px; margin: 20px 0">
<!-- 길찾기 폼 (왼쪽으로 이동) -->
<div class="route-finder" style="
flex: 1;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border: 2px solid #ff69b4;
">
<h3 style="color: #ff69b4; margin-bottom: 20px">길찾기</h3>
<div class="route-input">
<div class="mb-3">
<label for="start" class="form-label">출발지</label>
<input type="text" class="form-control" id="start" placeholder="출발지를 입력하세요" />
</div>
<div class="mb-3">
<label for="end" class="form-label">도착지</label>
<input type="text" class="form-control" id="end" placeholder="도착지를 입력하세요" />
</div>
<button class="route-button" onclick="findRoute()" style="
width: 100%;
padding: 10px;
background: #ff69b4;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
">
길찾기
</button>
</div>
<div id="route-result" style="margin-top: 20px">
<!-- 길찾기 결과가 여기에 표시됩니다 -->
</div>
</div>
<!-- 지도를 표시할 div (오른쪽으로 이동) -->
<div id="map" style="flex: 2; height: 400px; border-radius: 10px;">
<script>
// document.addEventListener("DOMContentLoaded", function () {
let mapContainer = document.getElementById('map'),
mapOption = {
center: new kakao.maps.LatLng(0, 0),
level: 5,
};
let map = new kakao.maps.Map(mapContainer, mapOption);
// 현재 위치 표시
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
let lat = position.coords.latitude,
lon = position.coords.longitude;
let locPosition = new kakao.maps.LatLng(lat, lon),
message = '<div style="padding:5px;">현재 위치</div>';
displayMarker(locPosition, message);
});
} else {
let locPosition = new kakao.maps.LatLng(0, 0),
message = 'geolocation을 사용할 수 없어요..';
displayMarker(locPosition, message);
}
function displayMarker(locPosition, message) {
let marker = new kakao.maps.Marker({
map: map,
position: locPosition,
});
let infowindow = new kakao.maps.InfoWindow({
content: message,
removable: true,
});
infowindow.open(map, marker);
map.setCenter(locPosition);
}
let geocoder = new kakao.maps.services.Geocoder();
// 로컬 스토리지에서 저장된 좌표 정보 불러오기
let savedStart = JSON.parse(localStorage.getItem('startLocation'));
let savedEnd = JSON.parse(localStorage.getItem('endLocation'));
// 확대/축소 컨트롤 추가
let zoomControl = new kakao.maps.ZoomControl();
map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
// 마커 정보 배열 (출발지, 도착지)
let markers = [
{
key: 'start',
info: '출발지',
position: savedStart
? new kakao.maps.LatLng(savedStart.lat, savedStart.lng)
: new kakao.maps.LatLng(36.3275, 127.4268),
imageUrl: 'https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/red_b.png',
storageKey: 'startLocation',
},
{
key: 'end',
info: '도착지',
position: savedEnd
? new kakao.maps.LatLng(savedEnd.lat, savedEnd.lng)
: new kakao.maps.LatLng(36.340309, 127.389999),
imageUrl: 'https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/blue_b.png',
storageKey: 'endLocation',
},
];
// 마커를 지도에 추가하고 드래그 이벤트 등록
markers.forEach(function (data) {
let markerImage = new kakao.maps.MarkerImage(
data.imageUrl,
new kakao.maps.Size(50, 50),
);
let marker = new kakao.maps.Marker({
position: data.position,
image: markerImage,
map: map,
draggable: true, // 드래그 가능하도록 설정
});
// 정보창 생성
let infowindow = new kakao.maps.InfoWindow({
content: `<div style="padding:5px;">${data.info}</div>`,
});
// 마커에 정보창 표시
infowindow.open(map, marker);
// 마커 드래그 종료 시 좌표 및 정보창 위치 업데이트
kakao.maps.event.addListener(marker, 'dragend', function () {
let position = marker.getPosition();
// 정보창도 마커의 새 위치로 이동
infowindow.setPosition(position);
// 새로운 좌표를 localStorage에 저장
localStorage.setItem(
data.storageKey,
JSON.stringify({ lat: position.getLat(), lng: position.getLng() }),
);
console.log(`${data.info} 위치 저장됨:`, position.getLat(), position.getLng());
});
// 각 마커 및 정보창을 객체에 저장
data.marker = marker;
data.infowindow = infowindow;
});
// 전역 변수로 출발지, 도착지 마커 지정
let startMarker = markers.find(m => m.key === 'start').marker;
let endMarker = markers.find(m => m.key === 'end').marker;
// 길찾기 버튼 클릭 시 링크 새 창 열기
function openDirection() {
let startPos = startMarker.getPosition();
let endPos = endMarker.getPosition();
let url = `https://map.kakao.com/link/to/도착지,${endPos.getLat()},${endPos.getLng()}/from/출발지,${startPos.getLat()},${startPos.getLng()}`;
window.open(url, '_blank');
}
// 입력창의 주소를 검색하여 마커 이동 및 좌표 저장
function setMarkerPosition(marker, addressInputId) {
let inputElement = document.getElementById(addressInputId);
geocoder.addressSearch(inputElement.value, function (result, status) {
if (status === kakao.maps.services.Status.OK) {
let coords = new kakao.maps.LatLng(result[0].y, result[0].x);
marker.setPosition(coords);
map.setCenter(coords);
localStorage.setItem(
addressInputId + 'Location',
JSON.stringify({ lat: result[0].y, lng: result[0].x }),
);
} else {
alert('검색 결과가 없습니다.');
}
});
}
///////////////////////////////////////////////////////////////////////
// 마커 이동 시 입력창에 좌표 업데이트
function updateInputValue(marker, addressInputId) {
let inputElement = document.getElementById(addressInputId);
let position = marker.getPosition();
inputElement.value = `${position.getLat()}, ${position.getLng()}`;
}
// 입력창 변경 시 해당 마커 이동
document.getElementById('start').addEventListener('change', function () {
setMarkerPosition(startMarker, 'start');
});
document.getElementById('end').addEventListener('change', function () {
setMarkerPosition(endMarker, 'end');
});
// 마커 드래그 후 입력창 값 업데이트
kakao.maps.event.addListener(startMarker, 'dragend', function () {
updateInputValue(startMarker, 'start');
});
kakao.maps.event.addListener(endMarker, 'dragend', function () {
updateInputValue(endMarker, 'end');
});
// 입력창의 주소를 기반으로 마커 위치를 이동시키는 함수
function setMarkerPosition(marker, addressInputId) {
let inputElement = document.getElementById(addressInputId);
geocoder.addressSearch(inputElement.value, function (result, status) {
if (status === kakao.maps.services.Status.OK) {
let coords = new kakao.maps.LatLng(result[0].y, result[0].x);
marker.setPosition(coords);
map.setCenter(coords);
// 변경된 좌표를 로컬 스토리지에 저장 (옵션)
localStorage.setItem(addressInputId + 'Location', JSON.stringify({ lat: result[0].y, lng: result[0].x }));
} else {
alert('검색 결과가 없습니다.');
}
});
}
// 출발지 입력창 값이 바뀌면 startMarker의 위치 업데이트
document.getElementById('start').addEventListener('change', function () {
setMarkerPosition(startMarker, 'start');
});
/////////////////////////////////////////////////////////
// function checkLoginStatus() {
// const accessToken = localStorage.getItem("accessToken");
// const authMenu = document.getElementById("authMenu");
document.addEventListener("DOMContentLoaded", function () {
geoBookmark()
});
function geoBookmark() {
fetch('http://localhost:3000/direction/bookmark')
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
console.log('✅ 패치 성공');
return response.json();
})
.then((data) => {
console.log('📌 북마커 데이터 로드 성공:', data);
let markerDataB = [];
if (data) { // 북마커 데이터 호출 및 지도에 추가
data.forEach(bookmark => {
console.log('이번 북마커는 : ', bookmark)
markerDataB.push({
// markerData.data.map((bookmark) => ({
position: new kakao.maps.LatLng(bookmark.latitude, bookmark.longitude),
imageUrl:
bookmark.sub_achievement_images ||
'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnZWDr%2FbtsMxEa1tat%2Fd4s5w26JdYrq0b7Qp9wDt1%2Fimg.png',
size: new kakao.maps.Size(40, 40),
info: bookmark.title || '이름 없음',
draggable: false,
});
console.log("북마커 좌표 확인:", bookmark.latitude, bookmark.longitude);
})
}
addMarkersToMap(markerDataB)
})
.catch((error) => console.error('Error fetching data:', error));
}
function addMarkersToMap(markerData) {
if (!markerData || markerData.length === 0) {
console.warn('📢 북마커 데이터 없음');
return;
}
console.log("다와간다 북마커 : ", markerData)
var bounds = new kakao.maps.LatLngBounds();
markerData.forEach(function (data) {
var markerImage = new kakao.maps.MarkerImage(data.imageUrl, data.size);
var marker = new kakao.maps.Marker({
position: data.position,
image: markerImage,
map: map,
draggable: data.draggable,
});
console.log("다와간다2 마커 : ", marker)
var infowindow = new kakao.maps.InfoWindow({
content: `<div style="padding:5px;">${data.info} <br>
<a href="https://map.kakao.com/link/map/${data.info},${data.position.getLat()},${data.position.getLng()}" target="_blank" style="color:skyblue">큰지도보기</a>
<a href="https://map.kakao.com/link/to/${data.info},${data.position.getLat()},${data.position.getLng()}" target="_blank" style="color:blue">길찾기</a></div>`,
});
kakao.maps.event.addListener(marker, 'mouseover', function () {
infowindow.open(map, marker);
});
kakao.maps.event.addListener(marker, 'mouseout', function () {
infowindow.close();
});
bounds.extend(data.position);
// infowindow.open(map, marker);
// bounds.extend(data.position);
});
map.setBounds(bounds);
}
</script>
</div>
</div>
</div>
<script>
// 사이드바 열기 함수
function openSidebar() {
document.getElementById('sidebar').style.width = '250px';
}
// 사이드바 닫기 함수
function closeSidebar() {
document.getElementById('sidebar').style.width = '0';
}
// 페이지 로드 시 로그인 상태에 따라 메뉴 표시
document.addEventListener('DOMContentLoaded', function () {
const accessToken = localStorage.getItem('accessToken');
const authMenu = document.getElementById('authMenu');
if (accessToken) {
// 사이드바 메뉴 업데이트
document.getElementById('loggedInMenu').style.display = 'block';
document.getElementById('loggedOutMenu').style.display = 'none';
// 상단 네비게이션 바 메뉴 업데이트
authMenu.innerHTML = `
<a href="/public/mypage.html">마이페이지</a>
<a href="#" onclick="logout()">로그아웃</a>
`;
// 메인 메뉴에 로그인 상태에서만 보이는 메뉴 추가
const menuList = document.getElementById('menuList');
if (menuList.querySelectorAll('a').length === 5) {
// 기본 메뉴만 있는 경우
menuList.innerHTML += `
<a href="/public/chattingrooms.html">채팅</a>
<a href="/public/pinkmong-dex.html">핑크몽 개인 도감</a>
<a href="/public/inventory.html">인벤토리 아이템</a>
<a href="/public/achievements.html">업적</a>
`;
}
} else {
// 사이드바 메뉴 업데이트
document.getElementById('loggedInMenu').style.display = 'none';
document.getElementById('loggedOutMenu').style.display = 'block';
// 상단 네비게이션 바 메뉴 업데이트
authMenu.innerHTML = `
<a href="/public/log-in.html">로그인</a>
<a href="/public/sign-up.html">회원가입</a>
`;
}
});
document.addEventListener("DOMContentLoaded", function () {
checkLoginStatus();
});
function checkLoginStatus() {
const accessToken = localStorage.getItem("accessToken");
const authMenu = document.getElementById("authMenu");
if (accessToken) {
fetch("http://localhost:3000/user/me", {
headers: { "Authorization": accessToken }
})
.then(response => response.json())
.then(userData => {
const profileImage = userData.profile_image || "./images/default-profile.png"; // 기본 이미지
authMenu.innerHTML = `
<div id="userProfileBox" onclick="goToProfile()" style="cursor: pointer;">
<img src="${profileImage}" alt="프로필 사진">
<span><span class="welcome-text">환영합니다!</span> <strong>${userData.nickname}</strong>님!</span>
</div>
<a href="#" onclick="logout()" style="margin-left: 15px; color: white; text-decoration: none;">로그아웃</a>
`;
})
.catch(error => {
console.error("유저 정보 불러오기 실패:", error);
authMenu.innerHTML = `<a href="/public/log-in.html" style="color: white;">로그인</a> / <a href="/public/sign-up.html" style="color: white;">회원가입</a>`;
});
} else {
authMenu.innerHTML = `<a href="/public/log-in.html" style="color: white;">로그인</a> / <a href="/public/sign-up.html" style="color: white;">회원가입</a>`;
}
}
function goToProfile() {
window.location.href = "/public/mypage.html";
}
async function logout() {
try {
const accessToken = localStorage.getItem('accessToken');
if (!accessToken) {
console.warn('🚨 로그아웃 시도: 이미 로그아웃된 상태');
window.location.href = '/public/log-in.html';
return;
}
// 🔹 서버에 로그아웃 요청 보내기
const response = await fetch(
'http://localhost:3000/user/auth/logout',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken,
},
credentials: 'include', // 쿠키도 함께 보내기
},
);
if (!response.ok) {
throw new Error('서버에서 로그아웃 처리 실패');
}
localStorage.removeItem("accessToken");
console.log("✅ 로그아웃 성공! 페이지 이동");
window.location.href = "/";
} catch (error) {
console.error('❌ 로그아웃 실패:', error);
alert('로그아웃 중 오류가 발생했습니다.');
}
}
// 길찾기 함수 (나중에 구현)
function findRoute() {
const start = document.getElementById('start').value;
const end = document.getElementById('end').value;
// 임시 알림
alert(
'길찾기 기능은 현재 개발 중입니다.\n출발지: ' +
start +
'\n도착지: ' +
end,
);
// 나중에 여기에 실제 길찾기 로직을 구현하면 됩니다.
const routeResult = document.getElementById('route-result');
routeResult.innerHTML = `
<div style="padding: 10px; background: #f8f9fa; border-radius: 5px;">
<p>검색 결과가 여기에 표시됩니다.</p>
</div>
`;
}
</script>
<!-- 사이드바 추가 -->
<div id="sidebar" class="sidebar">
<div class="sidebar-header">
<span class="close-sidebar" onclick="closeSidebar()">×</span>
</div>
<div class="sidebar-content">
<a href="/public/home.html">홈</a>
<a href="/public/event.html">이벤트</a>
<a href="/public/store-item.html">아이템샵</a>
<a href="/public/board.html">게시글 (커뮤니티)</a>
<a href="/public/ranking.html">랭킹</a>
<a href="/public/pinkmonglist.html">핑크몽 전체 조회</a>
<!-- 로그인 시 표시될 메뉴들 -->
<div id="loggedInMenu" style="display: none">
<a href="/public/chattingrooms.html">채팅</a>
<a href="/public/collection.html">핑크몽 개인 도감</a>
<a href="/public/inventory.html">인벤토리 아이템</a>
<a href="/public/achievements.html">업적</a>
<a href="/public/mypage.html">마이페이지</a>
<a href="#" onclick="logout()">로그아웃</a>
</div>
<!-- 로그인 안 된 경우 표시될 메뉴들 -->
<div id="loggedOutMenu">
<a href="/public/log-in.html">로그인</a>
<a href="/public/sign-up.html">회원가입</a>
</div>
</div>
</div>
<script src="./locationUpdates.js"></script>
<!-- public\locationUpdates.js -->
</body>
</html>
🔥 오늘 해결한 오류들 & 원인 분석
1️⃣ a.Lf is not a function 오류
✅ 원인
- kakao.maps.LatLng(latitude, longitude)에 잘못된 값이 들어감.
- latitude와 longitude가 undefined, null, NaN 등의 값으로 전달됨.
- geo service에서 latitude, longitude 순서가 반대로 되어 있었음.
🛠️ 해결 방법
✔ latitude와 longitude를 parseFloat()로 숫자로 변환
✔ isNaN(lat) || isNaN(lng) 체크하여 유효한 값만 사용
✔ geo service에서 latitude, longitude 올바른 순서로 수정
2️⃣ 북마커가 지도에 표시되지 않음
✅ 원인
- fetch()로 받아온 데이터가 문자열(string) 형태여서 kakao.maps.LatLng()에서 오류 발생.
- addMarkersToMap()에서 position: new kakao.maps.LatLng(bookmark.latitude, bookmark.longitude) 부분에서 잘못된 좌표 처리.
🛠️ 해결 방법
✔ latitude와 longitude를 parseFloat()로 변환하여 숫자로 처리
✔ isNaN() 검사하여 잘못된 좌표는 추가하지 않도록 설정
✔ console.log()로 데이터 흐름을 체크하며 디버깅
3️⃣ 북마커 위치가 반대로 표시됨
✅ 원인
- geo service에서 latitude와 longitude 순서가 반대로 저장됨.
- kakao.maps.LatLng(longitude, latitude)로 호출하면서 좌표가 뒤집어짐.
🛠️ 해결 방법
✔ new kakao.maps.LatLng(latitude, longitude) 순서를 올바르게 수정
✔ geo service에서 API 응답 데이터의 latitude, longitude 순서 정리
🚀 최종 해결 과정
✅ geo service에서 latitude, longitude 순서 수정
✅ parseFloat() 사용하여 좌표값을 숫자로 변환
✅ isNaN() 검사하여 유효하지 않은 좌표 제거
✅ console.log()로 데이터 흐름 체크 및 디버깅 완료
✅ 북마커가 정상적으로 지도에 표시됨! 🎯🔥
🎯 오늘의 교훈
1️⃣ 데이터를 받을 때 항상 형(type)을 확인하자! (string vs number)
2️⃣ 위도(latitude)와 경도(longitude) 순서를 항상 점검하자!
3️⃣ 콘솔 로그(console.log())를 적극 활용하여 데이터 흐름을 파악하자!
4️⃣ API 응답 데이터 구조를 항상 명확하게 이해하고 사용하자!
🎉 오늘의 성과
🚀 북마커 데이터 정상 로드 성공!
🚀 지도에 북마커가 정상적으로 표시됨!
🚀 좌표 순서 오류 해결 완료!
💡 앞으로 API 데이터를 사용할 때 console.log()로 미리 확인하는 습관을 들이자!
🔥 고생 많았다! 이제 북마커 기능 완벽하게 작동한다! 🔥