React Spring Boot 연동 로그인_3. 백엔드 : User 도메인용 Entity

송지윤·2026년 1월 3일

React

목록 보기
14/15

JPA

JPA 는 자바 객체와 데이터베이스 테이블을 자동으로 매핑해주는 표준 명세(인터페이스)
인터페이스는 약속된 규격

Hibernate

하버네이트는 JPA 표준 명세를 구현한 대표적인 ORM 프레임워크
프레임워크는 뼈대 또는 구조

ORM(Object-Relational Mapping)

객체 지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 테이블을 자동으로 연결(매핑)해주는 기술 또는 도구

UserEntity 생성

package com.example.backend.domain.user.entity;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.example.backend.domain.user.dto.UserRequestDTO;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "user_user_entity")
// 이 엔티티가 어떤 테이블과 연관돼있는지 나타냄
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
	@Id
    @GeneratedValue(
			strategy = GenerationType.SEQUENCE,
			generator =  "user_seq_gen")
	@SequenceGenerator(
			name = "user_seq_gen",
			sequenceName = "USER_SEQ",
			allocationSize = 1)
	private Long id;
	
    @Column(name = "username", unique = true, nullable = false, updatable = false)
    private String username;

    @Column(name = "password", nullable = false)
    private String password;

    @Column(name = "is_lock", nullable = false)
    private Boolean isLock;

    @Column(name = "is_social", nullable = false)
    private Boolean isSocial;

    @Enumerated(EnumType.STRING)
    @Column(name = "social_provider_type")
    private SocialProviderType socialProviderType;

    @Enumerated(EnumType.STRING)
    @Column(name = "role_type", nullable = false)
    private UserRoleType roleType;

    @Column(name = "nickname")
    private String nickname;

    @Column(name = "email")
    private String email;

    @CreatedDate
    @Column(name = "created_date", updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    @Column(name = "updated_date")
    private LocalDateTime updatedDate;
    
    public void updateUser(UserRequestDTO dto) {
    	this.email = dto.getEmail();
    	this.nickname = dto.getNickname();
    }
}

@Entity

  • 이 클래스가 JPA 엔티티임을 선언
  • JPA가 이 클래스를 DB 테이블과 매핑해서 관리함
  • 반드시 기본 생성자(@NoArgsConstructor)가 필요

@Table(name = "user_user_entity")

  • 매핑될 테이블 이름 지정
  • 없으면 클래스명(UserEntity)이 테이블명이 됨

테이블명을 명시한 이유

  • 네이밍 규칙 통일
  • 기존 DB 테이블과 맞추기 위함

@Id

  • PK(Primary Key) 지정

@GeneratedValue(strategy = GenerationType.IDENTITY)

  • PK 자동 증가 전략
  • DB가 AUTO_INCREMENT / IDENTITY 방식으로 생성

-> MariaDB에서 쓰는 방식

MariaDB

id BIGINT NOT NULL AUTO_INCREMENT

ORACLE

CREATE SEQUENCE USER_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE;
	@Id
	@GeneratedValue(
			strategy = GenerationType.SEQUENCE,
			generator =  "user_seq_gen")
	@SequenceGenerator(
			name = "user_seq_gen",
			sequenceName = "USER_SEQ",
			allocationSize = 1)
	private Long id;
	// MariaDB
	// @GeneratedValue(strategy = GenerationType.IDENTITY)
	/* DB별 프로파일 분리해서 사용하는 방법
	 * @Profile("oracle")
	 * @GeneratedValue(strategy = GenerationType.SEQUENCE, ...)
	 * 
	 * @Profile("mariadb")
	 * @GeneratedValue(strategy = GenerationType.IDENTITY)
	 * */

@GeneratedValue - ID를 어떻게 만들 것인가
strategy = SEQUENCE -> Oracle SEQUENCE 사용
generator = "user_seq_gen" -> 아래 @SequenceGenerator의 이름

@SequenceGenerator

@SequenceGenerator - 어떤 SEQUENCE를 어떻게 쓸 것인가
JPA 내부에서 사용하는 SEQUENCE 설정 이름 name = "user_seq_gen"
@GeneratedValue(generator=...) 와 연결됨
DB랑은 무고나함
sequenceName = "USER_SEQ"
DB에 실제 존재하는 Oracle SEQUENCE 이름

SELECT USER_SEQ.NEXTVAL FROM DUAL;

이 SEQUENCE를 쓰겠다는 뜻

allocationSize

allocationSize = 1
-> Hibernate가 한 번에 미리 가져올 ID 개수

  • 매 INSERT마다 DB 왕복
  • 가장 직관적
  • 성능은 최적 아님

DB

CREATE SEQUENCE USER_SEQ
CACHE 50;

JPA

allocationSize = 50

반드시 맞춰줘야함 -> DB 왕복 횟수 감소
이렇게 사용하는 이유는 대량 INSERT 대응

  • 배치 처리
  • 로그 적재
  • 이벤트 저장

NOCACHE -> SEQUENCE 값을 미리 메모리에 올리지 않음

CREATE SEQUENCE USER_SEQ
NOCACHE;
  • 매번 디스크에서 값을 가져옴
  • 성능 떨어짐
  • 대신 ID 손실 없음

실무에서 NOCACHE를 쓰면 안되는 이유

  • 성능 저하 심각
  • 트래픽 많은 서비스에서 병목 발생
  • PK는 연속될 필요 없음 (유일성만 보장하면 됨)

@Column

괄호 안에 name 은 DB 컬럼명
nullable = false 는 NOT NULL 을 의미
unique = true 는 UNIQUE 제약
updateable = false 는 UPDATE 시 변경 불가

@Column(name = "username", unique = true, nullable = false, updatable = false)

이 설정은 username 절대 중복 불가, 생성 이후 변경 불가

@Enumerated(EnumType.STRING)

  • Enum 타입은 DB에 저장하는 방식 지정

STRING을 쓰는 이유

  • Enum 순서 바뀌어도 DB 데이터 안전
  • 가독성 좋음
    UserRoleType 으로 보면
public enum UserRoleType {
	USER, ADMIN
}

이렇게 돼있음 UserRoleType 을 변수 roleType 으로 꺼내서 roleType.USER 이렇게 사용하거나 roleType.ADMIN 으로 사용해서 USER 아니면 ADMIN이 DB에 STRING 형태로 들어가게 하기 위해 씀
0이나 1로 값을 구분하면 의미 파악 힘듦

SocialProviderType

@Getter
public enum SocialProviderType {

	NAVER("네이버"),
	GOOGLE("구글");
	
	private final String description;
	
	private SocialProviderType(String description) {
		this.description = description;
	}
}

소셜 로그인 제공자 종류를 제한된 값으로 정의

NAVER : 실제 코드에서 쓰는 값
"네이버" : 설명용 문자열(사람이 읽기 좋게)
Enum 상수 - NAVER
description - 네이버

private final String description;

각 Enum 값이 자기만의 설명을 하나씩 가진다.
final -> 한 번 정하면 변경 불가
Enum 상수마다 고정 값
소셜 로그인 제공자를 코드에서는 NAVER/GOOGLE로 쓰고, 화면에서는 네이버/구글로 보여주기 위한 설계

Auditing (생성/수정 시간 자동 관리)

@EntityListeners(AuditingEntityListener.class)
  • 엔티티의 생명주기 이벤트 감지
  • 생성/수정 시간 자동 세팅 가능

반드시 설정 필요

@EnableJpaAuditing

보통 SpringBootApplication에 추가

@CreatedDate

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
  • 엔티티 최초 저장 시 자동 입력
  • 이후 수정 불가

@LastModifiedDate

@LastModifiedDate
private LocalDateTime updatedDate;
  • 엔티티 수정될 때마다 자동 갱신

Lombok 어노테이션

@Getter

모든 필드에 대해 getter 자동 생성

@NoArgsConstructor

  • 기본 생성자 생성
  • JPA 엔티티 필수

@AllArgsConstructor

모든 필드를 받는 생성자 생성

@Builder

빌더 패턴 지원
-> 생성자 파라미터 순서 문제 해결 + 가독성 증가

SocialProviderType

package com.example.backend.domain.user.entity;

import lombok.Getter;

@Getter
public enum SocialProviderType {

	NAVER("네이버"),
	GOOGLE("구글");
	
	private final String description;
	
	private SocialProviderType(String description) {
		this.description = description;
	}
}

UserRoleType

package com.example.backend.domain.user.entity;

public enum UserRoleType {
	USER, ADMIN
}

UserRepository

package com.example.backend.domain.user.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.backend.domain.user.entity.UserEntity;

public interface UserRepository extends JpaRepository<UserEntity, Long> {
	
	Boolean existsByUsername(String username);
	Optional<UserEntity> findByUsernameAndIsLockAndIsSocial(String username, Boolean isLock, Boolean isSocial);
}

UserEntity를 DB에서 조회/저장하는 전용 창구
UserEntity <-> user_user_entity 테이블
기본 CRUD는 이미 JpaRepository가 제공
INSERT, UPDATE, DELETE, PK 조회, 조건 조회 자동
개발자가 SQL 안 씀

Boolean existsByUsername(String username);

자동 생성되는 SQL

SELECT
    CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END
FROM user_user_entity
WHERE username = ?;

있으면 true 없으면 false 회원가입 시 아이디 중복 체크
빠르고 가벼운 조회

Optional<UserEntity> findByUsernameAndIsLockAndIsSocial(String username, Boolean isLock, Boolean isSocial);

자동 생성되는 SQL

SELECT *
FROM user_user_entity
WHERE username = ?
  AND is_lock = ?
  AND is_social = ?;

반환 타입

Optional<UserEntity>

이유

  • 결과가 없을 수도 있음
  • null 직접 처리 x
  • Optional로 명시적으로 표현

JpaAuditingConfig

package com.example.backend.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {

}

Entity의 CreatedDate, LastModifiedDate를 위한 Config 등록
@CreatedDate, @LastModifiedDate 가 실제로 동작하게 만드는 스위치

@EnableJpaAuditing

JPA 엔티티의 생성/수정 시간을 Spring이 자동으로 채워주도록 활성화
이게 없으면 @CreatedDate, @LastModifiedDate 아무 일도 안함

Auditing이 동작하기 위한 3가지 조건

조건 1 : 설정 클래스 등록

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}

조건 2 : 엔티티에 리스너 등록

@EntityListeners(AuditingEntityListener.class)

조건 3 : 필드에 Auditing 어노테이션

@CreatedDate
@LastModifiedDate

@Configuration

이 클래스를 Spring 설정 클래스로 등록
Spring이 시작할 때 @EnableJpaAuditing 감지, Auditing 기능 활성화

0개의 댓글