TIL[27일차].지도 장착!

남예지·2022년 12월 6일
0

TIL

목록 보기
21/47

넥스트js와 지도 라이브러리는 싱글 페이지 어플리케이션이라는 것 때문에 생각지 못한 오류가 날 수 있다. 이걸 한번 알아볼 예정.

맵을 프로젝트 내부에 적용하면 좀 더 좋은 편의성을 제공해줄 수 있지 않을까?
카카오 맵 API를 이용해서 프로젝트 내 지도를 직접 구현해보자.

+
지도 API는 카카오 뿐만 아니라 구글, 네이버(페이스북, 깃허브 등등) API도 많이 사용하는데 비용이나 기능의 종류의 차이가 있어 개발할 때 이러한 차이점을 고려하여 API를 선택하면 된다.


kakao Map API

카카오 디벨로퍼 사이트
https://developers.kakao.com/

카카오 지도에 들어가서 로그인 후 지도 -> 제품소개로 들어간다.

시작하기를 누르고 애플리케이션을 추가하기를 눌러 앱을 만들어준다.

만든 앱을 눌러주면 정보 페이지로 들어가진다.

보통 자바스크립트 키나 레스트 api를 많이 쓴다.

이제 사이트 메뉴에서 문서에 들어가서 지도를 본다.
웹에서 사용할것이니 자바스크립트를 눌러준다.


그 다음 시작하기를 누르고 따라하면 된다.

-카카오맵 구현 실습-

실습 1

import Head from 'next/head';

최상단에 Head import하기.

return(
	 <>
			<Head>
				<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey='JavaScript 앱 키 입력'"></script>
			</Head>
	 </>
)

리턴에 Head태그 안에 스크립트 넣고 아까 만든 앱 키 넣어주기

const container = document.getElementById('map'); 
//지도를 담을 영역의 DOM 레퍼런스

const options = { 
//지도를 생성할 때 필요한 기본 옵션
    center: new kakao.maps.LatLng(33.450701, 126.570667), //지도의 중심좌표.
    level: 3 //지도의 레벨(확대, 축소 정도)
};

new kakao.maps.Map(container, options); 
//지도 생성 및 객체 리턴

// return(
	// ...
		<div id='map' style={{ "width" : "500px", "height" : "400px" }}></div>
// )


독스에 다 잇으니 붙여넣은 다음 var를 const로 변경해줍니다.
그리고 kakao가 없다는 에러가 뜬다면?
const 나 let 없이 다운받으면 자동으로 window에 들어가는데 alert이나 confirm 등이 다 윈도우에 들어가 있는 속성이다. window.alert로 부른다. 마찬가지로 kakao도 window.kakao로 해준다.
-결과-

routed

카카오 맵은 routed를 할 때 한번에 안되고 페이지를 넘어간 뒤 새로고침을 해야 되는 현상이 나오는데
이러한 알 수 없는 오류가 나온다.

이런 오류가 나는 이유는
접속 또는 새로고침 시 프론트엔드 서버에서 다시 새로 받아온다. 이때는 카카오에서 api를 받아올 시간이 충분하다.
그런데 router.push로 페이지를 이동하게 되면 프론트엔드 서버에서 다운받아오는게 아닌 추가가 필요한 부분만 다운로드해서 보여준다.
속도가 빠르다.
이를 싱글페이지 어플리케이션이라고 한다.

<a href ="#"></a>가 있는데 왜 router.push를 쓰지?🤫
차이점은 a태그는 기존 html에서 지원하는 페이지 이동방식이고 라우터 푸시는 react/next에서의 페이지 이동방식이다.
a 태그 이동 방식은 프론트엔드 서버에서 새로 다운받기 때문에 속도가 평범하다.
router.push는 필요한 부분만 추가로 다운로드 하기때문에 빠르다.

아무튼 싱글페이지 어플리케이션(SPA)은 속도가 빠르기때문에 카카오가 지도를 가져오는 시간이 너무 느려서 오류가 난다.

이때 해결방법은 2가지다.
1. app 페이지에서부터(혹은 전 페이지에서) 카카오 다운받는 방법
2. 카카오가 다 받아지기 전에는 코드실행하지 않는 방식(이걸 쓸거임)

우리가 할 것
1. SPA - MPA 성능 차이 비교
2. _app.tsx에서 미리 받아보기 (비효율적)
3. 제대로 페이지 이동시 받고, 다 받아지면 실행하기

SPA - MPA 성능 차이 비교

웹과 앱이 나뉘면서 앱은 페이지를 다운받는 형식이 아니여서 빠르다보니 웹도 그렇게 할 수 없을까.
싱글페이지어플리케이션을 이용!

_app.tsx에서 미리 받아보기 (비효율적)

  • 모든 페이지에서 카캉맵을 다운로드 받으므로 비효율적임

제대로 페이지 이동시 받고, 다 받아지면 실행하기

useEffect를 사용한다!

script.src = "//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=0c2d716eaabacde4a664321997aa22c6";

키벨류 형식이고, 주소 자체에 ? 해서 넘기는 방법도 있다.
키벨류와 키벨류를 연결하는 방법은 & 연산자이다.
나머지는 docs에 load를 읽고 그대로 해주면 된다.

오버레이 마커 생성하기

리엑트에도 a처럼 link라는 태그가 있다.
둘 다 SPA 활용이 가능하다.
둘 중 뭘 써야할까
결론은 Link태그를 쓰자.
차이는 없지만 링크태그는 안쪽에 a태그를 붙이는데 (작동하지는 않음) 시멘틱태그이기 때문이다.

시멘틱 태그의 장점
html에 이름이 붙어서 의미를 알 수 있다.
1. 같은 개발자들간에 커뮤니케이션 용이
2. 검색엔진 최적화() SEO

그럼에도 불구하고 어쩔수 없이 함수를 써야할 때가있다.
게시글 등록 후 리턴받은 아이디의 상세페이지로 이동하는 경우!
이런경우는 미리 주소를 받을 수 없기때문에 함수를 사용해야한다.

refetch 그만!

리페치 하는 법
새로고침 시 알폴로 클라이언트 글로벌 스테이트에 갔다가 캐시가 있으면 먼저 확인하고 가져오는 fetch-polisy 정책을 따른다고 했다. 없으면 백앤드가서 가져오고 다시 글로벌 스테이트에 저장한다.
refetchQueries: [{ query: FETCH_BOARDS}],

리페치 없이 하는 법
이제는 게시글등록을 하번 하면 11번 게시글 내용을 받아오고 리패치 하지 않고 11번 데이터만 받아오기. 이 11번을 직접 Apollo에 등록하면 캐시스테이트에 저장되었기 때문에 api를 한번만 받아와도 된다.
캐시 스테이트가 바뀌므로 리렌더링이 자동으로 된다.

규모가 커질수록 작동을 한단계 없애는게 크다.

refetch 안쓰는 방법❗️
1. 뮤테이션 요청
2. cache 변경
이렇게 두 과정만 있으면 된다.

import { gql, useMutation, useQuery } from "@apollo/client";
import {
  IQuery,
  IQueryFetchBoardsArgs,
} from "../../src/commons/types/generated/types";

const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;

const DELETE_BOARD = gql`
  mutation deleteBoard($boardId: ID!) {
    deleteBoard(boardId: $boardId)
  }
`;
const CREATE_BOARD = gql`
  mutation createBoard($createBoardInput: CreateBoardInput!) {
    createBoard(createBoardInput: $createBoardInput) {
      _id
      writer
      title
      contents
    }
  }
`;
export default function StaticRoutedPage() {
  const { data } = useQuery<Pick<IQuery, "fetchBoards">, IQueryFetchBoardsArgs>(
    FETCH_BOARDS
  );

  const [deleteBoard] = useMutation(DELETE_BOARD);
  const [createBoard] = useMutation(CREATE_BOARD);

  const onClickDelete = (boardId: string) => () => {
    void deleteBoard({
      variables: { boardId },
      // refetchQueries: [{ query: FETCH_BOARDS}],
      update(cache, { data }) {
        cache.modify({
          fields: {
            fetchBoards: (prev, { readField }) => {
              console.log(prev);
              const deletedId = data.deleteBoard; // 삭제된 아이디
              const filteredPrev = prev.filter(
                (el) => readField("title", el) !== deletedId // el._id 가 안되므로, readField를 사요해 뽑아옴
                //  !el.__ref.includes(deletedId)
              );
              return [...filteredPrev];
            },
          },
        });
      },
    });
  };

  const onClickCreate = () => {
    void createBoard({
      variables: {
        createBoardInput: {
          writer: "철수",
          password: "1234",
          title: "제목입니다",
          contents: "내용입니다",
        },
      },
      // refetchQueries: [{ query: FETCH_BOARDS}],
      update(cache, { data }) {
        cache.modify({
          fields: {
            fetchBoards: (prev) => {
              return [data.createBoard, ...prev];
            },
          },
        });
      },
    });
  };

  return (
    <div>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <span style={{ margin: "10px" }}>{el.title}</span>
          <span style={{ margin: "10px" }}>{el.contents}</span>
          <button onClick={onClickDelete(el._id)}>삭제</button>
        </div>
      ))}
      <button onClick={onClickCreate}>게시글 등록하기</button>
    </div>
  );
}

update를 사용한다.

개발을 할때는 상활에 맞게 하는게 중요하다.
트레픽이 별로 없다면 리페치 하는게 더 낫고 유지보수도 쉽다.
오버 엔지니어링을 조심해야한다.

profile
총총

0개의 댓글