Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

공부일기

[Spring Boot 입문] JPA와 Entity 관계 매핑 이해하기 (ManyToOne, JoinColumn, 외래키) 본문

스프링 부트/개인 프로젝트

[Spring Boot 입문] JPA와 Entity 관계 매핑 이해하기 (ManyToOne, JoinColumn, 외래키)

석새우 2026. 3. 8. 23:06

1. 왜 JPA를 공부해야 하는가

MiniSNS 프로젝트를 진행하면서 가장 큰 변화는 데이터베이스를 직접 SQL로 다루지 않게 되었다는 점이었다.

예전에는 데이터를 조회하려면 이런 SQL을 작성했다.

SELECT * FROM post WHERE id = 1;

하지만 Spring Boot와 JPA를 사용하면 이렇게 작성한다.

postRepository.findById(1L);

SQL을 직접 작성하지 않아도 데이터 조회가 가능하다.

이것이 바로 JPA(Java Persistence API)가 하는 일이다.


2. JPA란 무엇인가

Jakarta Persistence 공식 문서에서는 JPA를 다음과 같이 정의한다.

JPA는 Java 객체와 관계형 데이터베이스 간의 매핑을 관리하는 표준 API이다.
(Jakarta Persistence Specification)

즉 JPA의 핵심 목적은 다음과 같다.

Java 객체 ↔ 데이터베이스 테이블

이 매핑을 자동으로 처리하는 것이다.

예를 들어 MiniSNS에서 작성한 Entity를 보면 다음과 같다.

@Entity
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;
    private String content;
}

이 코드는 다음 테이블과 매핑된다.

post
----------------
id
title
content

즉 JPA는 Java 객체를 DB 테이블처럼 사용할 수 있게 해주는 기술이다.


3. Spring Data JPA와 Hibernate

Spring Boot 프로젝트에서는 보통 Spring Data JPA를 사용한다.

구조를 이해하면 다음과 같다.

  • JPA → 표준 인터페이스
  • Hibernate → JPA 구현체
  • Spring Data JPA → JPA 사용을 편하게 만든 Spring 라이브러리, 이게 바로 레포지토리!!

Spring 공식 문서에서도 Spring Data JPA는 JPA 기반 데이터 접근을 단순화하는 Repository 추상화를 제공한다고 설명한다.


4. Entity란 무엇인가

Entity는 데이터베이스 테이블과 매핑되는 Java 객체이다.

MiniSNS 프로젝트의 User Entity 예시를 보면 다음과 같다.

@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;

    private String username;
}

 

여기서 사용된 어노테이션은,,
 
어노테이션 의미
@Entity JPA Entity 선언
@Id 기본키
@GeneratedValue 자동 증가

5. Entity 관계 매핑

MiniSNS 프로젝트에서는 다음 관계가 존재한다.

User 1 --- N Post
Post 1 --- N Comment
User 1 --- N Comment
 

  • 한 명의 사용자는 여러 게시글을 작성할 수 있다.
  • 한 게시글에는 여러 댓글이 달릴 수 있다.

이런 관계를 Entity 관계 매핑이라고 한다.


6. ManyToOne 관계

MiniSNS의 Post Entity를 보면 다음 코드가 있다.

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
 

이 코드는 다음 의미를 가진다.

많은 Post → 하나의 User
 

즉 게시글은 하나의 사용자에게 속한다.

DB 테이블로 보면 다음 구조가 된다.

post
------------------
id
title
content
user_id

여기서 user_id는 외래키이다. user_id 라는 컬럼명을 가진놈이 생성되는것임.

 

JoinColumn이 하는 일

@JoinColumn(name = "user_id")
 

이 어노테이션은 외래키 컬럼 이름을 지정하는 역할을 한다.

post.user_id → users.id
 

관계를 연결한다.

 

만약 어노테이션을 쓰지 않으면 JPA가 자동으로 해달 엔티티의 id와 연결해서 컬럼이름을 만든다.


7. Lazy Loading

Entity 관계 매핑을 사용하면 또 하나 중요한 개념이 등장한다.

바로 Lazy Loading이다.

예를 들어 게시글을 조회한다고 가정하자.

Post post = postRepository.findById(1L);
 

이때 Post 객체에는 User가 포함되어 있다. (위에서 봤듯이 외래키로 연결되어있어서...)

 

하지만 실제로 User 데이터를 바로 가져오지 않는다.

필요할 때 가져온다.

예를 들어 다음 코드가 실행될 때

post.getUser().getUsername();
 

그때 User 데이터를 조회한다.

이 방식을 Lazy Loading이라고 한다.

필요할 때 데이터 조회
 

이다.

 

그래서 지연로딩 설정을 어케 하냐면, ManyToOne에 설정할 수 있다.

@ManyToOne(fetch = FetchType.LAZY)

패치타입을 LAZY로 두면 내가 해당 객체를 불러와야 할때만 객체를 불러올 수 있다.

반면 EAGER로 두면 그냥 무조오오건 가져오는것이므로 LAZY로 보통 놓는 편이다.


8. Lazy Loading이 중요한 이유

Lazy Loading이 중요한 이유는 성능 문제(N+1) 때문이다.

예를 들어 게시글 목록을 조회하면

List<Post> posts = postRepository.findAll();
 

다음 상황이 발생할 수 있다.

게시글 조회 1번
사용자 조회 N번
 

이 문제를 N+1 문제라고 한다.

 

Lazy Loading을 사용하면 N+1 문제가 해결될 것이라고 생각하기 쉽다.
하지만 Lazy Loading은 단지 조회 시점을 늦출 뿐이며, 실제로 연관 데이터를 반복 접근하면 여전히 N번의 쿼리가 발생한다.

 

MiniSNS 프로젝트에서도 이 문제를 해결하기 위해 다음 방법을 사용했다.

  • EntityGraph
  • DTO Projection

이 내용은 이후 글에서 자세히 다룰 예정이다.


9. 이번에 정리한 핵심

이번 글을 통해 정리한 핵심 개념은 다음과 같다.

1️⃣ JPA

Java 객체와 DB 테이블을 매핑하는 표준 API

2️⃣ Entity

DB 테이블과 매핑되는 Java 객체

3️⃣ ManyToOne

여러 Entity가 하나의 Entity를 참조하는 관계

4️⃣ JoinColumn

외래키 컬럼 지정

5️⃣ Lazy Loading

필요할 때 연관 데이터를 조회하는 방식