[NextJS,Typescript] 카카오 API와 Firebase 연동하기 정리

Urther·2022년 7월 6일
3

nextJs

목록 보기
4/6
post-thumbnail

프론트엔드 프로젝트에 카카오 API와 Firebase를 연동하는 방법입니다.
REST API를 이용한 방식입니다.

카카오 developers 애플리케이션 추가

1. 내 애플리케이션 추가

카카오 developers console에서 애플리케이션을 추가해주었습니다.

앱키에서 REST API 키를 .env 파일에 추가했습니다.
NextJS에서는 NEXT_PUBLIC_KAKAO_REST_API= 방식으로 저장해주어야 합니다.

2. 카카오 로그인 Redirect URI 추가

아직 배포 자동화를 연결하지 않은 상태이기 때문에 3000번 포트를 사용했습니다. 저는 리다이렉트 받을 URI를 네이버도 사용해야하기 때문에 명시적으로 /oauth/kakao로 기재해주었습니다.

Redirect URI 가 필요한 이유 ?

  • 카카오 document에 올라온 동작 방식이다. /oauth/kakao 페이지에서 인가코드와 access_token 을 받고 사용자 정보를 받을 수 있다. (email, 닉네임 등) 그 때 user가 있으면 jwt 토큰을 발급해주어야하고, 없으면 root page로 리다이렉트 시켜주어야한다. 그런 작업을 /oauth/kakao에서 해주는 것이다.

2. 인가 코드 발급 받기

위의 UI에서 '카카오로 로그인하기'를 a태그로 마크업해준다.

<Link href="https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${process.env.NEXT_PUBLIC_KAKAO_REST_API}&redirect_uri=${REDIRECT_URI}">
	<a>카카오로 로그인하기
 <Link/>
  • ${process.env.NEXT_PUBLIC_KAKAO_REST_API} : 위에서 설정해두었던 .env 폴더의 카카오 REST_API 값이다.
  • ${REDIRECT_URI} : Redirect URI 주소다. 위에서 설정한 https://localhost:3000/oauth/kakao가 될 것이다.

이렇게 마크업을 해주고, 회원이 동의하고 로그인한다면 리다이렉트 페이지로 이동하게 된다.
리다이렉트 페이지의 routing 처리는 이렇게 해주었다.

│   ├── oauth
│   │   └── kakao
│   │       └── index.tsx

useEffect 훅을 이용하여 컴포넌트가 올라오기 전, 인가 코드를 받아올 수 있도록 한다.

/* oauth/kakao/index.tsx */

  useEffect(() => {
    const code = new URL(window.location.href).searchParams.get('code');
  }, []);

인가 코드를 통해 카카오 측에 user에 대한 정보를 받아올 수 있다.

3. access Token 발급 받기

카카오에서 user 정보를 받아올 수 있는 access token이다. access token 그대로 client 사용자에게 주어도 할 수 있는게 없다. 우리는 access token을 카카오 측에서 받고 그것을 이용해서 user 정보를 받아온다.

/* oauth/kakao/index.tsx */

  useEffect(() => {
    const code = new URL(window.location.href).searchParams.get('code');
    getToken(code)
  }, []);

getToken 이라는 함수를 만든다. parameter로 위에서 받았던 인가 코드를 입력해준다.
axios를 이용하여 post 요청을 보냈다. url은 고정이다.

  const getToken = async (code: string | null) => {
    try {
      const {
        data: { access_token },
      } = await axios({
        url: `https://kauth.kakao.com/oauth/token`,
        method: 'post',
        params: {
          grant_type: 'authorization_code',
          client_id: process.env.NEXT_PUBLIC_KAKAO_REST_API,
          REDIRECT_URI: REDIRECT_URI,
          code: code,
        },
      });

      return access_token;
    } catch (e) {
      console.log(e);
    }
  };
`

grant_type : 'authorization_code'
client_id : 발급 받은 REST API 키
redirect_uri : 설정해둔 redirect uri
code : 위에서 받아온 인증 코드

4. access token을 통해 유저 정보 가져오기

backend 소스 코드

REST API를 이용하여 클라이언트 측에서 유저 정보를 가져오는 것은 불가능하다.
프론트엔드 측에서 access token 을 갖고 유저 정보를 가져오는 방법은 자바스크립트 SDK 를 이용하는 방식이다. (그러나 자바스크립트 SDK 는 모바일 환경에서 로그인이 되지 않는 문제가 발생한다. 모바일에서도 로그인을 실행시키고 싶다면 백엔드가 필요하다.)

REF | 카카오의 CORS 정책
즉, 프론트엔드 스크립트에서 kapi.kakao.comAPI호출은 허용되지 않습니다.
CORS가 열려있는 kauth.kakao.comAPI의 경우에도 리다이렉트 URI로 되돌아가야하는 로그인(인가요청, 추가 항목 동의받기), 로그아웃은 ajax 방식으로 호출 할 수 없습니다. 즉, kauth.kakao.comAPI는 토큰 요청만 가능합니다.

수정 전 방법

결론적으로 Express를 이용하여 백엔드를 구축했다.
Poiemaweb - express 를 참고했고 module 방식이 편해서 package.json에서 type을 module로 지정해주었다. 추가적으로 nodemon을 설치하여 백엔드 서버가 업데이트 되어 보여줄 수 있도록 했다.

const corsOption = {
  origin: "http://localhost:3000",
  credentials: true,
};

app.use(cors(corsOption));

cors 라이브러리를 설치하여 프론트엔드 Port(3000번)을 허용해주었다.credentials 속성이 없다면 추후에 프론트엔드측에 cookie를 백엔드가 세팅할 수 없기 때문에 credential 을 true로 허용해주었다.

백엔드 (8000번 포트 사용했습니다.)에서 /oAuth/kakao 로 요청받기로 했다.

수정 후

위에서도 언급했지만 kapi는 javascript 환경에서 돌아가지 않기 때문에 node.js 환경을 구축해주어야합니다.

위의 방식은 동일 Organization 에서 다른 Repository 로 Express를 구축하는 방법입니다. (당연히 두 개의 Port 번호가 다르기 때문에 프론트 - 백엔드 간의 CORS 에러를 핸들해주어야합니다.) 하지만 수정 후에는 Next JS의 Custom Server 기능을 사용해보려 합니다. 사용 방법은 Express와 동일합니다. 그러나 프론트와 백엔드가 동일 Port에서 돌아가기 때문에 그 간의 CORS 에러를 고민하지 않으셔도 됩니다.

Next JS의 Custom Server 에서 어떻게 홈페이지를 구축했는지 작성해두었습니다.

수정 전 및 수정 후 공통

/* 프론트의 oauth/kakao/index.tsx */

      const { data } = await axios({
        url: 'http://localhost:8000/oAuth/kakao',
        /* custom 서버 이용시 : /oAuth/kakao 로 설정 */
        method: 'post',
        data: { access_token: access_token },
        withCredentials: true,
      });

프론트측에 access_token을 data로 전달해준다.
백엔드측에서는 access_token 을 받고 kapi에 정보를 요청한다.

/* 백엔드의 index.js */
app.post("/oAuth/kakao", async (req, res) => {
  try {
    const { access_token } = req.body;

    const {
      data: { kakao_account },
    } = await axios({
      url: "https://kapi.kakao.com/v2/user/me",
      method: "get",
      params: {
        secure_resource: true,
        property_key: ["kakao_account.email"],
      },
      headers: {
        Authorization: `Bearer ${access_token}`,
      },
    });
});

사용자 측의 Option(필수 아님)

  • secure_resource : https 를 사용할 것인지, http 를 사용할 것인지 적는다.
  • property_key는 요청할 데이터를 적어두면 된다.

5. Firebase 에서 user 정보 찾기

위에서 사용자의 email 주소를 찾고 Firebase에서 email에 해당하는 user가 있는지 없는지 찾는다.

fireStore의 Collection 은 이런 구조이고, 임시 목데이터를 넣어둔 상태다.
백엔드의 firebase.js 파일 내에서 파이어베이스 document를 보고 초기 설정은 해둔 상태이다.

collection 을 이용하여 db에 존재하는 "user"이름의 collection을 userRef로 잡는다.
인자로 전달받은 email과 일치하는 정보가 있는지 확인하고 있다면 true를 리턴해주고, 있다면 return false를 리턴해준다.

const userRef = collection(db, "user");

export const findUserInfo = async (email) => {
  try {
    let userCollectionID = null;
    const q = query(userRef, where("email", "==", email));
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      userCollectionID = doc.id;
    });

    if (userCollectionID) {
      return true;
    }

    return false;
  } catch (e) {
    console.log(e);
  }
};

6. jwt 토큰 발급

firebase에서 유저가 있다면 jwt를 이용하여 토큰을 발행해주고, 로그인을 성공시켜주어야 한다.
jsonwebtoken 라이브러리를 사용했다. .env 파일에 JWT_SECRET_KEY= 를 설정해준다. (아무 단어나 설정해주어도 된다.)

jwt.sign 함수를 이용하여 jwt 토큰을 생성해준다. id는 사용자의 email로 설정해주었고 유효기간은 60m (1시간)으로 설정해주었다. .env 파일에서 설정해준 jwt 키를 token에 담아준다.

/* 백엔드의 jwt.js 파일 */

export const makeToken = async (email) => {
  const token = jwt.sign(
    {
      id: email,
    },
    process.env.JWT_SECRET_KEY,
    {
      subject: "bodyBuddy jwtToken",
      expiresIn: "60m",
      issuer: "bodyBuddy",
    }
  );

  return token;
};

result가 true 인 경우(user가 존재해서 로그인을 성공시켜야하는 경우)에 res.cookie를 이용하여 생성한 jwt 토큰을 담아주고 데이터는 result :boolean 값으로 보내준다.

/* 백엔드의 index.js*/
    const result = await findUserInfo(email);
    const token = await makeToken(email);

    // 회원이 있는 경우
    if (result) {
      res.cookie("jwt_Token", token);
      res.send({
        result: true,
      });
    } 

그러면 프론트에 로그인이 성공하고, 쿠키가 담긴 것을 확인 할 수 있다.

profile
이전해요 ☘️ https://mei-zy.tistory.com

1개의 댓글

comment-user-thumbnail
2023년 10월 20일

많은 도움이 되었습니다. 감사합니다.

답글 달기