- 프론트엔드에서 인증이란, 로그인과 회원가입, 리다이렉션이다.
- 프론트엔드 애플리케이션은 백엔드에 HTTP 요청을 보냈을 때 403이 리턴되면, 로그인 페이지로 리다이렉트 해야한다.
- 또한, 로그인한 후 백엔드 서비스로부터 받은 토큰을 어딘가에 저장해 놓고 요청을 보낼 때마다 헤더에 Bearer 토큰으로 지정해 주어야 한다.
서버-사이드 라우팅
- 사이트.com/login을 입력하고 들어가면 이 요청은 GET 요청으로 해당 사이트의 서버에 전달된다.
- 서버는 login 경로를 보고 login.html 페이지를 반환한다.
- 브라우저는 받은 login.html을 렌더링하고, 이 경우 웹 페이지가 새로고침 또는 reloading 된다.
- 이런 라우팅을 서버-사이드 라우팅이라고 한다.
클라이언트-사이드 라우팅
- 한 페이지에서만 동작하는 싱글 페이지 애플리케이션(SPA)이 사용하는 라우팅은 클라이언트-사이드 라우팅
- 클라이언트-사이드 라우팅은 서버로 어떤 요청도 보내지 않는다.
- 모든 라우팅은 클라이언트 코드, 즉 자바스크립트(리액트)가 해결한다.
- http://localhost:3000에 접속하면 이후에 필요한 모든 페이지와 라우팅 로직 등 프론트엔드에 구현한 모든 로직이 리턴된다.
- http://localhost:3000/login을 브라우저 주소창에 입력하면 리액트 라우터가 이를 가로챈다.
- 리책트 라우터의 로직은 URL을 파싱한 후 login 템플릿을 렌더링한다.
- 클라이언트-사이드 라우팅은 보편적으로 브라우저가 동작하는 방식과 다르기 때문에 라이브러리가 필요하다.
- react-router-dom은 클라이언트-사이드 라우팅 라이브러리 중 하나
AppRouter.js
import React from "react";
import "./index.css";
import App from './App';
import Login from "./Login";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Box from "@material-ui/core/box";
import Typography from "@material-ui/core/Typography";
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{"Copyright © "}
fsoftwareengineer, {new Date().getFullYear()}
{"."}
</Typography>
);
}
class AppRouter extends React.Component {
render() {
return (
<div>
<Router>
<div>
<Routes>
<Route path="/login" element={<Login/>}/>
<Route path="/" element={<App />}/>
</Routes>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Router>
</div>
);
}
}
export default AppRouter;
App.js
import React from "react";
import Todo from "./Todo";
import AddTodo from "./AddTodo";
import "./App.css";
import { List, Paper, Container } from "@material-ui/core";
import { call } from "./service/ApiService";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
}
componentDidMount() {
call("/todo", "GET", null).then((response) =>
this.setState({ items: response.data })
);
}
// 생략
}
ApiService.js
import { API_BASE_URL } from "../app-config";
export function call(api, method, request) {
let headers = new Headers({
"Content-Type": "application/json",
});
let options = {
headers: headers,
url: API_BASE_URL + api,
method: method,
};
if (request) {
options.body = JSON.stringify(request);
}
return fetch(options.url, options)
.then((response) => {
if (response.status === 403) {
window.location.href = "/login";
return Promise.reject(response.error);
}
if (!response.ok) {
return Promise.reject(response.json());
}
return response.json();
});
}
로컬 스토리지
- 웹 스토리지를 이용하면 사용자의 브라우저에 데이터를 key-value 형태로 저장할 수 있다.
- 웹 스토리지에는 세션 스토리지와 로컬 스토리지 두 개가 있다.
- 세션 스토리지는 브라우저를 닫으면 사라지고, 로컬 스토리지는 브라우저를 닫아도 사라지지 않는다.
- 사용자가 브라우저를 재시작할 때마다 로그인하게 하고 싶으면 세션 스토리지를, 브라우저를 재시작해도 로그인 상태를 유지하고 싶으면 로컬 스토리지를 사용하면 된다.
- 로컬 스토리지는 도메인마다 따로 저장되기 때문에, 다른 도메인의 자바스크립트는 다른 도메인의 로컬 스토리지를 알지 못한다.
- 로그인 시 받은 토큰을 로컬 스토리지에 저장하고, 백엔드에 API 콜을 할 때마다 로컬 스토리지에서 액세스 토큰을 불러와 해더에 추가한다.
import { API_BASE_URL } from "../app-config";
const ACCESS_TOKEN = "ACCESS_TOKEN";
export function call(api, method, request) {
let headers = new Headers({
"Content-Type": "application/json",
});
const accessToken = localStorage.getItem("ACCESS_TOKEN");
if (accessToken && accessToken !== null) {
headers.append("Authorization", "Bearer "+ accessToken);
}
let options = {
headers: headers,
url: API_BASE_URL + api,
method: method,
};
// 생략
}
export function signin(userDTO) {
return call("/auth/signin", "POST", userDTO)
.then((response) => {
if (response.token) {
localStorage.setItem(ACCESS_TOKEN, response.token);
window.location.href = "/";
}
});
}
- 로그아웃은 반대로 로컬 스토리지에 존재하는 액세스 토큰을 제거하고 로그인 페이지로 리다이렉트
export function signout() {
localStorage.setItem(ACCESS_TOKEN, null);
window.location.href = "/login";
}
'Frontend' 카테고리의 다른 글
Promise와 async/await (0) | 2022.03.17 |
---|---|
ES6+ 문법 정리 (0) | 2022.03.17 |
백엔드 통합과 CORS (0) | 2022.01.20 |
이벤트 처리 (0) | 2022.01.20 |
프론트엔드 기초 (0) | 2022.01.20 |