[FE] 인증 - 로그인과 라우팅

김주언·2022년 6월 18일
0

TODO LIST

목록 보기
12/18
post-thumbnail

백엔드 하고나면 이제 프론트엔드는 또 안된다^^.. 백엔드가 이제 인증을 따지기 때무네...
그래서 프론트에서도 인증을 해줘야한다.

프론트에서 인증이란?!
→ 로그인과 회원가입 그리고 리다이렉트~!

요구사항은 아래와 같다

  1. 백엔드에 HTTP 요청을 보내고, 백엔드 서버에서 403코드를 리턴하면 로그인 페이지로 리다이렉트
  2. 로그인 후 백엔드로부터 받은 토큰을 저장해두어야 하고, 이 토큰을 백엔드에 어떤 요청을 보낼 때마다 헤더에 토큰을 함께 지정해서 요청을 전송해야한다

따라서,
  1. 로그인 페이지 구현
  2. 리다이렉트 로직 구현
  3. 토큰 저장을 위한 로컬 스토리지 사용 로직 구현
  4. 회원가입 페이지 구현

을 할 것이다~!! 😞


라우팅

  • react-router-dom
  • 리다이렉션 로직 구현

react-router-dom

SSR (Server Side Routing)

  1. http://example.com
    example.com 이라는 도메인을 가진 서버에 GET 요청을 보내는 것

  2. index.html
    서버가 클라이언트에게 렌더링할 html과 다른 파일드을 보내는 것

  3. http://example.com/login
    마찬가지로 example.com 이라는 도메인을 가진 서버에 GET 요청을 보내는 것

  4. login.html
    서버는 클라이언트로부터 전달받은 요청http://example.com/login 에서 /login이라는 경로를 보고 해당 페이지를 반환

위와 같은 방식을 서버 사이드 라우팅이라고 한다.

하지만 나는 한 페이지에서 동작하는 싱글 페이지 애플리케이션(SPA)을 만들거지

CSR (Client Side Routing)

한 페이지에서만 동작하는 싱글 페이지 애플리케이션으로 사용하는 라우팅은 클라이언트 사이드 라우팅이다.

이 라우팅 방식은 서버로 페이지를 얻기 위한 요청을 보내지 않는다. 대신 라우팅을 자바스크립트가 수행한다. (클라이언트 코드가 직접 라우팅 수행)

  1. 서버로 접속

  2. 프론트엔드 서버가 리액트 앱을 리턴한다. 이 앱은 앞으로 브라우저에서 필요한 모든 리소스를 한번에 가지고 온다. (모든 html, css, js 파일 등을 모두 가지고 있는 것)
    즉, 처음 접속하면 이후에 필요한 모든 페이지와 라우팅 로직 등을 모두 리턴받는 것

  3. http://localhost:3000/login을 브라우저 창에 입력하면 리액트 라우터가 이를 인터셉트(?? 뺏는다??라고 해야하나)

  4. 리액트 라우터가 url을 파싱하여 login.js 파일을 렌더링 한다.

첫 접속 이후 모든 동작을 클라이언트측에서 처리하고 있다. (브라우저 내부적으로 처리한다)
그런데 클라이언트 사이드 라우팅은 브라우저의 보편적 동작 방식과 달라서 라이브러리가 필요한 것임


로그인 페이지 라우팅

컴포넌트 생성

yarn add react-router-dom

Login.jsx

src 폴더 내부에 routes 폴더를 생성하고 routes 폴더 내부에 Login.jsx 파일을 생성했음

import React from 'react';

const Login = () => {
  return <div>Login</div>;
};

export default Login;

AppRouter.jsx

src 폴더 내부에 AppRouter.js 파일을 생성한다.

import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import App from './App';
import Login from './routes/Login';

const AppRouter = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </BrowserRouter>
  );
};

export default AppRouter;

yarn start 해서 브라우저창에 /login 붙여서 확인해보면 잘 된드아아


로그인 페이지로 리다이렉트

  • 접근거부 받았을 때
  • 로그인하지 않은 상태일 때 (HTTP 메서드 종류 불문)

Access Denied는 언제 받으묘??
→ 리퀘스트가 거부 됐을 때?

리퀘스트가 거부된다는 것은?
→ API 콜 실패

API 콜 실패는 ??
→ 백엔드에서 ~~

즉 localhost:8080/todo가 접근거부를 하는것이다~!

다시 app-config.js 를 확인해보면 아래와 같다.

let backendHost;

const hostname = window && window.location && window.location.hostname;

if (hostname === 'localhost') {
  backendHost = 'http://localhost:8080';
}

export const API_BASE_URL = `${backendHost}`;

API_BASE_URL가 http://localhost:8080로 설정되어 있다.

그리고 ApiService.js를 확인해보면 아래와 같다.

import { API_BASE_URL } from '../app-config';

export function call(api, method, request) {
  let options = {
    headers: new Headers({ 'Content-Type': 'application/json' }),
    url: API_BASE_URL + api,
    method: method,
  };

  if (request) {
    options.body = JSON.stringify(request);
  }
  return fetch(options.url, options).then((response) =>
    response.json().then((json) => {
      if (!response.ok) {
        return Promise.reject(json);
      }
      return json;
    })
  );
}

API_BASE_URL 쪽으로 api 콜을 보내는 함수가 작성되어 있다.

마지막으로 App.js를 확인해보면

  useEffect(() => {
    call('/todo', 'GET', null).then((res) => {
      setItems(res.data);
    });
  }, []);

http://localhost:8080/todo로 API를 호출하는 것을 확인할 수 있다.

아무튼 결론은 백엔드가 접근 제한을 한다는 것..

💡 리다이렉트 로직을 API 서비스를 구현한 코드 쪽에 추가해야겠군아!! 라는 결론


ApiService의 call()fetch()를 사용한다. fetch()를 사용하면 Promise를 반환받는 것이고, Promise는 then을 사용하여 결과를 처리할 수 있다.

그런데 이 때 반환된 결과가 ( 반환된 response가) 에러라면, 이 결과는 catch()에서 받아볼 수 있다.

그러니까!! 접근 거부를 받은것에 관한 리다이렉션 처리는 ApiService에서 catch로 처리하면 되겠다 ^___^ ...

ApiService.js

// 생략
  if (request) {
    options.body = JSON.stringify(request);
  }
  return fetch(options.url, options)
    .then((response) => {
      console.log(response.ok);
      if (!response.ok) {
        return Promise.reject(response);
      }
    })
    .catch((error) => {
      console.log(error.status);
      if (error.status === 403) {
        window.location.href = '/login';
      }
      return Promise.reject(error);
    });
}

catch() 추가해서 403에러 받으면 /login으로 리다이렉트 하도록 했다
그리고 then() 부분 수정했음


❓ 백엔드와의 통신 오류
Unexpected end of JSON input 에러 메시지...

ApiService.js
원래 코드

// 생략

  return fetch(options.url, options).then((response) =>
   // 문제 발생 부분                                       
    response.json().then((json) => {
      if (!response.ok) {
        return Promise.reject(json);
      }
      return json;
    })
  );
}

Response 인터페이스의 json() 메서드는 응답 스트림을 받아서 이를 완성해주는 메서드이다. Promise를 반환해주는데, 응답 바디의 JSON을 자바스크립트 오브젝트로 파싱하여서 resolve에 담아서 보내줌

메서드 이름이 json()인 이유는 이 메서드의 결과가 JSON인게 아니고!!
응답 바디에 포함되어 있는 JSON을 자바스크립트 객체로 파싱하였기 때문이다!!

아무튼 내가 작성한 백엔드는 JsonResponse를 보내는게 아니고 HttpResponse를 보내니까 에러가 난거같다...

그리고 프론트에서 전송한 HTTP GET에 대한 백엔드 서버의 응답에
애초에 body가 없었다 허허거참

response.json() 부분을 없애주면 댐...

아오~!! 🥹


profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글