게시판이 있고 그 게시판의 댓글이 있는 서버를 구축한다고 해보자.
그럼 우선 Table을 두개 만들어야한다. Post와 Comment 테이블을 만들어보자.
[POST]
Column Name | Data Type | Description |
id | BIGINT | 게시글 고유 ID (Primary Key) |
title | VARCHAR | 게시글 제목 |
content | VARCHAR | 게시글 내용 |
author | VARCHAR | 게시글 작성자 |
[COMMENT]
Column Name | Data Type | Description |
id | BIGINT | 댓글 고유 ID (Primary Key) |
content | VARCHAR | 댓글 제목 |
author | VARCHAR | 댓글 작성자 |
이렇게 테이블을 만들면 문제가 있다. 테이블 댓글 모두 잘 만들어지지만 특정 댓글이 어떤 게시글에 포함된건지 알 수 없다.
즉, 서로를 연결해주는 장치가 있어야한다.
이때 사용하는게 외래키(Foreign Key)이다.
외래키란? 두 테이블 간의 관계를 나타내는 열(Column)이다. 하나의 테이블에서 다른 테이블의 특정 데이터를 참조할 때 사용하는 키죠. 쉽게 말해, "이 데이터가 저 테이블의 어느 데이터와 연결되어 있어"라고 알려주는 역할
그래서 우리는 외래키를 Comment 테이블에 추가해줘서 Post 테이블의 ID 데이터를 참조해야한다.
[POST]
Column Name | Data Type | Description |
id | BIGINT | 게시글 고유 ID (Primary Key) |
title | VARCHAR | 게시글 제목 |
content | VARCHAR | 게시글 내용 |
author | VARCHAR | 게시글 작성자 |
[COMMENT]
Column Name | Data Type | Description |
id | BIGINT | 댓글 고유 ID (Primary Key) |
content | VARCHAR | 댓글 제목 |
author | VARCHAR | 댓글 작성자 |
post_id | BIGINT | 해당 댓글이 달린 게시물의 ID (Foreign Key) |
이렇게 테이블을 구성하고 앤티티는 아래와 같이 구성해주어야한다.
//[POST]
package noticeboard.spring_noticeboard.domain;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String author;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>(); //게시물에 달린 여러 댓글을 리스트로 관리
public Post() {}
public Post(Long id, String title, String content, String author) {
this.id = id;
this.title = title;
this.content = content;
this.author = author;
}
// 댓글 추가
public void addComment(Comment comment) {
comments.add(comment);
comment.setPost(this);
}
}
@OneToMany: "하나가 여러 개를 가질 수 있다"
- what: 게시물(Post)이 여러 개의 댓글(Comment)을 가질 수 있다는 것을 나타낸다.
- why: 데이터베이스에서는 하나의 게시물이 여러 개의 댓글과 연결될 수 있는데, 이를 객체 관계에서도 표현하기 위해 @OneToMany 어노테이션을 사용한다.
mappedBy = "post": 반대쪽 관계에서의 연결 설정
- what: mappedBy는 "이 관계가 어디에 의해 관리되는지"를 나타낸다. 여기서 mappedBy = "post"는 Comment 엔티티에 있는 private Post post; 필드를 참조하고 있다는 뜻이다.
- why: 두 객체(Post와 Comment)가 서로 연관된 상태에서 "누가 관계의 주인인지"를 정해야 한다.. 여기서 "주인"은 외래 키를 실제로 관리하는 쪽입니다. 댓글(Comment)에서 post라는 필드가 관계의 주인이 되며, 그 필드로 연결된 댓글들이 게시물에 달려 있다는 의미를 가진다.
- how:
- mappedBy = "post"를 통해서 댓글이 어떤 게시물에 속하는지를 Comment 엔티티 안의 post 필드를 통해 알 수 있게 해준다.
- 이렇게 하면, 게시물(Post)이 댓글을 직접 관리하는 것이 아니라, 댓글(Comment)이 자신이 달린 게시물을 알고 있는 구조가 된다.
//[COMMENT]
package noticeboard.spring_noticeboard.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
private String author;
public Comment() {}
public Comment(Long id, String content, String author) {
this.id = id;
this.content = content;
this.author = author;
}
@ManyToOne
@JsonIgnore //양방향 관계에서 발생하는 순환 참조 문제인 JSON 직력화중 무한 루프를 방지하는 어노테이션
@JoinColumn(name = "post_id")
private Post post;
}
ManyToOne: 여러 댓글이 하나의 게시물에 속할 수 있다
- what: 이 어노테이션은 댓글(Comment)이 여러 개일 수 있지만, 각 댓글은 반드시 하나의 게시물(Post)에 속한다는 것을 의미gks다. 즉, 여러 댓글이 하나의 게시물에 속할 수 있는 관계를 나타낸다.
- why: 예를 들어, 하나의 게시물에 여러 사람이 댓글을 달 수 있다. 그렇다면 각 댓글은 반드시 어느 하나의 게시물에 속해야 해야한다. **@ManyToOne**을 사용해 이 구조를 표현한다.
- easy:
- **하나의 게시물(Post)**에 **여러 개의 댓글(Comment)**이 달릴 수 있다.
- 하지만 **각 댓글(Comment)**은 **단 하나의 게시물(Post)**에만 속한다.
@JsonIgnore: 순환 참조 문제를 해결
- what: 이 어노테이션은 JSON 직렬화 중 발생할 수 있는 무한 루프 문제를 방지하기 위해 사용된다. 이 문제는 두 객체가 서로를 계속 참조하는 구조에서 발생할 수 있다.
- why:
- 게시물(Post) 객체는 **여러 댓글(Comment)**을 참조하고, 댓글 객체도 **게시물(Post)**을 참조하는 양방향 참조 관계에 있다.
- 이런 상황에서, 만약 JSON 형식으로 데이터를 반환할 때, 게시물이 댓글을 포함하고, 댓글이 다시 게시물을 참조하면 이 참조가 끝없이 반복되면서 무한 루프가 발생할 수 있다.
- @JsonIgnore는 이를 방지하기 위해, 댓글 객체의 post 필드를 JSON 응답에서 무시하게 만든다. 즉, 댓글의 정보는 보내지만, 그 댓글이 어느 게시물에 속하는지는 응답에 포함되지 않도록 한다.
@JoinColumn(name = "post_id"): 외래 키 설정
- what: 이 어노테이션은 댓글 테이블에서 게시물과의 관계를 설정하는 열(컬럼)을 지정한다. 즉, 댓글이 어느 게시물에 달려 있는지 표시하는 컬럼이 무엇인지 정의하는 것이다.
- **name = "post_id"**는 댓글 테이블에서 해당 댓글이 달린 게시물의 ID를 저장하는 컬럼 이름이 post_id임을 지정합니다. 이 post_id 컬럼은 게시물 테이블의 id와 연결된다.
- why: 댓글 테이블은 어떤 게시물에 달렸는지 알기 위해 게시물의 ID를 외래 키로 가져야 한다. 이때 **@JoinColumn(name = "post_id")**를 사용해서 댓글 테이블에서 이 외래 키가 저장될 컬럼 이름을 post_id로 지정한 것이다.
- easy:
- 댓글 테이블에서 각 댓글이 속한 게시물의 ID가 **post_id**라는 컬럼에 저장된다.
- 이 post_id 컬럼을 통해 댓글과 게시물 사이의 연결 관계를 유지할 수 있다.
private Post post;: 댓글이 속한 게시물
- what: 이 필드는 댓글이 어느 **게시물(Post)**에 속해 있는지를 가리키는 필드이다.
- why: 각 댓글은 반드시 하나의 게시물에 달려 있어야 한다. 그래서 댓글 객체는 자신이 달린 게시물을 기억해야 한다. Post post 필드를 통해 이 댓글이 속한 게시물을 참조할 수 있습니다.
- easy: 댓글 객체는 **"내가 달린 게시물이 무엇인지"**를 알고 있어야 하고, 이를 위해 Post post 필드를 사용합니다.
post_id와 Post post의 관계
- **post_id**는 데이터베이스에서 댓글이 어느 게시물에 속해 있는지를 숫자로 관리합니다.
- **Post post;**는 그 post_id를 기반으로 자바 코드에서 해당 게시물 객체와 연결됩니다.
- 즉, post_id는 데이터베이스에서 관계를 설정하고, Post post;는 그 데이터를 객체로 연결하여 댓글이 속한 게시물의 세부 정보에 접근할 수 있게 합니다.
'Server > Spring' 카테고리의 다른 글
스프링 데이터 JPA (0) | 2024.08.27 |
---|---|
회원 관리 - 전체 구조 (0) | 2024.08.27 |
JPA를 통해서 DB와 연결하기 (0) | 2024.08.26 |
H2 데이터 베이스 설치 및 테이블 생성 (0) | 2024.08.26 |
회원 관리 - 프론트) 3. 모든 회원 조회하기 (0) | 2024.08.25 |