NextJS 14에서 kakao API 사용하기

ChanghyeonO·2023년 11월 29일
0
post-thumbnail
post-custom-banner

NextJS 14버전에서 카카오 API를 활용하려 구글링을 해보니 대부분 2022년이나 23년 초 글이었다. Next 12버전이 압도적으로 많았던 것 같은데, 폴더구조가 꽤나 변경되어 크게 도움이 안된 것 같다.
ChatGPT한테 물어봐도 자꾸 12버전이나 알려줘서 큰 도움이 안되었다.
아마 14버전 KakaoAPI 사용법 작성한 블로그는 내가 첨 아닐까?! 😇

API 발급

일단 카카오 API 활용하는데에 필요한 API 키 값 받는 건 너무 간단하고 다들 알거라 생각해 빠르게 이미지 한장으로 넘어가겠다.
여기서 발급받으면 된다.

도메인 연결

로컬 실행해봐야하니 로컬 주소와 이후 배포할 도메인 주소를 넣어주었다.

API 연결

NextJS에서 제공하는 Script 컴포넌트를 import하고 body태그 하단에 추가해주었다.

import type { Metadata } from "next";
import { FontClassNames } from "./Styles/Font";
import "./Styles/GlobalStyles.css";
import Script from "next/script";

export const metadata: Metadata = {
  title: "GGulJam",
  description: "Generated by ChanghyeonOh",
  icons: {
    icon: "../../public/sleeping.png",
  },
};

declare global {
  interface Window {
    Kakao: any;
  }
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body className={FontClassNames}>{children}</body>
      <Script
        src="https://developers.kakao.com/sdk/js/kakao.js"
        strategy="afterInteractive"
      />
    </html>
  );
}

전체 코드는 위와 같고, 타입스크립트를 사용하고 있기 때문에 declare global { interface Window { Kakao: any; } } 로 카카오 타입을 지정해주어야한다.
또한 strategy="afterInteractive"를 추가해주어, 주요 콘텐츠들이 로드 된 이후 이 스크립트가 로드 되게 만들어, 초기 로딩시간에 영향을 주지 않도록 하였다.

컴포넌트 작성

import React, { useEffect } from "react";
import Image from "next/image";
import KakaoImage from "../../../public/kakao_logo.png";

type KakaoShareButtonProps = {
  description: string;
};

const KakaoShareButton = ({ description }: KakaoShareButtonProps) => {
  const shareUrl = typeof window !== "undefined" ? window.location.href : "";

  useEffect(() => {
    if (typeof window !== "undefined") {
      const { Kakao } = window;

      if (!Kakao.isInitialized()) {
        Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY);
      }
    }
  }, []);

  const handleShare = () => {
    const { Kakao } = window;

    Kakao.Share.sendDefault({
      objectType: "text",
      text: description,
      link: {
        mobileWebUrl: shareUrl,
        webUrl: shareUrl,
      },
    });
  };

  return (
    <div onClick={handleShare}>
      <Image
        className="w-10 h-10 cursor-pointer"
        src={KakaoImage}
        alt="카카오톡 공유 이미지"
      />
    </div>
  );
};

export default KakaoShareButton;

src/app 경로에 공유 버튼 컴포넌트를 만들었다.
shareUrl은 현재 페이지의 URL을 저장하고 있다가 공유 버튼 클릭할 경우 열리는 페이지 주소로 사용된다
useEffect를 사용해 컴포넌트 렌더링시 카카오 SDK를 초기화 하고, 공유버튼을 생성한다. 여기에 Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY); .env파일을 생성해서 아까 발급받았던 카카오 자바스크립트 키를 넣어준다.
카카오톡 공유하기 탬플릿 여기 사이트에서 원하는 형식의 공유하기 탬플릿을 골라 형식대로 작성해주면 된다.

컴포넌트 사용

"use client";

import React, { useState } from "react";
import KakaoShareButton from "../Components/KakaoShare";

const Calculator = () => {
  const [hour, setHour] = useState("00");
  const [minute, setMinute] = useState("00");
  const [resultTimes, setResultTimes] = useState<string[]>([]);

  const calculateTime = () => {
    let currentHour = Number(hour);
    let currentMinute = Number(minute);

    let resultTime = [];

    for (let i = 0; i < 6; i++) {
      currentMinute -= 30;

      if (currentMinute < 0) {
        currentMinute += 60;
        currentHour -= 1;
      }

      currentHour -= 1;

      if (currentHour < 0) {
        currentHour += 24;
      }

      resultTime.unshift(
        `${currentHour.toString().padStart(2, "0")}:${currentMinute
          .toString()
          .padStart(2, "0")}`,
      );
    }
    setResultTimes(resultTime);
  };

  const renderSelectOptions = (start: number, end: number) => {
    const options = [];
    for (let i = start; i <= end; i++) {
      const value = i < 10 ? `0${i}` : i.toString();
      options.push(<option key={i}>{value}</option>);
    }
    return options;
  };

  const shareDescription = `최적의 수면 시간 계산 결과입니다.\n3번째 이전에 자는 것을 권장합니다.\n${resultTimes
    .map((time, index) => `${index + 1}번째 : ${time}`)
    .join("\n")}`;

  return (
    <div
      className="
      flex flex-col items-center justify-center text-center 
      bg-container-bg rounded-3xl
      sm:w-full md:w-3/5 lg:w-1/3 py-16"
    >
      <div>
        <h1 className="text-2xl font-bold mb-8">
          🕖기상 시간을 체크해주세요.🕖
        </h1>
        <div className="flex items-center justify-center gap-5 mb-4">
          <div className="mb-4">
            <label htmlFor="hour-select" className="mr-2 font-bold">
              시 :
            </label>
            <select
              value={hour}
              onChange={e => setHour(e.target.value)}
              className="w-24 text-center border border-gray-400 bg-gray-300 rounded-md font-bold"
            >
              {renderSelectOptions(0, 23)}
            </select>
          </div>
          <div className="mb-4">
            <label htmlFor="minute-select" className="mr-2 font-bold">
              분 :
            </label>
            <select
              id="minute-select"
              value={minute}
              onChange={e => setMinute(e.target.value)}
              className="w-24 text-center border border-gray-400 bg-gray-300 rounded-md font-bold"
            >
              {renderSelectOptions(0, 59)}
            </select>
          </div>
        </div>

        {resultTimes.length === 0 ? (
          <button
            className="bg-black hover:bg-gray-800 text-white font-bold py-2 px-4 rounded-lg"
            onClick={calculateTime}
          >
            계산하기
          </button>
        ) : (
          <div>
            <div className="flex flex-col justify-center mt-8">
              <div className="text-xl font-bold">잠자기 최적의 시간</div>
              <div className="font-bold mb-4">{resultTimes[0]}</div>
            </div>
            <div>
              <p>이후 잠자기 최적의 시간</p>
              {resultTimes.slice(1).map((time, index) => (
                <div className="mt-4" key={index}>
                  {index + 2}번째 추천 시간 {time}
                </div>
              ))}
            </div>
            <div className="flex align-center justify-center gap-4 mt-8 h-10">
              <button
                className="bg-black hover:bg-gray-800 text-white font-bold py-2 px-4 rounded-lg"
                onClick={calculateTime}
              >
                다시 계산하기
              </button>
              <KakaoShareButton description={shareDescription} />
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

export default Calculator;

이후 KakaoShareButton을 사용할 페이지 파일에 import 해주고 추가해주었다.
계산결과를 텍스트로 공유해주어야 하기에 props로 넘겨주어, 아까 카카오 버튼 컴포넌트에 추가해주면 된다.

카카오 버튼 잘 뜨는 거 확인했고, 공유하기 버튼을 누르면 아래처럼 잘 출력되는 것을 확인할 수 있다.
이번에 NextJS를 처음 사용해보는데 구조가 리액트랑 비슷하면서도 달라서 헷갈렸다.
그래도 이전에 카카오맵 api를 사용해본 경험이 있기도 하고, 운좋게도 참고할만한 블로그를 찾아 나름 빠르게 마무리할 수 있었던 것 같다.
추가 기능 구현 가보자고~!

profile
꾸준한 기록을 통해, 좋은 개발자가 되겠습니다.
post-custom-banner

0개의 댓글