로그인 처리를 Controller에서 하지 않고 Spring Security에서 동작하도록 설정

.formLogin(form -> form
    .loginProcessingUrl("/login") //리액트에서 POST 요청을 보낼 주소
    .successHandler((request, response, authentication) -> {
        response.setStatus(HttpServletResponse.SC_OK); // 성공 시 200 OK 반환
    })
    .failureHandler((request, response, exception) -> {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 실패 시 401 반환
    })
)

 

리액트와 같은 클라이언트 사이드 렌더링 방식 에서는 서버가 HTML 파일을 반환하지 않고, 상태값으로 반환해야 한다.

 

접근 URL 별 다른 화면을 보여주기 위해 라우터 라이브러리 설치

npm install react-router-dom

 

 

리액트에서는 Route Path = "URL"를 통해 URL별 페이지 분기처리가 가능하다.

 

app.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Login from './Login';

function App() {
  return (
    <Router>
      <div style={{ padding: '20px' }}>
        <h1>Spring Boot, React 연동</h1>
        <nav>
           <a href="/login">로그인 이동</a> | <a href="/join">회원가입 이동</a>
        </nav>
        <hr />

	//URL 변경에 따라 화면 변경
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/main" element={<h2>메인 페이지입니다.</h2>} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

route에 의해 /login에 접근 시 Login.jsx 파일의 화면 실행

 

import { useState } from 'react';
import axios from 'axios';

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = (e) => {
    e.preventDefault(); // 폼 제출 시 페이지 새로고침 방지

    // 스프링 시큐리티는 기본적으로 x-www-form-urlencoded 형식을 기대한다.
    const formData = new FormData();
    formData.append('username', username);
    formData.append('password', password);

    axios.post('http://localhost:8080/login', formData, {
      withCredentials: true // 세션 쿠키를 받아오기 위해 필수
    })
    .then(res => {
        alert("로그인 성공!");
        window.location.reload(); // 성공 후 상태 업데이트를 위해 새로고침
    })
    .catch(err => {
        alert("로그인 실패: 아이디 또는 비밀번호를 확인하세요.");
    });
  };

  return (
    <div style={{ maxWidth: '300px', margin: '50px auto' }}>
      <h2>커스텀 로그인</h2>
      <form onSubmit={handleLogin}>
        <input
          type="text"
          placeholder="아이디"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          style={{ display: 'block', width: '100%', marginBottom: '10px' }}
        />
        <input
          type="password"
          placeholder="비밀번호"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          style={{ display: 'block', width: '100%', marginBottom: '10px' }}
        />
        <button type="submit" style={{ width: '100%' }}>로그인</button>
      </form>
    </div>
  );
}

export default Login;

 

로그인 시 전송 데이터 포맷을 FormData 로 하는 이유 :

스프링 시큐리티의 기본 필터인 UsernamePasswordAuthenticationFilter는 application/x-www-form-urlencoded 형식의 데이터를 기대한다.

그 이외의 타입이나, 변수명이 들어오면 값을 제대로 받지 못해 인증로직을 타지않는다.(별도의 인증 로직 구현 필요)

 

+ Recent posts