Blog-v3(리팩토링),양방향 매핑, 인라인 뷰, 테이블 쪼개기

양방향 매핑(Bidirectional Mapping), 인라인 뷰(Inline View)
홍윤's avatar
Sep 04, 2024
Blog-v3(리팩토링),양방향 매핑, 인라인 뷰, 테이블 쪼개기

1. 양방향 매핑(Bidirectional Mapping)

 
💡
양방향 매핑(Bidirectional Mapping)은 객체-관계 매핑(Object-Relational Mapping, ORM) 프레임워크(예: JPA, Hibernate)에서 두 엔티티 간의 관계를 양방향으로 설정하는 것을 의미합니다. 즉, 두 엔티티가 서로를 참조하도록 하는 것입니다. 이렇게 하면 엔티티 A에서 엔티티 B를 참조할 수 있고, 반대로 엔티티 B에서도 엔티티 A를 참조할 수 있습니다.

양방향 매핑의 필요성

양방향 매핑은 데이터베이스에서 외래 키(Foreign Key) 관계를 자바 객체의 관계로 변환할 때 유용합니다. 이를 통해 두 엔티티 간의 관계를 더 명확하게 만들고, 더 직관적으로 데이터를 다룰 수 있습니다. 예를 들어, UserOrder 엔티티 간의 관계를 양방향으로 설정하면, 특정 사용자의 모든 주문을 가져올 수 있고, 특정 주문에 연결된 사용자를 쉽게 참조할 수 있습니다.

1. 엔티티 클래스 정의

Reply.java
jpackage org.example.springv3.reply; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.example.springv3.board.Board; import org.example.springv3.user.User; import org.hibernate.annotations.CreationTimestamp; import java.sql.Timestamp; @Setter @Getter @Table(name = "reply_tb") @NoArgsConstructor @Entity public class Reply { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String comment; // 댓글 내용 @ManyToOne(fetch = FetchType.LAZY) private User user; //N포드 생각 @ManyToOne(fetch = FetchType.LAZY) private Board board; @CreationTimestamp // em.persist 할때 발동 private Timestamp createdAt; }
Board.java
//양방향 매핑 @OneToMany(mappedBy = "board") private List<Reply> replies;

2. 양방향 매핑 설정

  • @OneToMany 어노테이션:
    • 이 어노테이션은 Board 엔티티와 Reply 엔티티 간의 "1대 다" 관계(One-to-Many)를 정의합니다.
    • 여기서 "1대 다" 관계는 하나의 Board(게시글)가 여러 개의 Reply(댓글)을 가질 수 있음을 의미합니다.
  • mappedBy = "board":
    • mappedBy 속성은 양방향 관계에서 연관 관계의 주인이 아닌 쪽에서 사용됩니다.
    • mappedBy = "board"Reply 엔티티에서 board 필드가 이 관계의 주인임을 나타냅니다. 즉, Reply 엔티티에 board라는 필드가 있으며, 이 필드가 Board와의 관계를 정의하는 주체가 됩니다.
    • mappedBy가 설정된 엔티티는 주도권이 없는 엔티티이며, 실제로 관계를 관리하지 않습니다. 관계를 관리하는 것은 주인 쪽 엔티티(여기서는 Reply 엔티티의 board 필드)가 됩니다.
  • private List<Reply> replies;:
    • List<Reply> 타입의 replies 필드는 Board 엔티티와 관련된 모든 Reply 엔티티의 목록을 나타냅니다.
    • replies 필드는 해당 Board와 연결된 여러 개의 Reply를 나타내는 컬렉션입니다.
    • 이 컬렉션은 Board 엔티티를 기준으로 한 Reply 엔티티들을 가져오거나 설정하는 데 사용됩니다.

양방향 매핑의 장단점

장점

  • 직관적인 데이터 탐색: 엔티티 간의 관계를 양방향으로 설정하면, 양쪽에서 데이터를 탐색하는 것이 더 직관적이고 유연해집니다.
  • 상태 일관성 유지: 관계를 올바르게 설정하고 관리하면, 양쪽 엔티티의 상태가 항상 일관성을 유지합니다.

단점

  • 복잡성 증가: 양방향 매핑을 설정하면 관계를 설정하고 관리하는 로직이 복잡해질 수 있습니다.
  • 무한 루프 문제: 양방향 매핑 시, 직렬화(예: JSON 변환) 과정에서 양쪽 엔티티가 서로를 참조하여 무한 루프가 발생할 수 있습니다. 이를 방지하기 위해 @JsonIgnore 같은 어노테이션을 사용하여 직렬화를 피해야 하는 필드를 지정할 수 있습니다.

결론

양방향 매핑은 엔티티 간의 관계를 더 명확하게 하고, 양쪽에서 데이터를 쉽게 탐색할 수 있게 해줍니다. 그러나 이를 사용할 때는 관계의 복잡성 증가와 상태 관리에 주의해야 합니다. ORM을 사용하는 개발에서는 상황에 맞는 매핑 방식을 선택하는 것이 중요합니다.

 

2. 인라인 뷰(Inline View)

💡
  • 인라인 뷰(Inline View)는 SQL에서 사용되는 개념으로, 쿼리 내에 작성된 **서브쿼리(Subquery)**가 마치 테이블처럼 동작하도록 만드는 것을 말합니다. 즉, 인라인 뷰는 쿼리의 FROM 절에 포함된 서브쿼리를 통해 생성된 임시 테이블을 의미합니다.

인라인 뷰의 사용 목적

  1. 복잡한 쿼리 간소화:
      • 인라인 뷰를 사용하면 복잡한 SQL 쿼리를 작은 단계로 분리하여 간소화할 수 있습니다.
      • 여러 개의 서브쿼리나 복잡한 집계 쿼리를 단일 쿼리로 결합할 때 유용합니다.
  1. 일시적 데이터 집합 생성:
      • 특정 조건을 만족하는 일시적인 데이터 집합을 생성하여, 그 데이터를 기반으로 추가적인 필터링, 그룹핑, 또는 집계를 수행할 수 있습니다.
  1. 데이터 중복 제거:
      • 인라인 뷰를 사용하여 데이터 중복을 제거하거나 특정 계산 결과를 임시로 저장한 다음, 그 결과를 사용하여 메인 쿼리에서 추가적인 처리를 수행할 수 있습니다.

인라인 뷰의 예제

1.
SELECT * FROM ( SELECT bt.id, bt.title, bt.content, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id where bt.id = 5 );
notion image
2.
select * from ( SELECT bt.id, bt.title, bt.content, ut.id u_id, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id ) where u_id = 2;
notion image
  1. id와 보드 id 를 조인해서 찾아내기
SELECT * FROM ( SELECT bt.id, bt.title, bt.content, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id where bt.id = 5 ) t1 inner join reply_tb t2 on t1.id = t2.board_id;
notion image
  1. 2번을 찾는 결과
SELECT * FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id LEFT OUTER JOIN reply_tb rt on bt.id = rt.board_id LEFT OUTER JOIN user_tb rut on rut.id = rt.user_id where bt.id = 2;
notion image
  1. 결과(인라인 뷰로 할 거면 하나 하나씩 잡고 가야한다.)
SELECT * FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id INNER JOIN reply_tb rt on bt.id = rt.board_id INNER JOIN user_tb rut on rut.id = rt.user_id where bt.id = 5;
notion image

인라인 뷰의 장점

  • 코드 재사용 및 가독성 향상:
    • 인라인 뷰를 사용하면 서브쿼리의 결과를 재사용할 수 있으며, 복잡한 쿼리를 이해하기 쉽게 분할할 수 있습니다.
  • 성능 향상:
    • 특정 조건에 맞는 데이터를 미리 필터링하거나 그룹화하여 메인 쿼리에서 처리할 데이터 양을 줄일 수 있습니다.
  • 복잡한 논리적 작업 수행:
    • 하나의 쿼리에서 수행하기 어려운 복잡한 논리적 작업을 여러 단계로 나눠 수행할 수 있습니다.

인라인 뷰의 단점

  • 성능 저하 가능성:
    • 복잡한 인라인 뷰를 사용할 경우, 특히 큰 데이터셋에 대해 다중 레벨의 서브쿼리를 사용하면 성능이 저하될 수 있습니다.
  • 읽기 어려운 코드:
    • 지나치게 많은 인라인 뷰를 사용하면 쿼리가 복잡해져 가독성이 떨어질 수 있습니다.

결론

인라인 뷰는 SQL 쿼리를 더욱 강력하고 유연하게 만들어 주는 유용한 도구입니다. 이를 적절하게 사용하면 복잡한 데이터를 간단히 처리할 수 있고, 다양한 조건을 쉽게 적용할 수 있습니다. 그러나 성능과 가독성을 항상 고려하여 사용하는 것이 중요합니다.
💡
오류나는 이유는 SELECT보다 WHERE, FROM 이 먼저 실행되기 때문에 SELECT bt.id, bt.title, bt.content, ut.id u_id, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id where u_id = 2;

 

3.테이블 쪼개는 근거

💡

1. 테이블 쪼개는 근거

(1) 오브젝트로 표현해야 할 때 (테이블 쪼개기 - 정규화)
  • 새로운 필드를 추가하고 싶지만, 이를 하나의 오브젝트(객체)로 표현하는 것이 더 적합할 때 테이블을 쪼갭니다.
  • 예: 댓글 시스템에서 댓글 정보를 관리할 때, 하나의 테이블에 모든 댓글 정보를 넣는 대신, 댓글 테이블을 별도로 만듭니다.
    • 댓글 테이블: 댓글번호(PK), 댓글내용, 댓글시간, 댓글주인(user_id), 댓글 게시글 번호(board_id)
(2) 컬렉션으로 표현해야 할 때 (테이블 쪼개기)
  • 필드를 추가하려는 경우, 이 필드가 다수의 값을 가질 수 있는 컬렉션 형태라면, 테이블을 쪼개서 별도의 테이블로 관리합니다.
  • 예: 게시글에 여러 개의 댓글이 달릴 수 있기 때문에, 댓글을 게시글 테이블에 직접 넣지 않고 별도의 댓글 테이블로 관리합니다.

2. 연관관계

유저, 게시글, 댓글 간의 관계 설정:
  • 유저(1) - 게시글(N): 한 명의 유저가 여러 개의 게시글을 작성할 수 있으므로 1대N 관계입니다.
  • 유저(1) - 댓글(N): 한 명의 유저가 여러 개의 댓글을 작성할 수 있으므로 1대N 관계입니다.
  • 게시글(1) - 댓글(N): 한 개의 게시글에 여러 개의 댓글이 달릴 수 있으므로 1대N 관계입니다.

댓글 테이블의 필수 컬럼

  • PK: 댓글의 고유 식별자
  • user_id: 댓글을 작성한 유저의 ID (외래 키)
  • board_id: 댓글이 달린 게시글의 ID (외래 키)
  • comment: 댓글 내용
  • created_at: 댓글 작성 시간

컬렉션과 빌더 패턴

  • 컬렉션을 빌더 패턴에서 제외하는 이유:
    • 양방향 매핑에서 컬렉션을 관리하려면 관계의 일관성을 유지하기 위해 컬렉션 관리 메서드를 별도로 작성하는 것이 좋습니다.
    • 빌더 패턴을 사용해 엔티티를 생성할 때, 컬렉션을 초기화하거나 관리하는 로직은 별도로 처리하여 양방향 관계를 정확하게 설정합니다.
    • 이는 코드의 가독성을 높이고, 데이터베이스의 무결성을 유지하는 데 도움이 됩니다.

결론

테이블을 쪼개는 것은 데이터의 정규화와 관계형 데이터베이스의 구조를 최적화하는 중요한 과정입니다. 이를 통해 데이터 중복을 줄이고, 유지보수성과 성능을 향상시킬 수 있습니다. 유저, 게시글, 댓글 간의 1대N 관계를 설정하고, 양방향 매핑을 정확히 관리하기 위해서는 빌더 패턴에서 컬렉션을 별도로 관리하는 것이 좋습니다.
 
 
 
 
 
Share article

Uni