펀딩 사이트에서 펀딩을 위한 정보를 담기 위한 table project entity의 정보와 다른 table들의 정보를 한 눈에 확인하기 위한 project list api이다

project list에 관련된 table
처음 project list 조회 기능은 한개의 api에서만 사용했고, 필터 기능또한 tag, 제목 검색 2가지 밖에 존재하지 않았기 때문에 native query로 충분히 구현가능 하다고 생각했었다
WITH filtered_projects AS (
SELECT distinct p.*
FROM project p
join project_tag pt on p.id = pt.project_id
where (조건 %s)
),
project_count AS (
SELECT COUNT(*) AS total_count
FROM filtered_projects
)
SELECT fp.id, fp.title, fp.created_at, fp.expired ,
(CAST((SELECT SUM(f.free_price + op.price) FROM funding f left join `option` op on op.id = f.option_id WHERE f.project_id = fp.id) AS DOUBLE) * 100.0 / fp.goal) AS completion_rate,
(SELECT COUNT(DISTINCT r.user_id) FROM `like` r WHERE r.project_id = fp.id) AS like_count,
(SELECT COUNT(f.user_id) FROM funding f WHERE f.project_id = fp.id) AS user_count,
(SELECT i.uri FROM img i WHERE i.id = fp.img_id) AS title_img,
(cast(%sas signed) )as is_like,
(select count(pv.id) from project_view as pv where pv.project_id = fp.id) as view_count ,
pc.total_count
FROM filtered_projects fp
CROSS JOIN project_count pc
order by
limit
이후 where, order by, limit값을 java 코드로 작성하여 String 형식으로 넣어주는 방법을 사용했다
이후 funding table 다양화와 다른 col 추가를 위해서는 project list 조회 함수가 유지보수 / 기능 확장이 가능하게 수정해둘 필요가 있었다
예전 부터 고민했던 동적 query 작성을 위한 orm 중 query dsl을 선택하여 도입하게 되었다
JPAQuery JPAQueryFactory.select(Expression args...)
EntityPathBase<ProjectEntity> : query dsl실행 시 entity마다 자동 생성되는 class (entity의 별명으로 사용됨)
BooleanExpression : Expression type중 한가지, where 조건문에 사용됨
OrderSpecifier : order by절에서 사용됨
-- 불가능
select(
project.title,
( select count(*) from like where like.project.eq(project) ) as like_count
)
from project
order by like_count
select에서 조회한 값을 사용한 정렬이 불가능하다 때문에 order by에서 똑같은 sub query를 실행해야한다
구현한 내용이다 (+약간의 생략)
.select(Projections.constructor(ResponseProjectListDto.class,
projectEntity.id,
projectEntity.title,
(생략),
projectEntity.titleImg,
completeRate(projectEntity), // completeRate
likeCount(projectEntity), // like count
fundingUserCount(projectEntity), // user_count
isLike(projectEntity, dto.getUserId()), // is_like
viewCount(projectEntity), // view_count
totalCount(projectEntity, dto) // total _ select count :: totalCount
))
.from(projectEntity)
.leftJoin(생략)
.where(
where(projectEntity, projectTagEntity, dto.getUserId(), dto.getTagIds(), dto.getSearch())
)
.orderBy(
orderByType(projectEntity, ProjectOrderType.getType(dto.getOrder()), dto.getDesc()),
projectEntity.createdAt.desc()
)
.limit(dto.getPageCount())
.offset(dto.getPageCount() * dto.getPage())
.fetch();
충분히 native query보다는 유지 보수 가능한 코드가 되었다 하지만 query 성능 자체는 크게 감소하게 된 것 같다
일단 project list 조회 기능 함수의 refactoring은 이것으로 마치고 추후 funding table 세분화, 다른 col이 추가되었을 때 이전 native query로 가능한지 여부에 따라 roll back을 결정해야 할 것 같다