티스토리 뷰

로그인을 테스트해볼 수 있는 화면은 React 프로젝트로 만들어 보도록 하겠다

(npm, node.js는 설치되어 있다고 가정하고 진행한다)

 

 

1. 이를 위해 vite를 사용해 React 프로젝트부터 만들어보자

$npm create vite@latest front-oauth
  • 프로젝트명은 알기 쉽게 front-oauth라고 적어줬다
  • 위의 명령어를 실행하면 framework 등 선택 화면이 나오는데 아래와 같이 선택했다

 

 

2. 위의 설치 과정이 끝난 후 localhost:5173으로 접속하면 아래와 같은 화면이 나오면 설치 과정이 정상적으로 완료된 것이다

 

 

2. 간단한 테스트를 위해 로그인 버튼만 존재하는 화면을 만들어보자. 

function App() {
  
  return (
    <>
      <button>로그인</button>
    </>
  )
}

export default App
  • 기존 App 함수에 있는 내용과 import문을 전부 제거하고 버튼만 만들어줬다(화면이 마음에 안 들어도 넘어가자..)

로그인 버튼이 있는 화면

 

 

3. 이제 버튼을 클릭하면 로그인이 되도록 API를 호출하는 기능을 넣어보자

단, 이 때 OAuth2 로그인은 API 호출이 아닌 웹페이지가 이동하도록 작동하기 때문에 axios 호출이 아닌 window.location.href로 이동한다

function App() {
  const LOGIN_URL =
  'http://localhost:8081/users/oauth2/login?state=http://localhost:5173';

  const login = () => {
    window.location.href = LOGIN_URL;
  };
  
  return (
    <>
      <button onClick={login}>로그인</button>
    </>
  )
}

export default App
  • 우리는 로그인을 클라이언트 서버를 통해서 진행하기 때문에 클라이언트 서버 주소인 localhost:8081로 이동하고 로그인이 완료된 뒤 현재 화면으로(localhost:5173)으로 돌아오기 위해 현재 주소를 state로 전달했다

 

4. 로그인이 완료되고 현재 화면으로 돌아와도 변한 것이 없기 때문에 알기 어렵다. 확인을 위해서 화면이 처음 렌더링 될 때 서버로 user 정보를 요청해서 user 정보가 있을 경우엔 user 정보를 보여주고 없을 경우에는 로그인 버튼을 보여주도록 코드를 변경해 보자

 

4-1. user 정보를 관리하기 위해 UserType을 만들자

export type UserType = {
  id: number,
  email: string,
  name: string
};

 

4-2. 서버에서 user 정보를 얻기 위해 서버 호출을 위한 axios 설치 및 api 호출 함수를 작성한다

$npm install axios@latest
  • axios 설치
import axios from "axios";
import type { UserType } from "./UserType";

const api = axios.create({
    baseURL: "http://localhost:8081", // user 서버
    withCredentials: true
});

export const getMeApi = async (): Promise<UserType> => {
  const response = await api.get<{body: UserType}>(`/users/me`);

  return response.data.body;
};
  • user 정보를 얻기 위한 api 함수 작성
  • 앞에서 클라이언트 서버를 만들 때 토큰 정보를 쿠키로 저장했기 때문에 이를 위해 axios를 생성할 때 withCredentials 옵션을 true로 설정했다

 

4-3. 이제 화면이 처음 렌더링 될 때 앞에서 말한 작업을 하도록 App.tsx 파일을 수정해 보자

import { useEffect, useState } from "react";
import type { UserType } from "./UserType";
import { getMeApi } from "./LoginApi";

function App() {
  const LOGIN_URL =
  'http://localhost:8081/users/oauth2/login?state=http://localhost:5173';

  const login = () => {
    window.location.href = LOGIN_URL;
  };

  const [userInfo, setUserInfo] = useState<UserType>();

  useEffect(() => {
    getMeApi()
    .then(setUserInfo)
    .catch(e => {
      console.log("login fail ", e);
    });
  }, []);
  
  return (
    <>
    { userInfo ?  
        `${userInfo.name}님 안녕하세요.`
      : <button onClick={login}>로그인</button>
    }
    </>
  )
}

export default App

 

 

이렇게 하면 리액트에서의 모든 작업은 완료됐다. 하지만 테스트해보면 계속해서 서버의 응답이 401로 에러가 발생하는 것을 볼 수 있다. 이건 Spring Security의 기본 동작이 access token을 Authorization 헤더에서 읽어서 처리하기 때문인데 이를 정상적으로 동작하게 하는 방법은 아래와 같다

  1. access token을 요청하는 API를 만들어서 리액트에서 요청 헤더 Authorization에 추가하거나
  2. 서버에서 쿠키에서 값을 읽어와서 처리하게 하면 된다

우리는 두 번째 방법인 서버가 쿠키에서 access token을 읽어서 처리하는 방법으로 해결해 보도록 하자

 

 

5. 서버가 쿠키에서 access token을 처리하게 하기 위해서 클라이언트 서버에 아래 코드를 추가

@Component
public class CookieBearerTokenResolver implements BearerTokenResolver {

    @Override
    public String resolve(HttpServletRequest request) {
        String cookies = request.getHeader(HttpHeaders.COOKIE);

        return cookies != null ? getCookieValue(cookies, "accessToken") : null;
    }

    private String getCookieValue(String cookies, String name) {
        return Arrays.stream(cookies.split(";"))
            .filter(cookie -> cookie.split("=")[0].trim().equals(name))
            .findFirst()
            .map(cookie -> cookie.split("=")[1].trim())
            .orElse(null);
    }
}
  • 쿠키에서 토큰을 읽어와 처리하는  빈을 정의
private final BearerTokenResolver cookieBearerTokenResolver;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf(AbstractHttpConfigurer::disable)
    	// 생략...
        .oauth2ResourceServer(oauth2 ->
            oauth2
                .jwt(jwt -> jwt.decoder(jwtDecoder)
                    .jwtAuthenticationConverter(customJwtAuthenticationConverter)
                )
                .bearerTokenResolver(cookieBearerTokenResolver)
        );

    return http.build();
}
  • SecurityFilter의 리소스서버 정의 부분에 BearerTokenResolver를 등록해 줬다

 

 

이제 서버까지 모든 작업이 완료됐다.

 

테스트를 해보면 먼저 권한서버에 로그인을 요청하는 화면이 나온다

 

로그인을 완료하면 처음 접속했던 프론트 화면(localhost:5173)에 로그인 버튼이 아닌 user의 이름이 나오는 것을 볼 수 있다

 

이로써 SpringSecurity로 OAuth2 인증서버 만들기 작업이 완료됐다!


1. SpringSecurity로 OAuth2 인증서버 만들기 - 1 (권한서버 만들기)

2. SpringSecurity로 OAuth2 인증서버 만들기 - 2 (클라이언트서버 만들기)

3. SpringSecurity로 OAuth2 인증서버 만들기 - 3 (리소스서버 만들기)

4. SpringSecurity로 OAuth2 인증서버 만들기 - 4 (JWT payload 커스텀)

5. SpringSecurity로 OAuth2 인증서버 만들기 - 5 (Frontend) End.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
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