ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코드로 배우는 스프링부트 웹 프로젝트 Day 12
    Spring/코드로 배우는 스프링부트 웹 프로젝트 2022. 9. 7. 21:57

    /* Annotation들은 스프링부트 프로젝트의 Annotation 정리 페이지에 따로 정리해두었습니다. */

     

    [ 연관관계와 관계형 데이터베이스 설계 ]

    데이터베이스상에서의 관계를 해석할 때는 항상 PK(Primary Key) 쪽에서 해석 & 반영하면 혼란을 줄일 수 있음

     

    [ PK로 설계, FK로 연관관계 해석 ]

    회원, 게시물, 댓글의 관계를 PK를 기준으로 설계하면 다음과 같음

     

    1. 회원이 있어야만 게시글 작성 가능 => 회원 테이블을 먼저 설계

    2. 게시글을 작성할 때는 특정 회원과의 관계를 설정해주어야 함

    3. 댓글은 게시글이 있어야만 작성 가능 => 게시글을 먼저 설계하고 댓글 테이블이 게시글을FK로 작성

    • 게시물은 회원과 다대일 관계 (N : 1)
    • 댓글은 게시물과 다대일 관계 (N : 1)

    JPA는 객체지향의 입장에서 이들의 관계를 보기 때문에 데이터베이스와  달리 아래와 같은 선택이 가능함

    • 회원 엔티티가 게시물 엔티티들을 참조하게 설정해야 하는가?
    • 게시물 엔티티에서 회원 엔티티를 참조하게 설정해야 하는가?
    • 회원, 게시물 엔티티 객체 양쪽에서 서로를 참조하게 설정해야 하는가?

     

    가장 추천하는 방법

    관계형 데이터베이스 모델링을 위주로 해서 구성하는 것이 편리함

    FK(외래키)를 사용하는 엔티티가 PK(주키)를 가진 엔티티를 참조하는 구조로 설계하면 데이터베이스와 동일한 구조가 되기 때문에 관계를 이해하는 것도 편하고 자동으로 테이블이 생성될 때도 유용함

     

     

    [ 예제 프로젝트의 생성 ]

     

    추가 라이브러리: Spring Boot DevTools, Lombok, Spring Data JPA

     

    build.gradle 에 의존성 추가

    implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client'
    implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-java8time'

     

    application.properties 파일에 JPA관련 설정 추가

    spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
    spring.datasource.url=jdbc:mariadb://localhost:12345/bootex
    spring.datasource.username=bootuser
    spring.datasource.password=bootuser
    
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.properties.hibernate.format_sql=true
    spring.jpa.show-sql=true
    
    spring.thymeleaf.cache=false

     

    [ 날짜/시간 처리 설정 ]

     

    entity/BaseEntity 클래스

    package org.zerock.board2.entity;
    
    import lombok.Getter;
    import org.springframework.data.annotation.CreatedDate;
    import org.springframework.data.annotation.LastModifiedDate;
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    
    import javax.persistence.Column;
    import javax.persistence.EntityListeners;
    import javax.persistence.MappedSuperclass;
    import java.time.LocalDateTime;
    
    @MappedSuperclass
    @EntityListeners(value = {AuditingEntityListener.class})
    @Getter
    public class BaseEntity {
    
        @CreatedDate
        @Column(name = "regdate", updatable = false)
        private LocalDateTime regDate;
    
        @LastModifiedDate
        @Column(name = "moddate")
        private LocalDateTime modDate;
        
    }

    @MappedSuperclass 어노테이션을 사용하여 테이블을 생성하지 않고, 자식 클래스에게 매핑 정보만 제공하도록 함

     

     

    [ 엔티티 클래스 추가 ]

     

    연관관계를 설정하는 것은 처음부터 설정하는 방식 보다는 엔티티 클래스들을 구성한 이후에 각 엔티티 클래스의 연관관계를 고민해서 설정하는 것이 더 수월함

     

    entity 패키지에 Member, Board, Reply 엔티티 클래스 추가

     

    [ 회원 엔티티 클래스 ]

     

    Member 클래스

    package org.zerock.board2.entity;
    
    import lombok.*;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @ToString
    public class Member extends BaseEntity{
        
        @Id
        private String email;
        
        private String password;
        
        private String name;
    }

     

    Member 클래스

    • 이메일 주소를 PK로 이용
    • 데이터베이스 설계에서도 PK만 가지고, FK를 사용하지 않기 떄문에 별도의 참조가 필요하지 않음

     

    Board 클래스

    package org.zerock.board2.entity;
    
    import lombok.*;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @ToString
    public class Board extends BaseEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long bno;
        
        private String title;
        
        private String content;
        
        // 작성자는 아직 처리하지 않음
    }

     

    Reply 클래스

    package org.zerock.board2.entity;
    
    import lombok.*;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @ToString
    public class Reply extends BaseEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long rno;
        
        private String text;
        
        private String replyer;
        
        //Board와 연관관계는 아직 작성하지 않음
    }

     

    [ @ManyToOne 어노테이션 ]

     

    앞으로 생성할 bard 테이블과 member 테이블에는 FK를 이용한 참조가 걸려 있게 됨

     => member쪽의 email을 board에서는 FK로 참조하는 구조

     

    JPA에서 관계를 고민할 때는 FK쪽을 먼저 해석해보면 편리함

     👉 FK를 사용하는 board쪽의 관계를 먼저 살펴봄

     

    board와 Member의 관계는 N : 1 의 관계

    👉 @ManyToOne을 적용

     

    @ManyToOne은 데이터베이스상에서 외래키의 관계로 연결된 엔티티 클래스에 설정함

     

    Board 클래스 수정

    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @ToString(exclude = "writer") // ToString 은 항상 exclude
    public class Board extends BaseEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long bno;
    
        private String title;
    
        private String content;
        
        @ManyToOne
        private Member writer; // 연관관계 지정
    }
    

     

    Reply 클래스 수정

    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @ToString(exclude = "board")
    public class Reply extends BaseEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long rno;
    
        private String text;
    
        private String replyer;
    
        @ManyToOne
        private Board board;
    }
    

     

    생성된 외래키 확인

     

     

    [ Repository 인터페이스 추가 ]

    각 엔티티에 맞는 Repository 인터페이스 추가

     

    MemberRepository 인터페이스

    package org.zerock.board2.repository;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.zerock.board2.entity.Member;
    
    public interface MemberRepository extends JpaRepository<Member, Long> {
    }
    

     

    BoardRepository 인터페이스

    package org.zerock.board2.repository;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.zerock.board2.entity.Board;
    
    public interface MemberRepository extends JpaRepository<Board, Long> {
    }

     

    ReplyRepository 인터페이스

    package org.zerock.board2.repository;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.zerock.board2.entity.Reply;
    
    public interface ReplyRepository extends JpaRepository<Reply, String> {
    }
    

     

     

Designed by Tistory.