이번 글에서는 MyBoard 프로젝트의 도메인 모델을 어떻게 설계했는지,
그 과정에서 어떤 고민을 했고 어떤 기준으로 구조를 결정했는지 정리해보려 합니다.
MyBoard는 게시판 서비스로서 다음과 같은 주요 도메인으로 구성되어 있습니다.
User
: 사용자 계정 정보를 관리Article
: 게시글 정보 (제목, 내용, 작성자 등)Comment
: 댓글 정보 (내용, 작성자, 게시글 참조)ArticleLike
, CommentLike
: 좋아요 기록을 위한 조인 테이블현재는 User
와 Article
, Comment
간에 양방향 연관관계를 사용하고 있습니다.
// User
@OneToMany(mappedBy = "user")
private List<Article> articles;
// Article
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
이 구조를 선택한 이유는 다음과 같습니다:
다만, 양방향 연관관계는 아래와 같은 점을 주의해야 합니다:
@JsonManagedReference
, @JsonBackReference
또는 ModelMapper
설정이 필요현재는 JPA 기본 사용법을 익히고 연관 탐색을 실습하는 목적에서 양방향을 적용했지만,
필요에 따라 단방향으로 리팩토링할 계획도 가지고 있습니다.
ArticleLike
, CommentLike
는 별도의 조인 테이블로 분리했습니다.
(article_id, user_id)
/ (comment_id, user_id)
복합 유니크 제약 조건을 걸어 중복 방지이렇게 설계하면 다음과 같은 이점이 있습니다:
추가로, 성능을 고려하여 likeCount 필드를 도메인에 따로 두었습니다.
@Column(nullable = false)
private long likeCount = 0L;
이는 좋아요 수를 매번 조인해서 계산하기보다,
실시간으로 증가/감소시키는 방식을 통해 빠르게 조회할 수 있도록 하기 위함입니다.
엔티티 생성 시 단순 생성자 대신 정적 팩토리 메서드를 사용했습니다.
public static User create(String email, String password, String username) {
return User.builder()
.email(email)
.password(password)
.username(username)
.build();
}
public static Article create(String title, String content, User user) {
return Article.builder()
.title(title)
.content(content)
.author(user.getUsername())
.user(user)
.likeCount(0L)
.build();
}
정적 팩토리 메서드는 객체 생성의 명확한 의도를 전달할 수 있고,
추가적인 유효성 검사나 로직 삽입이 용이한 장점이 있습니다.
모든 엔티티에 공통으로 필요한 createdAt
, updatedAt
필드를
@MappedSuperclass
로 정의된 BaseTimeEntity
를 상속하여 관리하고 있습니다.
@Getter
@MappedSuperclass
public class BaseTimeEntity{
@Column(nullable = false)
private LocalDateTime createdAt;
@Column(nullable = false)
private LocalDateTime updatedAt;
@PrePersist
public void onCreate(){
this.createdAt = LocalDateTime.now();
this.updatedAt = this.createdAt;
}
@PreUpdate
public void onUpdate(){
this.updatedAt = LocalDateTime.now();
}
}
이렇게 하면 각 엔티티에서 중복 없이
시간 정보를 자동으로 관리할 수 있어 유지보수가 편리합니다.
이번 도메인 설계에서는 연관관계 방향, 테이블 분리 기준, 생성 방식 등을 고민하며
단순히 돌아가는 코드가 아닌, 유지보수성과 확장성을 고려한 구조를 만들고자 했습니다.
아직 실무 수준의 복잡한 도메인은 아니지만,
향후 단방향 전환, 연관 최소화, 이벤트 기반 구조 전환 등도 실험해볼 계획입니다.
다음 글에서는 JWT 기반 인증/인가 구현기를 공유할 예정입니다.
읽어주셔서 감사합니다 🙌