[Project] 소셜 로그인 구현하기 (FE 관점에서)

✨ 강은비·2022년 6월 21일
3

Project

목록 보기
4/9
post-thumbnail

🐽 모아모아는 자체적인 회원가입과 로그인 기능이 없고, 소셜로그인으로만 로그인이 가능하다. 회원가입의 장벽이 있을 것이라 생각해서 사용자 접근성 관점에서 소셜로그인으로 쉽게 모아모아 서비스를 사용하도록 결정했다.


🙌 소셜 로그인 구현하기

소셜 로그인 구현 방법에는 여러가지가 있지만 모아모아 팀에서는 아래와 같이 구현하였다.

  1. (프론트) 백엔드에서 정해준 소셜로그인 url로 브라우저 창의 url를 변경한다.
    const loginUrl = `${process.env.REACT_APP_LOGIN_BASE_URL}/api/oauth2/authorization/${type}?redirect_uri=${process.env.REACT_APP_LOGIN_REDIRECT_URL}`;
     window.location.assign(loginUrl);
  2. (백엔드) 인가코드 가져오기, 토큰 발급받기, 유저 정보 가져오기, access tokenrefresh token을 만들어 프론트 측에 보내기 - redirect
  3. (프론트) access tokenrefresh token을 받으면 로그인 성공했다고 판단하여 메인페이지로 redirect

참고



토큰을 어디에 저장해야 할까? 🤔

localStorage vs Cookie

서버에서 발급해주는 JWT를 이용하여 사용자 정보를 조회할 수 있기 때문에 탈취당하지 않도록 클라이언트 측에서 잘 보관해야 한다.

XSS (Cross Site Scripting)

  • 공격자가 악의적인 js 코드를 웹 브라우저에서 실행시키는 것
  • 이 방법으로 브라우저에 저장된 중요 정보들을 탈취 가능하다.

CSRF (Cross Site Request Forgery)

  • 정상적인 request를 가로채서 변조된 request를 보내 악의적인 동작을 수행하는 공격

localStorage

  • js 코드로 쉽게 접근 가능하기 때문에, XSS에 취약하다.
  • 하지만, 자동으로 request에 담기는 쿠키와 다르게 js 코드에 의해 헤더에 담기므로 XSS를 뚫지 않은 이상 CSRF 공격에는 안전하다.
  • 자동으로 http request에 담아서 보내기 때문에, CSRF 공격에 취약하다.
  • httpOnly 옵션을 사용하면 js에서 쿠키에 접근하지 못하기 때문에, localStorage에 비해 XSS 공격으로부터 안전하다.

🐽 모아모아에서는 만료 시간이 30분인 access tokenlocalStorage에 저장하고, 상대적으로 만료시간이 긴 refresh tokenhttpOnly옵션을 가진 cookie에 저장하기로 결정했다!!



📬 로그인 이후 api 호출 방식

  1. localStorage에 저장된 access token을 Authorization headerBearer {access token} 형태로 넣는다.
  2. 만약 access token 만료 시간이 지났거나 30초 미만으로 남았다면 reissue 요청을 보낸다. 아니라면 api를 호출한다.
  3. 응답으로 access token을 받고, 재발급받은 access token을 Authorization headerBearer {access token} 형태로 넣어 api를 호출한다.
export const setToken = async () => {
  const token = localStorage.getItem(process.env.REACT_APP_TOKEN_KEY);
  axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  const expiredAt = new Date(
    localStorage.getItem(process.env.REACT_APP_EXPIRED_AT_KEY),
  );
  const today = new Date();
  const diffTime = expiredAt.getTime() - today.getTime();
  if (diffTime <= 30000) {
    const { data } = await axios.get(`${baseUrl}/reissue`, {
      withCredentials: true,
    });
    today.setMinutes(today.getMinutes() + 30);
    localStorage.setItem(process.env.REACT_APP_TOKEN_KEY, data.accessToken);
    localStorage.setItem(process.env.REACT_APP_EXPIRED_AT_KEY, today);
    axios.defaults.headers.common[
      'Authorization'
    ] = `Bearer ${data.accessToken}`;
  }
};

// 모든 api를 호출하기 전에 setToken 함수를 호출하여 실행해야 한다.


🐛 Bug Issues

CORS

소셜로그인을 axios get 요청으로 처리했는데 아래와 같이 CORS 문제가 발생했다.

해결
소설 로그인 요청은 redirect_uri로 리다이렉트 되어야 해서 비동기 통신 방식으로 요청하면 안된다. 리다이렉트받을 수 있도록 브라우저 창의 url를 바꿔야 한다.

참고 : 소셜로그인 CORS 문제


쿠키 이슈 (refresh token)

백엔드 측 서버를 배포를 하고 프론트는 로컬에서 배포된 서버를 이용했다. redirecturl에서 access token은 잘 받았는데 쿠키에 refresh token이 없는 상황이 계속 발생했다. 그 이유는 프론트와 백엔드 도메인이 일치해야 쿠키 전달이 가능하다.


해결
어떻게 해결할지 백엔드 분이랑 이야기를 많이 했다.

  • 먼저, 프론트도 배포를 해서 배포된 사이트에서 쿠키 전달이 잘 이루어지는지 확인했다.
  • 하지만 생각해보니 소셜 로그인 이후 다른 api를 호출할 때 access token이 필요하고 access token 만료 시간이 지나면 refresh token이 필요했다.
  • 다시 로그인을 해서 새로운 access token을 받으면 되지만, refresh token을 이용하여 access token을 재발급받는 api 테스트도 로컬에서 필요했다.
  • 결국, 소셜 로그인 이후의 api 호출을 프론트에서 로컬로 테스트하기 위해서는 백엔드 서버를 로컬로 돌리기로 했다.
  • 백엔드 분들이 감사하게도 CORS 관련 설정도 다 해주셨고 백엔드 서버에 변경사항이 생길 때마다 빌드 파일을 보내주셨다. 🙇‍♀️🙇‍♂️🙇‍♀️

    모아모아 백엔드 서버 로컬로 실행하기

  • 배포가 자동화된 이후에는 간편하게 로컬 빌드 파일을 가져오는 방법을 알려주셨다. 👍👍
  • 이제 로컬에서는 http://localhost:8080로 api를 호출해야 하고, 배포된 사이트에서는 https://moamoadev.shop로 api를 호출해야 한다. 개발과 배포 상태에서의 백엔드 서버 url를 구분하기 위해 env 파일을 활용했다.

    📝 env 파일 활용하기

    • 환경변수를 관리할 수 있고, developmentproduction 여부에 따라 환경변수 값을 다르게 지정할 수 있다.
    • CRA에서는 환경변수를 사용하기 위해서 dotenv 모듈이 설치되어 있으며, 루트 디렉토리에 .env 파일을 만드면 된다.
    • 환경변수명은 반드시 REACT_APP으로 시작해야 한다.
    • .env : 기본파일
    • .env.development : 개발자 환경에서 로딩됨
    // .env.development
    REACT_APP_LOGIN_BASE_URL = http://localhost:8080
    REACT_APP_LOGIN_REDIRECT_URL = http://localhost:3000/oauth/redirect
    REACT_APP_BACKEND_BASE_URL = http://localhost:8080
    REACT_APP_TOKEN_KEY = TOKEN
    REACT_APP_EXPIRED_AT_KEY = EXPIRED_AT
    • .env.production : 배포 환경에서 로딩됨
    // .env.production
    REACT_APP_LOGIN_BASE_URL =  https://moamoadev.shop
    REACT_APP_LOGIN_REDIRECT_URL = https://moamoadev.shop/oauth/redirect
    REACT_APP_TOKEN_KEY = TOKEN
    REACT_APP_EXPIRED_AT_KEY = EXPIRED_AT


👀 느낀점

로그인 기능을 구현하는 것이 백엔드와의 첫 협업 경험이었다. 소셜 로그인 기능을 함께 구현하면서 쿠키 이슈를 해결하는 것이 가장 어려웠다.😭 다른 도메인 간에 쿠키를 전달하는 방법이 있지만, 모아모아 팀은 백엔드 서버 관리하시는 분 덕분에 프론트가 로컬에서 CORS 문제 없이 백엔드 서버를 돌릴 수 있었고, 쿠키 전달 문제도 해결되었다. 백엔드 서버를 어떻게 실행하는지 친절히 알려주셔서 감사했다. 🙇‍♀️🙇‍♂️🙇‍♀️

소셜 로그인 기능을 구현하면서 새롭게 배운 것이 많았다.

  • 로그인 상태 인증 방법 (세션 vs JWT)
  • access token과 refresh token
  • 토큰 저장 위치 => 협의해야 하는 부분!!
  • 로그인 이후 api 호출 방식 등등

그리고 .env 파일로 환경변수를 관리하는 방법을 알았지만, developmentproduction 여부에 따라 환경변수 값을 다르게 지정할 수 있는 방법을 새롭게 알게 되었다.
이제는 로그인 기능을 구현할 때 팀원들과 협의해야 하는 부분과 프론트가 어떤 역할을 해야 하는지 감을 잡았다!! 🙌

0개의 댓글