Skip to content

feat: [ALT-214] 회원가입 약관 동의 API 구현#84

Merged
hodoon merged 15 commits into
devfrom
feat/ALT-214
May 8, 2026
Merged

feat: [ALT-214] 회원가입 약관 동의 API 구현#84
hodoon merged 15 commits into
devfrom
feat/ALT-214

Conversation

@hodoon
Copy link
Copy Markdown
Contributor

@hodoon hodoon commented May 7, 2026

관련 문서

https://www.notion.so/BE-API-35986553162880c8aba5dcb1db372d6a?v=3288655316288071b53d000c67f996d3&source=copy_link

Summary by CodeRabbit

  • New Features

    • 공개 약관 목록 조회용 공개 API 추가
    • 회원가입(일반/소셜) 시 약관 선택 필수화 및 동의된 약관 목록 제출 요구
    • 사용자 약관 동의 내역 저장 및 추적 기능 추가
  • Bug Fixes / Validation

    • 필수 약관 미동의 시 명확한 400 오류 코드(필수 약관 미동의) 반환
  • Tests

    • 약관 검증 및 가입 흐름 관련 단위 테스트 보강

@hodoon hodoon requested review from juny0955 and ysw789 May 7, 2026 06:25
@hodoon hodoon self-assigned this May 7, 2026
@hodoon hodoon added the FEAT label May 7, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

발행된 약관을 조회하는 공개 API를 추가하고, 가입(정규·소셜) 시 클라이언트가 제출한 약관 타입 집합을 검증해 필수 약관 누락 시 예외를 던지며, 동의한 약관을 UserTermsAgreement로 저장합니다.

변경 사항

약관 동의 기능 통합

Layer / File(s) Summary
도메인 엔티티 및 포트 계약
src/main/java/com/dreamteam/alter/domain/terms/entity/UserTermsAgreement.java, src/main/java/com/dreamteam/alter/domain/terms/port/inbound/GetPublishedTermsListUseCase.java, src/main/java/com/dreamteam/alter/domain/terms/port/outbound/TermsQueryRepository.java, src/main/java/com/dreamteam/alter/domain/terms/port/outbound/UserTermsAgreementRepository.java
약관 동의 추적용 JPA 엔티티과 공개 약관 조회/저장용 포트 인터페이스를 추가합니다.
API 스펙·DTO·컨트롤러
src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicControllerSpec.java, src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/dto/PublishedTermsItemResponseDto.java, src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserRequestDto.java, src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserWithSocialRequestDto.java, src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java
공개 약관 응답 DTO와 가입 요청 DTO에 agreedTermsTypes 필드를 추가하고 공개 컨트롤러 스펙/구현을 추가합니다.
저장소 구현 및 조회 유스케이스
src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/TermsQueryRepositoryImpl.java, src/main/java/com/dreamteam/alter/application/terms/usecase/GetPublishedTermsList.java
QueryDSL 서브쿼리로 타입별 최신 발행 약관을 조회하는 findLatestPublishedPerType() 구현과 이를 사용하는 읽기 전용 use-case를 추가합니다.
약관 동의 검증 서비스
src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java
클라이언트가 제출한 약관 타입 집합을 최신 발행 약관과 비교해 필수 항목 누락 시 REQUIRED_TERMS_NOT_AGREED 예외를 던지고, 일치하는 최신 Terms 목록을 반환합니다.
가입 서비스 변경 (정규)
src/main/java/com/dreamteam/alter/application/user/usecase/CreateUser.java
CreateUser에 TermsAgreementValidator를 주입하고 validateAndResolve(request.getAgreedTermsTypes())를 호출해 resolved Terms를 생성 트랜잭션으로 전달합니다.
가입 트랜잭션 변경 (정규)
src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserTx.java
프로세스 시 notification(야간 포함)을 저장하고, resolved Terms로부터 UserTermsAgreement 엔티티를 생성해 saveAll로 영속화합니다.
가입 서비스 변경 (소셜)
src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocial.java
소셜 가입에서도 약관 검증을 수행해 resolved Terms를 트랜잭션으로 전달합니다.
가입 트랜잭션 변경 (소셜)
src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTx.java
resolved Terms를 기반으로 UserTermsAgreement를 생성해 saveAll로 저장합니다.
UserTermsAgreement JPA·어댑터
src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/UserTermsAgreementJpaRepository.java, src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/UserTermsAgreementRepositoryImpl.java
Spring Data JPA 리포지토리와 포트 구현을 추가하여 saveAll을 위임합니다.
예외 코드 정의
src/main/java/com/dreamteam/alter/common/exception/ErrorCode.java
필수 약관 미동의 상황을 위한 REQUIRED_TERMS_NOT_AGREED 에러 코드를 추가합니다.
단위/통합 테스트
src/test/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidatorTests.java, src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java, src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTests.java
검증 서비스와 가입 흐름의 성공·실패 경로(필수 약관 미동의 포함)를 검증합니다.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Controller
  participant Validator
  participant UseCase
  participant Repo
  participant CreateTx
  Client->>Controller: POST /signup (agreedTermsTypes)
  Controller->>Validator: validateAndResolve(agreedTermsTypes)
  Validator->>Repo: findLatestPublishedPerType()
  Repo-->>Validator: List~Terms~
  alt missing required
    Validator-->>Controller: CustomException REQUIRED_TERMS_NOT_AGREED
    Controller-->>Client: 400
  else all good
    Validator-->>CreateTx: List~Terms~
    CreateTx->>Repo: saveAll(UserTermsAgreement)
    CreateTx-->>Controller: GenerateTokenResponseDto
    Controller-->>Client: 200
  end
Loading

예상 코드 리뷰 난이도

🎯 3 (Moderate) | ⏱️ ~25 minutes

관련 PR

제안 검토자

  • juny0955
  • ysw789
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.50% 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 제목은 ALT-214 이슈 번호와 함께 회원가입 약관 동의 API 구현이라는 주요 변경 사항을 명확하게 설명하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ALT-214

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.

Copy link
Copy Markdown

@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: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java`:
- Around line 22-23: The `@Resource`(name=...) on the private final field
getPublishedTermsList in TermsPublicController should be replaced with a Spring
`@Qualifier` to ensure name-based injection works with constructor injection;
remove the `@Resource` annotation and apply `@Qualifier`("getPublishedTermsList") to
the injection point (either the generated constructor parameter or an explicit
constructor) while keeping the field as private final and using constructor
injection (via `@RequiredArgsConstructor` or an explicit constructor) so Spring
will use the named bean for GetPublishedTermsListUseCase.

In
`@src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserRequestDto.java`:
- Around line 63-65: CreateUserRequestDto's agreedTermsIds only checks the list
itself with `@NotNull` but allows null elements, which can cause
TermsAgreementValidator.findById(null) to throw; add element-level Bean
Validation so each list element is non-null (e.g., apply `@NotNull` to the generic
type of agreedTermsIds and consider `@NotEmpty` on the collection) and adjust
imports/tests accordingly to ensure requests like [1, null, 3] are rejected
before reaching TermsAgreementValidator.

In
`@src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java`:
- Line 20: The validateAndResolve method in TermsAgreementValidator performs
multiple DB reads without a transaction, which can cause non-repeatable reads;
fix it by annotating the method validateAndResolve with `@Transactional`(readOnly
= true) (or apply the same annotation at class level if appropriate), ensure the
org.springframework.transaction.annotation.Transactional import is present, and
run tests to verify behavior remains unchanged.
- Around line 31-35: The current resolver in TermsAgreementValidator uses
termsQueryRepository.findById(id) which only applies notDeleted() and can return
DRAFT/unpublished Terms; update the resolution to only accept published terms by
adding/using a repository method that enforces status = PUBLISHED (e.g.,
TermsQueryRepository.findPublishedById or a modified findById that filters
status), and throw the same CustomException(ErrorCode.TERMS_NOT_FOUND, "약관 ID: "
+ id) when the published-term lookup returns empty so unpublished IDs cannot be
resolved or persisted as UserTermsAgreement.
- Around line 31-35: TermsAgreementValidator currently maps agreedTermsIds and
calls termsQueryRepository.findById for each id causing N+1 queries; change this
to a single bulk fetch by adding/using a repository method like
termsQueryRepository.findAllByIds(List<Long> ids) to retrieve all Term entities
at once, then validate that all requested ids were returned (compare sizes or
check missing ids) and throw the existing CustomException
(ErrorCode.TERMS_NOT_FOUND) for any missing id(s); update the logic in the
method that references agreedTermsIds and remove the per-id findById stream
usage to use the bulk result instead.

In
`@src/main/java/com/dreamteam/alter/application/terms/usecase/GetPublishedTermsList.java`:
- Line 3: GetPublishedTermsList currently depends on adapter DTO
PublishedTermsItemResponseDto which violates hexagonal boundaries; change the
use case and its execute() to return a domain List<Terms> (or appropriate domain
DTO/entity) instead of PublishedTermsItemResponseDto, remove the direct import
of PublishedTermsItemResponseDto from GetPublishedTermsList and
GetPublishedTermsListUseCase, and move the mapping from domain Terms ->
PublishedTermsItemResponseDto into the controller layer (controller should call
GetPublishedTermsList.execute(), map the returned List<Terms> to
PublishedTermsItemResponseDto, and return that to the client).

In
`@src/main/java/com/dreamteam/alter/domain/terms/entity/UserTermsAgreement.java`:
- Around line 4-20: UserTermsAgreement and other domain classes currently import
jakarta.persistence and Spring auditing annotations (e.g.,
AuditingEntityListener, `@CreatedDate`) which couples the domain to
infrastructure; remove all JPA/Spring-specific annotations and imports from the
domain class (UserTermsAgreement) and make it a pure POJO/value object
representing domain state and behavior, then create a separate persistence
representation (e.g., UserTermsAgreementEntity) in the
persistence/infrastructure layer that contains the jakarta.persistence
annotations, AuditingEntityListener use, and mapping fields; add mapping code
(mapper/constructor) between UserTermsAgreement and UserTermsAgreementEntity and
update repositories/services to persist the entity instead of the domain object
so the domain package has zero infrastructure dependencies.

In
`@src/main/java/com/dreamteam/alter/domain/terms/port/inbound/GetPublishedTermsListUseCase.java`:
- Around line 3-9: The GetPublishedTermsListUseCase inbound port currently
returns adapter DTO PublishedTermsItemResponseDto (import at top) which couples
the domain layer to the adapter; change the port to return a domain-level type
(e.g., List<Terms> or a new domain/port-specific response record) instead of
PublishedTermsItemResponseDto, update the interface signature of
GetPublishedTermsListUseCase.execute() accordingly, and move the
PublishedTermsItemResponseDto mapping into the adapter/controller so adapters
convert domain Terms → PublishedTermsItemResponseDto.

In
`@src/main/java/com/dreamteam/alter/domain/terms/port/outbound/UserTermsAgreementRepository.java`:
- Around line 1-7: The domain port interfaces (e.g.,
UserTermsAgreementRepository, ReportRepository, TermsRepository) currently
extend Spring Data's JpaRepository which leaks infrastructure into the domain;
change each port to a pure contract by removing JpaRepository and declaring only
the domain-needed methods (e.g., save(UserTermsAgreement),
Optional<UserTermsAgreement> findById(Long), existsByUserId(...)); then create
corresponding adapter implementations under adapter/outbound that extend
JpaRepository (or wrap a JpaRepository) to fulfill the port contracts, wiring
conversions/mapping as needed so the domain layer has zero Spring Data
dependencies.

In
`@src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java`:
- Around line 110-111: The verifications using anyList() for
cacheRepository.deleteAll and createUserTx.process are too loose; update them to
assert the actual lists passed (e.g., expected cache keys and terms) by
capturing or matching arguments: replace
then(cacheRepository).should().deleteAll(anyList()) with a stronger check using
an ArgumentCaptor or Mockito.argThat to compare the list contents to the
expected list, and similarly replace
then(createUserTx).should(never()).process(any(), any(), any(), anyBoolean(),
anyBoolean(), anyList()) with a matcher/captor that verifies the exact last
argument list (or uses eq(expectedTerms)) so the test fails on regressions;
apply the same change for the other occurrences referencing createUserTx.process
and cacheRepository.deleteAll in this test class.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b084980-b748-4d4a-a38d-34dd315385f1

📥 Commits

Reviewing files that changed from the base of the PR and between c05f998 and 1ffd035.

📒 Files selected for processing (20)
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicControllerSpec.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/dto/PublishedTermsItemResponseDto.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserRequestDto.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserWithSocialRequestDto.java
  • src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/TermsQueryRepositoryImpl.java
  • src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java
  • src/main/java/com/dreamteam/alter/application/terms/usecase/GetPublishedTermsList.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUser.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserTx.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocial.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTx.java
  • src/main/java/com/dreamteam/alter/common/exception/ErrorCode.java
  • src/main/java/com/dreamteam/alter/domain/terms/entity/UserTermsAgreement.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/inbound/GetPublishedTermsListUseCase.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/outbound/TermsQueryRepository.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/outbound/UserTermsAgreementRepository.java
  • src/test/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidatorTests.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTests.java

Comment thread src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java Outdated
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (3)
src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java (1)

20-34: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

@Transactional(readOnly = true) 누락

validateAndResolve()는 DB 읽기 전용 작업임에도 트랜잭션 어노테이션이 없습니다. Spring은 readOnly 속성을 통해 기저 데이터 접근 레이어를 최적화하며, Hibernate는 세션 FlushMode를 MANUAL로 설정하고 더티 체크를 비활성화합니다. 또한 읽기 전용 트랜잭션은 읽기-쓰기 메서드는 기본 데이터 소스를, @Transactional(readOnly = true) 메서드는 읽기 전용 데이터 소스를 사용하도록 라우팅이 가능합니다.

🛡️ 수정 제안
+import org.springframework.transaction.annotation.Transactional;

 `@Component`("termsAgreementValidator")
 `@RequiredArgsConstructor`
 public class TermsAgreementValidator {

     private final TermsQueryRepository termsQueryRepository;

+    `@Transactional`(readOnly = true)
     public List<Terms> validateAndResolve(Set<TermsType> agreedTermsTypes) {

As per coding guidelines, "Read-only operations use @Transactional(readOnly = true)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java`
around lines 20 - 34, The validateAndResolve method in TermsAgreementValidator
performs only read operations but lacks a transactional annotation; annotate the
validateAndResolve(Set<TermsType> agreedTermsTypes) method (or the containing
class) with `@Transactional`(readOnly = true) so Spring/Hibernate can optimize
reads (e.g., set FlushMode.MANUAL and route to read-only datasource); ensure the
annotation is imported from
org.springframework.transaction.annotation.Transactional and applied to the
method signature or class-level as appropriate.
src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java (1)

22-23: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

@Resource(name = ...) 이름 기반 주입이 여전히 동작하지 않습니다.

@RequiredArgsConstructor가 생성하는 생성자 파라미터에 @Resourcename 힌트가 전달되지 않으므로 지정한 빈 이름이 무시됩니다. 현재는 구현체가 하나뿐이라 타입 기반으로 우연히 동작하지만, 구현체가 추가되면 NoUniqueBeanDefinitionException이 발생합니다.

@Qualifier로 교체하세요.

♻️ 수정 방안
+import org.springframework.beans.factory.annotation.Qualifier;
 ...
-    `@Resource`(name = "getPublishedTermsList")
+    `@Qualifier`("getPublishedTermsList")
     private final GetPublishedTermsListUseCase getPublishedTermsList;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java`
around lines 22 - 23, The `@Resource`(name = "getPublishedTermsList") on the final
GetPublishedTermsListUseCase field in TermsPublicController won't pass the name
into the Lombok-generated constructor; replace it with Spring's `@Qualifier` to
preserve the bean name for constructor injection: remove `@Resource` and annotate
the GetPublishedTermsListUseCase injection point with
`@Qualifier`("getPublishedTermsList") so the `@RequiredArgsConstructor-created`
constructor receives the qualifier and the correct bean is selected at runtime
(apply the same change for any other `@Resource` usages that need name-based
injection).
src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java (1)

192-204: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

anyList() 기반 검증이 회귀를 놓칠 수 있습니다.

성공 케이스에서 createUserTx.processagreedTerms 인자 및 cacheRepository.deleteAll의 키 목록을 anyList()로 검증하면 잘못된 인자가 전달되어도 테스트가 통과됩니다. termsAgreementValidator.validateAndResolve의 반환값이 이미 List.of(termsMock1, termsMock2)로 명확히 스텁되어 있으므로 eq(...)로 강화할 수 있습니다.

♻️ 강화된 검증 예시
-            then(createUserTx).should().process(eq(request), eq("01012345678"), any(), eq(true), eq(false), anyList());
-            then(cacheRepository).should().deleteAll(anyList());
+            then(createUserTx).should().process(eq(request), eq("01012345678"), any(), eq(true), eq(false), eq(List.of(termsMock1, termsMock2)));
+            then(cacheRepository).should().deleteAll(
+                eq(List.of("SIGNUP:PENDING:signup-session-id", "SIGNUP:CONTACT:01012345678"))
+            );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java`
around lines 192 - 204, Replace the loose anyList() verifications with exact
equality checks: assert that createUserTx.process(...) receives the resolved
agreed terms by using eq(List.of(termsMock1, termsMock2)) for the agreedTerms
parameter (call site: createUserTx.process) and assert
cacheRepository.deleteAll(...) is called with eq(List.of(termsMock1,
termsMock2)) (call site: cacheRepository.deleteAll); keep the existing stubbing
of termsAgreementValidator.validateAndResolve(List) as-is and update the two
then(...).should() verifications to use eq(...) matching the stubbed list.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java`:
- Around line 22-23: The `@Resource`(name = "getPublishedTermsList") on the final
GetPublishedTermsListUseCase field in TermsPublicController won't pass the name
into the Lombok-generated constructor; replace it with Spring's `@Qualifier` to
preserve the bean name for constructor injection: remove `@Resource` and annotate
the GetPublishedTermsListUseCase injection point with
`@Qualifier`("getPublishedTermsList") so the `@RequiredArgsConstructor-created`
constructor receives the qualifier and the correct bean is selected at runtime
(apply the same change for any other `@Resource` usages that need name-based
injection).

In
`@src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java`:
- Around line 20-34: The validateAndResolve method in TermsAgreementValidator
performs only read operations but lacks a transactional annotation; annotate the
validateAndResolve(Set<TermsType> agreedTermsTypes) method (or the containing
class) with `@Transactional`(readOnly = true) so Spring/Hibernate can optimize
reads (e.g., set FlushMode.MANUAL and route to read-only datasource); ensure the
annotation is imported from
org.springframework.transaction.annotation.Transactional and applied to the
method signature or class-level as appropriate.

In
`@src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java`:
- Around line 192-204: Replace the loose anyList() verifications with exact
equality checks: assert that createUserTx.process(...) receives the resolved
agreed terms by using eq(List.of(termsMock1, termsMock2)) for the agreedTerms
parameter (call site: createUserTx.process) and assert
cacheRepository.deleteAll(...) is called with eq(List.of(termsMock1,
termsMock2)) (call site: cacheRepository.deleteAll); keep the existing stubbing
of termsAgreementValidator.validateAndResolve(List) as-is and update the two
then(...).should() verifications to use eq(...) matching the stubbed list.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ce10aaf7-d562-4776-a484-970a397d7057

📥 Commits

Reviewing files that changed from the base of the PR and between 1ffd035 and 80454be.

📒 Files selected for processing (15)
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/controller/TermsPublicController.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserRequestDto.java
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/user/dto/CreateUserWithSocialRequestDto.java
  • src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/TermsQueryRepositoryImpl.java
  • src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java
  • src/main/java/com/dreamteam/alter/application/terms/usecase/GetPublishedTermsList.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUser.java
  • src/main/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocial.java
  • src/main/java/com/dreamteam/alter/common/exception/ErrorCode.java
  • src/main/java/com/dreamteam/alter/domain/terms/entity/UserTermsAgreement.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/inbound/GetPublishedTermsListUseCase.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/outbound/TermsQueryRepository.java
  • src/test/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidatorTests.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTests.java

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java`:
- Around line 22-35: TermsAgreementValidator.validateAndResolve currently calls
agreedTermsTypes.contains(...) and can NPE if agreedTermsTypes is null; add an
explicit null guard at the start of validateAndResolve (e.g., if
(agreedTermsTypes == null) throw new
CustomException(ErrorCode.REQUIRED_TERMS_NOT_AGREED);) so that a domain-level
CustomException is thrown instead of a NullPointerException before any uses of
agreedTermsTypes in the required-check and the final filter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f61519e8-482c-495b-a4d3-eb2e6d0e6bed

📥 Commits

Reviewing files that changed from the base of the PR and between 80454be and f2319fa.

📒 Files selected for processing (6)
  • src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/UserTermsAgreementJpaRepository.java
  • src/main/java/com/dreamteam/alter/adapter/outbound/terms/persistence/UserTermsAgreementRepositoryImpl.java
  • src/main/java/com/dreamteam/alter/application/terms/service/TermsAgreementValidator.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/outbound/UserTermsAgreementRepository.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserTests.java
  • src/test/java/com/dreamteam/alter/application/user/usecase/CreateUserWithSocialTests.java

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/dto/PublishedTermsItemResponseDto.java`:
- Line 47: PublishedTermsItemResponseDto currently builds version with
".version("v" + result.version())" which yields "vnull" if result.version() is
null; change this to null-safe handling by checking result.version() first
(e.g., if result.version() == null throw an IllegalStateException or return a
clear default/empty value) and only prepend "v" when a non-null version exists;
update the builder usage in PublishedTermsItemResponseDto to use that
null-checked value (referencing result.version() and the .version(...) builder
call).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f457d222-3d44-4ee5-a6a2-34d241e7bf4f

📥 Commits

Reviewing files that changed from the base of the PR and between f2319fa and 509338e.

📒 Files selected for processing (4)
  • src/main/java/com/dreamteam/alter/adapter/inbound/general/terms/dto/PublishedTermsItemResponseDto.java
  • src/main/java/com/dreamteam/alter/application/terms/usecase/GetPublishedTermsList.java
  • src/main/java/com/dreamteam/alter/domain/terms/port/inbound/GetPublishedTermsListUseCase.java
  • src/main/java/com/dreamteam/alter/domain/terms/result/GetPublishedTermsListResult.java

@hodoon hodoon merged commit c8102a0 into dev May 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants