야침차게 시작한 사이드 프로젝트 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 - 다 갈아 엎었다.ㅎㅎ
'개발 > Field-Passer 프로젝트' 카테고리의 다른 글
[Field-Passer 프로젝트] 쿼리 최적화 (N + 1 문제 해결하기) (0) | 2023.02.04 |
---|---|
[Field-Passer 프로젝트] Spring Data JPA 페이징 처리하기 (0) | 2023.02.03 |
[Field-Passer 프로젝트] SpringBoot 로그인 시 예외 처리하기 (0) | 2023.01.30 |
[Field-Passer 프로젝트] Rest API에서 JSON 반환하기 (1) | 2023.01.29 |
[Field-Passer 프로젝트] 패키지 구조 결정하기 (0) | 2023.01.27 |
댓글