// 카카오 맵 지도 연동하기
// 카카오 개발자 사이트 ..!
import Head from "next/head";
import { useEffect } from "react";
declare const window: typeof globalThis & {
kakao: any;
};
// 윈도우에 카카오라는것도 있어! 하고 알려쥬기
export default function kakaoMapPage() {
useEffect(() => {
// 3. api, 빈 div 받고 나서 그다음에 그 안에 지도를 그려주기 위해서 useEffect 사용
// 4. 지도 띄우는 코드 작성
const container = document.getElementById("map"); //지도를 담을 영역의 DOM 레퍼런스
// container라는 상수 값에 map의 이름을 가진 id 태그를 가져와 담아주기
const options = {
// 지도를 생성할 때 필요한 기본 옵션 (초기값 설정)
center: new window.kakao.maps.LatLng(37.4847, 126.9027), //지도의 중심좌표.
level: 3, //지도의 레벨(확대, 축소 정도)
};
const map = new window.kakao.maps.Map(container, options); //지도 생성 및 객체 리턴
// container라는 div태그에 options을 그려줘
}, []);
return (
<>
<Head>
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=12a523204d108e11d65325b65bc2213a"
></script>
{/* 2. 실제 지도를 그리는 api 불러오기 */}
</Head>
<div id="map" style={{ width: 500, height: 400 }}></div>
{/* 1.지도 담을 영역 만들기 */}
</>
);
}
그런데 버튼을 클릭해서 페이지를 이동한 뒤 카카오맵이 출력되도록 하면 오류가 나게 된다
Router를 사용하지 않고 html의 a 태그를 사용하면 오류가 안나고 잘 작동됨
어떤 차이가 있는거지????
SPA(single page application)
spa에서는 서비스에 처음 접속할 때 모든 페이지의 데이터를 다 받아온다
그리고 router를 통해 페이지를 이동할 때, 실제로는 페이지의 일부에 해당하는 컴포넌트만 교체한 뒤 페이지를 다시 그려오게 된다 (re-rendering)페이지를 이동할 때 걸리는 시간이 압도적으로 짧음
MPA(multi page application)
서로 다른 url을 가진 페이지들이 각각 독립적으로 존재함
프론트엔드 서버 페이지를 그린 뒤 브라우저로 html, css, js를 보내주는 작업을 매 페이지 이동시마다 거치게 된다
(주소를 입력해서 이동하는 것과 a 태그를 통해 이동하는 것은 동일한 기능)mpa는 페이지 이동시마다 서버에 요청해서 데이터를 받아와야하기 때문에 느림
a 태그를 사용하면 페이지 이동시 오류가 사라지지만, 페이지를 이동했을 때 페이지 자체가 새로 로딩되기 때문에 SPA 프레임워크인 Next.js를 사용하는 의미가 없어진다.
링크 태그 이용해주고
script 다운로드를 완료하고 카카오 맵 로드도 완료한 뒤에 지도를 불러오도록 코드 수정해주기
export default function kakaoMapPage() {
useEffect(() => {
// 3. api, 빈 div 받고 나서 그다음에 그 안에 지도를 그려주기 위해서 useEffect 사용
const script = document.createElement("script");
// 5. 이동하기 버튼 클릭시 오류 수정하기 위해서 스크립트 태그 만들어주기
script.src =
"//dapi.kakao.com/v2/maps/sdk.js?appkey=12a523204d108e11d65325b65bc2213a&autoload=false";
// 6. 스크립트의 src에 이 주소를 넣을거야
document.head.appendChild(script);
// 7. Head의 자식에 script를 넣을거야
script.onload = () => {
// 8. window.kakao가 로드되면 그때 이 함수 실행시켜줘
window.kakao.maps.load(function () {
// v3가 모두 로드된 후, 이 콜백 함수가 실행됩니다.
// 4. 지도 띄우는 코드 작성
const container = document.getElementById("map"); //지도를 담을 영역의 DOM 레퍼런스
// container라는 상수 값에 map의 이름을 가진 id 태그를 가져와 담아주기
const options = {
// 지도를 생성할 때 필요한 기본 옵션 (초기값 설정)
center: new window.kakao.maps.LatLng(37.4847, 126.9027), //지도의 중심좌표.
level: 3, //지도의 레벨(확대, 축소 정도)
};
const map = new window.kakao.maps.Map(container, options); //지도 생성 및 객체 리턴
// container라는 div태그에 options을 그려줘
console.log(map);
});
};
}, []);
return (
<div>
{/* <button onClick={onClickMoveToMap}>맵으로 이동하기 !</button> */}
<Link href="/29-03-kakao-map-routed">
<a>맵으로 이동하기 !!</a>
</Link>
</div>
);
}
Link 안에 a 태그를 넣으면
**시맨틱 요소**
를 가지고 있는 html 태그로 렌더링이 되기 때문에 웹 표준이나 검색 엔진 최적화 차원에서도 좋음그렇기 때문에 가능한 부분에서는 가급적 Link 태그를 사용하는 것이 좋다 !
Link 사용이 불가능할 때만 router.push 쓰기!
(특정 게시물을 등록, 삭제, 수정 한 다음 자동으로 페이지 이동시켜줄 때는 router push, 근데 이제 버튼을 클릭해서 이동하고 싶을 때는 link 태그 쓰면 됨 (메뉴 눌러서 이동시키기 등등))
시맨틱 태그란? 그 자체 만으로도 `**의미를 담고 있는 html 태그**`
많이 사용되는 시맨틱 태그에는 **header, footer, nav, section, aside** 등이 있음
callback 함수란? : 다른 함수의 인자로 들어가는 함수
function aaa(qqq){ // 함수 로직 } aaa(function(){})
위의 코드에서 aaa 함수의 인자에 들어가는 function(){} 를 callback 함수라고 한다
화살표 함수로 바꾸면?aaa(() => {})
callback 함수를 사용하는 이유는? : 특정한 요청이 끝난 뒤, 그 결과 값을 가지고 다른 요청을 실행시켜야 할 때
function aaa(qqq){
// 외부 API에 데이터 요청하는 로직
// ...
// ...
// 요청 끝!
const result = "요청으로 받아온 데이터 결과값"
qqq(result) // 요청 끝나면 qqq 실행시키기
}
aaa(result) => {
console.log("요청이 끝났습니다.")
console.log("요청으로 받아온 데이터는" + result + "입니다")
}
async/await 가 없었을 때 비동기를 동기적으로 처리하기 위해서 callback을 많이 사용했음!
const onClickCallback = () => {
// axios 없을 때 사용하던 방식
// aaa야 api 로드 다 되면, 이 함수 실행시켜줘! 하고 callback 함수 집어넣기
const aaa = new XMLHttpRequest(); // 서버와 상호 작용하기 위해 사용되는 객체
aaa.open("get", `http://numbersapi.com/random?min=1&max=200`);
aaa.send();
aaa.addEventListener("load", (res: any) => {
const num = res.target.response.split(" ")[0]; // 랜덤 숫자 가지고오기
const bbb = new XMLHttpRequest();
bbb.open("get", `http://koreanjson.com/posts/${num}`); // 랜덤으로 가지고 온 숫자에 해당하는 게시물 불러오기
bbb.send();
bbb.addEventListener("load", (res: any) => {
// 가지고 온 데이터가 문자열로 되어있어서 id만 뽑아오기 힘들기 때문에 객체로 바꿔줌
const userId = JSON.parse(res.target.response).UserId;
const ccc = new XMLHttpRequest();
ccc.open("get", `http://koreanjson.com/posts?userId=${userId}`); // 뽑아온 id가 작성한 게시물 보기
ccc.send();
ccc.addEventListener("load", (res: any) => {
console.log(JSON.parse(res.target.response)); // 최종 결과값!
});
});
});
};
이렇게 하면.. 콜백지옥에 빠진다 그래서 다음으로 나온게 promise !!
axios
.get("http://numbersapi.com/random?min=1&max=200")
.then((res) => {
const num = res.data.split(" ")[0];
return axios.get(`https://koreanjson.com/posts/${num}`);
})
.then((res) => {
const userId = res.data.UserId;
// prettier-ignore
return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
})
.then((res) => {
console.log(res.data);
});
promise를 사용할 경우 각 요청들이 체인처럼 연결되는데,
이걸 프로미스 체인 또는 프로미스 체이닝(Promise chaining) 이라고 부름
그런데 Promise도 콜백 지옥은 해결했지만, 직관적이지 못한 문제가 있음그래서 나온게!! Async/Await
const onClickAsyncAwait = async () => {
// prettier-ignore
const res1 = await axios.get("http://numbersapi.com/random?min=1&max=200");
const num = res1.data.split(" ")[0];
console.log("여기는 2번입니다~");
const res2 = await axios.get(`https://koreanjson.com/posts/${num}`);
const userId = res2.data.UserId;
// prettier-ignore
const res3 = await axios.get(`https://koreanjson.com/posts?userId=${userId}`)
console.log(res3.data);
};
async / await를 이용하면 코드가 직관적이고 심플해짐 !
async / await는 promise에만 붙일 수 있다!
axios가 promise 객체를 이용해 만들어진 라이브러리이기 때문에 async/await를 사용할 수 있는 것
promise로 실행하는 함수는 태스트 큐에 들어간다
- 매크로 태스크 큐 : setTimeout, setInterval 등
- 마이크로 태스크 큐 : Promise
태스크 큐들 간의 실행 순서는? : 매크로, 마이크로가 부딪힐 경우 마이크로 태스트 큐가 우선순위
export default function EventLoopWithQueuePage() {
const onClickTimer = () => {
console.log("===============시작~~===============");
// 비동기 작업 (매크로큐에 들어감)
setTimeout(() => {
console.log("저는 setTimeout! 매크로 큐!! 0초 뒤에 실행될 거예요!!!");
}, 0);
new Promise((resolve) => {
resolve("철수");
}).then((res) => {
console.log("저는 Promise! - 1!! 마이크로 큐!! 0초 뒤에 실행될 거예요!!!");
});
// 비동기 작업 (매크로큐에 들어감)
setInterval(() => {
console.log("저는 setInterval! 매크로 큐!! 1초 마다 계속 실행될 거예요!!!");
}, 1000);
let sum = 0;
for (let i = 0; i <= 9000000000; i += 1) {
sum = sum + 1;
}
new Promise((resolve) => {
resolve("철수");
}).then((res) => {
console.log("저는 Promise! - 2!! 마이크로 큐!! 0초 뒤에 실행될 거예요!!!");
});
console.log("===============끝~~===============");
};
return <button onClick={onClickTimer}>시작</button>;
}
Promise와 setInterval, setTimeout를 함께 실행시키면 이렇게 마이크로 큐가 먼저 실행되는걸 확인 할 수 있음