Skip to content

Conversation

@zzaekkii
Copy link
Member

@zzaekkii zzaekkii commented Oct 26, 2025

close #109

☑️ 완료 태스크

메인 작업

  • 도메인 모델과 JPA 엔티티 분리
  • 새 아키텍처에 맞게 구조 수정

코드 개선 및 변경 사항

  • 조회 전용 쿼리 분리
  • queryDSL 적용 (동적 쿼리, User와 PrReview 조인)
  • 조회 방식 page에서 no-offset으로 변경
  • 요청 데이터 validation 적용
  • 불필요한 @Transactional 제거
  • 요청글 수정 폼 조회 API 제거
  • 네이밍 일관성을 위해 테이블명 pr_review -> pr_reviews로 변경
  • URI pr-review -> pr-reviews로 변경 및 시큐리티 permitAll 경로 수정
  • 요청글 생성과 수정 request dto 분리 (지금은 같은 스펙이지만 추후 확장 고려)

공통 부분 변경 사항

  • 에러 상수 409 (충돌) 추가
  • pr_reviews 테이블 생성 flyway migration 추가

🔎 PR 내용

최근에 자소서랑 이것저것 취업 준비하느라 작업 반영이 조금 밀렸네요 😥
새 아키텍처 적용도메인 모델과 JPA 엔티티 분리 외에도 여러가지 코드 개선/변경 사항이 있었습니다.

우선 작업 내용 설명 전에 추후 작업 계획에 대해서인데요.

  1. 조회수 관리 및 중복 증가 방지 작업
  2. 요청글 수동 마감 API 추가
  3. 테스트 코드 작성 및 Swagger 문서화
  4. Github API 연동 및 채택 기능 추가
  5. 마감 시각이 지난 요청글에 대해 자동 마감 처리

최대한 10월 끝나기 전에 3번까지 마무리 할 계획이고요.
그렇게 되면 채택 기능을 제외한 PR 리뷰 요청글 커뮤니티는 활성화 가능하게 됩니다.
이 부분은 작업하면서 연서랑 얘기 나눌게요.


이제 작업 내용들을 핵심만 간략히 설명해보겠습니다.

0️⃣ 새 아키텍처 적용

image

백문불여일견아니겠습니까?
남의 작업물은 이해하기도, 파악하기도 번거롭기에 글로 설명하면 애매할 것 같았습니다.

PR 리뷰 요청글 영역이 크게 어떤 흐름으로 움직이고, 어떻게 구성되었는지 그렸는데요.
이해되지 않는 부분이 있다면 댓글 달아주세요.

1️⃣ 도메인 모델과 JPA 엔티티 분리

image

기존에 JPA 엔티티가 결합되어 있던 도메인 모델에서 둘을 똑 떼어냈습니다.

그리고 작성자 정보(user)의 경우 조인을 사용하지 않고, user_id만 둬서 간접 참조하도록 했습니다.
특정 유저의 PR 리뷰 요청글을 불러와야 하는 경우엔 WHERE를 사용해 검색하도록 했습니다.


최종적으로 그 부분은 QueryDSL을 사용해서 처리했는데,
단순 조건 검색 외에도 동적 필터링 및 조인 처리를 위해 QueryDSL을 적용했습니다.
(자세한 내용은 아래 후술)

2️⃣ QueryDSL 사용 (동적 쿼리)

image

PR 리뷰 요청글 특성상 조회에 검색 필터링 조건(유저, 날짜순, 조회순)이 항상 붙어있기에,
이 부분을 QueryDSL을 사용해 동적으로 깔끔하게 처리해줬습니다.

또한, 요청글 조회의 경우엔 글 정보 + 작성자 정보가 필요하기에
QueryDSL .join을 이용해 단일 쿼리만으로 필요한 데이터 전체를 가져오도록 했습니다.

3️⃣ 불필요한 @Transactional 제거

image

현재 조회 메서드들은 @Transactional이 필요치 않아 제거해주었습니다.

위 두 메서드는 수정, 삭제지만 단일 DB 작업만 수행하고 있으므로,
별도의 트랜잭션 경계 처리는 불필요하다고 판단해서 어노테이션을 제거했습니다.

4️⃣ 조회 방식 page에서 no-offset으로 변경

image

기존 page 기반 페이징 방식을 no-offset 방식(Slice)으로 변경했습니다.

우선 UI 상으로도 무한 스크롤 형식으로 설계되어 있고,
PR 리뷰 요청글(게시판)의 특성상 최신순 정렬이 잦기 때문에
offset 기반 페이징은 데이터 증가 시 성능이 저하될 수 있다고 생각했습니다.

📷 스크린샷

… refactor/#109/domain-infra-split

# Conflicts:
#	Moyoy-Api/src/main/java/com/moyoy/api/follow/application/response/GithubFollowDetectionResult.java
#	Moyoy-Api/src/main/java/com/moyoy/api/ranking/application/RankingService.java
#	Moyoy-Api/src/main/java/com/moyoy/api/support/SlicePagingUtils.java
#	Moyoy-Api/src/test/java/com/moyoy/api/follow/presentation/GithubFollowControllerTest.java
#	Moyoy-Api/src/test/java/com/moyoy/api/ranking/presentation/RankingControllerTest.java
#	Moyoy-Common/src/main/java/com/moyoy/common/constant/MoyoConstants.java
#	Moyoy-Common/src/main/java/com/moyoy/common/error/CommonErrorCode.java
#	Moyoy-Common/src/main/java/com/moyoy/common/page/PageData.java
#	Moyoy-Domain/src/main/java/com/moyoy/domain/ranking/RankingRepository.java
#	Moyoy-Domain/src/main/java/com/moyoy/domain/support/error/pr_review/PrReviewErrorCode.java
#	Moyoy-Infra/src/main/java/com/moyoy/infra/database/ranking/RankingRepositoryImpl.java
@zzaekkii zzaekkii self-assigned this Oct 26, 2025
@zzaekkii zzaekkii added :trollface: 재영 Further information is requested 🔨 Refactor 코드 리팩토링 labels Oct 26, 2025
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
12.3% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@zzaekkii zzaekkii merged commit ba8bb32 into develop Oct 28, 2025
1 of 2 checks passed
@seungryul99
Copy link
Member

조회수 관리 및 중복 증가 방지 작업

추후 작업하기로 한 이 부분, 조회수가 우리 서비스에서 어느정도로 데이터 정합성이 맞춰져야 하는지 생각해 보며 진행하면 좋을 것 같아요.

이력서나 포폴 작성하면서 들었던 조언중에, 해결하지 않아도 되는, 실제로 문제가 되지 않을 문제를 굳이 문제로 만들어서 해결하는 건 설득력이 부족하다고 하더군요.

예를 들어 "우리 서비스에서는 조회수 1000을 달성하면 1만원 상품권을 줍니다." 같은 요구사항이 있다면, 아마 조회수 중복방지를 굉장히 엄격하게 해야 할거 같습니다.

다만, 그냥 재미로 보여주는 데이터라면 엄격하게 하지 않아도 될 것 같네요, Redis에 IP 혹은 userId 기반으로 조회수 중복 관리 관련 집합을 둔다던가 .. 등등

깊게 생각해 보지는 않았지만, 개인적으로 관련 키워드 중에 당장 떠오르는게 HyperLogLog와 증분쿼리 정도가 있습니다.

@seungryul99
Copy link
Member

아 추가로 Redis 사용/운영관리 는 뜨거운 감자가 될 수있는 주제이긴 한데, 여러가지 선택지 중 한 개로 보고, 정 방법이 없다면 제대로 공부해서 적용한다는 마인드로 사용해 보는것도 좋을것 같습니다!

@seungryul99
Copy link
Member

불필요한 @transactional 제거

이 부분, Spring Data JPA를 이용할 경우, 아마 내부 구현체 모든 메서드에 사실상 @transactional이 붙어있습니다. 우리가 Service Layer에 @transactional을 사용하지 않을경우에도 사실상 이용은 될 거 같습니다.

이 부분에 대한 키워드로 @transactional의 동작방식과, 트랜잭션 전파 이쪽 한번 살펴 보면 좋을 것 같습니다!

아마 readOnly가 붙은 트랜잭션이 사용될 겁니다. 여기에 추가적으로, OSIV에 대해서 공부해 보면 좋을거 같아요.

제가 정확히 기억은 나지 않지만, 아마 OSIV true인 상태에서 트랜잭션 범위 좁힌다고 해서, DB 커넥션이 빨리 반납되지 않는 것으로 알고 있어요!

@zzaekkii
Copy link
Member Author

추후 작업하기로 한 이 부분, 조회수가 우리 서비스에서 어느정도로 데이터 정합성이 맞춰져야 하는지 생각해 보며 진행하면 좋을 것 같아요.

이력서나 포폴 작성하면서 들었던 조언중에, 해결하지 않아도 되는, 실제로 문제가 되지 않을 문제를 굳이 문제로 만들어서 해결하는 건 설득력이 부족하다고 하더군요.

예를 들어 "우리 서비스에서는 조회수 1000을 달성하면 1만원 상품권을 줍니다." 같은 요구사항이 있다면, 아마 조회수 중복방지를 굉장히 엄격하게 해야 할거 같습니다.

다만, 그냥 재미로 보여주는 데이터라면 엄격하게 하지 않아도 될 것 같네요, Redis에 IP 혹은 userId 기반으로 조회수 중복 관리 관련 집합을 둔다던가 .. 등등

깊게 생각해 보지는 않았지만, 개인적으로 관련 키워드 중에 당장 떠오르는게 HyperLogLog와 증분쿼리 정도가 있습니다.

아하, 음 안그래도 저희 서비스에서 조회수가 가지는 중요도에 대해서 고민 중이었습니다.
조회수순으로 정렬하는 기능이 있어 조회수가 완벽히는 아니더라도 잘 반영되어야 할 것 같다고 생각하고 있는데요.

특히 익명 사용자가 조회 가능한 서비스라 익명 사용자의 조회수 중복 증가가 일어난다면,
항상 상위권에 표시되는 악의적인 일이 발생하지 않을까하는 고민을 하면서도 너무 과한가 싶기도 하네요.

아무튼 더 고민해보고 적용해보도록 하겠습니다.

@zzaekkii
Copy link
Member Author

이 부분, Spring Data JPA를 이용할 경우, 아마 내부 구현체 모든 메서드에 사실상 @transactional이 붙어있습니다. 우리가 Service Layer에 @transactional을 사용하지 않을경우에도 사실상 이용은 될 거 같습니다.

아, 이 부분은 서비스 레이어에서의 @Transactional 사용에 대한 의도로 언급한 것이었는데요.
알려주신 트랜잭션 전파와 내부 동작, OSIV에 대해서도 더 살펴보도록 하겠습니다!

@seungryul99
Copy link
Member

seungryul99 commented Oct 29, 2025

특히 익명 사용자가 조회 가능한 서비스라 익명 사용자의 조회수 중복 증가가 일어난다면, 항상 상위권에 표시되는 악의적인 일이 발생하지 않을까하는 고민을 하면서도 너무 과한가 싶기도 하네요.

아무튼 더 고민해보고 적용해보도록 하겠습니다.

아마 하이퍼로그로그 알고리즘? 이란게 있는 걸로 아는데, 굉장히 재밌어 하실거 같습니다. 해당 부분은 키워드만 들어봐서 저도 자세히는 모르겠네요.

@zzaekkii
Copy link
Member Author

특히 익명 사용자가 조회 가능한 서비스라 익명 사용자의 조회수 중복 증가가 일어난다면, 항상 상위권에 표시되는 악의적인 일이 발생하지 않을까하는 고민을 하면서도 너무 과한가 싶기도 하네요.
아무튼 더 고민해보고 적용해보도록 하겠습니다.

아마 하이퍼로그로그 알고리즘? 이란게 있는 걸로 아는데, 굉장히 재밌어 하실거 같습니다. 해당 부분은 키워드만 들어봐서 저도 자세히는 모르겠네요.

하핫 그런가요? 운동 끝나고 알아봐야겠네요
알려주셔서 감사합니다~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 리팩토링 size/XXL :trollface: 재영 Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: 아키텍처 변경 + 도메인 모델과 JPA 엔티티 분리

3 participants