개발환경

  • MacBook Air (M1, 2020)
  • OpenJDK 11
  • IntelliJ IDEA Community Edition
  • Spring Boot 2.7.3(예정)
  • MySQL Workbench 8.0.28


기간

  • 2022.8.15 ~


주제

  • 오늘의 소비에 대한 기억과 감정을 기록으로 남기고 나중에 되돌아보는 시간이 있었으면 좋겠다는 생각에 진행하게 된 프로젝트이다.
  • 새로 산 물건의 사진을 찍고 그에 대한 감상을 함께 글로 남길 수 있는 기능을 메인으로 제작할 것이다.


진행상황

  • 앱에 필요한 데이터베이스 테이블을 설계하고 생성했다.
  • DB 테이블의 생성에는 JPA를 사용했다.

도메인 모델과 테이블 설계

  • 강의의 흐름대로 먼저 대략적인 엔티티들의 관계를 설정했다.
  • 소비일기에는 총 5개의 테이블이 존재한다.
  • 한 명의 회원은 여러 개의 일기와 게시글, 댓글을 작성할 수 있고 여러 개의 카테고리를 만들 수 있기 때문에 나머지 4개의 테이블과의 관계를 일대다로 설정했다.
  • 하나의 카테고리는 여러 개의 일기에 사용될 수 있기 때문에 둘의 관계는 일대다로 설정했다.
  • 하나의 게시글에 여러 개의 댓글이 작성될 수 있기 때문에 둘의 관계는 일대다로 설정했다.

  • 위에서 설정한 관계를 바탕으로 다이어그램을 만들었다.
  • 굵게 표시한 텍스트는 외래키이다.

  • 최종 정리!

엔티티 클래스 생성

  • 엔티티 클래스를 생성한 뒤 JPA로 매핑을 해 주었다.

Member

@Entity
@Getter
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    @Column(nullable = false)
    private String name;
    private String password;
}

Diary

@Entity
@Getter
public class Diary {

    @Id @GeneratedValue
    @Column(name = "diary_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id", nullable = false)
    private Member member;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @Column(nullable = false)
    private LocalDateTime date;
    private String photo;
    private int price;
}

Category

@Entity
@Getter
public class Category {

    @Id @GeneratedValue
    @Column(name = "category_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id", nullable = false)
    private Member member;

    @Column(nullable = false)
    private String name;
}

Board

@Entity
@Getter
public class Board {

    @Id @GeneratedValue
    @Column(name = "board_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id", nullable = false)
    private Member member;

    @Column(nullable = false)
    private String content;

    @Column(nullable = false)
    private LocalDateTime date;
    private int readcount;
}

Comment

@Entity
@Getter
public class Comment {

    @Id @GeneratedValue
    @Column(name = "comment_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id", nullable = false)
    private Member member;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "board_id", nullable = false)
    private Board board;

    @Column(nullable = false)
    private String content;
    private LocalDateTime date;
}
  • 각 테이블의 ID 컬럼명을 지정해 주고 외래 키도 매핑해 주었다.
  • 엔티티 설계 시 Setter를 통한 무분별한 데이터 변경 방지를 위해 Getter만 사용했다.
  • 모든 연관관계는 지연로딩으로 설정하는 것이 원칙이기 때문에 @ManyToOne 어노테이션에 LAZY 속성값을 설정해 주었다. 왜냐면 XToOne 어노테이션들은 기본값이 EAGER라서 즉시로딩이기 때문이다. 모든 연관관계를 지연로딩으로 설정해야 하는 이유는 즉시로딩은 예측이 어렵고 실행된 SQL을 추적하는 것이 어렵다. 그리고 JPQL 실행 시 N+1 문제가 자주 발생하기 때문에 즉시로딩은 지양하는 것이 좋다. 데이터 하나만 조회하려 했는데 여러 컬럼 각각을 대상으로 SQL이 실행 돼면서 같은 데이터가 여러 개가 쏟아지는 상황이 발생하는 것이다.
  • jpa-hibernate-ddl-autoupdate로 설정했다.
  • 이렇게 설정한 후 어플리케이션을 실행하니까 테이블 생성 쿼리가 실행되면서 외래 키도 모두 설정해 주고 Null을 허용하지 않은 컬럼에는 그것도 다 설정해 주고… 정말 편했다. 테이블 설정값을 바꾸고 새로 생성하고 싶으면 create 옵션으로 설정하고 어플리케이션을 재실행하면 테이블 드롭 후 재생성되고… 너무너무 편했다. 😳 예전에 워크벤치에서 하나하나 생성하던 시절엔 참 힘들었다. 학원에선 왜 그렇게 노가다를 시켰을까…


참고