Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

미니는 밍기적

[REACT] 프론트와 백엔드 간의 로그인 통신 방법 본문

Frontend/GAN:ERATE

[REACT] 프론트와 백엔드 간의 로그인 통신 방법

sefdcrxe 2023. 7. 6. 14:39

우선 글을 들어가기에 앞서, 프론트와 백엔드 간의 통신

 

[ 로그인 방식 ]

[ HTTP 통신 방식 ]

웹에서 데이터 교환 ->

FrontEnd에서 ID와 Password를 입력하여 BackEnd로 데이터를 보낼 때, fetch나 axios같은 HTTP 비동기 통신 라이브러리를 사용

 

서버와 클라이언트가 항상 연결되어 있는 것이 아니라!

클라이언트가 요청(request)을 보내면 순간 연결이 되고, 서버가 응답(response)을 보내면 통신은 종료된다는 것이다.

-> 상태를 저장하지 않아 자원 낭비를 방지한다는 장점

 

 


1. session 방식 > 서버에서 사용자의 세션 데이터를 저장해서 로그인 기능을 구현하는 방법

프론트와 서버 각각의 역할

  1. [프론트] 프론트 쪽에서 로그인 페이지에서 아이디, 비번을 서버로 POST함
    a. [서버] 서버는 기존에 회원가입을 한 사람이면 서버에서 세션아이디를 만들어줌
    b. [서버] 세션 아이디를 담을 변수, DB 공간을 마련해 거기 저장 (세션데이터라고 부름)
    c. [서버] 그걸 쿠키라는 것에 포션해서 고객 브라우저에 그 쿠키를 강제 저장시킴
  2. [프론트] 클라 쪽에서 /mypage(로그인 해야만 들어갈 수 있는 페이지)를 요청
    a. [서버] 서버는 해당 요청을 받으면 일단 로그인 했는지 여부를 확인해야 함
    b. [서버] 쿠키에 세션 아이디가 포함되어 있는지 검사
    c. [서버] 만약, 있으면 통과시켜주고 그 과정에서 회원의 이름, 나이, 성별 등의 DB정보가 필요하면 꺼내옴

 

2.  json web token 방식 > 세션 데이터를 서버에 저장하지 않고, 마이페이지를 열람할 수 있는 열쇠(토큰)을 사용자에게 쥐어줌. session방식 보다 그 열쇠에 더 많은 정보들이 들어감.

 

  1. [프론트] 프론트에서 로그인 페이지에서 아이디, 비번을 서버로 POST
    • [서버] 그 아이디, 비번이 DB에 저장된 회원정보와 맞다면 서버는 Token하나를 브라우저로 보냄 → 여기서 Token은 긴 암호화 된 문자열로, 사용자가 로그인 했었는지, 아이디는 무엇인지 이런 정보들 넣을 수 있으며, 위조가 불가능하도록 특별 서명이 추가됨
  2. [프론트] 해당 토큰은 프론트 딴에서 로컬스토리지에 저장 or 쿠키에 저장 (만료 기한이 있는 key, value형태의 저장소)
  3. [프론트] 클라 쪽에서 /mypage(로그인 해야만 들어갈 수 있는 페이지)를 요청할 때 해당 토큰을 헤더에 붙여서 POST (로컬 스토리지에 있는 정보 or 쿠키에 있는 정보)
    • [서버] 서버는 해당 토큰이 적법한 지 검사해서 이상이 없으면 응답함

 

!! refresh token방식

  1. [프론트엔드] ID와 비밀번호를 준다.
  2. [백엔드] ID와 비밀번호를 검증하고 AccessToken과 RefreshToken, AccessToken의 만료시간을 반환해준다. 이 때 생성한 RefreshToken은 DB에 {ID,RefreshToken}으로 저장한다.
  3. [프론트엔드] 반환받은 AccessToken을 매 api 호출마다 헤더에 붙여서 전송한다.
  4. [백엔드] api호출시 헤더의 AccessToken을 확인하고 유효한지, 만료기간이 지났는지를 체크 후 api를 동작시킨다.
  5. [프론트엔드] AccessToken의 만료 기간이 지나거나, 30초 미만으로 남았다면, 백엔드에 RefreshToken을 붙여 Reissue 요청을 보낸다.
  6. [백엔드] Reissue요청이 들어올 경우, RefreshToken이 DB에 있는 것인지 확인한 후, 맞다면 AccessToken과 새로운 AccessToken 만료 시간을 반환한다.
  7. [프론트엔드] Reissue결과 반환된 AccessToken과 만료기간을 저장하여 다음 api호출에 사용한다.

3.  소셜 로그인 방식

  1. [프론트] 프론트 쪽에서 페이스북 로그인 버튼 제공해 페이스북으로부터 유저 이름, 아이디 정보 받아서 그 정보를 서버로 POST
    a. [서버] 서버에선 이 사람의 이름, 아이디 정보를 받아서 세션이나 토큰을 만들어줌
  2. [프론트] 클라 쪽에서 /mypage(로그인 해야만 들어갈 수 있는 페이지)를 POST
    a. [서버] 세션 방식을 쓴 경우 ) 서버는 세션이 있는 지를 검사
    b. [서버] JWT방식을 쓴 경우 ) 클라 쪽에서 온 토큰이 적법한 지를 검사

 


[ 로그인 로직 ]

 

 

클라이언트에서 처리하기

먼저, React 최상단 index.js에서 axios에 withCredentials를 true로 설정해줘야 refreshToken cookie를 주고받을 수 있다.

 

import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";

import App from "./App";

axios.defaults.baseURL = "https://www.abc.com";
axios.defaults.withCredentials = true;

 

 

onLogin = (email, password) => {
	const data = {
		email,
		password,
	};
	axios.post('/login', data).then(response => {
		const { accessToken } = response.data;

		// API 요청하는 콜마다 헤더에 accessToken 담아 보내도록 설정
		axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

		// accessToken을 localStorage, cookie 등에 저장하지 않는다!

	}).catch(error => {
		// ... 에러 처리
	});
}

------

1. 로그인 & 2. 로그인 만료되기 전에 연장

const JWT_EXPIRY_TIME = 24 * 3600 * 1000; // 만료 시간 (24시간 밀리 초로 표현)

onLogin = (email, password) => {
    const data = {
        email,
        password,
    };
    axios.post('/login', data)
        .then(onLoginSuccess)
        .catch(error => {
            // ... 에러 처리
        });
}

onSilentRefresh = () => {
    axios.post('/silent-refresh', data)
        .then(onLoginSuccess)
        .catch(error => {
            // ... 로그인 실패 처리
        });
}

onLoginSuccess = response => {
    const { accessToken } = response.data;

    // accessToken 설정
    axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

    // accessToken 만료하기 1분 전에 로그인 연장
    setTimeout(onSilentRefresh, JWT_EXPIRRY_TIME - 60000);
}

3. 페이지 리로드 될 때 로그인 연장

// 어플리케이션이 실행될 때마다 다시 로그인 시도
class App extends Componet {
    componentDidMount() {
        onSilentRefresh();
    }
    // ...
}