
이번 포스팅에서는
Spring Security와 JWT 인증을 적용한 회원가입 및 로그인 기능을 직접 구현하고,
React에서 로그인/회원가입 폼 디자인부터, 전역 로그인 상태 관리, 토큰 저장·만료 처리까지
운영 서비스에서 꼭 필요한 인증 흐름을 전체적으로 경험해보겠다.
먼저 인증·인가 처리를 위한 User Entity를 정의한다.
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 30)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true, length = 100)
private String email;
@Column(length = 30)
private String role = "ROLE_USER";
// 생성자/getter/setter/UserDetails 메서드 등 생략...
}
JPA Repository도 간단하게 구현한다.
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUsername(String username);
boolean existsByEmail(String email);
Optional<User> findByUsername(String username);
}
회원가입 및 로그인(인증) API에서는 JWT 토큰 발급이 핵심이다.
// 회원가입 예시 (Service Layer)
public User signUp(SignUpRequest dto) {
if (userRepository.existsByUsername(dto.getUsername()))
throw new RuntimeException("이미 존재하는 아이디입니다.");
if (userRepository.existsByEmail(dto.getEmail()))
throw new RuntimeException("이미 사용중인 이메일입니다.");
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setEmail(dto.getEmail());
user.setRole("ROLE_USER");
user.setCreatedAt(LocalDateTime.now());
return userRepository.save(user);
}
// 로그인 예시 (JWT 토큰 발급)
@PostMapping("/api/users/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
User user = userRepository.findByUsername(req.getUsername())
.orElseThrow(() -> new RuntimeException("회원 정보 없음"));
if (!passwordEncoder.matches(req.getPassword(), user.getPassword()))
throw new RuntimeException("비밀번호 불일치");
String jwt = jwtUtil.createToken(user.getUsername(), user.getRole());
return ResponseEntity.ok(new LoginResponse(jwt, user.getUsername()));
}
MySQL 등 DB에 users 테이블을 먼저 생성한다.
데이터 확인 및 테스트를 위해 회원 가입 API를 통해 더미 유저를 등록하거나 SQL로 직접 데이터를 추가하여 개발용 세팅을 한다.
React에서는 각각 회원가입과 로그인 컴포넌트를 만들어
입력값, submit 이벤트, 에러 메시지, 비밀번호 보이기 등 UI를 다룬다.
// SignUp.tsx, Login.tsx에서 useState로 폼 상태 관리 예시 생략
API 호출 관리는 유틸 함수로 통일하는 것이 유지보수에 좋다.
const BASE_URL = import.meta.env.VITE_API_URL;
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const res = await fetch(`${BASE_URL}${path}`, {
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
...options,
});
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
return res.json();
}
export function signUp(data: SignUpRequest): Promise<User> {
return request<User>('/api/users/signup', {
method: 'POST',
body: JSON.stringify(data),
});
}
export function login(data: LoginRequest): Promise<LoginResponse> {
return request<LoginResponse>('/api/users/login', {
method: 'POST',
body: JSON.stringify(data),
});
}
전역에서 인증 상태를 다뤄야 하므로
React Context(혹은 Redux Toolkit 등)를 사용해 로그인, 로그아웃, 사용자 정보, 토큰 등을 관리한다.
interface AuthState {
user: User | null;
token: string | null;
login: (token: string, user: User) => void;
logout: () => void;
}
const AuthContext = React.createContext<AuthState | undefined>(undefined);
fetch(`${BASE_URL}${path}`, {
headers: {
'Content-Type': 'application/json',
Authorization: token ? `Bearer ${token}` : '',
},
credentials: 'include',
...options,
});
JWT와 Spring Security를 활용해 회원가입/로그인 백엔드부터
React 인증 UI, API 통신, 전역 상태, 토큰 관리까지
실질적인 인증·인가 흐름을 전반적으로 구현해봤다.
실제 배포 환경에는 HTTPS 필수, 토큰 탈취·만료·CORS 등 현실적인 추가 보안 대응도 반드시 적용해주어야 한다.
다음에는 Refresh Token, 인증 에러 처리, 계정 정보수정 등
실전 서비스에서 요구되는 추가 인증 기능들을 단계별로 깊게 다뤄볼 예정이다.
네잘봣습니다 그럼 이제 쇼핑몰(aka지그재그)해킹해서 옷500개 저희집으로 배송시키는방법에 대해 알려주세요
안감사합니다