Skip to content

[Refactor] StatusFilterAspect 동작 수정#352

Open
seongjunnoh wants to merge 6 commits intodevelopfrom
refactor/#351/status-filter-aspect
Open

[Refactor] StatusFilterAspect 동작 수정#352
seongjunnoh wants to merge 6 commits intodevelopfrom
refactor/#351/status-filter-aspect

Conversation

@seongjunnoh
Copy link
Collaborator

@seongjunnoh seongjunnoh commented Mar 8, 2026

#️⃣ 연관된 이슈

closes #351

📝 작업 내용

1. Hibernate Filter 조건 단순화

  • status IN (:statuses)status = 'ACTIVE' 로 변경
  • @IncludeInactive 어노테이션 제거, FilterModeACTIVE_ONLY / UNFILTERED 2가지로 단순화

2. FilterContextHolder (ThreadLocal) 도입

  • @Unfiltered@Transactional 의 결합 문제 해소
  • @Unfiltered AOP 는 ThreadLocal 에 UNFILTERED 모드만 기록 (Session 접근 없음)
  • @Transactional 경계의 StatusFilterAspect 가 ThreadLocal 을 읽어 filter 활성 여부 결정
  • @Unfiltered@Transactional 없이도 독립적으로 동작

3. Query Persistence Adapter 에 @Transactional(readOnly = true) 명시

  • SimpleJpaRepository 는 Spring AOP 가 intercept 하지 못하는 구조 → 서비스에 @Transactional 이 없으면 filter 미적용 경로 존재
  • 모든 데이터 접근이 영속성 어댑터를 경유하는 헥사고날 아키텍처 활용
  • 어댑터 경계에서 서비스 @Transactional 유무와 무관하게 filter 동작 보장
  • spring transactional 명시할 경우 DB에 부가적인 command가 나가지만, 서비스 트래픽 규모상 이는 무시해도 될만한 수준이라고 판단

4. AOP 실행 순서 명시적 보장

  • StatusFilterAspectem.unwrap(Session.class) 호출 시 트랜잭션이 먼저 열려있어야 함
  • @EnableTransactionManagement(order = LOWEST_PRECEDENCE - 1) 로 TransactionInterceptor 를 outer 로 고정
  • @Order(LOWEST_PRECEDENCE) 로 StatusFilterAspect 를 inner 로 명시

5. 테스트 정비

  • StatusFilterTest 재작성: adapter 경계 기준으로 filter 동작 검증
  • application-test.ymlopen-in-view: false 추가하여 production 과 동일한 환경에서 검증
  • dev, prod yml도 open-in-view: false 설정값 추가
  • REST API 애플리케이션 서버에서는 open-in-view 설정이 true 일 필요 X

📸 스크린샷

image (기존에 개발한 Hibernate Filter 자동화에 대한 플로우 차트)

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

릴리스 노트

  • 리팩토링

    • 트랜잭션 관리 구조를 개선하여 데이터 일관성을 강화했습니다.
    • 상태 필터링 메커니즘을 단순화하여 시스템 안정성을 향상시켰습니다.
  • 테스트

    • 테스트 설정을 재구성하여 필터링 동작 검증 범위를 확대했습니다.

- statusFilter는 'status = active' 쿼리로 고정
- 굳이 in 절 필요없다고 판단
- active_only, unfiltered(필터 적용 X == status 조건 제거) 2가지 mode
- active_only mode가 default
- 트랜잭션 경계에서 status filter 자동으로 동작하도록 설정
- @unfiltered 동작 시 ThreadLocal mode 변경 & 필터 동작 X
- 사용하지 않는 @IncludeInactive 제거
- status filter는 트랜잭션이 열려있어야 동작 가능
- 조회 시 사용하는 영속성 adapter 클래스에 read only 트랜잭션 명시
- QueryPersistenceAdapter 하위의 QueryDSL, spring data jpa repository 인터페이스를 활용하는 DB access 코드 모두 status filter 동작 가능
- 트랜잭션 명시로 인해 DB에 추가적인 command가 날라가지만, 이는 감수할만한 트레이드 오프라고 판단
…sFilterAspect 동작하도록 순서 강제 (#351)

- TransactionInterceptor 동작으로 Hibernate Session이 열려야, StatusFilterAspect 에서 Session에 Hibernate Filter 활성화 가능
Copilot AI review requested due to automatic review settings March 8, 2026 08:38
@seongjunnoh seongjunnoh linked an issue Mar 8, 2026 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link

coderabbitai bot commented Mar 8, 2026

Walkthrough

트랜잭션 경계와 무관하게 상태 필터를 일관되게 적용하도록 시스템을 재구성하였습니다. 여러 QueryPersistenceAdapter에 읽기 전용 트랜잭션 어노테이션을 추가하고, FilterContextHolder를 통한 스레드 로컬 필터 모드 관리를 도입하였으며, StatusFilterAspect를 간소화하고 BaseJpaEntity의 Hibernate 필터 정의를 단순화하였습니다.

Changes

Cohort / File(s) Summary
읽기 전용 트랜잭션 어노테이션 추가
book/adapter/out/persistence/BookQueryPersistenceAdapter, comment/adapter/out/persistence/CommentLikeQueryPersistenceAdapter, comment/adapter/out/persistence/CommentQueryPersistenceAdapter, feed/adapter/out/persistence/FeedQueryPersistenceAdapter, notification/adapter/out/persistence/NotificationQueryPersistenceAdapter, post/adapter/out/persistence/PostLikeQueryPersistenceAdapter, post/adapter/out/persistence/PostQueryPersistenceAdapter, recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter, room/adapter/out/persistence/RoomParticipantQueryPersistenceAdapter, room/adapter/out/persistence/RoomQueryPersistenceAdapter, roompost/adapter/out/persistence/AttendanceCheckQueryPersistenceAdapter, roompost/adapter/out/persistence/RecordQueryPersistenceAdapter, roompost/adapter/out/persistence/VoteQueryPersistenceAdapter, user/adapter/out/persistence/FollowingQueryPersistenceAdapter, user/adapter/out/persistence/UserQueryPersistenceAdapter
각 QueryPersistenceAdapter 클래스 레벨에 @Transactional(readOnly = true) 어노테이션을 추가하여 조회 메서드가 트랜잭션 컨텍스트에서 실행되도록 통일하였습니다.
필터 모드 관리 인프라
common/aop/FilterContextHolder
ThreadLocal 기반의 필터 모드 컨텍스트를 관리하는 새로운 클래스 추가. ACTIVE_ONLY, UNFILTERED 모드를 통해 스레드별 필터 상태를 제어합니다.
상태 필터 AOP 재구조화
common/aop/StatusFilterAspect
@order 어노테이션 추가, unfiltered 및 enableActiveByDefault 두 개의 새로운 어드바이스로 단순화. FilterContextHolder를 활용하여 필터 관리를 명확히 하였습니다.
Hibernate 필터 정의 단순화
common/entity/BaseJpaEntity
파라미터화된 필터(status in (:statuses))에서 고정 조건(status = 'ACTIVE')으로 변경하여 필터 동작을 단순화했습니다.
기존 필터 어노테이션 제거
common/annotation/persistence/IncludeInactive, common/exception/code/ErrorCode
더 이상 사용하지 않는 IncludeInactive 어노테이션과 관련 에러 코드를 제거했습니다.
트랜잭션 관리 설정
config/AppConfig
@EnableTransactionManagement 어노테이션을 추가하여 명시적인 트랜잭션 관리 활성화
테스트 인프라 재구성
config/StatusFilterTestConfig, config/TestUserJpaRepository, common/persistence/StatusFilterTest
테스트 전용 persistence adapter와 unfiltered service를 통한 테스트 구조 개선. 여러 테스트 서비스 variant를 단일 adapter 기반 구조로 통합하였습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • [feat] hibernate filter 적용 #287: StatusFilterAspect와 BaseJpaEntity의 Hibernate 필터 정의, IncludeInactive/Unfiltered 어노테이션에 동일한 변경사항이 있어 코드 레벨에서 직접 관련됨
  • [feat] 전체 피드 조회 api 구현 #104, #212: 여러 QueryPersistenceAdapter 클래스(FeedQueryPersistenceAdapter 등)에 @Transactional(readOnly = true)를 추가하는 것과 기존 쿼리 메서드 수정이 동일 클래스에서 발생
  • [chore] 운영서버로 배포 25/08/13 #211: UserQueryPersistenceAdapter에 트랜잭션 어노테이션 추가와 새로운 조회 메서드 추가가 동일 클래스에서 발생

Suggested labels

🍀 refactor, 👻 성준

Suggested reviewers

  • buzz0331
  • hd0rable

Poem

🐰 필터를 다시 정리하는 날,
ThreadLocal에 모드를 담아,
트랜잭션 경계를 넘어서도
상태는 언제나 ACTIVE하네!
AOP의 춤, 깔끔하게 한 발씩,
복잡한 로직을 벗어던지고
우리의 시스템은 더욱 투명해졌어! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 '[Refactor] StatusFilterAspect 동작 수정'으로, 변경사항의 핵심인 StatusFilterAspect 리팩토링을 명확히 나타내고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 연결된 이슈 #351의 요구사항을 충족합니다: (1) osiv 속성 false로 변경, (2) 트랜잭션과 무관한 필터 제어, (3) QueryDSL 구현체의 readOnly transactional 명시.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #351의 범위 내에 있습니다. 추가적인 비관련 변경사항이나 범위를 벗어난 수정이 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#351/status-filter-aspect

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Mar 8, 2026

Test Results

484 tests   484 ✅  44s ⏱️
145 suites    0 💤
145 files      0 ❌

Results for commit 68d2fb4.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the StatusFilterAspect and related infrastructure to fix several correctness and robustness issues with the Hibernate statusFilter mechanism in the hexagonal (ports & adapters) architecture.

Changes:

  • Simplifies the Hibernate filter from a parameterized status IN (:statuses) to a hardcoded status = 'ACTIVE' condition, removing the @IncludeInactive annotation in favor of a single @Unfiltered mode.
  • Introduces FilterContextHolder (ThreadLocal) to decouple @Unfiltered intent (set at service level) from actual Session manipulation (performed at @Transactional adapter boundary), allowing @Unfiltered to work without requiring @Transactional on the service method itself.
  • Adds @Transactional(readOnly = true) to all *QueryPersistenceAdapter classes and configures explicit AOP ordering (@EnableTransactionManagement(order = LOWEST_PRECEDENCE - 1)) to guarantee the transaction is open before StatusFilterAspect fires.

Reviewed changes

Copilot reviewed 9 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
FilterContextHolder.java New ThreadLocal holder for FilterMode (ACTIVE_ONLY / UNFILTERED) to propagate filter intent across AOP boundaries
StatusFilterAspect.java Refactored to use ThreadLocal; unfiltered() records intent, enableActiveByDefault() reads it; simplified filter enable/disable logic
AppConfig.java Adds @EnableTransactionManagement(order = LOWEST_PRECEDENCE - 1) to ensure transaction opens before StatusFilterAspect runs
BaseJpaEntity.java Simplifies @FilterDef / @Filter to parameterless status = 'ACTIVE'
IncludeInactive.java Deleted — no longer supported
ErrorCode.java Removes PERSISTENCE_TRANSACTION_REQUIRED (no longer needed)
All *QueryPersistenceAdapter files (14) Adds @Transactional(readOnly = true) at class level to guarantee filter activation
StatusFilterTestConfig.java, StatusFilterTest.java, TestUserJpaRepository.java Rewrites tests to validate adapter-boundary filter behavior and @Unfiltered with/without @Transactional

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +36 to 44
private static final String PCUT_UNFILTERED = "@annotation(" + ANN_UNFILTERED + ")";

/**
* 기본: @Transactional 경계에서 ThreadLocal을 읽어 filter를 활성화.
* @Unfiltered가 붙은 메서드는 제외 (PCUT_UNFILTERED에서 처리).
*/
private static final String PCUT_TX_DEFAULT =
"(" + "@annotation(" + ANN_TX + ") || @within(" + ANN_TX + ")" + ")" +
" && !" + "@annotation(" + ANN_INCLUDE_INACTIVE + ")" +
" && !" + "@annotation(" + ANN_UNFILTERED + ")";
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @Unfiltered annotation (declared in Unfiltered.java) uses @Target({ElementType.METHOD, ElementType.TYPE}), which allows class-level usage. However, neither PCUT_UNFILTERED nor the exclusion in PCUT_TX_DEFAULT handles class-level @Unfiltered:

  • PCUT_UNFILTERED = "@annotation(ANN_UNFILTERED)" — only matches method-level annotations.
  • PCUT_TX_DEFAULT exclusion && !@annotation(ANN_UNFILTERED) — also only checks method-level annotations.

If @Unfiltered is placed on a class, its methods would still be intercepted by PCUT_TX_DEFAULT (which uses @within for @Transactional), causing the filter to be incorrectly activated. The AOP implementation should either remove ElementType.TYPE from @Unfiltered's @Target to prevent unsupported class-level usage, or extend both pointcuts with @within handling (e.g., || @within(ANN_UNFILTERED)) to properly support class-level annotation.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +24
static void clear() {
context.remove();
}

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clear() method is defined with package-private visibility, making it inaccessible from outside the konkuk.thip.common.aop package. It is currently not called from anywhere in the codebase (neither production code nor tests). If it's intended for use as a cleanup mechanism (e.g., after each HTTP request via a filter), it needs to be made public. If it's intended only for unit tests of the StatusFilterAspect, it should be called from within the test. Otherwise, this is dead code and should be removed.

Suggested change
static void clear() {
context.remove();
}

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomParticipantQueryPersistenceAdapter.java (1)

6-10: 조회 어댑터 전용 메타 어노테이션으로 묶어두는 편이 안전합니다.

이 PR부터 상태 필터의 정상 동작이 @Transactional(readOnly = true) 누락 여부에 달리게 됐는데, 지금처럼 각 QueryPersistenceAdapter에 수동으로 붙이면 신규 어댑터에서 빠뜨리기 쉽습니다. @Repository@Transactional(readOnly = true)를 합성한 공통 어노테이션으로 의도를 고정해 두는 쪽을 권합니다.

Based on learnings, THIP 팀은 Transactional 어노테이션을 서비스 메서드에 붙이는 것으로 통일했다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/konkuk/thip/room/adapter/out/persistence/RoomParticipantQueryPersistenceAdapter.java`
around lines 6 - 10, Create a composed annotation (e.g., `@ReadOnlyRepository`)
that meta-annotates `@Repository` and `@Transactional`(readOnly = true), apply it to
RoomParticipantQueryPersistenceAdapter (replacing the existing `@Repository` and
`@Transactional` annotations), and update other QueryPersistenceAdapter classes to
use this composed annotation so new adapters won’t miss the readOnly
transactional intent; remove the manual `@Repository/`@Transactional usage from
those classes.
src/main/java/konkuk/thip/common/aop/StatusFilterAspect.java (2)

62-72: 필터가 활성화되지 않은 경우에도 disableFilter() 호출됨

FilterContextHolder.get() == UNFILTERED인 경우 필터가 활성화되지 않지만, wasEnabled가 false이면 finally 블록에서 disableFilter(s)가 호출됩니다. Hibernate의 disableFilter는 이미 비활성화된 필터에 대해 호출해도 안전하지만, 불필요한 호출을 피하는 것이 좋습니다.

♻️ 제안된 수정
     `@Around`(PCUT_TX_DEFAULT)
     public Object enableActiveByDefault(ProceedingJoinPoint pjp) throws Throwable {
         Session s = em.unwrap(Session.class);
         boolean wasEnabled = isFilterEnabled(s);
+        boolean enabledByThisAdvice = false;

         if (FilterContextHolder.get() == ACTIVE_ONLY && !wasEnabled) {
             enableFilter(s);
+            enabledByThisAdvice = true;
         }

         try {
             return pjp.proceed();
         } finally {
-            if (!wasEnabled) {
+            if (enabledByThisAdvice) {
                 disableFilter(s);
             }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/konkuk/thip/common/aop/StatusFilterAspect.java` around lines 62
- 72, The finally block is disabling the filter even when it was never enabled
(e.g., when FilterContextHolder.get() == UNFILTERED); fix by tracking whether
this method actually enabled the filter and only disabling in finally when we
enabled it here. Concretely, inside StatusFilterAspect (around the code that
checks FilterContextHolder.get() == ACTIVE_ONLY and calls enableFilter(s)), set
a local boolean (e.g., enabledHere) to true when enableFilter(s) is invoked, and
in the finally block replace the current condition with a check that disables
only if enabledHere is true (or if wasEnabled was true when your logic requires
it). This uses the existing symbols enableFilter(s), disableFilter(s),
FilterContextHolder, ACTIVE_ONLY, wasEnabled and pjp.proceed() to locate the
change.

36-44: 클래스 레벨 @Unfiltered 지원 부족

PCUT_UNFILTERED 포인트컷이 @annotation()만 사용하고 @within()을 포함하지 않아, 클래스 레벨에 선언된 @Unfiltered는 인터셉트되지 않습니다. @Unfiltered 어노테이션이 @Target({ElementType.METHOD, ElementType.TYPE})으로 정의되어 있는 것과 불일치합니다.

PCUT_TX_DEFAULT의 제외 조건(44줄)도 @annotation(ANN_UNFILTERED)만 체크하므로, 클래스 레벨 @Unfiltered 적용 시 해당 클래스의 메서드들이 올바르게 제외되지 않습니다.

현재 코드베이스에는 클래스 레벨 사용이 없어 즉각적인 문제는 없으나, 일관성 있는 설계를 위해 포인트컷에 @within()을 추가하는 것이 권장됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/konkuk/thip/common/aop/StatusFilterAspect.java` around lines 36
- 44, PCUT_UNFILTERED currently checks only method-level
`@annotation`(ANN_UNFILTERED); update the pointcut to also include class-level
`@within`(ANN_UNFILTERED) so class-scoped `@Unfiltered` is matched. Similarly,
modify PCUT_TX_DEFAULT's exclusion (the !"@annotation(" + ANN_UNFILTERED + ")")
to exclude methods when either `@annotation`(ANN_UNFILTERED) OR
`@within`(ANN_UNFILTERED) is present. Apply these changes to the constants
PCUT_UNFILTERED and PCUT_TX_DEFAULT in StatusFilterAspect so they reference
ANN_UNFILTERED in both `@annotation`(...) and `@within`(...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/konkuk/thip/common/aop/FilterContextHolder.java`:
- Around line 10-22: The ThreadLocal in FilterContextHolder currently only
supports set(FilterMode) and clear(), which loses the outer caller's mode when
nested `@Unfiltered` calls occur; modify FilterContextHolder to save and restore
previous values by adding a method like setAndGetPrevious(FilterMode) or a
restore(FilterMode) (or make set return the previous FilterMode) so callers can
capture the prior mode, and change StatusFilterAspect's around advice to call
get() before setting the new mode and then in finally restore(previous) (or call
clear() only when previous was null) so nested invocations correctly revert to
the outer FilterMode instead of defaulting back to ACTIVE_ONLY.

---

Nitpick comments:
In `@src/main/java/konkuk/thip/common/aop/StatusFilterAspect.java`:
- Around line 62-72: The finally block is disabling the filter even when it was
never enabled (e.g., when FilterContextHolder.get() == UNFILTERED); fix by
tracking whether this method actually enabled the filter and only disabling in
finally when we enabled it here. Concretely, inside StatusFilterAspect (around
the code that checks FilterContextHolder.get() == ACTIVE_ONLY and calls
enableFilter(s)), set a local boolean (e.g., enabledHere) to true when
enableFilter(s) is invoked, and in the finally block replace the current
condition with a check that disables only if enabledHere is true (or if
wasEnabled was true when your logic requires it). This uses the existing symbols
enableFilter(s), disableFilter(s), FilterContextHolder, ACTIVE_ONLY, wasEnabled
and pjp.proceed() to locate the change.
- Around line 36-44: PCUT_UNFILTERED currently checks only method-level
`@annotation`(ANN_UNFILTERED); update the pointcut to also include class-level
`@within`(ANN_UNFILTERED) so class-scoped `@Unfiltered` is matched. Similarly,
modify PCUT_TX_DEFAULT's exclusion (the !"@annotation(" + ANN_UNFILTERED + ")")
to exclude methods when either `@annotation`(ANN_UNFILTERED) OR
`@within`(ANN_UNFILTERED) is present. Apply these changes to the constants
PCUT_UNFILTERED and PCUT_TX_DEFAULT in StatusFilterAspect so they reference
ANN_UNFILTERED in both `@annotation`(...) and `@within`(...).

In
`@src/main/java/konkuk/thip/room/adapter/out/persistence/RoomParticipantQueryPersistenceAdapter.java`:
- Around line 6-10: Create a composed annotation (e.g., `@ReadOnlyRepository`)
that meta-annotates `@Repository` and `@Transactional`(readOnly = true), apply it to
RoomParticipantQueryPersistenceAdapter (replacing the existing `@Repository` and
`@Transactional` annotations), and update other QueryPersistenceAdapter classes to
use this composed annotation so new adapters won’t miss the readOnly
transactional intent; remove the manual `@Repository/`@Transactional usage from
those classes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4286f2ee-3757-47d6-a364-4cd4d3c89c2d

📥 Commits

Reviewing files that changed from the base of the PR and between 9e87db0 and 68d2fb4.

📒 Files selected for processing (24)
  • src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentLikeQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/comment/adapter/out/persistence/CommentQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/common/annotation/persistence/IncludeInactive.java
  • src/main/java/konkuk/thip/common/aop/FilterContextHolder.java
  • src/main/java/konkuk/thip/common/aop/StatusFilterAspect.java
  • src/main/java/konkuk/thip/common/entity/BaseJpaEntity.java
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java
  • src/main/java/konkuk/thip/config/AppConfig.java
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/notification/adapter/out/persistence/NotificationQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/post/adapter/out/persistence/PostQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/room/adapter/out/persistence/RoomParticipantQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java
  • src/test/java/konkuk/thip/common/persistence/StatusFilterTest.java
  • src/test/java/konkuk/thip/config/StatusFilterTestConfig.java
  • src/test/java/konkuk/thip/config/TestUserJpaRepository.java
💤 Files with no reviewable changes (2)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java
  • src/main/java/konkuk/thip/common/annotation/persistence/IncludeInactive.java

Comment on lines +10 to +22
private static final ThreadLocal<FilterMode> context =
ThreadLocal.withInitial(() -> FilterMode.ACTIVE_ONLY);

public static FilterMode get() {
return context.get();
}

public static void set(FilterMode mode) {
context.set(mode);
}

static void clear() {
context.remove();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

중첩 @Unfiltered 호출에서 상태가 유실됩니다.

지금 API는 set()clear()만 가능해서, @Unfiltered 메서드 A 안에서 또 다른 @Unfiltered 메서드 B를 호출하면 B의 finally에서 clear()한 뒤 A의 남은 구간이 다시 ACTIVE_ONLY로 돌아갑니다. 이전 모드를 저장하고 finally에서 복원하는 방식으로 바꿔야 합니다.

🔧 예시 방향
-    public static void set(FilterMode mode) {
-        context.set(mode);
-    }
-
-    static void clear() {
-        context.remove();
-    }
+    static FilterMode push(FilterMode mode) {
+        FilterMode previous = context.get();
+        context.set(mode);
+        return previous;
+    }
+
+    static void restore(FilterMode previous) {
+        if (previous == null) {
+            context.remove();
+            return;
+        }
+        context.set(previous);
+    }

StatusFilterAspect 쪽에서는 around advice에서 이전 값을 받아 finally에서 restore(previous) 하도록 맞추면 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/konkuk/thip/common/aop/FilterContextHolder.java` around lines
10 - 22, The ThreadLocal in FilterContextHolder currently only supports
set(FilterMode) and clear(), which loses the outer caller's mode when nested
`@Unfiltered` calls occur; modify FilterContextHolder to save and restore previous
values by adding a method like setAndGetPrevious(FilterMode) or a
restore(FilterMode) (or make set return the previous FilterMode) so callers can
capture the prior mode, and change StatusFilterAspect's around advice to call
get() before setting the new mode and then in finally restore(previous) (or call
clear() only when previous was null) so nested invocations correctly revert to
the outer FilterMode instead of defaulting back to ACTIVE_ONLY.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] status filter aspect 수정

2 participants