[React + Spring Boot] 쇼핑몰 클론 코딩 : Part 5. 회원가입 & 로그인 기능 구현

이지연·2025년 11월 8일
post-thumbnail

개요

이번 포스팅에서는
Spring Security와 JWT 인증을 적용한 회원가입 및 로그인 기능을 직접 구현하고,
React에서 로그인/회원가입 폼 디자인부터, 전역 로그인 상태 관리, 토큰 저장·만료 처리까지
운영 서비스에서 꼭 필요한 인증 흐름을 전체적으로 경험해보겠다.


엔티티 및 레퍼지토리 생성 및 프론트 API 연동

Spring Boot Entity & Repository 생성

먼저 인증·인가 처리를 위한 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 메서드 등 생략...
}
  • 사용자 정보(id, username, password, email, role 등)가 필수 필드로 구성된다.
  • role은 "ROLE_USER"를 기본값으로 설정하고, 추후 권한 구분에 활용할 수 있다.
  • password는 보안을 위해 반드시 해시 처리 후 저장해야 한다.

JPA Repository도 간단하게 구현한다.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    boolean existsByUsername(String username);
    boolean existsByEmail(String email);
    Optional<User> findByUsername(String username);
}
  • username, email로 중복 확인 및 사용자 조회 메서드를 미리 정의해둔다.

JWT 기반 회원가입/로그인 API 구현

회원가입 및 로그인(인증) 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에서 API 호출해 데이터 받아오기 - 회원가입/로그인/인증 연동

1. 회원가입 및 로그인 폼 UI 구현

React에서는 각각 회원가입과 로그인 컴포넌트를 만들어
입력값, submit 이벤트, 에러 메시지, 비밀번호 보이기 등 UI를 다룬다.

// SignUp.tsx, Login.tsx에서 useState로 폼 상태 관리 예시 생략
  • 각 폼에서는 입력값을 useState, 오류도 useState로 컨트롤하고
  • 제출시 각각 signup, login API로 직결된다

2. API 호출 유틸 함수 및 인증 함수 분리

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),
  });
}
  • JWT 토큰이 응답될 경우, 추후 로컬/세션스토리지 또는 상태관리 컨텍스트에 저장한다.

3. 로그인 상태 전역 관리 (Context or Redux)

전역에서 인증 상태를 다뤄야 하므로
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);
  • 로그인 성공 시 token과 user 저장,
  • 로그아웃하면 상태 초기화 및 토큰 삭제.

4. 토큰 저장 및 만료 처리 로직

  • JWT 토큰은 localStorage, sessionStorage, in-memory 중 적합한 방법을 선택해 저장한다.
  • 만료 감지 시 자동 로그아웃, 혹은 백엔드에서 401 응답을 받으면 강제 로그아웃 처리를 한다.
  • 인증이 필요한 API 요청에는 Authorization 헤더에 Bearer 토큰을 항상 포함해야 한다.
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, 인증 에러 처리, 계정 정보수정 등
실전 서비스에서 요구되는 추가 인증 기능들을 단계별로 깊게 다뤄볼 예정이다.

profile
Eazy하게

1개의 댓글

comment-user-thumbnail
2025년 12월 11일

네잘봣습니다 그럼 이제 쇼핑몰(aka지그재그)해킹해서 옷500개 저희집으로 배송시키는방법에 대해 알려주세요
안감사합니다

답글 달기