-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[YS-98] feat: 지역별 공고 게시글 개수 조회 API 구현 및 WebExceptionHandler 리팩터링 #29
Conversation
fun getExperimentPostCounts(input: Any): Any { | ||
return when (input) { | ||
is GetExperimentPostCountsByRegionUseCase.Input -> getExperimentPostCountsByRegionUseCase.execute(input) | ||
is GetExperimentPostCountsByAreaUseCase.Input -> getExperimentPostCountsByAreaUseCase.execute(input) | ||
else -> throw IllegalArgumentException("Invalid input type: ${input::class.java.simpleName}") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번 기능은 하나의 유즈케이스에 모든 로직을 담기에는 규모가 크다고 판단하여, 두 유즈케이스로 나누어 구현했습니다. input으로 온 파라미터 유무에 따라 적합한 유즈케이스를 실행하도록 구현했습니다.
val regionData = region.let { experimentPostGateway.countExperimentPostByRegionGroupedByArea(it) } ?: emptyList() | ||
val areaCounts = getAreaCounts(region, regionData) | ||
|
||
return Output(total, areaCounts) | ||
} | ||
|
||
private fun getAreaCounts(region: Region?, regionData: List<Tuple>): List<PostCountsByArea> { | ||
return region?.getAreas()?.map { area -> | ||
val areaCount = findAreaCount(regionData, area) | ||
PostCountsByArea(name = area.displayName, count = areaCount) | ||
} ?: emptyList() | ||
} | ||
|
||
private fun findAreaCount(regionData: List<Tuple>, area: Area): Int { | ||
return regionData.find { tuple -> | ||
val regionArea = tuple.get(0, Area::class.java) | ||
regionArea == area | ||
}?.get(1, Long::class.java)?.toInt() ?: 0 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지역별 공고 개수를 매칭하는 과정에서, 각 Area에 해당하는 공고 개수를 찾기 위해 regionData와 매칭 작업을 진행했습니다. 지역 구분이 많아 휴먼 에러가 발생할 수 있어, 이처럼 응답 필드를 정확히 매칭하고자 했습니다.
다만, 이 API는 자주 호출될 거라 기대하는데 매번 매칭 작업을 반복하는 것은 비효율적이라 생각합니다. 추후 레디스 캐싱 등을 활용해 최적화할 필요가 있을 것 같습니다… 🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지역별 공고 개수를 매칭하는 과정에서 지역 구분이 많아 복잡할 수 있는 작업을 정확히 처리해주시고, 처리 방식을 나아가 고민하신 점이 인상깊었어요! 😊
말씀하신 우려사항에 대해서도 공감가는 게, 해당 API는 상당히 많이 호출될 거라 판단되어서 성능 최적화가 필요하다고 생각이 들어요. 🤔 특히 회원가입이나 공고 등록등에서 드롭다운으로 지역을 필터링하는 방법을 채택하는 반면, 공고 조회에서는 공고 개수를 보여준다는 구조라서요.
지수님 말씀처럼 레디스 캐싱을 도입해 자주 조회되는 데이터를 캐싱하는 방식도 좋고, 정기적인 배치 작업으로 캐싱 데이터를 갱신하는 방식도 고려해볼 수 있을 것 같아요.
예를 들어, 지역별 공고 개수를 캐싱에 저장해두고, 일정 주기로 배치 작업을 통해 갱신하면 실시간성을 어느 정도 유지하면서도 성능 최적화에 대한 이슈가 해결되지 않을까,, 개인적인 생각입니다!
멋진 설계와 고민 감사드리며, 추후에 디벨롭 과정에서 이 부분을 함께 논의해봐도 좋을 것 같아요! 🙌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞아요! 저도 이 부분을 구현하면서 만약 나중에 레디스를 활용한다면, TTL을 설정하여 적당히 실시간성을 유지하면서도 성능을 개선하면 좋겠다고 생각했어요. 👍
요즘 개발하다 보니, 서비스 특성상 레디스의 필요성을 더 많이 느끼는 것 같아요. MVP 기능 개발 이후에는 레디스 같은 인프라나 기술을 적극적으로 활용해 성능이나 안정성을 개선해 나가고 싶습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebExceptionHandler를 리팩터링하는 것이 상당히 큰 작업이었을 텐데, 꼼꼼하게 처리해주신 점이 인상 깊었어요!
JPQL과 querydsl 혼합 방식에 대해 코멘트 남겨드렸으니 가볍게 확인 부탁드릴게요.
수고 많으셨습니다~!
@Query("SELECT e.area, COUNT(e) FROM experiment_post e WHERE e.region = :region GROUP BY e.area") | ||
fun countExperimentPostByRegionGroupedByArea(@Param("region") region: Region): List<Tuple> | ||
|
||
@Query("SELECT e.region, COUNT(e) FROM experiment_post e GROUP BY e.region") | ||
fun countExperimentPostGroupedByRegion(): List<Tuple> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 Repository에서 JPQL을 활용해 간단한 정적 쿼리를 작성하신 점이 효율적이고 깔끔하다고 생각됩니다! 😊
다만 저번 PR에서 querydsl 세팅을 마친 상태라 어느 정도 합의가 필요하면 좋을 것 같아요.
특히 프로젝트에서 정적 쿼리와 동적 쿼리가 혼재되어 있다면,
저는 JPQL과 querydsl을 상황에 따라(예를 들자면 동적쿼리나 복잡한 조인연산 등등에는 querydsl, 아니라면 JPQL 방식을 채택한다든지...) 어느정도 혼합해서 사용하는 것도 좋은 접근법이라고 생각되는데, 혹시 이 부분에 대해 지수님은 어떻게 생각하시는지 궁금합니다! 🙌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 JPQL과 QueryDSL을 상황에 맞게 적절히 활용하는 것이 효율적이라고 생각합니다!
이전에 모든 쿼리를 QueryDSL로만 처리했던 경험이 있는데, 한두 줄로 끝나는 간단한 쿼리도 QueryDSL로 구현하려다 보니 오히려 개발 생산성이 떨어진다고 느낀 적이 많았어요.
그래서 제 개인적인 의견으로는, 간단한 쿼리는 JPQL을 활용하고, 복잡하거나 동적 쿼리가 필요한 경우에는 QueryDSL을 사용하는 방향이 장기적으로 더 좋은 선택이라고 생각합니다.
수정님도 이 의견에 동의하신다면, 이 방향으로 진행해 보면 좋을 것 같아요!
Quality Gate passedIssues Measures |
💡 작업 내용
파라미터가 입력 안 했을 때
파라미터 입력했을 때
✅ 셀프 체크리스트
🙋🏻 확인해주세요
🔗 Jira 티켓
https://yappsocks.atlassian.net/browse/YS-98