본문 바로가기

Spring Boot

[Spring Boot] JPA 연관 관계 설정하기

728x90

JPA를 사용하다 보면 여러 엔티티 간의 연관 관계를 매핑해야 할 때가 많습니다. 데이터베이스의 테이블 간 관계를 어떻게 매핑하고, 이를 엔티티 간 관계로 어떻게 설정할지에 따라 코드의 복잡성이 달라집니다. 특히, 실무에서는 엔티티와 DTO(Data Transfer Object)를 분리하여 사용하는 경우가 많기 때문에, 연관 관계를 관리할 때 여러 방법을 사용하게 됩니다. 이 글에서는 OneToMany, ManyToOne과 같은 JPA 연관 관계 매핑을 설정하고, DTO를 활용하여 데이터를 변환하는 방법을 소개하겠습니다.

 

 

1. JPA 연관 관계 매핑 기본 개념

OneToMany, ManyToOne의 개념

OneToMany와 ManyToOne 관계는 가장 기본적인 관계 중 하나로, 두 엔티티 간의 관계를 나타낼 때 사용됩니다.

  • OneToMany: 하나의 엔티티가 여러 엔티티와 관계를 가질 때 사용됩니다. 예를 들어, 하나의 게시글(Post)에 여러 개의 댓글(Reply)이 달릴 수 있는 구조입니다.
  • ManyToOne: 반대로 여러 엔티티가 하나의 엔티티와 관계를 가질 때 사용됩니다. 하나의 댓글은 하나의 게시글에만 달리므로 ManyToOne 관계를 사용합니다.

양방향 vs 단방향 매핑

JPA에서는 연관 관계를 양방향 또는 단방향으로 매핑할 수 있습니다.

  • 단방향 매핑: 한쪽 엔티티만 다른 엔티티를 참조합니다. 예를 들어, 게시글만 댓글을 참조하고, 댓글은 게시글을 참조하지 않는 경우입니다.
  • 양방향 매핑: 두 엔티티 모두 서로를 참조합니다. 게시글이 댓글을 참조하고, 댓글도 게시글을 참조하는 경우입니다.

예시 코드

@Entity
@Data
public class Post {

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

    private String title;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Reply> replies = new ArrayList<>();

}

@Entity
@Data
public class Reply {

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

    private String content;

    @ManyToOne
    @JoinColumn(name = "post_id")
    private Post post;

}

 

  • @OneToMany(mappedBy = "post"): 댓글이 게시글을 참조하는 관계를 나타냅니다.
  • @ManyToOne: 댓글이 하나의 게시글에만 속해 있음을 나타냅니다.
  • @JoinColumn(name = "post_id"): 데이터베이스에서 댓글이 게시글과 연결되는 외래 키입니다.

 

 

2. DTO를 사용한 연관 관계 관리

왜 DTO를 사용하는가?

DTO(Data Transfer Object)는 엔티티와 분리되어 데이터를 전송할 때 사용되는 객체입니다. 실무에서는 보안, 성능, 유지보수성을 위해 엔티티와 DTO를 분리하여 사용하는 것이 일반적입니다. 특히 연관 관계가 있는 엔티티를 JSON으로 직렬화할 때, 순환 참조(Circular Reference) 문제가 발생할 수 있기 때문에, DTO를 사용하여 이런 문제를 해결할 수 있습니다.

예시 코드: Post와 Reply의 DTO

public class PostDTO {

    private Long id;
    private String title;
    private List<ReplyDTO> replies;

}

public class ReplyDTO {

    private Long id;
    private String content;

}

 

 

DTO로 변환하는 방법

실무에서는 엔티티에서 DTO로 변환하거나 DTO를 엔티티로 변환하는 과정을 Converter를 통해 관리합니다.

public class PostConverter {

    public static PostDTO toPostDTO(Post post) {
        PostDTO postDTO = new PostDTO();
        postDTO.setId(post.getId());
        postDTO.setTitle(post.getTitle());

        List<ReplyDTO> replyDTOs = post.getReplies().stream()
            .map(ReplyConverter::toReplyDTO)
            .collect(Collectors.toList());

        postDTO.setReplies(replyDTOs);
        return postDTO;
    }

    public static Post toPostEntity(PostDTO postDTO) {
        Post post = new Post();
        post.setId(postDTO.getId());
        post.setTitle(postDTO.getTitle());

        List<Reply> replies = postDTO.getReplies().stream()
            .map(ReplyConverter::toReplyEntity)
            .collect(Collectors.toList());

        post.setReplies(replies);
        return post;
    }
}

public class ReplyConverter {

    public static ReplyDTO toReplyDTO(Reply reply) {
        ReplyDTO replyDTO = new ReplyDTO();
        replyDTO.setId(reply.getId());
        replyDTO.setContent(reply.getContent());
        return replyDTO;
    }

    public static Reply toReplyEntity(ReplyDTO replyDTO) {
        Reply reply = new Reply();
        reply.setId(replyDTO.getId());
        reply.setContent(replyDTO.getContent());
        return reply;
    }
}

 

장점

  1. 안전한 데이터 전송: 엔티티의 민감한 정보를 숨기고, 필요한 데이터만 전송할 수 있습니다.
  2. 유연성: 엔티티가 변경되어도 DTO는 그대로 유지하거나 최소한의 수정으로 유지할 수 있습니다.
  3. 순환 참조 문제 해결: 연관된 엔티티를 DTO로 변환해 전송할 때 발생하는 순환 참조 문제를 해결할 수 있습니다.

3. 정리

JPA를 사용해 엔티티 간의 연관 관계를 매핑하는 것은 애플리케이션의 데이터 모델을 설계하는 데 중요한 부분입니다. OneToMany, ManyToOne과 같은 관계를 설정하고, DTO를 사용하여 엔티티를 외부에 노출하지 않음으로써 보안성과 유지보수성을 높일 수 있습니다. Converter를 활용하면 엔티티와 DTO 간의 변환도 유연하게 처리할 수 있습니다.

728x90