02b Session·Service·Repository 📚 전체 맵 03a 동기·비동기·Ajax
Series 02 · Spring 기초 · 3/3

ENTITY
JPA
RELATION

@Entity 어노테이션 완전 해부 · @PrePersist·@PreUpdate 자동 시간 처리
@ManyToOne·@JoinColumn 연관관계 — DB 테이블과 Java 객체의 연결

02a Controller·Mapping·DTO 02b Session·Service·Repository 02c Entity·JPA·연관관계
01 Java 기초 02 Spring 기초 03 웹/통신 04 프로젝트 코드 05 심화 06 Security 07 JVM 08 DI
이 파일의 학습 목적
02c — @Entity · JPA · @ManyToOne — DB와 Java를 연결하는 방법

Board 테이블이 User 테이블을 참조하는데, Java 코드에서는 board.getUser().getNickname()처럼 객체로 접근한다. DB의 FK(외래키)와 Java의 객체 참조를 연결해주는 것이 JPA다. 02c는 Board·User Entity 구조와 @ManyToOne 연관관계를 완전히 이해하는 것이 목표다.

이 파일에서 배울 3가지
  • @Entity 어노테이션 완전 해부 — @Id, @GeneratedValue, @Column
  • @PrePersist · @PreUpdate — 저장/수정 시 자동 시간 입력
  • @ManyToOne · @JoinColumn — 게시글↔유저 연관관계
프로젝트 코드 연결
  • · Board.java — @ManyToOne User user
  • · board.getUser().getNickname() — toDto()에서
  • · deleteByUser(User user) — 유저 삭제 시 게시글 먼저
시리즈 순서: 02a·02b → 02c ← 지금 여기 → 03a 동기·비동기·Ajax → 03b CORS·axios
01 — @Entity 어노테이션 완전 해부
DB 테이블과 Java 클래스를 연결하는 JPA 핵심 어노테이션들
🗂️ DB 테이블 ↔ Java 클래스 매핑 JPA
📋 비유 — 엑셀 시트와 Java 클래스
DB 테이블 = 엑셀 시트 (행·열로 데이터 저장)
Entity 클래스 = 시트 한 행을 담는 Java 객체
@Entity 붙이면 → JPA가 "이 클래스는 DB 테이블이야" 인식 → 테이블 자동 생성
Java — 프로젝트 User Entity 전체 해부
@Entity // JPA: 이 클래스는 DB 테이블
@Table(name = "users") // DB 테이블명 지정 (없으면 클래스명 소문자)
@Getter // Lombok: getter 자동 생성
@NoArgsConstructor // Lombok: 기본 생성자 (JPA 필수)
public class User {

@Id // Primary Key 지정
@GeneratedValue(strategy = GenerationType.IDENTITY)
// AUTO_INCREMENT (DB가 자동 증가)
private Long id;

@Column(unique = true, nullable = false)
// UNIQUE NOT NULL 제약조건
private String username;

@Column(nullable = false) // NOT NULL
private String password;

private String nickname; // @Column 없으면 기본값 적용

private String role; // ROLE_USER / ROLE_ADMIN

private LocalDateTime createdAt;
}
DB 테이블 (users)
id BIGINT AUTO_INCREMENT PK
username VARCHAR UNIQUE NOT NULL
password VARCHAR NOT NULL
nickname VARCHAR
role VARCHAR
created_at DATETIME
Java Entity (User.java)
@Id Long id
@Column(unique) String username
@Column(nullable=false) String password
String nickname
String role
LocalDateTime createdAt
어노테이션역할SQL 대응
@Entity이 클래스는 DB 테이블CREATE TABLE
@Table(name="users")테이블명 지정CREATE TABLE users
@IdPrimary KeyPRIMARY KEY
@GeneratedValue자동 증가AUTO_INCREMENT
@Column(unique=true)중복 불가UNIQUE
@Column(nullable=false)null 불가NOT NULL
💡 @NoArgsConstructor가 Entity에 필수인 이유
JPA가 DB에서 데이터를 꺼낼 때 빈 객체를 먼저 만들고 값을 채움
→ 파라미터 없는 기본 생성자가 없으면 JPA가 객체를 만들 수 없어서 오류 발생
02 — @PrePersist · @PreUpdate
INSERT·UPDATE 직전에 자동 실행 — 생성시간·수정시간 자동 처리
JPA 라이프사이클 콜백 Lifecycle
📸 비유 — 자동 타임스탬프
사진 찍을 때 날짜·시간이 자동으로 찍히듯
@PrePersist = INSERT(저장) 직전에 자동 실행
@PreUpdate = UPDATE(수정) 직전에 자동 실행
매번 수동으로 시간 설정 안 해도 됨
Java — Board Entity @PrePersist·@PreUpdate
@Entity
public class Board {

private LocalDateTime createdAt;
private LocalDateTime updatedAt;

@PrePersist // INSERT 직전에 자동 실행
public void prePersist() {
this.createdAt = LocalDateTime.now(); // 생성 시간 자동 설정
this.updatedAt = LocalDateTime.now();
}

@PreUpdate // UPDATE 직전에 자동 실행
public void preUpdate() {
this.updatedAt = LocalDateTime.now(); // 수정 시간 자동 업데이트
}
}
Java — User Entity @PrePersist — 기본 role 설정
@Entity
public class User {

private String role;
private LocalDateTime createdAt;

@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
if (this.role == null)
this.role = "ROLE_USER"; // 기본 role 자동 설정
// 회원가입 시 role 안 넣어도 자동으로 ROLE_USER
}
}
💡 @PrePersist 활용 패턴
생성시간·수정시간 자동 설정 → 매번 LocalDateTime.now() 직접 안 써도 됨
기본값 설정 → role = "ROLE_USER" 자동 부여
Service 코드가 깔끔해짐
03 — @ManyToOne · @JoinColumn
게시글 N개 : 유저 1명 — 다대일 연관관계 · 지연로딩 vs 즉시로딩
🔗 연관관계 매핑 Relation
📝 비유 — 게시글과 작성자
게시글 여러 개(N) : 유저 한 명(1) = 다대일(ManyToOne) 관계
DB에서는 board.user_id 컬럼으로 연결
Java에서는 board.getUser()로 User 객체 직접 접근 가능
DB 테이블 관계
-- board 테이블
id | title | user_id
1 | 안녕 | 1 ← user_id로 연결
2 | 반가워| 1
3 | 질문 | 2

-- users 테이블
id | username
1 | hong
2 | kim
Java 객체 관계
public class Board {
@ManyToOne
@JoinColumn(name="user_id")
private User user; // 객체로!
}

// 사용할 때
board.getUser().getNickname();
// SQL JOIN 자동 실행
Java — Board Entity @ManyToOne 전체 코드
@Entity
public class Board {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String title;
private String content;

@ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 (성능 권장)
@JoinColumn(name = "user_id") // DB 컬럼명 지정
private User user;
// board.getUser() 호출 시 JPA가 자동으로
// SELECT * FROM users WHERE id = board.user_id 실행

private LocalDateTime createdAt;

@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
}
}
FetchType동작권장 여부
FetchType.LAZYgetUser() 호출할 때 그때서야 DB 조회 — 필요할 때만✅ 권장 (성능 좋음)
FetchType.EAGERBoard 조회할 때 User도 같이 조회 — 항상⚠️ N+1 문제 발생 가능
⚠️ N+1 문제란?
게시글 100개 조회 시 EAGER면 → 게시글 1번 조회 + User 100번 조회 = 101번 쿼리
LAZY면 → 필요할 때만 User 조회
항상 LAZY 기본으로 쓰고, 필요할 때 fetch join으로 해결
→ 자세한 내용은 09 JPA 완전 이해에서 다룸
Spring 기초 시리즈 — 전체 정리
02a · 02b · 02c — 이 구조가 1차·2차·3차 프로젝트 전체의 뼈대
02a
Controller·Mapping·DTO
@Controller vs @RestController
URL 매핑 어노테이션
DTO 보안·최적화
02b
Session·Service·Repository
HttpSession 로그인 흐름
3계층 역할 분리
JpaRepository 자동 쿼리
02c
Entity·JPA·연관관계
@Entity 어노테이션 해부
@PrePersist 자동 시간
@ManyToOne 연관관계
흐름 — 02 시리즈 전체 연결 — 게시글 작성 요청 처리
// 1. React → POST /api/board/write (JSON)

// 2. Controller 수신 (02a)
@PostMapping("/api/board/write")
public ResponseEntity<?> write(@RequestBody BoardDto dto) {
boardService.write(dto); // Service 위임 (02b)
return ResponseEntity.ok("success");
}

// 3. Service 처리 (02b)
public void write(BoardDto dto) {
Board board = new Board();
board.setTitle(dto.getTitle());
board.setUser(loginUser); // 연관관계 설정 (02c)
boardRepository.save(board); // Repository 저장 (02b)
// @PrePersist 자동 실행 → createdAt 자동 설정 (02c)
}

// 4. Entity → DB (02c)
// INSERT INTO board (title, user_id, created_at) VALUES (?, ?, NOW())
02b Session·Service·Repository 📚 전체 맵 03a 동기·비동기·Ajax