본문 바로가기
개발/Field-Passer 프로젝트

[Field-Passer 프로젝트] JPA 사용해서 Entity 설계하기

by 코코의 주인 2023. 1. 29.

야침차게 시작한 사이드 프로젝트 Field-Passer의 개발 첫 삽으로 Entity를 설계해보도록 하겠다.


1. memeber 도메인 - 회원 서비스

1) Member 엔티티 <-> MEMBER 테이블

MEMBER 테이블은 서비스의 근간이 되는 테이블이라고 생각한다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "MEMBER")
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Integer memberId;	// ----------> ①

    @Column(name = "EMAIL")
    private String email;

    @Column(name = "PASSWORD")
    private String password;

    @Column(name = "MEMBERNAME")
    private String memberName;

    @Column(name = "PROFILE_IMG")
    private String profileImg;
    
    @Column(name = "PRIVILEGE")
    private byte privilege;	// ----------> ②

    @Column(name = "AUTHORITY")
    private byte authority;	// ----------> ③

    @Column(name = "SIGNUP_DATE")
    private LocalDateTime signUpDate;

    @Column(name = "VISIT_COUNT")
    private Integer visitCount;

    @Column(name = "DELETE_CHECK")
    private byte delete;

    @OneToOne(mappedBy = "member", fetch = FetchType.LAZY)
    private Admin admin;


    public String convertPrivilege() {	// ----------> ④
        if (this.privilege == 0) {
            return "일반 회원";
        } else {
            return "관리자";
        }
    }

    public String convertAuthority() {	// ----------> ⑤
        if (this.authority == 0) {
            return "이메일 인증 전";
        } else {
            return "인증 완료";
        }
    }

    public void promote() {	// ----------> ⑥
        this.privilege = 1;
    }
}

①번 : DB 설계할 때 INT형을 사용했다. BIGINT를 사용했으면 Entity에서도 Long으로 처리했겠지만 굳이 그럴 필요 없어 보여서 Integer로 자료형을 선택했다. 다음부터는 DB를 짤 때 메모리를 아끼지 않고 BIGINT로 해야겠다.

 

②, ③번 : DB 설계에서 좀 아쉬운 점인데 저 부분을 BOOLEAN으로 처리했다. VARCHAR()로 선언하고 아래와 같은 방법으로 @Enumerated(EnumType.STRING)으로 처리했으면 좋았을 거 같다는 생각이 든다.

@Column(name = "PRIVILEGE")
@Enumerated(EnumType.STRING)
private Privilege privilege;

@Column(name = "AUTHORITY")
@Enumerated(EnumType.STRING)
private Authority authority;


public enum Authority {
    인증_전, 인증_완료
}

public enum Privilege {
    USER, ADMIN
}

④, ⑤번 : ②, ③번에서 받아온 값을 문자열로 바꿔주려고 만든 메서드다.

 

⑥번 : Admin이랑 1대1 매칭이라 넣었다. 자세한 설명은 아래에서 한다.


2. admin 도메인 - 관리자 페이지

1) Admin 엔티티 <-> ADMINLIST 테이블

ADMINLIST 테이블이랑 매칭되는 Entity다.

MEMBER 테이블이랑 1 : 1 매칭된다.

 전통적인 DB 설계에서는 저렇게 하는 게 맞지만 요즘은 개발자 편의를 위해 MEMBER테이블에 ADMIN_ID를 FK로 넣어두는 것도 많이 한다고 한다. 우리도 사실 관리자 리스트만 따로 조회하는 경우는 거의 없고 대부분 MEMBER 테이블을 조회해서 해당 객체가 관리자인지를 보는 게 대부분이라 저렇게 하는게 맞았을 거 같다.

 근데 관리자를 가정하고 멤버 정보를 추가로 조회하는 거라 큰 상관은 없으려나? 일단 1대1 양방향 매칭 해뒀다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "ADMINLIST")
public class Admin {

    @Id
    @Column(name = "ADMIN_ID")
    private int adminId;

    @Column(name = "PROMOTE_DATE")
    private LocalDateTime promoteDate;

    @OneToOne(fetch = FetchType.LAZY)	// ----------> ①
    @JoinColumn(name = "ID")
    private Member member;

    public void promote() {	// ----------> ②
        member.promote();
    }
}

①번 :  FK를 ADMINLIST에 넣어뒀기 때문에 Admin Entity에서 Join해줬다. 연관관계의 주인은 FK를 가진 객체로 하는 것이 좋다.

 

②번 : 양방향 연관관계 편의 메서드를 작성했다. Admin이 새로 등록될 때 연관관계로 이어진 member 객체의 previlege 값을 변경해줘야 하기 때문이다.


3. support 도메인 - 고객 센터

1) Punish 엔티티 <-> PUNISH_LIST 테이블

접수된 신고 내역을 토대로 이용 정지된 사용자를 관리하는 테이블인 PUNISH_LIST 테이블과 연결되는 Entity다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "PUNISH_LIST")
public class Punish {

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "PUNISH_LIST")
public class Punish {

    @Id
    @GeneratedValue
    @Column(name = "PUNISH_ID")
    private int punishId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "REPORT_ID", referencedColumnName = "REPORT_ID")
    private Report report;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ADMIN_ID", referencedColumnName = "ID")
    private Member admin;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TARGET_ID", referencedColumnName = "ID")
    private Member target;

    @Embedded
    private PunishPeriod punishPeriod;	// ----------> ①

}

①번 : 정지 기간은 임베디드 타입으로 선언하면 좋을 거 같아서 적용했다.

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Embeddable
public class PunishPeriod {

    @Column(name = "JUDGE_DATE")
    @Temporal(TemporalType.TIMESTAMP)
    private Date judgeDate;

    @Column(name = "RELEASE_DATE")
    @Temporal(TemporalType.TIMESTAMP)
    private Date releaseDate;
}

2) Question 엔티티 <-> QUESTIOIN 테이블

사용자가 남긴 문의 사항을 관리하는 QUESTION 테이블이랑 연결되는  Entity다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "QUESTION")
public class Question {

    @Id
    @GeneratedValue
    @Column(name = "QUESTION_ID")
    private int questionId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ID")
    private Member member;

    @Column(name = "TITLE")
    private String title;

    @Column(name = "CATEGORY")
    @Enumerated(EnumType.STRING)
    private QuestionCategory questionCategory;

    @Column(name = "REGISTER_DATE")
    private LocalDateTime registerDate;

    @Column(name = "UPDATE_DATE")
    private LocalDateTime updateDate;

    @Column(name = "PROCCESS")
    @Enumerated(EnumType.STRING)
    private QuestionProccess questionProccess;
}
public enum QuestionCategory {
    거래_관련, 서비스_관련, 계정_관련
}

public enum QuestionProccess {
    답변_전, 답변_완료
}

3) Answer 엔티티 <-> ANSWER 테이블

사용자가 남긴 문의 사항을 관리하는 QUESTION 테이블이랑 연결되는  Entity다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "ANSWER")
public class Answer {

    @Id
    @GeneratedValue
    @Column(name = "RESPONSE_ID")
    private int responseId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ID")
    private Member adminId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "QUESTION_ID")
    private Question question;

    @Column(name = "TITLE")
    private String title;

    @Column(name = "CONTENT")
    private String content;

    @Column(name = "REGISTER_DATE")
    private LocalDateTime registerDate;
}

4) Report 엔티티 <-> REPORT 테이블

신고된 회원 목록을 관리하는 REPORT 테이블과 매칭된 Entity다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "Report")
public class Report {

    @Id
    @GeneratedValue
    @Column(name = "REPORT_ID")
    private int reportId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName = "ID")
    private Member reporterMember;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName = "ID")
    private Member targetMember;

    @Column(name = "CATEGORY")
    @Enumerated(EnumType.STRING)
    private ReportCategory reportCategory;

    @Column(name = "CONTENT")
    private String content;

    @Column(name = "REPORT_DATE")
    @Temporal(TemporalType.TIMESTAMP)
    private Date reportDate;

    @Column(name = "PROCCESS")
    @Enumerated(EnumType.STRING)
    private ReportProcess reportProcess;
}

4. post 도메인 - 게시판

1) Post 엔티티 <-> POST 테이블

접수된 신고 내역을 토대로 이용 정지된 사용자를 관리하는 테이블인 PUNISH_LIST 테이블과 연결되는 Entity다.

컬럼이 너무 많아서 만들면서 제일 힘들었다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "POST")
@Where(clause = "DELETE_CHECK = 0 AND BLIND = 0")
public class Post {
    @Id
    @Column(name = "POST_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int postId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ID")
    private Member member;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CATEGORY_ID")
    private Category category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "DISTRICT_ID")
    private District district;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "STADIUM_ID")
    private Stadium stadium;

    @Column(name = "TITLE", nullable = false)
    private String title;

    @Column(name = "CONTENT", nullable = false)
    private String content;

    @CreationTimestamp
    @Column(name = "REGISTER_DATE")
    private LocalDateTime registerDate;

    @UpdateTimestamp
    @Column(name = "UPDATE_DATE")
    private LocalDateTime updateDate;

    @Column(name = "DELETE_DATE")
    private LocalDateTime deleteDate;

    @Column(name = "START_TIME", nullable = false)
    private LocalDateTime startTime;

    @Column(name = "END_TIME", nullable = false)
    private LocalDateTime endTime;

    @Column(name = "IMAGE_URL", nullable = false)
    private String imageUrl;

    @Column(name = "TRANSACTION_STATUS")
    @Enumerated(EnumType.STRING)
    private TransactionStatus transactionStatus;

    @Column(name = "PRICE", nullable = false)
    private int price;

    @Column(name = "VIEW_COUNT", nullable = false)
    private Integer viewCount;

    @Column(name = "WISH_COUNT", nullable = false)
    private Integer wishCount;

    @Column(name = "BLIND" , columnDefinition = "TINYINT(1) DEFAULT 0", length = 1)
    private int blind;

    @Column(name = "DELETE_CHECK", columnDefinition = "TINYINT(1) DEFAULT 0", length = 1)
    private int deleteCheck;

    @PrePersist
    public void prePersist() {
        this.viewCount = this.viewCount == null ? 0 : this.viewCount;
        this.wishCount = this.wishCount == null ? 0 : this.wishCount;
    }
}

 


2) Stadium 엔티티 <-> STADIUM_LIST 테이블

접수된 신고 내역을 토대로 이용 정지된 사용자를 관리하는 테이블인 PUNISH_LIST 테이블과 연결되는 Entity다.

컬럼이 너무 많아서 만들면서 제일 힘들었다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "STADIUM_LIST")
public class Stadium {

    @Id
    @Column(name = "STADIUM_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int stadiumId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "CATEGORY_ID")
    private Category category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "DISTRICT_ID")
    private District district;

    @Column(name = "STADIUM_NAME")
    private String stadiumName;

    @Column(name = "LATITUDE")
    private String latitude;

    @Column(name = "LONGITUDE")
    private String longitude;

    @Column(name = "PHONE")
    private String phone;

    @Column(name = "IMAGE_URL")
    private String defaultImageUrl;

    @OneToMany(mappedBy = "stadium")
    private List<Post> postList = new ArrayList<>();
}

3) Category 엔티티 <-> CATEGORY 테이블

접수된 신고 내역을 토대로 이용 정지된 사용자를 관리하는 테이블인 PUNISH_LIST 테이블과 연결되는 Entity다.

컬럼이 너무 많아서 만들면서 제일 힘들었다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "CATEGORY_LIST")
public class Category {

    @Id
    @Column(name = "CATEGORY_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int categoryId;

    @Column(name = "CATEGORY")
    private String categoryName;

    @OneToMany(mappedBy = "category")
    private List<Post> postList = new ArrayList<>();

    @OneToMany(mappedBy = "category")
    private List<Stadium> stadiumList = new ArrayList<>();
}

4) District 엔티티 <-> DISTRICT 테이블

접수된 신고 내역을 토대로 이용 정지된 사용자를 관리하는 테이블인 PUNISH_LIST 테이블과 연결되는 Entity다.

컬럼이 너무 많아서 만들면서 제일 힘들었다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@Entity
@Table(name = "DISTRICT_LIST")
public class District {

    @Id
    @Column(name = "DISTRICT_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int districtId;

    @Column(name = "DISTRICT")
    private String districtName;

    @OneToMany(mappedBy = "district")
    private List<Post> postList = new ArrayList<>();

    @OneToMany(mappedBy = "district")
    private List<Stadium> stadiumList = new ArrayList<>();
}

일단은 이렇게 설계했는데 내 실수가 발견되지 않는 이상 수정할 일은 없을 거 같다.

 

2023.02.03 - 다 갈아 엎었다.ㅎㅎ

댓글