로그인 처리를 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 형식의 데이터를 기대한다.
그 이외의 타입이나, 변수명이 들어오면 값을 제대로 받지 못해 인증로직을 타지않는다.(별도의 인증 로직 구현 필요)
'프로젝트 끄적이기' 카테고리의 다른 글
| 8. Spring Boot + React 결합 (0) | 2026.01.29 |
|---|---|
| 7. 현재 로그인한 사용자 정보 활용 (0) | 2026.01.28 |
| 6. 로그인 session (0) | 2026.01.27 |
| 5.PasswordEncoder를 이용한 비밀번호 암호화 (0) | 2026.01.27 |
| 4. Spring Security 로그인 인증 방식 (0) | 2026.01.27 |