Skip to content

refactor: Presentation Layer 구조 개선#7

Merged
bebeis merged 7 commits intodevelopmentfrom
refactor/controller
Jun 12, 2025
Merged

refactor: Presentation Layer 구조 개선#7
bebeis merged 7 commits intodevelopmentfrom
refactor/controller

Conversation

@bebeis
Copy link
Contributor

@bebeis bebeis commented Jun 12, 2025

Summary by CodeRabbit

  • 신규 기능

    • 회원 인증 및 회원가입, 소셜 로그인, 토큰 갱신 등 인증 관련 API가 추가되었습니다.
    • 회원 계정 관리(비밀번호 변경, 탈퇴), 주소 관리, 예산 관리, 카테고리 선호도 관리 등 마이페이지 관련 API가 세분화되어 제공됩니다.
    • 학교 그룹 등록/수정/삭제 전용 API가 추가되었습니다.
  • 기능 개선 및 변경

    • 그룹 및 회원 프로필 관련 엔드포인트가 분리·재구성되어 관리가 용이해졌습니다.
    • 기존에 제공되던 일부 그룹, 회원, 예산, 선호도 관련 API가 통합 또는 분리되었습니다.
  • 버그 수정

    • 일부 데이터 조회 시 잘못된 엔티티 그래프 설정이 수정되었습니다.
  • 테스트

    • 서비스, 도메인, 컨트롤러 전반에 걸쳐 다양한 통합 테스트와 단위 테스트가 추가되어 신뢰성이 향상되었습니다.
  • 문서화 및 기타

    • 불필요하거나 중복된 테스트 코드 및 컨트롤러가 정리되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jun 12, 2025

Walkthrough

이 변경사항은 인증, 회원, 그룹, 예산, 선호도 등 여러 도메인에 걸쳐 컨트롤러 구조를 대대적으로 리팩토링하고, 테스트를 대폭 보강하며, 일부 도메인 엔티티와 서비스의 필드 및 메서드를 제거합니다. 기존 컨트롤러와 테스트 클래스가 삭제되고, 새로운 REST API 컨트롤러와 통합 테스트가 추가되었습니다.

Changes

파일/경로 그룹 변경 요약
src/main/java/com/stcom/smartmealtable/domain/member/MemberProfile.java, src/main/java/com/stcom/smartmealtable/service/MemberProfileService.java MemberProfile 엔티티에서 기본 예산 필드 및 관련 메서드 제거, 서비스 메서드 제거
src/main/java/com/stcom/smartmealtable/repository/MemberProfileRepository.java @entitygraph 구문 오류 수정
src/main/java/com/stcom/smartmealtable/service/BudgetService.java 일별 예산 일괄 저장 로직으로 개선
src/main/java/com/stcom/smartmealtable/service/MemberService.java validateDuplicatedEmail의 JavaDoc 삭제
src/main/java/com/stcom/smartmealtable/web/controller/AuthController.java, AuthTokenController.java, MemberAccountController.java, MemberAddressController.java, MemberBudgetController.java, MemberPreferenceController.java, SchoolGroupController.java 신규 REST API 컨트롤러 다수 추가: 인증, 회원 계정, 주소, 예산, 선호도, 학교 그룹 관리 등
src/main/java/com/stcom/smartmealtable/web/controller/GroupController.java 학교 그룹 생성/수정 관련 엔드포인트 및 내부 DTO 삭제, 외부 DTO로 대체
src/main/java/com/stcom/smartmealtable/web/controller/MemberController.java 컨트롤러 → DTO 컨테이너 클래스로 변경, 모든 엔드포인트 삭제
src/main/java/com/stcom/smartmealtable/web/controller/MemberProfileController.java 주소/선호도/예산 관련 모든 엔드포인트 및 DTO 삭제, 프로필 조회/수정만 남김
src/main/java/com/stcom/smartmealtable/web/controller/LoginController.java, OAuth2Controller.java 로그인 및 OAuth2 컨트롤러 삭제
src/main/java/com/stcom/smartmealtable/web/dto/group/GroupDto.java, SchoolGroupCreateRequest.java, SchoolGroupUpdateRequest.java 그룹/학교그룹 관련 신규 DTO 클래스 추가
src/test/java/com/stcom/smartmealtable/... (테스트 전체) 서비스, 리포지토리, 컨트롤러, 도메인 등 전반에 걸친 통합/단위 테스트 대량 추가. 기존 Mockito 기반 단위 테스트 다수 삭제, 실제 DB 기반 통합 테스트로 대체
src/test/java/com/stcom/smartmealtable/service/exception/ResourceNotFoundException.java 빈 예외 클래스 추가(내용 없음)

Sequence Diagram(s)

sequenceDiagram
actor User
participant AuthController
participant MemberService
participant JwtTokenService

User->>AuthController: POST /api/v1/auth/signup (회원가입)
AuthController->>MemberService: 이메일 중복 확인, 회원 저장
AuthController->>JwtTokenService: JWT 토큰 생성
AuthController-->>User: JWT 토큰 반환

User->>AuthTokenController: POST /api/v1/auth/login (로그인)
AuthTokenController->>LoginService: 로그인 인증
AuthTokenController->>JwtTokenService: JWT 토큰 생성
AuthTokenController-->>User: JWT 토큰 반환

User->>MemberAccountController: PATCH /api/v1/members/me/password (비밀번호 변경)
MemberAccountController->>MemberService: 비밀번호 변경

User->>MemberAddressController: PUT /api/v1/members/me/addresses (주소 등록)
MemberAddressController->>MemberProfileService: 주소 저장

User->>MemberBudgetController: GET/PUT/PATCH /api/v1/members/me/budgets (예산 조회/등록/수정)
MemberBudgetController->>BudgetService: 예산 관련 비즈니스 처리

User->>MemberPreferenceController: GET/POST /api/v1/members/me/preferences (선호도 조회/저장)
MemberPreferenceController->>MemberCategoryPreferenceService: 선호도 저장/조회
Loading

Possibly related PRs

  • feat: 회원 도메인/API 개발 #1: MemberProfile의 기본 예산 필드 및 관련 메서드 도입 PR로, 본 PR은 해당 필드와 메서드를 제거하므로 직접적인 코드 레벨 연관성이 있습니다.

Poem

🐇
새 아침, API가 깔끔해졌네!
컨트롤러 토끼가 깡총깡총,
로그인, 회원가입, 주소, 예산까지
테스트밭에 무럭무럭 자라난다네.
오래된 코드는 굿바이,
새싹 코드로 봄바람 솔솔~
오늘도 깡총!

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 27

🔭 Outside diff range comments (1)
src/main/java/com/stcom/smartmealtable/web/controller/MemberProfileController.java (1)

28-34: 🛠️ Refactor suggestion

입력값 유효성 검증이 부족합니다
nickName, memberType, groupId 등이 null 이거나 비어 있을 때의 처리 로직이 없습니다. 서비스 예외로 미루기보다 컨트롤러 단계에서 @Validated 제약을 추가하는 편이 API 일관성에 도움이 됩니다.

🧹 Nitpick comments (50)
src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupUpdateRequest.java (1)

11-23: Create‧Update DTO 중복 구조 개선 제안
SchoolGroupCreateRequest 와 필드·검증 애너테이션 구성이 동일합니다. 두 DTO 의 상속/인터페이스 추출 또는 Java 17 record 활용으로 중복을 제거하면 유지보수가 수월해집니다.

src/test/java/com/stcom/smartmealtable/domain/group/SchoolGroupTest.java (1)

32-35: 세부 주소(detailAddress) 검증 누락
주소 변경 로직의 완전성을 보장하려면 detailAddress 필드까지 검증해 주세요.
예) assertThat(group.getAddress().getDetailAddress()).isEqualTo("행정관 2층");

src/test/java/com/stcom/smartmealtable/domain/group/CompanyGroupTest.java (1)

33-35: detailAddress 검증 추가 권장
SchoolGroupTest 와 동일하게 세부 주소까지 검증하면 변경 영향 파악이 쉬워집니다.

src/test/java/com/stcom/smartmealtable/repository/GroupRepositoryTest.java (1)

38-40: 결과 건수에 대한 명시적 검증 추가
현재는 첫 번째 엔티티만 확인하고 있어 기대 사이즈가 잘못돼도 통과합니다.
assertThat(result).hasSize(1); 등을 추가하면 테스트 강도가 향상됩니다.

src/main/java/com/stcom/smartmealtable/web/dto/group/GroupDto.java (1)

10-16: 응답 DTO 에 Setter 노출 불필요
@Data 는 setter 까지 생성하여 불변성이 깨질 수 있습니다. 읽기 전용 DTO 인 경우 @Getter 또는 Java record 전환을 고려해 주세요.

src/main/java/com/stcom/smartmealtable/web/controller/GroupController.java (1)

24-31: 빈 키워드 오류 시 HTTP 상태 코드 명확화 필요

keyword.isBlank()인 경우에도 ApiResponse.createError만 반환하고 HTTP 상태 코드는 그대로 200일 가능성이 있습니다. 클라이언트 입장에서는 성공과 실패를 HTTP 코드로 구분하는 것이 직관적이므로 400(Bad Request)로 내려주는 방식을 고려해 주세요.

만약 ApiResponse 내부에서 상태 코드를 설정하지 않는다면, ResponseEntity를 사용하거나 @ResponseStatus(HttpStatus.BAD_REQUEST)를 적용하는 것이 좋습니다.

src/test/java/com/stcom/smartmealtable/service/GroupServiceIntegrationTest.java (1)

49-53: Reflection 사용 대신 전용 팩토리/생성자 사용 권장

테스트에서 ReflectionTestUtils로 내부 필드를 주입하고 있는데, 이는 도메인 모델 캡슐화를 깨뜨립니다. 테스트 전용 정적 팩토리 메서드나 빌더를 도메인 객체에 제공하면 리플렉션 의존성을 제거할 수 있습니다.

src/main/java/com/stcom/smartmealtable/web/controller/SchoolGroupController.java (1)

28-33: 리소스 생성 시 201 Created 및 Location 헤더 반환 고려

POST 요청으로 학교 그룹을 생성한 후 201 Created 상태 코드와 함께 Location 헤더(신규 리소스 URI)를 반환하면 REST 규약에 더 부합합니다. ResponseEntity.created(URI)를 사용하는 방안을 검토해 주세요.

src/test/java/com/stcom/smartmealtable/service/GroupServiceAdditionalIntegrationTest.java (1)

51-65: @Transactional 테스트에서 중복 @Rollback 어노테이션 불필요

@Transactional을 사용한 Spring 테스트는 기본적으로 테스트 종료 시 롤백됩니다. 개별 테스트 메서드에 @Rollback을 추가로 선언하지 않아도 동일한 효과가 있으니 제거하여 어노테이션 중복을 줄일 수 있습니다.

src/test/java/com/stcom/smartmealtable/service/GroupServiceCompleteIntegrationTest.java (2)

201-204: 미완성 테스트 처리 필요

해당 메서드는 실제 검증 로직 없이 주석만 남아 있습니다. CI 안정성을 위해 최소한 @Disabled("추후 CompanyGroup 도입 시 작성 예정")로 표기하거나, 구현 완료 시점을 앞당기는 것을 권장드립니다.


62-68: 중복 초기화 로직 유틸화 제안

Address 빌더 호출이 세 번 반복되고 있어 가독성이 떨어집니다. 정적 헬퍼나 @BeforeEach 내부의 작은 팩토리 메서드로 추출하면 테스트가 더 간결해집니다.

src/main/java/com/stcom/smartmealtable/service/MemberService.java (1)

19-24: 불필요한 의존성 주입 제거 고려

addressEntityRepository 필드가 선언만 되고 실제 메서드에서 전혀 사용되지 않습니다. 사용 계획이 없다면 주입을 제거하여 의존성·테스트·가독성을 간소화하세요.

-    private final AddressEntityRepository addressEntityRepository;
src/main/java/com/stcom/smartmealtable/service/BudgetService.java (1)

61-70: 일괄 저장 로직은 좋지만 중복·성능 이슈 점검 필요

  1. 이미 존재하는 일일 예산이 있을 경우 saveAll 시 제약 조건 위반으로 실패할 가능성이 있습니다.
    • 저장 전 존재 여부를 필터링하거나, ON CONFLICT 전략(예: JPA merge) 고려가 필요합니다.
  2. ArrayList 초기 용량을 YearMonth.now().lengthOfMonth() 로 설정하면 불필요한 리사이즈를 줄일 수 있습니다.
- List<DailyBudget> budgets = new ArrayList<>();
+ int days = startDate.lengthOfMonth() - startDate.getDayOfMonth() + 1;
+ List<DailyBudget> budgets = new ArrayList<>(days);
src/test/java/com/stcom/smartmealtable/repository/FoodCategoryRepositoryTest.java (1)

28-34: ReflectionTestUtils 사용 최소화 제안

엔티티에 Setter 혹은 빌더가 있다면 ReflectionTestUtils.setField 대신 정식 API로 값을 지정하는 편이 유지보수에 유리합니다.
필드가 불변이어야 한다면 테스트용 팩토리 메서드를 두는 것도 한 방법입니다.

src/main/java/com/stcom/smartmealtable/domain/member/MemberProfile.java (1)

55-62: 불필요한 FQN 사용

new java.util.ArrayList<>() 대신 상단 import java.util.ArrayList; 를 추가하고 간결하게 new ArrayList<>() 로 작성하면 가독성이 좋아집니다.

src/test/java/com/stcom/smartmealtable/repository/TermAgreementRepositoryTest.java (2)

39-42: ReflectionTestUtils 남용

테스트 목적이라도 필드 접근을 리플렉션으로 우회하면 유지보수성이 떨어집니다.
Term 엔티티에 테스트 전용 빌더나 생성자를 제공하는 방법을 고려해 주세요.


52-58: 검증 범위 확장 제안

isAgreed 값에 대한 검증이 빠져 있습니다. 또한 Optional 을 두 번 get() 하기보다는 hasValueSatisfying 를 사용하면 NPE 우려 없이 더 읽기 쉬운 테스트가 됩니다.

-        var found = repository.findById(agreement.getId());
-
-        // then
-        assertThat(found).isPresent();
-        assertThat(found.get().getTerm().getTitle()).isEqualTo("테스트 약관");
-        assertThat(found.get().getMember().getEmail()).isEqualTo("agree@test.com");
+        assertThat(repository.findById(agreement.getId()))
+                .hasValueSatisfying(a -> {
+                    assertThat(a.isAgreed()).isTrue();
+                    assertThat(a.getTerm().getTitle()).isEqualTo("테스트 약관");
+                    assertThat(a.getMember().getEmail()).isEqualTo("agree@test.com");
+                });
src/test/java/com/stcom/smartmealtable/repository/TermRepositoryTest.java (1)

42-56: 테스트 유틸리티 코드 중복

ReflectionTestUtils 를 이용해 필드를 세팅하는 보일러플레이트가 여러 테스트에서 반복됩니다.
엔티티에 패키지-private 생성자 또는 빌더를 제공해 테스트 코드 중복을 줄여 보세요.

src/test/java/com/stcom/smartmealtable/repository/SocialAccountRepositoryTest.java (1)

36-44: 시간 의존성 제거 권장

LocalDateTime.now() 를 사용하면 타임존이나 시스템 시간 변경에 따라 테스트가 오동작할 수 있습니다.
Clock 을 주입받아 고정값을 사용하거나 LocalDateTime.of(...) 로 명시적인 값을 넣어 테스트를 결정론적으로 만드시는 편이 좋습니다.

src/test/java/com/stcom/smartmealtable/service/MemberServiceFailureIntegrationTest.java (1)

28-35: @Rollback 어노테이션 사용 일관성

클래스 레벨에 @Transactional 이 선언되어 있으므로 각 테스트는 기본적으로 롤백됩니다.
특정 테스트 메서드에만 @Rollback 을 다시 선언하면 의도를 파악하기 어렵습니다. 전부 제거하거나 필요시 @Commit 으로 명시적으로 커밋하는 형태로 통일하세요.

src/test/java/com/stcom/smartmealtable/repository/MemberProfileRepositoryTest.java (1)

46-50: 엔티티 그래프 검증이 확실하지 않습니다

assertThat(found.get().getMember().getEmail()) 만으로는 member 연관 엔티티가 실제로 fetch-join 되었는지 확인할 수 없습니다. 트랜잭션이 열려 있으면 LAZY 로딩도 통과하므로, Hibernate.isInitialized(found.get().getMember()) 등을 이용해 초기화 여부를 명시적으로 검증하거나 EntityManagerFactory 의 통계 조회로 추가 쿼리 발생 여부를 확인하는 방식이 보다 안전합니다.

src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceIntegrationTest.java (1)

44-48: 시간 기반 단정식이 경계 조건에서 실패할 수 있습니다

isAfter(LocalDateTime.now()) 는 밀리초 단위 경합으로 드물게 실패할 수 있습니다.
isAfterOrEqualTo 로 완화하거나, tokenExpiresAt 계산 시점과 비교값을 동일 객체로 두고 검증하는 편이 안전합니다.

src/main/java/com/stcom/smartmealtable/web/controller/MemberController.java (2)

16-24: DTO에 기본 생성자 부재

Lombok @Data + @AllArgsConstructor 조합만으로는 Jackson 직렬화/역직렬화 시 기본 생성자가 없어 실패할 수 있습니다.
@NoArgsConstructor 또는 @Builder 추가를 고려해주세요.


34-39: Boolean 필드 네이밍

isAgreed 는 Boolean wrapper 이므로 게터가 getIsAgreed() 로 생성되어 직관성이 떨어집니다. agreed 로 필드명을 바꾸는 것이 일반적입니다.

src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceIntegrationTest.java (1)

60-66: Reflection 사용 최소화 권장

테스트에서 ReflectionTestUtils.setFieldname 을 주입하고 있습니다.
엔티티에 테스트용 팩터리 메서드·builder 를 제공하면 리플렉션 없이도 가독성과 안정성을 유지할 수 있습니다.

src/test/java/com/stcom/smartmealtable/service/LoginServiceIntegrationTest.java (1)

30-31: 중복된 @Rollback 어노테이션 제거 제안

클래스에 이미 @Transactional 이 선언되어 있어 테스트 종료 시 자동 롤백됩니다.
메서드 레벨의 @Rollback 은 중복 설정이므로 제거해도 동일하게 동작합니다.

src/test/java/com/stcom/smartmealtable/service/MemberServiceAdditionalIntegrationTest.java (1)

28-39: 테스트 픽스처 저장 방식 통일 권장

setUp() 에서 memberRepository.save() 를 직접 호출하고 있습니다. 다른 테스트 클래스들은 memberService.saveMember() 를 사용합니다.
서비스 계층을 통한 저장으로 통일하면 추후 도메인 로직(이벤트, 암호화 등) 변경 시 테스트 누락을 방지할 수 있습니다.

src/test/java/com/stcom/smartmealtable/service/MemberServiceIntegrationTest.java (2)

55-61: 리포지토리 직접 저장 → 서비스 계층 사용으로 교체 권장

통합 테스트에서는 도메인 규칙(비밀번호 암호화, 이벤트 발행 등)을 포함한 서비스 계층을 거쳐 엔티티를 생성하는 편이 안정적입니다.
memberRepository.save(member); 부분을 memberService.saveMember(member); 로 교체하는 것을 고려해 주세요.


32-35: 메서드 단위 @Rollback 중복

클래스가 @Transactional 이므로 메서드 레벨 @Rollback 은 필요 없습니다. 삭제하여 어노테이션 중복을 줄이세요.

src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceCompleteIntegrationTest.java (1)

42-44: Member 생성 시 비밀번호 정책 누락

new Member("test@example.com") 형태로 비밀번호 없이 엔티티를 생성하고 있습니다.
다른 서비스 로직(로그인, 암호 정책 검사 등)과의 일관성을 위해 Member.builder().rawPassword(...) 등을 사용해 실제 비밀번호 필드를 채워 두는 편이 좋습니다.

src/test/java/com/stcom/smartmealtable/service/BudgetServiceCompleteIntegrationTest.java (1)

249-256: LocalDate.now() / YearMonth.now() 사용으로 인한 플래키 테스트 위험
호출 시점이 자정을 경계로 넘어가면 서비스 내부 시각과 단정 시각이 달라질 수 있습니다. Clock 을 주입하거나 테스트에서 고정 시각을 사용해 안정성을 확보하세요.

Also applies to: 269-275

src/test/java/com/stcom/smartmealtable/service/BudgetServiceAdditionalIntegrationTest2.java (1)

84-93: 주간 예산 결과 정렬 가정이 명시적이지 않습니다
서비스가 날짜 오름차순으로 반환한다는 계약이 문서화되어 있지 않으면 인덱스-기반 단정이 깨질 수 있습니다.

  1. 서비스 스펙에 정렬 보장을 명시하거나
  2. 테스트에서 sorted() 후 검증하는 방식으로 변경을 권장합니다.
src/main/java/com/stcom/smartmealtable/web/controller/MemberProfileController.java (1)

13-13: 와일드카드 import(*) 사용은 지양해주세요
정확한 클래스를 명시하면 IDE 자동 정리 및 리팩터링 시 충돌을 줄일 수 있습니다.

src/test/java/com/stcom/smartmealtable/service/TermServiceIntegrationTest.java (1)

66-76: 리플렉션으로 내부 필드를 설정하는 것은 유지보수성을 해칩니다
엔티티에 빌더/팩토리 메서드를 추가해 테스트 코드에서 리플렉션을 제거하세요. 이는 코드 가독성과 리팩터링 안정성을 높입니다.

src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceAdditionalIntegrationTest.java (2)

35-36: @Rollback 애노테이션은 불필요합니다

클래스 레벨에 @Transactional이 선언되어 있으므로 각 테스트는 기본적으로 롤백됩니다. 메소드 수준에 별도로 @Rollback을 추가하면 중복 선언이 됩니다.


52-57: 검증 범위가 부족합니다 – refreshToken·만료시간도 함께 확인하세요

토큰 갱신 로직을 검증할 때 access token 한 가지만 확인하고 있습니다.
refreshToken, tokenExpiresAt도 함께 검증하면 회귀를 더 확실히 방지할 수 있습니다.

-assertThat(updated.getAccessToken()).isEqualTo("new");
+assertThat(updated.getAccessToken()).isEqualTo("new");
+assertThat(updated.getRefreshToken()).isEqualTo("new_r");
+assertThat(updated.getTokenExpiresAt()).isAfter(LocalDateTime.now());
src/test/java/com/stcom/smartmealtable/service/LoginServiceCompleteIntegrationTest.java (2)

150-155: 토큰 만료 시각 검증 추가 권장

Access/Refresh 토큰 값만 확인하고 있어 만료 시각이 제대로 연장되었는지는 알 수 없습니다.

 assertThat(updatedAccount.getRefreshToken()).isEqualTo("new_refresh_token");
+assertThat(updatedAccount.getTokenExpiresAt()).isAfter(LocalDateTime.now());

47-84: setup 로직 중복 제거 제안

@BeforeEach가 비교적 무거운 엔티티 저장을 반복합니다.
테스트 수가 증가하면 I/O 비용이 커지므로, 공통 픽스처를 @TestInstance(Lifecycle.PER_CLASS) 또는 @BeforeAll + 각 테스트에서 필요한 추가 데이터만 준비하는 방식으로 최적화해볼 수 있습니다.

src/test/java/com/stcom/smartmealtable/service/LoginServiceAdditionalIntegrationTest.java (2)

60-63: 예외 메시지 검증은 최소화하는 것이 안전합니다

구체적인 메시지까지 검증하면 i18n 변경이나 사소한 워딩 수정에도 테스트가 깨집니다.
타입만 검증하거나, 메시지는 contains 수준으로 완화하는 것을 권장합니다.


138-143: tokenExpiresAt 갱신 여부도 함께 검증하면 좋습니다

기존 소셜 계정의 토큰 교체 시 만료 시각까지 변경되는지 확인해 두면 안전합니다.

 assertThat(updatedAccount.getRefreshToken()).isEqualTo("new-refresh");
+assertThat(updatedAccount.getTokenExpiresAt()).isAfter(LocalDateTime.now());
src/test/java/com/stcom/smartmealtable/repository/MemberCategoryPreferenceRepositoryTest.java (1)

54-60: ReflectionTestUtils 대신 설정자/생성자 사용 권장

엔티티 필드 주입에 ReflectionTestUtils를 사용하면 리팩터링 시 취약합니다.
테스트용 생성자·빌더·팩토리 메서드를 제공해 직접 값을 설정하도록 변경해 보세요.

src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceAdditionalIntegrationTest.java (1)

5-7: 패키지 네이밍 컨벤션 위반

com.stcom.smartmealtable.domain.Address.Address 처럼 대문자 패키지는 자바 컨벤션에서 권장되지 않습니다.
domain.address.Address 로 정리하는 것이 가독성과 일관성에 좋습니다.

src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceAdditionalIntegrationTest.java (2)

70-79: ReflectionTestUtils 사용은 불필요한 내부 구현 의존을 초래합니다

카테고리 이름 세팅을 위해 ReflectionTestUtils 로 private 필드에 직접 접근하고 있습니다.
엔티티에 생성자·빌더·setter 중 하나를 제공해 테스트가 도메인 모델의 캡슐화를 깨지 않도록 해주세요.

- category1 = new FoodCategory();
- ReflectionTestUtils.setField(category1, "name", "한식");
+ category1 = FoodCategory.builder()
+         .name("한식")
+         .build();

121-124: 예외 메시지를 정확히 매치하면 구현 변경에 과도하게 결합됩니다

hasMessage("존재하지 않는 프로필입니다") 처럼 전체 문자열을 비교하면
메시지 포맷이 조금만 변해도 테스트가 깨집니다.
hasMessageContaining("존재하지 않는 프로필") 정도로 느슨하게 매칭하거나
커스텀 에러 코드를 사용하는 방식을 고려해 주세요.

src/main/java/com/stcom/smartmealtable/web/controller/MemberAddressController.java (1)

60-69: 필드 검증 애너테이션이 누락되었습니다

roadAddress, detailAddress, alias 는 필수 값처럼 보이나
@NotBlank/@SiZe 등 Bean Validation 애너테이션이 없습니다.
컨트롤러 레이어에서 유효성 검사를 확실히 해 주세요.

src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceIntegrationTest.java (1)

46-49: findAll().get(0) 는 데이터 증가 시 오동작할 수 있습니다

테스트 픽스처 외에 다른 프로필이 존재할 경우 첫 번째 요소가 원하는 값이 아닐 수 있습니다.
memberId 로 조회하거나 저장 직후 flush 후 ID 로 다시 조회하는 방식이 안전합니다.

src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceCompleteIntegrationTest.java (1)

116-119: 예외 메시지의 전체 일치 검증은 변경에 취약합니다

여러 테스트에서 hasMessage("존재하지 않는 …") 로 전체 문자열을 비교하고 있습니다.
서비스 레이어 메시지가 변경되면 테스트가 무의미하게 실패하므로
hasMessageContaining 으로 완화하거나 에러 코드를 사용해 주세요.

src/main/java/com/stcom/smartmealtable/web/controller/MemberAccountController.java (1)

38-42: 회원 탈퇴 이후 토큰 무효화·세션 정리 필요

memberService.deleteByMemberId 호출 뒤에도 클라이언트 측 액세스/리프레시 토큰은 여전히 유효합니다.
탈퇴 직후 재호출·재인증을 차단하려면 블랙리스트 처리 또는 Spring Security 세션 무효화를 권장합니다.

src/main/java/com/stcom/smartmealtable/web/controller/AuthController.java (1)

87-103: DTO 필드에 필수값 애너테이션 추가 권장

email, password, confirmPassword, fullNametermId, isAgreed@NotBlank/@NotNull 을 부여해 클라이언트 입력 오류를 조기에 차단하는 편이 안전합니다.

src/main/java/com/stcom/smartmealtable/web/controller/AuthTokenController.java (1)

80-88: DTO 필드에 @NotBlank/@NotNull 추가 필요

EmailLoginRequest, SocialLoginRequest, RefreshTokenRequest 모두 필수 값이므로 null/빈 문자열 검사를 명시적으로 선언해 주세요.

Also applies to: 90-97, 99-103

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9369b4a and b4286f0.

📒 Files selected for processing (64)
  • src/main/java/com/stcom/smartmealtable/domain/member/MemberProfile.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/repository/MemberProfileRepository.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/service/BudgetService.java (2 hunks)
  • src/main/java/com/stcom/smartmealtable/service/MemberProfileService.java (0 hunks)
  • src/main/java/com/stcom/smartmealtable/service/MemberService.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/AuthController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/AuthTokenController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/GroupController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/LoginController.java (0 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberAccountController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberAddressController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberBudgetController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberController.java (2 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberPreferenceController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/MemberProfileController.java (2 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/OAuth2Controller.java (0 hunks)
  • src/main/java/com/stcom/smartmealtable/web/controller/SchoolGroupController.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/dto/group/GroupDto.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupCreateRequest.java (1 hunks)
  • src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupUpdateRequest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/domain/Budget/BudgetDomainIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/domain/group/CompanyGroupTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/domain/group/SchoolGroupTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/AddressEntityRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/FoodCategoryRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/GroupRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/MemberCategoryPreferenceRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/MemberProfileRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/SocialAccountRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/TermAgreementRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/repository/TermRepositoryTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/BudgetServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/BudgetServiceAdditionalIntegrationTest2.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/BudgetServiceCompleteIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/BudgetServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/GroupServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/GroupServiceCompleteIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/GroupServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/GroupServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/LoginServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/LoginServiceCompleteIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/LoginServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/LoginServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceAdditionalIntegrationTest2.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceCompleteIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberServiceFailureIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/MemberServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceCompleteIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/TermServiceAdditionalIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/TermServiceIntegrationTest.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/service/TermServiceTest.java (0 hunks)
  • src/test/java/com/stcom/smartmealtable/service/exception/ResourceNotFoundException.java (1 hunks)
  • src/test/java/com/stcom/smartmealtable/web/controller/MemberControllerTest.java (0 hunks)
💤 Files with no reviewable changes (12)
  • src/main/java/com/stcom/smartmealtable/service/MemberProfileService.java
  • src/test/java/com/stcom/smartmealtable/service/GroupServiceTest.java
  • src/main/java/com/stcom/smartmealtable/web/controller/OAuth2Controller.java
  • src/main/java/com/stcom/smartmealtable/web/controller/LoginController.java
  • src/test/java/com/stcom/smartmealtable/web/controller/MemberControllerTest.java
  • src/test/java/com/stcom/smartmealtable/service/MemberServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/LoginServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/TermServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/MemberCategoryPreferenceServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/BudgetServiceTest.java
  • src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceTest.java
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/test/java/com/stcom/smartmealtable/repository/TermAgreementRepositoryTest.java (1)
src/test/java/com/stcom/smartmealtable/repository/TermRepositoryTest.java (1)
  • DataJpaTest (14-57)
src/test/java/com/stcom/smartmealtable/service/LoginServiceIntegrationTest.java (1)
src/main/java/com/stcom/smartmealtable/exception/PasswordFailedExceededException.java (1)
  • PasswordFailedExceededException (3-12)
src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupUpdateRequest.java (1)
src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupCreateRequest.java (1)
  • Data (9-24)
src/main/java/com/stcom/smartmealtable/web/controller/SchoolGroupController.java (1)
src/main/java/com/stcom/smartmealtable/web/controller/GroupController.java (1)
  • RestController (16-39)
src/test/java/com/stcom/smartmealtable/repository/FoodCategoryRepositoryTest.java (1)
src/test/java/com/stcom/smartmealtable/repository/MemberCategoryPreferenceRepositoryTest.java (1)
  • DataJpaTest (20-84)
src/test/java/com/stcom/smartmealtable/repository/TermRepositoryTest.java (2)
src/test/java/com/stcom/smartmealtable/repository/FoodCategoryRepositoryTest.java (1)
  • DataJpaTest (15-42)
src/test/java/com/stcom/smartmealtable/repository/TermAgreementRepositoryTest.java (1)
  • DataJpaTest (16-60)
src/test/java/com/stcom/smartmealtable/repository/MemberCategoryPreferenceRepositoryTest.java (2)
src/test/java/com/stcom/smartmealtable/repository/FoodCategoryRepositoryTest.java (1)
  • DataJpaTest (15-42)
src/test/java/com/stcom/smartmealtable/repository/MemberProfileRepositoryTest.java (1)
  • DataJpaTest (15-52)
src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceAdditionalIntegrationTest.java (1)
src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceIntegrationTest.java (1)
  • SpringBootTest (15-49)
src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceIntegrationTest.java (1)
src/test/java/com/stcom/smartmealtable/service/SocialAccountServiceAdditionalIntegrationTest.java (1)
  • SpringBootTest (19-80)
🔇 Additional comments (13)
src/main/java/com/stcom/smartmealtable/web/controller/SchoolGroupController.java (1)

44-48: 삭제 엔드포인트 중복 여부 확인 요청

/api/v1/schools/{id}/api/v1/groups/{id} 두 곳에서 모두 groupService.deleteGroup을 호출하고 있습니다. 역할이 겹치면 클라이언트가 어떤 엔드포인트를 사용해야 할지 혼란스러울 수 있으니, 도메인별 책임 구분 또는 하나의 엔드포인트 통합을 검토해 주세요.

src/test/java/com/stcom/smartmealtable/service/GroupServiceCompleteIntegrationTest.java (1)

88-92: 예외 메시지의 도메인 용어가 혼동을 줄 수 있습니다

현재 테스트는 존재하지 않는 그룹에 대해 "존재하지 않는 회원입니다"라는 메시지를 기대합니다. ‘회원’ 대신 ‘그룹’이 더 적절한 표현이므로, 서비스 구현 또는 테스트 중 한쪽을 수정하세요. 테스트 기준으로 정정 예시:

-                .hasMessage("존재하지 않는 회원입니다");
+                .hasMessage("존재하지 않는 그룹입니다");

세 곳 모두 동일하게 반영해야 합니다.

Also applies to: 194-197, 226-230

src/test/java/com/stcom/smartmealtable/domain/Budget/BudgetDomainIntegrationTest.java (1)

135-140: 사용률 0% 검증 개선 👍

stripTrailingZeros()를 이용한 비교로 불필요한 스케일 차이 문제를 제거한 점이 좋습니다. 테스트 안정성이 향상되었습니다.

src/main/java/com/stcom/smartmealtable/repository/MemberProfileRepository.java (1)

18-20: EntityGraph 경로 수정 확인 완료

"addressHistory", "group"를 개별 경로로 분리하여 잘못된 JPQL 생성 문제를 해결했습니다. 정상 동작할 것으로 보입니다.

src/test/java/com/stcom/smartmealtable/service/MemberServiceFailureIntegrationTest.java (1)

54-62: 플러시 누락 가능성

memberRepository.save(member); 이후 바로 동일 트랜잭션에서 조회하므로 현재는 문제없지만, 서비스 메서드가 별도 트랜잭션(예: @Transactional(propagation = REQUIRES_NEW))으로 동작하도록 변경될 경우 테스트가 실패할 수 있습니다. em.flush(); 추가를 고려해 주세요.

src/test/java/com/stcom/smartmealtable/repository/AddressEntityRepositoryTest.java (1)

43-49: 테스트 로직 이상 없음

CRUD 흐름이 명확하며 검증 대상도 충분합니다. 추가 의견 없습니다.

src/test/java/com/stcom/smartmealtable/service/LoginServiceIntegrationTest.java (1)

29-46: isNewUser 플래그 기대값 검증 재검토 필요

memberService.saveMember()로 이미 회원을 생성한 뒤 로그인하면 기존 회원이므로 isNewUserfalse 가 되어야 자연스럽습니다.
현재 테스트는 true 를 기대하고 있어 도메인 의도와 불일치할 가능성이 큽니다. 의도된 시나리오(첫 로그인 vs. 기존 로그인)를 확인하고 기대값을 조정하거나 서비스 로직을 점검해 주세요.
또한 메서드 시그니처에 throws Exception 이 불필요하게 선언되어 있습니다.

src/test/java/com/stcom/smartmealtable/service/TermServiceAdditionalIntegrationTest.java (1)

59-71: 예외 메시지 단정식이 구현과 맞는지 확인 필요

필수 약관에 동의해야 합니다 메시지를 검증하고 있으나, 실제 서비스 메시지가 변경되면 테스트가 과도하게 깨질 수 있습니다.
비즈니스 메시지가 빈번히 변한다면 hasMessageContaining 대신 예외 타입만 검증하거나 상수로 관리하는 방법을 고려해 보세요.

src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceAdditionalIntegrationTest2.java (1)

24-26: jakarta.transaction.Transactional 사용 시 테스트 롤백 동작 확인 필요
Spring Test 는 기본적으로 org.springframework.transaction.annotation.Transactional 과 연동됩니다. 패키지가 다른 경우 설정에 따라 롤백이 적용되지 않을 수 있으니 의도한 동작인지 확인 바랍니다.

src/test/java/com/stcom/smartmealtable/service/LoginServiceCompleteIntegrationTest.java (1)

86-97: isNewUser 플래그 의미 재검토 필요

프로필이 없는 기존 회원의 첫 로그인에서 isNewUsertrue 로 검증되고 있습니다.
플래그가 “신규 회원 여부”를 뜻한다면 기존 회원에게 true 가 나오지 않아야 합니다. 비즈니스 규칙을 다시 확인해주세요.

src/test/java/com/stcom/smartmealtable/service/LoginServiceAdditionalIntegrationTest.java (1)

149-169: 새 소셜 로그인 테스트에서 profileId 기대값 확인

신규 회원 생성 시 result.getProfileId()가 null 인지, 아니면 기본 프로필이 자동 생성돼야 하는지 요구사항을 다시 점검해 주세요.

src/test/java/com/stcom/smartmealtable/service/MemberProfileServiceAdditionalIntegrationTest.java (1)

68-71: Entity 상태 확인 시 flush/clear 권장

삭제 후 같은 트랜잭션 내에서 다시 조회하면 1차 캐시에 남은 엔티티를 볼 위험이 있습니다. em.flush(); em.clear(); 후 조회하면 실제 DB 상태를 검증할 수 있습니다.

src/main/java/com/stcom/smartmealtable/web/controller/MemberPreferenceController.java (1)

28-43: Java 16 Stream#toList() 사용 시 런타임 JDK 버전 확인 필요

Stream.toList() 는 JDK 16+ API입니다. 운영 환경이 11 / 8 이라면 컴파일조차 되지 않습니다.
JDK 17 사용이 확정되지 않았다면 collect(Collectors.toList()) 로 변경하거나 빌드 툴 체계를 재확인해 주세요.

@@ -0,0 +1 @@

No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ResourceNotFoundException 클래스 구현 및 위치 재배치 필요
현재 파일이 src/test/java에 빈 상태로 추가되어 있어 빌드 오류 및 예외 처리가 불가합니다. 실제 예외 클래스는 src/main/java/com/stcom/smartmealtable/service/exception 경로로 이동하고, 아래와 같이 구현을 추가하세요:

+package com.stcom.smartmealtable.service.exception;
+
+public class ResourceNotFoundException extends RuntimeException {
+    public ResourceNotFoundException(String message) {
+        super(message);
+    }
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/test/java/com/stcom/smartmealtable/service/exception/ResourceNotFoundException.java
at line 1, the file is empty and incorrectly placed in the test directory
causing build errors. Move this file to
src/main/java/com/stcom/smartmealtable/service/exception and implement the
ResourceNotFoundException class properly as a custom exception extending
RuntimeException or Exception, including constructors for message and cause to
enable correct exception handling.

Comment on lines +9 to +10
@Data
@AllArgsConstructor
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

기본 생성자(@NoArgsConstructor) 부재로 JSON 역직렬화 실패 가능성
Spring MVC/Jackson은 기본 생성자가 없으면 @RequestBody 바인딩 시 객체를 생성하지 못해 HttpMessageNotReadableException 이 발생할 수 있습니다. @AllArgsConstructor 만으로는 해결되지 않으므로 기본 생성자를 추가하거나 @JsonCreator 기반 생성자 방식을 명시해야 합니다.

 import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
 ...
 @Data
 @AllArgsConstructor
+@NoArgsConstructor
 public class SchoolGroupUpdateRequest {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Data
@AllArgsConstructor
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
...
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SchoolGroupUpdateRequest {
// existing fields and methods
}
🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupUpdateRequest.java
around lines 9 to 10, the class lacks a no-argument constructor which can cause
JSON deserialization failures in Spring MVC/Jackson. To fix this, add a
no-argument constructor by annotating the class with @NoArgsConstructor or
explicitly define a default constructor. This will ensure proper object creation
during @RequestBody binding and prevent HttpMessageNotReadableException.

Comment on lines +20 to +23
org.springframework.test.util.ReflectionTestUtils.setField(group, "address", address);
org.springframework.test.util.ReflectionTestUtils.setField(group, "name", "테스트IT");
org.springframework.test.util.ReflectionTestUtils.setField(group, "industryType", IndustryType.IT);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ReflectionTestUtils 남용은 테스트 유지보수성을 저하시킴
프로덕션 코드에 적절한 생성자/팩터리를 추가해 테스트에서 리플렉션을 제거하는 편이 안전하고 가독성이 높습니다.

🤖 Prompt for AI Agents
In src/test/java/com/stcom/smartmealtable/domain/group/CompanyGroupTest.java
around lines 20 to 23, ReflectionTestUtils is used to set private fields
directly, which harms test maintainability. To fix this, add appropriate
constructors or factory methods in the production CompanyGroup class that allow
setting these fields during object creation, then update the test to use these
constructors or factories instead of reflection.

Comment on lines +6 to +11
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class SchoolGroupCreateRequest {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

역직렬화를 위해 기본 생성자 추가 필요

@AllArgsConstructor만 선언되어 있어 Jackson이 DTO를 역직렬화할 때 기본 생성자를 찾지 못해 HttpMessageNotReadableException이 발생할 수 있습니다. Lombok의 @NoArgsConstructor(access = AccessLevel.PROTECTED) 등을 함께 선언해 주세요.

 @Data
-@AllArgsConstructor
+@AllArgsConstructor
+@NoArgsConstructor(access = AccessLevel.PROTECTED)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/dto/group/SchoolGroupCreateRequest.java
around lines 6 to 11, the class only has @AllArgsConstructor, so Jackson cannot
find a default constructor during deserialization, causing
HttpMessageNotReadableException. Add Lombok's @NoArgsConstructor with access =
AccessLevel.PROTECTED to provide a protected no-args constructor for
deserialization.

Comment on lines +30 to +44
@TestConfiguration
static class FakeKakaoConfig {
@Bean
KakaoAddressApiService kakaoAddressApiService() {
return new KakaoAddressApiService() {
@Override
public Address createAddressFromRequest(AddressRequest requestDto) {
return Address.builder()
.roadAddress(requestDto.getRoadAddress())
.detailAddress(requestDto.getDetailAddress())
.build();
}
};
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

테스트용 Bean 주입 충돌 가능성

FakeKakaoConfig에서 동일 타입의 Bean을 추가로 등록합니다. 본 애플리케이션 컨텍스트에 KakaoAddressApiService Bean이 이미 존재한다면 NoUniqueBeanDefinitionException이 발생합니다. @Primary, @Qualifier 또는 @MockBean을 활용해 충돌을 명시적으로 방지해 주세요.

🤖 Prompt for AI Agents
In
src/test/java/com/stcom/smartmealtable/service/GroupServiceAdditionalIntegrationTest.java
around lines 30 to 44, the FakeKakaoConfig defines a KakaoAddressApiService bean
that may conflict with an existing bean of the same type in the application
context, causing NoUniqueBeanDefinitionException. To fix this, annotate the
FakeKakaoConfig's KakaoAddressApiService bean with @Primary or use @Qualifier to
distinguish it, or alternatively replace it with @MockBean in the test to
explicitly avoid bean injection conflicts.

Comment on lines +69 to +79
@PostMapping("/signup/terms")
public ApiResponse<Void> agreeTerms(@UserContext MemberDto memberDto,
@RequestBody List<TermAgreementRequest> agreements) {
termService.agreeTerms(
memberDto.getMemberId(),
agreements.stream()
.map(dto -> new TermAgreementRequestDto(dto.getTermId(), dto.getIsAgreed()))
.toList()
);
return ApiResponse.createSuccessWithNoContent();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

약관 동의 로직의 입력 검증 부족

agreements 리스트에 대한 @Valid·@NotNull 검증이 없습니다. null 리스트 혹은 요소 누락 시 의도치 않은 DB 상태가 발생할 수 있습니다.

🤖 Prompt for AI Agents
In src/main/java/com/stcom/smartmealtable/web/controller/AuthController.java
around lines 69 to 79, the agreeTerms method lacks input validation for the
agreements list. Add @Valid and @NotNull annotations to the agreements parameter
to ensure the list is not null and its elements are validated. This prevents
unintended database states caused by null or incomplete agreement data.

Comment on lines +39 to +50
@PathVariable("date") @DateTimeFormat(iso = ISO.DATE) String date,
@RequestParam("limit") Long limit) {
budgetService.registerDefaultDailyBudgetBy(memberDto.getProfileId(), limit, LocalDate.parse(date));
return ApiResponse.createSuccessWithNoContent();
}

@PatchMapping("/daily/{date}")
public ApiResponse<Void> editDailyBudget(@UserContext MemberDto memberDto,
@PathVariable("date") @DateTimeFormat(iso = ISO.DATE) String date,
@RequestParam("limit") Long limit) {
budgetService.editDailyBudgetCustom(memberDto.getProfileId(), LocalDate.parse(date), limit);
return ApiResponse.createSuccessWithNoContent();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

limit 파라미터 검증 누락

예산 한도는 음수값이 들어와선 안 됩니다. @Positive(또는 @PositiveOrZero) 애너테이션을 적용해 런타임 검사를 추가해 주세요.

-@RequestParam("limit") Long limit
+@RequestParam("limit") @Positive Long limit

Also applies to: 78-90

🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/controller/MemberBudgetController.java
at lines 39-50 and 78-90, the limit parameter lacks validation to prevent
negative values. Add the @Positive or @PositiveOrZero annotation to the limit
parameter in both methods to enforce runtime validation that disallows negative
budget limits.

Comment on lines +31 to +35
public ApiResponse<DailyBudgetResponse> dailyBudgetByDate(@UserContext MemberDto memberDto,
@PathVariable("date") @DateTimeFormat(iso = ISO.DATE) String date) {
DailyBudget dailyBudget = budgetService.getDailyBudgetBy(memberDto.getProfileId(), LocalDate.parse(date));
return ApiResponse.createSuccess(DailyBudgetResponse.of(dailyBudget));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

String 파라미터를 다시 파싱하는 불필요한 오버헤드

@PathVariable LocalDate date 로 직접 바인딩하면 LocalDate.parse(...) 호출이 불필요하며, 포맷 오류도 Spring MVC가 400으로 처리해 줍니다.

-@PathVariable("date") @DateTimeFormat(iso = ISO.DATE) String date
+@PathVariable("date") @DateTimeFormat(iso = ISO.DATE) LocalDate date

서비스 호출부에서도 LocalDate.parse(date) 제거하세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/controller/MemberBudgetController.java
around lines 31 to 35, the method parameter date is currently a String and then
parsed to LocalDate inside the method, causing unnecessary overhead. Change the
@PathVariable parameter type from String to LocalDate and remove the
LocalDate.parse(date) call in the service invocation. This lets Spring MVC
handle the date parsing and format validation automatically.

Comment on lines +51 to +58
@PostMapping("/logout")
public ApiResponse<Void> logout(HttpServletRequest request) {
String jwt = request.getHeader("Authorization");
if (Objects.nonNull(jwt)) {
jwtBlacklistService.addToBlacklist(jwt);
}
return ApiResponse.createSuccessWithNoContent();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bearer 토큰 파싱 오류 가능성

Authorization 헤더 전체를 블랙리스트에 저장하면 "Bearer " 접두사가 포함돼 검증 시 매치에 실패할 수 있습니다. 접두사를 제거하고 공백·대소문자를 방어적으로 처리하세요.

-String jwt = request.getHeader("Authorization");
-if (Objects.nonNull(jwt)) {
-    jwtBlacklistService.addToBlacklist(jwt);
-}
+String bearer = request.getHeader("Authorization");
+if (StringUtils.hasText(bearer) && bearer.startsWith("Bearer ")) {
+    String jwt = bearer.substring(7);
+    jwtBlacklistService.addToBlacklist(jwt);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping("/logout")
public ApiResponse<Void> logout(HttpServletRequest request) {
String jwt = request.getHeader("Authorization");
if (Objects.nonNull(jwt)) {
jwtBlacklistService.addToBlacklist(jwt);
}
return ApiResponse.createSuccessWithNoContent();
}
@PostMapping("/logout")
public ApiResponse<Void> logout(HttpServletRequest request) {
String bearer = request.getHeader("Authorization");
if (StringUtils.hasText(bearer) && bearer.startsWith("Bearer ")) {
String jwt = bearer.substring(7);
jwtBlacklistService.addToBlacklist(jwt);
}
return ApiResponse.createSuccessWithNoContent();
}
🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/controller/AuthTokenController.java
around lines 51 to 58, the logout method adds the entire Authorization header
value to the blacklist, including the "Bearer " prefix. To fix this, parse the
header to remove the "Bearer " prefix in a case-insensitive manner and trim any
whitespace before adding the token to the blacklist. This ensures the stored
token matches the format used during validation.

Comment on lines +73 to +78
@PostMapping("/token/refresh")
public ApiResponse<AccessTokenRefreshResponse> refreshAccessToken(@UserContext MemberDto memberDto,
@RequestBody RefreshTokenRequest request) {
String accessToken = jwtTokenService.createAccessToken(memberDto.getMemberId(), memberDto.getProfileId());
return ApiResponse.createSuccess(new AccessTokenRefreshResponse(accessToken, 3600, "Bearer"));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

리프레시 토큰 검증이 전혀 이루어지지 않습니다

refreshAccessToken 는 전달된 리프레시 토큰을 무시하고 새 액세스 토큰을 발급합니다. 악성 사용자가 만료된/위조된 토큰으로도 무제한 재발급이 가능해집니다.

-@PostMapping("/token/refresh")
-public ApiResponse<AccessTokenRefreshResponse> refreshAccessToken(@UserContext MemberDto memberDto,
-                                                                  @RequestBody RefreshTokenRequest request) {
-    String accessToken = jwtTokenService.createAccessToken(memberDto.getMemberId(), memberDto.getProfileId());
+@PostMapping("/token/refresh")
+public ApiResponse<AccessTokenRefreshResponse> refreshAccessToken(@RequestBody RefreshTokenRequest request) {
+    jwtTokenService.validateRefreshToken(request.getRefreshToken());   // 서명·만료 검증
+    String accessToken = jwtTokenService.createAccessTokenFromRefresh(request.getRefreshToken());
     return ApiResponse.createSuccess(new AccessTokenRefreshResponse(accessToken, 3600, "Bearer"));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping("/token/refresh")
public ApiResponse<AccessTokenRefreshResponse> refreshAccessToken(@UserContext MemberDto memberDto,
@RequestBody RefreshTokenRequest request) {
String accessToken = jwtTokenService.createAccessToken(memberDto.getMemberId(), memberDto.getProfileId());
return ApiResponse.createSuccess(new AccessTokenRefreshResponse(accessToken, 3600, "Bearer"));
}
@PostMapping("/token/refresh")
public ApiResponse<AccessTokenRefreshResponse> refreshAccessToken(@RequestBody RefreshTokenRequest request) {
jwtTokenService.validateRefreshToken(request.getRefreshToken()); // 서명·만료 검증
String accessToken = jwtTokenService.createAccessTokenFromRefresh(request.getRefreshToken());
return ApiResponse.createSuccess(new AccessTokenRefreshResponse(accessToken, 3600, "Bearer"));
}
🤖 Prompt for AI Agents
In
src/main/java/com/stcom/smartmealtable/web/controller/AuthTokenController.java
around lines 73 to 78, the refreshAccessToken method does not validate the
provided refresh token before issuing a new access token. To fix this, add logic
to verify the refresh token's validity and authenticity using jwtTokenService or
equivalent before creating and returning a new access token. If the refresh
token is invalid or expired, respond with an appropriate error instead of
issuing a new token.

@bebeis bebeis merged commit 8c3b748 into development Jun 12, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant