- 회원 정보 기능은 가장 간단하게, 랜덤 쿠키값을 부여해서 구분하면 할 수 있겠지만, 현재 프로젝트의 목적이 서비스는 아니므로 건너뛰겠다.
- 학습을 목적으로, Comment를 독자적인 애그리거트가 아닌, Post의 Child Entity로 두었다.
Child Entity vs. Value Object vs. DTO의 비교
- 링크 참고
JDBC를 사용하면서, child Entity를 생성/수정/삭제하는 방법
- 링크 참고
이벤트의 설계 방법
- 링크 참고
JPA도 아닌데, @Transactional을 굳이 필수로 붙여야 할까?
- 단순 조회 로직에 대해선 @Transactional 없이 사용해보자.
리포지토리 vs DAO
- DAO
- DB 접근만을 위한 객체
- 저수준 DB API를 구현한 객체.
- 단순 DB 접근에 초점
- Repository
- 애그리거트 전체를 추상화하여, 통째로 영속성 저장소에서 가져오기 위한 객체
- DB 접근에 대한 개념을 추상화한 객체
- 트랜잭션 경계에 초점
메인 페이지에서 각 게시글 별로 게시글 내 댓글 수를 표기해야 하는데, 이걸 리포지토리에서 어떻게 탐색하게 하는 것이 좋을까?
현재 필요한 도메인 구조가 이게 맞나?
현재 도메인 구조
- 게시글
- 댓글
"게시글 항목"이 필요하지 않나?
- 비즈니스 로직 상, 게시글과 댓글의 정보를 조회할 때 사용할 엔티티가 필요하다.
- 근데, 해당 "게시글 항목"은, 게시글과 댓글 항목에 의해 변화만 이루어지는, "도메인"으로 승격되기엔 부족한 엔티티이다.
- 별도의 조회 모듈 만들기?
- 현재 프로젝트 규모에, 조회 모듈을 따로 두는건 오버엔지니어링인 듯.
- 해결책 - 애플리케이션 레이어에 Query Repository 두기
- 애플리케이션 레이어에 필요한 DTO를 따로 두고
- 도메인 모델의 무결성을 지켜주자.
그래서 쿼리를 어떻게 날리지?
1. group by + count 쿼리 <- 선택함
- count(comment.id) 로 해당 게시글 별 댓글 수를 DB레벨에서 조회한다.
- 장점
- 실시간성이 높다.
- 구현이 DB 쿼리로 끝난다.
- 단점
- Group By 연산과 같은 추가 연산이 롱 트랜잭션 형태로 발생하기 때문에, DB 부하가 아래에 비해 높다.
2. 비정규화 + 비동기 처리
- 별도 댓글 카운트를 계산하는 테이블을 생성하여 비동기로 댓글 수를 계산하고, 최종 일관성을 보장하는 형태로 구현한다.
- 장점
- db 부하가 적다.
- 단점
- 구현 복잡도가 높다.
- 실시간성이 떨어진다.
조인
- jdbc에서 join 때문에 같은 이름의 컬럼이 두개 이상 생기면 어떻게 구분해야 하지?
- 쿼리 날릴 때, 별칭 붙여서 날리면
- 결과 ResultSet이 별칭 붙어서 나온다.
- 적절하게 RowMapper 이용해서 받아내자.
- ResultSetExtractor
JPA의 영속성 전이/고아 객체 처리
- JPA의 영속성 전이/고아 객체 처리는 훌륭하다.
- CascadeType.ALL
- orphanRemoval=true
- 객체 생성/삭제 모두 별도 DAO 없이도 처리할 수 있도록 관리해준다.
- JDBC 쓸때는 어쩔 수 없이,
- Batch delete/insert 를 하거나
- 해당 child entity에 대하여 DAO를 설계해야 한다.
페이징
카운트 쿼리 어떻게 날리지?
- count(*)
- count(id)
- count(1)
셋 모두 기본적으로 "가장 폭이 좁은(길이가 짧은) 인덱스"를 골라, 풀 스캔을 통해 count 연산을 수행한다.
기본적으로 저장되는 클러스터링 인덱스는 테이블 레코드 전체를 갖고 있다.
그렇다면, count 쿼리 용으로 가장 폭이 좁은 인덱스를 하나 만들어두는게 좋나?
삽입/수정/삭제 O(logN) 부담을 count쿼리를 위해 만든다?
실제 사용자의 사용 분포 & 테스트로 확실한 결과를 봐야 알겠지만, 직감적으론 별로일 것 같다.
인덱스가 아예 없어서 레코드의 길이가 부담이 될 정도로 길다면, 그 전에 인덱스가 하나쯤 걸려 있을듯.
패키지 구조를 간단하게 아스키 아트로 표현하는 방법
- tree 유틸리티 사용하기
다음 스텝에서 바꿔보고 싶은 것
아마 다음 프로젝트는 단순 Tool 사용법과 그 기능 분석이 주를 이룰 것 같아, 도메인 설계를 하게 되진 않을 것 같다.
하지만 그 다음 "도서 관리 시스템"에서는 사용하게 될 것 같으니, 일단 기록해두자.
풀 리퀘스트 단위
- 초기 도메인 모델 엔티티 작성은 엔티티 별로 단일 PR이 필요하다.
- 그 이후로는 E2E 기능 단위 PR이 제일 자연스러운 버전을 형성하는 듯.
- 각 기능 별로 "도메인 엔티티 구현 1회/E2E 기능 구현1회" 방식이 PR 단위로 제일 적합한듯 하다.
패키지 구조
- 링크 참고
- 이전 프로젝트에서
- DDD의 핵심 구성요소가 무엇이 있는지는 파악할 수 있었다.
- 근데 "이 방식이 실무에서 쓰일까?" 라고 묻는다면 아닐 듯 하다.
- 모듈이 지나치게 깊숙하게 박혀 있어, 같은 모듈의 레이어 간 의존에 import문이 너무 길어진다.
- 그래서 이번 프로젝트에는
- 클린 아키텍처의 "레이어->모듈->구성요소" 방식을 차용해 보았다.
- 다음 프로젝트때는 원래 사용하던 "모듈->레이어->구성요소" 방식을 다시한번 엄밀하게 적용해봐야겠다.
인프라->도메인 모델 의존이 발생하는 로직 구현에 어노테이션 적극 활용하기
- 인프라 로직에서 도메인 로직에 대한 직접 의존을 피하자
- "id" 로 해당 객체 ID 값 참조하는 문제 해결하기.
- 도메인 로직에 인프라 설정이 들어가는게 아쉽긴 하지만, 도메인 로직 자체가 어노테이션의 변화에 영향을 받는 것은 아니니, 의존이라고 보지 않아도 괜찮지 않을까...?
- 일단 해보고 느껴보자.
- 설정과 도메인 모델을 분리하자.
- 리플렉션의 한계 -> 런타임 환경의 예상치 못한 예외 -> 어노테이션으로 완화
MySQL+테스트컨테이너 환경 구성
- 인메모리 H2 DB도 많이 유용하지만
- Docker + MySQL(MariaDB) + TestContainer환경도 구축해서 테스트해보고 싶다.
- 외부 스키마 관리 기능 (Liquibase, Flyway)랑 함께 사용해보기
'Article > 개인 프로젝트' 카테고리의 다른 글
[Spring JdbcTemplate] JdbcTemplate을 이용한 상품 관리 기능 구현기 (0) | 2025.04.26 |
---|---|
[Spring JdbcTemplate] Spring JdbcTemplate 연습기 (0) | 2025.04.25 |