개발 일지

Urther·2022년 7월 3일
0

nextJs

목록 보기
3/6

과정을 남기기 위해 작성한 미완성 글입니다.

1. 카카오 로그인 API 연결

카카오 developers에서 애플리케이션을 설정해주었다. 지난 프로젝트에서 다른 팀원이 카카오 로그인 API 연결을 SDK로 하셨다가 REST API 로 변경하신게 기억에 남아 REST API를 이용하려고 한다.

애플리케이션에서 Redirect URI 를 설정할 수 있는데 나는 추후에 Naver API 도 연결해줄 것이기 때문에 /oauth/kakao 로 설정해두었다.

카카오 로그인 REST API 문서

리다이렉트 URI은 인가 코드(access token) 이 넘어가게 된다.

  1. 카카오 계정 로그인 동의 얻는다
  2. 리다이렉트 URI 에서 하는 작업 : (사용자에겐 찰나 혹은 보이지 않을 수 있다.) access token 을 백엔드로 넘겨서 jwt를 cookie에 넣어준다거나의 작업들을 할 수 있다. 이 곳에서 로그인 해야하는 회원인지 / 회원가입해야하는 회원인지 구분하여 관련 page로 이동이 가능하다.

2. 인증 코드 발급 받기

https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${process.env.NEXT_PUBLIC_KAKAO_REST_API}&redirect_uri=${REDIRECT_URI}

  • ${process.env.NEXT_PUBLIC_KAKAO_REST_API} : 애플리케이션을 등록했을 때 받은 REST API KEY 인데 보안 이슈 때문에 .env 파일에 담아두었다.

  • ${REDIRECT_URI} : 위에서 설정한 리다이렉트 URI다.

이 곳으로 Get 요청을 보낸다.
button 태그로 axios get 요청은 먹히지 않는다. 그래서 a태그의 href로 get 요청을 보냈다.

회원이 동의를 한다면 설정해둔 리다이렉트 페이지로 이동하게 된다. 주소에서 code를 통해 인증 코드를 받아올 수 있다.

    const code = new URL(window.location.href).searchParams.get('code');

3. access Token 받기

access token 을 사용자의 쿠키 혹은 타 저장소에 넣거나 하는 일은 없어야 한다.

카카오에 user 정보를 받아오는 access token 이지 우리가 사용하는 jwt의 토큰이 아니다 ! access token을 그대로 사용하는 것은 하면 안된다.

  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);
    }
  };

리다이렉트 URI 컴포넌트에서 위에서 얻어온 code를 이용하여 access token을 가지고 온다.

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

🆘 프론트엔드 측에서 CORS 에러

  • 7월 4일 문제 발생

카카오의 CORS 정책

프론트엔드 측에서 kapi 를 이용하여 user 정보를 가져오려고 했는데 문제가 발생했다. 처음에는 axios로 header에 넣어주는 문제라고 생각했는데 그렇다고 하면 CORS 에러가 아닐텐대? 라는 생각으로 찾아보니 ... REST API 를 사용하여 생긴 문제였다.

카카오 CORS 정책 상, REST API 를 이용하여 유저 정보를 가져오는 것은 백엔드를 사용해서 가져와야한다는 것이다.

이에 대한 해결책으로,
1. 자바스크립트 sdk 방법으로 로그인하는 방법 ( 이전 프로젝트에서 sdk 방식을 이용하니까 모바일에서 지원을 하지 못해서 탈락 )
3. Express 를 사용해서 백엔드를 구현하는 방법

다른 팀원들은 Express를 쓰지 않을텐데 나만 express를 구현하는게 맞을지, 그리고 express를 짜는데 시간을 꽤 투자해야하기 때문에 걱정된다.

📣 Express를 이용하여 유저 정보 가져오기

7월 4일 새벽 7시 추가
Express 서버 구축은 이 곳을 참고하였다. poiemaweb.com- Routing, Middleware, Static file, Template engine

이 방법은 백엔드 개발자 없이 프론트엔드 개발자가 사용할 수 있는 최선의 방법인 것 같다. (백엔드 개발자가 있는 프로젝트라면, 받은 access token을 정해진 api 에 넘겨주면 백엔드 개발자가 jwt 토큰을 저장소에 저장까지 시켜주는 것으로 알고 있다.)

yarn add axios body-parser nodemon express cors

백엔드 서버에 설치한 패키지는 일단 이정도다. 추후 jwt 라던가 토큰을 만들려면 더 추가해야할 것같다.

package.json 파일에 scripts 를 아래와 같이 추가해주면 자동으로 업데이트해서 보여준다.

    "start": "nodemon --watch index.js",

프론트엔드에서는 위에서 받은 access_token을 data로 넘겨주었다.

     const result = await axios({
       url: 'http://localhost:8000/oAuth/kakao',
       method: 'post',
       data: { access_token: access_token },
     });

express 서버는 req.body 로 access_token을 받아서 유저 정보를 가져올 수 있게끔 한다. params에 담긴 내용들은 필수가 아니라 option 에 해당한다.

app.post("/oAuth/kakao", async (req, res) => {
 try {
   const { access_token } = req.body;

   const result = 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}`,
     },
   });

   console.log(result);
 } catch (e) {
   console.log(e);
 }
});

📄 JWT 자체 토큰 발행

  • 7월 4일 (월) 고민
    access token을 바로 사용할 수 없고 사용자에게 토큰을 발급해주어야하는데 express 를 사용해서 토큰을 발급해야하는지, 혹은 리액트 상에서도 토큰 발급이 가능한지 궁금하다.

리액트 상에서도 jwt 발급이 가능한 글을 보았지만, express 에서 jwt를 발급해주고 firebase에서 체크해야할 것 같다 ! ( ㅠㅠ )

7월 4일 15시 추가
Express를 쓰지 않으려 파이어베이스 툴을 사용하려 했던 것이었다. 프론트엔드 개발자가 쿼리문(파베도 쓰지만 난이도 있지 않음)을 깊게 배우지 않고도 사용할 수 있는 툴이기도 하고 프론트엔드에서도 쿼리문 날리기 편해서였다. 그래서 결론은, 팀원들은 서버가 딱히 필요 없고 나만 서버가 필요하다 🥲

서버에서 로그인 요청이 들어오면 jwt 토큰을 verify 하고, 만약 유효하지 않다면 파이어스토어를 뒤져볼 생각이다.

로그인 요청 시 내가 생각한 동작 흐름도는 아래와 같다.

1. Header에 jwt 토큰을 담아 보낸다.
- 만약 jwt 토큰이 없거나 유효하지 않다면 2번으로 간다.
- 만약 jwt 토큰이 유효하다면 번으로 간다.

2. email을 가지고 FireStore을 뒤져본다.
- 만약 email 이 없다면 회원가입으로 리다이렉트 시켜준다.
- 만약 email 이 있다면 jwt 토큰을 생성시켜서 client cookie에 담아준다.

3. 로그인 성공시킨다.

  • 7월 5일 24시 추가
  1. JWT의 유효성 검사는 로그인 했을 때 가능한 작업 등이나 이럴 때 미들웨어로 유효성을 검사해준다.
  • 만약 유효성이 만료되었다면 jwt 지워주고 로그아웃 처리
  1. 다시 로그인으로 돌아와서 access token을 통해 유저 정보를 가져온다.
  2. email 이 fireStore 에 있다면 cookie에 jwt 토큰을 심어준다.

jsonwebtoken 을 이용해서 토큰을 만들었다. id에 있는 email은 사용자의 email을 담아주었다.

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

쿠키 세팅

  • 7월 5일 23시 추가

res.send로 jwt를 프론트에 보내시는 분들도 있었지만 백엔드에서 cookie를 세팅해주는게 맞다고 판단했다. 이전에도 서버에서 cookie가 세팅이 안되어서 골먹은적이 있는데 그 때 너무 바빠서 그 부분 문제 해결에 참여하지 못해서 헤맸다.

res.cookie 를 통해 쿠키 세팅이 가능한데 프론트쪽에 쿠키가 세팅이 되지 않는 문제가 발생했다. 그 이유는 사용하는 port가 달라서이기 때문이다. CORS 의 origin 만 열어주면 된다고 생각했는데 credentials도 열어주어야 한다.

  • 나는 front 가 아니라 back 에서 3000포트(프론트)를 열어줬는데
const corsOption = {
  origin: "http://localhost:3000",
  credentials: true,
};

credentials에 오타가 있어서 쿠키가 세팅되지 않은 것이였다... 🥲
그렇게 jwt 쿠키 세팅 완료 !


  • 7월 6일 추가

회원가입 페이지에서 user redux 에서 정보 없으면 root 페이지로 튕기게끔 해놨다. 파이어베이스에 유저 등록까지 완료했음 !

네이버 로그인 연결해야하는데 카카오랑 또 다르다 🥲 encode URI해주는 state가 추가적으로 생기게 되는데 이 부분은 더 고민해보겠습니다 🙏

jwt verify

7월 16일 추가

app.get("/verify", (req, res) => {
  const token = req.cookies;
  console.log(token);
});

verify로 해줬고, 요청하면.. cookie 바로 가는거 아닌가요?
cookiepaser install 해주어야한다고 해서 설치했는데 [Object: null prototype] {} [Object: null prototype] {} 가 떴다. 후. 프론트에서도 credential 문제인가 싶다.

      axios.defaults.withCredentials = true;

axios에서 credential 을 열어주었다 ! 해결완

네이버 로그인 구현

7월 16일 추가 +

  const initializeNaverLogin = () => {
    const naver = (window as any).naver;
    // let naverLogin: any;

    const naverLogin = new naver.LoginWithNaverId({
      clientId: process.env.NEXT_PUBLIC_NAVER_CLIENT_ID,
      callbackUrl: '/oauth/naver',
      isPopup: true,
      loginButton: { color: 'green', type: 1, heigth: '60' },
    });
    naverLogin.init();
  };

green으로 해주었는데 클릭 요소는 잡히는데 왜 투명으로 보이는지 모르겠다.. .

7월 17일 추가
height 오타가있었다. 이걸로 몇 시간을 허비했는지 모르겠다! 오타 잘체크 !
그런데 네이버 로그인 버튼 커스텀 하고 싶을 땐.. 어떻게 해주어야하는지 더 고민해야한다

7월 24일 추가

일단 네이버 아이디로 로그인하는 구조를 확인했다. 어떻게.. img만 갈아끼우면 될 것같은데 ref를 써서 a태그의 innerHtml 형식으로 사용할 수 있을 지 모르겠다.

User정보 체크 파트

로그인해서 접속해야하는 페이지들이 있다. 그래서 , useEffect 처리가 필요할 것 같은데 사이트가 로그인해서 접속해야하는 페이지 >>>>>> 로그인 안해도 접속 가능한 페이지다. 그렇기 때문에 훅으로 빼려고 한다.

  1. token 이 있는지 체크
    -> 없으면 회원가입 or 로그인 페이지로 라우팅처리
  2. useSlice(redux 정보) 있는지 확인
    -> 없으면, token을 가지고 유저 정보를 찾아야한다.

근데, 고민이 되는 부분은 firebase에서 user인지 trainer인지 찾아야한다. 어떤 방법이 나은지 고민해야함

뒤집어야할 용기 .. !

7월 24일 16시 추가

trainer로 로그인을 했는지,user로 로그인을 했는지 고민을 하지 못했다. 차이점이 있어야하는데 스흡 뒤집어야할 것 같다..

7월 31일 15시 추가

백엔드 파트 레포를 따로 만들었었는데 그럴 필요가 없었다. 우하하
다시 next js 커스텀 서버 하하하하
근데 firebase 관련 설정에서 type이 제대로 맞지 않아서 build 오류가 왕창 뜬다. 이걸 해결해야 잘 돌아가는지 안돌아가는지.. 확인이 가능한 부분이다.
이미 작성해온 백엔드 레포의 코드들을 프론트쪽으로 옮겨오면서 정리해야한다. 슬프지만 웃고 있다. 제법 힘든거 조아하나봐 나...

8월 2일 1시 추가
build 없이도 서버가 돌아가는 방법에 대해 알아챘다 호호
http 도 읽어야하고.. nextjs도 개발하고 바쁘다바빠
오늘안에 로그인을 다시 재정비해볼 생각이다... !!!

다시 시작

8월 2일 22시 추가

  • jwtwebtoken 설치 완료
  • bodyParser 설치 완료 - req.body undefined 문제 해결

새로 짜는 것보다 기존에 있는 방식을 바꾸는 것이 더 어렵게 느껴진다.

문제 발생 ) server에서 API 폴더 안에 있는 firebase를 읽어오고 있지 못하고 있다. server안에서도 firebase 를 생성해줘야하는지 궁금하다. 그런 경우가 있다면 왜 굳이 nextjs 서버를 이용해야하는거지..?

터미널이 내 말을 들어줬으면 좋겠다....
오늘까지 해결해야지 으쌰리 ~

8월 4일 00시 추가

위의 문제.. 스택오버플로우도 다 뒤져보고 구글링이란 구글링은 다 해봤다. 결론은 해결했는데 조금 어이 없는 곳에서 헤매고 있었다.

TSError: ⨯ Unable to compile TypeScript:

이 문구에 꽂혀서 왜 컴파일이 안되는거지..? 생각이 들어서 tsconfig 파일을 확인하고, package.json 파일에서 server 부분을 뒤져보기도 했다.

결론적으로 실제로 reviewProps라고 해준 타입의 문제였다. 해당 타입을 선언한 interface 다른 폴더의 trainer.d.ts 로 선언되어 있었다.. 🥲 그.. 부분을 해결해주니 말끔하게 타 폴더도 접근이 가능해졌다.

해결완료 .. ^^
이제 깔끔하게 API 명세 적고 하면 된다..

아, 그리고 jwt token 에 원래 email 로 저장하려 했는데 email로 파이어베이스 도는 것보다 collection ID 로 저장하는게 더 빠르지 않을까라는 생각에 collection ID로 교체했다.

다시 정리 (8월 5일 추가)

로그인에 접근할 수 있는 것은 jwt가 없다는 가정 아래

  • 백엔드에서 access token 을 받고, user를 확인한다. (이메일을 받는다)
  • user 정보가 있다면 jwt에 user Collection 심어주고, trainer 혹은 user slice에 정보 기입해준다.
  • user 정보가 없다면 user Slice에만 정보 기입해주고 회원가입할 수 있는 페이지로 보내준다.

갑자기 이건 모지히..

let app;
if (!getApps().length) {
  app = initializeApp(firebaseConfig);
} else {
  app = getApp();
}

next js + typescript + firebase + express 환경에서 잘 발생하는 오류라고 한다. 이미 initial 된 것을 또 initial 시켜주려니까 오류가 뜨는 것이당 허허.. 이렇게 해결~


위에서 해결하려해도 백엔드에서 접근하려고하면 위와 같은 오류가 뜬다.

rest Connection RunQuery 라는데 REST는 REST API인가 아니면 나머지 커넥션..(?) BadRequest가 뜰일이 뭐가 있지.

백엔드에서 databaseId가 설정되지 않은 것을 확인할 수 있다.

프론트쪽이랑 비교해보면 잘 알 수 있다..!
왜 백엔드쪽에서 설정이 안들어가고 프론트쪽에서만 설정이 들어가는거지..?

뭐가 문제인지 진짜 모르겠다. 백엔드 쪽에서도 firebase를 따로 설정해주었는데 console.log(db)를 찍어보았을 때 아무런 설정도 되어있지 않은 것을 확인했다. next js 공식 문서를 뒤져봐도 어떻게 동작하는지를 모르겠어서 너무...이 오류들이 고통스럽다..........
위에서 jwt 로 고민할 때가 조금 더 .. 제법.. 행복했을지도..

8월 7일 (일) 추가 +
firebase .. storage만 일단 export 해보고 안되면 일단 더 고민하지 말고 express 서버 레포 따로 뺀걸로 고민해보려고 한다. 노력에 비해 성과가 너무 안좋다하하

드디어 해결 완료

절반 행복하고 절반 행복하지 않다.

문제 원인

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_MSG_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_APP_ID,
};

원래 firebaseConfig 파일을 이렇게 설정해두었었는데, express 서버에서 undefined로 뜨게 된다. 실제로 express 서버 내에서 console.log(process.env.NEXT_PUBLIC_API_KEY) 를 찍었을 때는 정상 출력되는데 왜 undefined가 뜨는지까지는 아직 잘 모르겠다.

[Next.js] 환경변수가 undefined 일 때 이 문제라고 생각하고 next js config 파일도 변경해보았는데 의미 없었다.

그래서 process.env 파일을 사용하지 않고, gitignore에 들어가는 파일을 하나 만들어두고, 문자열로 사용했다..

정확한 방법이 아닐 수도 있지만 진짜 여러개 시도해서 이 방법이 제일 깔끔했다. 살려조.

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

0개의 댓글