Skip to content

Conversation

@daeun-han
Copy link
Member

@daeun-han daeun-han commented Jan 5, 2026

📌 PR 제목

[feat] 유저 및 반려견 정보 수정 API 구현


✨ 요약 설명

마이페이지에서 사용자의 프로필 정보와 반려견 정보를 수정할 수 있는 API를 구현했습니다. 특히, 설계 원칙에 따라 조회(Query)와 수정(Command)의 책임을 명확히 분리하여 Facade 계층을 구성했습니다.


🧾 변경 사항

  • 유저 정보 수정: PATCH /api/v1/users/me/userInfo API 구현
  • 반려견 정보 수정: PATCH /api/v1/users/me/pets/{petId} API 구현

📂 PR 타입

  • 기능 추가
  • 버그 수정
  • 리팩토링
  • 문서 수정
  • 기타 (직접 작성: )

🧪 테스트

  • Postman으로 API 호출 확인
  • 로컬 실행 결과 화면 캡처 포함

✅ 체크리스트

  • 깃 & 코드 컨벤션을 지켰는가?
  • Swagger/문서화는 최신 상태인가?
  • 기능 변경 시 영향 받는 모듈을 확인했는가?

💬 리뷰어에게 전달할 말

  • 수정 API 실행 후 별도의 조회 호출 없이도 최신 데이터를 확인할 수 있도록, Command 서비스 실행 후 Query 서비스를 호출하여 상세 정보를 반환하도록 설계했는데 이 방법이 적절했을지 알려주시면 감사하겠습니다!

  • 또한, Facade 계층을 구성하면서 기존에 혼재되어 있던 Facade들의 네이밍을 통일하면 좋을 것 같아 의견드립니다.


🔗 관련 이슈

아래 이슈번호 에 번호를 적으면 풀리퀘스트 머지 완료 시 자동으로 해당 이슈가 닫힙니다.

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 사용자 프로필 조회 및 수정 기능 추가
    • 반려견 정보 수정 기능 추가
    • 지역 정보 업데이트 기능 추가
  • Refactor

    • 나이 기반 계산에서 생년월일 기반 계산으로 변경
    • 반려견 성향(traits) 기능 제거 및 관련 API 정리
    • 회원가입 프로세스 간소화

✏️ Tip: You can customize this high-level summary in your review settings.

@daeun-han daeun-han self-assigned this Jan 5, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

사용자와 반려견의 정보 수정 기능을 추가하면서 나이 기반 데이터를 생년월일 기반으로 변경합니다. 반려견 특성(trait) 관련 기능을 제거하고, @UserId 어노테이션을 여러 컨트롤러에 통합합니다.

Changes

Cohort / File(s) 변경 내용
User 도메인 - DTO 및 Command
src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/CreateUserRequestDto.java, UpdateUserInfoRequestDto.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/response/UserInfoResponseDto.java, UserRegisterResponseDto.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/dto/request/CreateUserCommand.java, UpdateUserInfoCommand.java
loginId/password 제거, age에서 birth(LocalDate)로 변경. UserRegisterResponseDto 필드 간소화. UserInfoResponseDto에 email 필드 추가 및 gender 파싱 로직 추가.
User 도메인 - Service 및 Facade
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserService.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java, UserRegisterFacade.java
updateUserInfo() 메서드 추가. saveUser() 시그니처 변경(userId 추가). UserQueryService에서 이메일 조회 기능 추가. UpdateUserInfoFacade 신규 추가. UserRegisterFacade에서 이미지 처리 제거.
User 도메인 - Repository 및 Entity
src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/entity/UserEntity.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/domain/repository/SocialAccountRepository.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/SocialAccountRepositoryImpl.java, SpringDataSocialAccountRepository.java
loginId/password 필드 제거, birth 필드 추가. updateProfile() 메서드 추가. SocialAccountRepository에 findByUser_UserId() 메서드 추가.
User 도메인 - Controller
src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java
@UserId 어노테이션 추가 및 userId 파라미터 통합. 이미지 업로드 제거. /me/userInfo GET/PATCH 엔드포인트 추가. /me/pets/{petId} PATCH 엔드포인트 추가. UpdateUserInfoFacade, UserPetCommandFacade 의존성 주입.
User 도메인 - Exception
src/main/java/org/sopt/pawkey/backendapi/domain/user/exception/UserErrorCode.java
UNAUTHORIZED_ACCESS 에러 코드 신규 추가.
Pet 도메인 - DTO 및 Command
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/PetRequestDto.java, UpdatePetRequestDto.java
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetProfileResponseDto.java
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/request/CreatePetCommand.java, UpdatePetCommand.java
age/isAgeKnown에서 birth(LocalDate)로 변경. petTraits 필드 제거. PetProfileResponseDto에 imageUrl, birth, age, dbti 필드 추가. UpdatePetRequestDto 신규 추가.
Pet 도메인 - Trait 관련 파일 제거
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/SelectedOptionForPetTraitCategory.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryListResponseDto.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryResponseDto.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/response/PetTraitCategoryResult.java (삭제)
반려견 특성 카테고리 관련 DTO 및 Result 클래스 제거.
Pet 도메인 - Entity 및 Trait 제거
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetEntity.java
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitCategoryEntity.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitOptionEntity.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitSelectedEntity.java (삭제)
age/isAgeKnown에서 birth로 변경, dbti 필드 추가, petTraitSelectedEntityList 제거. updateProfile() 메서드 추가. Trait 관련 Entity 클래스 모두 제거.
Pet 도메인 - Repository 정리
src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetRepository.java, PetRepositoryImpl.java
src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitOptionRepository.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitSelectedRepository.java (삭제)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitCategoryRepository.java (삭제)
findAllPetTraitCategoriesWithOptions() 메서드 제거, findById() 추가. Trait 관련 Repository 클래스 모두 제거.
Pet 도메인 - Service
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetQueryService.java
savePet() 시그니처 변경(ImageEntity 제거), 트레이트 처리 제거. updatePetInfo() 메서드 신규 추가(인증 확인 포함). getAllPetTraitCategories() 메서드 제거.
Pet 도메인 - Controller
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/controller/PetController.java
@RequestMapping 경로 변경(/pets/traits → /pets), getPetTraitCategories() 엔드포인트 제거.
Pet 도메인 - Error Code
src/main/java/org/sopt/pawkey/backendapi/domain/pet/exception/PetErrorCode.java
CATEGORY_OPTION_NOT_FOUND를 PET_NOT_FOUND로 변경.
Pet 도메인 - Query Service 추가
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/command/UserPetCommandFacade.java
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserPetQueryService.java
UserPetCommandFacade 신규 추가(반려견 수정). UserPetQueryService에 getPetProfile() 메서드 추가.
기타 컨트롤러 - @UserId 통합
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostController.java
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostFilterController.java
src/main/java/org/sopt/pawkey/backendapi/domain/region/api/controller/RegionController.java
src/main/java/org/sopt/pawkey/backendapi/domain/review/api/controller/ReviewController.java
src/main/java/org/sopt/pawkey/backendapi/domain/routes/api/controller/RouteController.java
@UserId 어노테이션 추가/정렬. PostFilterController.filterPosts()에서 userId 출처 변경.
기타 정리
src/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java
src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/entity/PostImageEntity.java
src/main/java/org/sopt/pawkey/backendapi/domain/auth/api/controller/AuthController.java
src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java
미사용 import 제거, import 순서 정렬.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant UC as UserController
    participant UUF as UpdateUserInfoFacade
    participant US as UserService
    participant UQS as UserQueryService
    participant UR as UserRepository
    participant DB as Database

    Client->>UC: PATCH /api/v1/users/me/userInfo<br/>userId, UpdateUserInfoRequestDto
    UC->>UUF: execute(userId, command)
    
    rect rgb(200, 220, 240)
    Note over UUF,DB: User Info Update Flow
    UUF->>US: updateUserInfo(userId, command)
    US->>UR: findById(userId)
    UR->>DB: SELECT User WHERE userId
    DB-->>UR: UserEntity
    UR-->>US: UserEntity
    
    rect rgb(240, 220, 200)
    Note over US: Authorization Check<br/>(if needed)
    end
    
    US->>US: updateProfile(name, birth, gender)
    US->>UR: save(userEntity)
    UR->>DB: UPDATE User SET name, birth, gender
    DB-->>UR: success
    UR-->>US: updated UserEntity
    end

    rect rgb(220, 240, 200)
    Note over UUF,DB: Fetch Updated Info
    UUF->>UQS: getUserInfo(userId)
    UQS->>UR: findById(userId)
    UR->>DB: SELECT User WHERE userId
    DB-->>UR: UserEntity
    UR-->>UQS: UserEntity
    UQS->>UQS: Build UserInfoResponseDto
    UQS-->>UUF: UserInfoResponseDto
    end
    
    UUF-->>UC: UserInfoResponseDto
    UC-->>Client: ApiResponse<UserInfoResponseDto>
Loading
sequenceDiagram
    actor Client
    participant UC as UserController
    participant UPCF as UserPetCommandFacade
    participant PS as PetService
    participant PR as PetRepository
    participant UPQS as UserPetQueryService
    participant DB as Database

    Client->>UC: PATCH /api/v1/users/me/pets/{petId}<br/>userId, petId, UpdatePetRequestDto
    UC->>UPCF: updatePetInfo(userId, petId, command)
    
    rect rgb(200, 220, 240)
    Note over UPCF,DB: Pet Update with Auth Check
    UPCF->>PS: updatePetInfo(userId, petId, command)
    PS->>PR: findById(petId)
    PR->>DB: SELECT Pet WHERE petId
    DB-->>PR: PetEntity
    PR-->>PS: PetEntity
    
    rect rgb(240, 200, 200)
    Note over PS: Authorization Validation<br/>user.userId == pet.user.userId?<br/>if false → UNAUTHORIZED_ACCESS
    end
    
    PS->>PS: updateProfile(name, birth, gender,<br/>isNeutered, breed)
    PS->>PR: save(petEntity)
    PR->>DB: UPDATE Pet SET name, birth, gender...
    DB-->>PR: success
    UR-->>PS: updated PetEntity
    end

    rect rgb(220, 240, 200)
    Note over UPCF,DB: Fetch Updated Pet Profile
    UPCF->>UPQS: getPetProfile(petId)
    UPQS->>PR: findById(petId)
    PR->>DB: SELECT Pet WHERE petId
    DB-->>PR: PetEntity
    PR-->>UPQS: PetEntity
    UPQS->>UPQS: Build PetProfileResponseDto<br/>from(petEntity)
    UPQS-->>UPCF: PetProfileResponseDto
    end
    
    UPCF-->>UC: PetProfileResponseDto
    UC-->>Client: ApiResponse<PetProfileResponseDto>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

🐿️다은🐿️, ✨ Feature, 🔨 Refactor

Suggested reviewers

  • dltnals317
  • bingle625

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 '[feat] 유저 및 반려견 정보 수정 API 구현'은 명확하고 간결하며, 주요 변경사항인 사용자 및 반려견 정보 수정 API 구현을 정확히 요약하고 있습니다.
Description check ✅ Passed PR 설명이 제공된 템플릿 구조를 따르며, 요약, 변경사항, PR 타입, 테스트, 체크리스트, 리뷰 요청사항, 관련 이슈를 모두 포함하고 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항이 연결된 이슈 #200의 요구사항을 충족합니다. 유저 정보 수정 DTO, 반려견 정보 수정 DTO, 비즈니스 로직, 그리고 PATCH 엔드포인트 구현이 모두 완료되었습니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경사항이 #200에서 요청된 유저 및 반려견 정보 수정 API 구현 범위 내에 있으며, 관련 데이터 모델, 서비스, Facade 계층의 필요한 리팩토링도 포함되어 있습니다.

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.

@daeun-han
Copy link
Member Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Fix all issues with AI Agents 🤖
In
@src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java:
- Line 28: In AppleTokenVerifier.java update the deprecated import: replace the
old io.jsonwebtoken.SignatureException import with
io.jsonwebtoken.security.SignatureException to match the jjwt 0.12+ API used by
verifyWith(); ensure the import at the top of the AppleTokenVerifier class is
changed so caught/handled exceptions reference the new SignatureException type
consistent with verifyWith().

In
@src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/UpdatePetRequestDto.java:
- Line 6: Remove the unused import of UpdateUserInfoCommand from
UpdatePetRequestDto.java; locate the import statement "import
org.sopt.pawkey.backendapi.domain.user.application.dto.request.UpdateUserInfoCommand;"
in the UpdatePetRequestDto class and delete it (or run your IDE's
optimize/organize imports) so there are no unused imports left.

In
@src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/request/UpdatePetCommand.java:
- Around line 5-11: The UpdatePetCommand record is missing validation
annotations for the gender and birth fields; add @Pattern(regexp = "^(M|F)$",
message = "성별은 M 또는 F이어야 합니다.") to the gender field and @PastOrPresent(message =
"생년월일은 현재 또는 과거 날짜여야 합니다") to the birth field (keeping the same constraints used
by PetRequestDto/entity), and import the corresponding
javax.validation.constraints annotations so validation runs on UpdatePetCommand
(or UpdatePetRequestDto) during request binding.

In
@src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetEntity.java:
- Around line 93-99: UpdatePetRequestDto currently only validates birth with
@NotNull so future dates can slip through; add the javax.validation.constraint
@PastOrPresent to the birth field in UpdatePetRequestDto so the API layer
rejects future dates before reaching PetEntity.updateProfile, ensuring the birth
property is validated during request binding/validation.

In
@src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/UpdateUserInfoRequestDto.java:
- Around line 18-20: The gender field in UpdateUserInfoRequestDto currently only
uses @NotBlank and allows invalid values; change it to a strongly-typed enum by
creating a Gender enum with values M and F, update the DTO's gender type to
Gender and replace @NotBlank with @NotNull (and keep the @Schema
description/example), and then adjust any code that maps/deserializes the
request (controllers/services) to handle Gender instead of String;
alternatively, if you prefer not to add an enum, validate the String with
@Pattern(regexp = "^[MF]$") and a @NotBlank/@NotNull to enforce only "M" or "F".

In
@src/main/java/org/sopt/pawkey/backendapi/domain/user/domain/repository/SocialAccountRepository.java:
- Line 15: findByUser_UserId currently returns an Optional<SocialAccountEntity>
without platform or primary-account constraints, which is unsafe because a user
can have multiple SocialAccountEntity rows; update the repository and/or domain
to make the intent explicit: either remove findByUser_UserId and replace calls
in UserQueryService with a repository method that includes platform (e.g.,
findByUser_UserIdAndPlatform) or add a primary flag to SocialAccountEntity and a
query like findByUser_UserIdAndIsPrimaryTrue, or alternatively add a
@OneToMany(mappedBy="user") collection to UserEntity and change UserQueryService
to select the correct account deterministically (by platform or primary) before
assuming a single result; ensure all usages of findByUser_UserId are updated to
the chosen deterministic query.

In
@src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/entity/UserEntity.java:
- Around line 57-59: Region became non-nullable on UserEntity (the region field
annotated with @JoinColumn(name = "region_id")), but
UserService.findOrCreateUserBySocialId still constructs users with region(null),
causing runtime constraint errors; either revert to @JoinColumn(name =
"region_id", nullable = true) on the RegionEntity reference or update
findOrCreateUserBySocialId to supply a valid RegionEntity (e.g., fetch or create
a default Region via RegionRepository and set it on the new UserEntity) so new
users never get region = null, ensuring consistency with the DB constraint.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostFilterController.java (2)

56-56: 불필요한 타입 변환을 제거하세요.

Line 54에서 userId가 이미 Long 타입으로 선언되어 있으므로, Line 56의 Long.valueOf(userId) 호출은 불필요합니다.

🔎 수정 제안
-		PostListResponseDto response = postQueryFacade.getFilterPostList(requestDto, Long.valueOf(userId));
+		PostListResponseDto response = postQueryFacade.getFilterPostList(requestDto, userId);

40-45: GET 엔드포인트를 @userid 어노테이션으로 마이그레이션하고 미사용 파라미터를 정리해주세요.

현재 PostFilterController의 두 엔드포인트가 서로 다른 인증 방식을 사용하고 있으며, 이는 의도된 설계가 아닌 불완전한 마이그레이션으로 보입니다.

변경 필요 사항:

  • GET 엔드포인트(Line 41): @RequestHeader(USER_ID_HEADER) @NotNull Integer userId@UserId Long userId로 통일
  • POST 엔드포인트(Line 54)와 코드베이스 전체에서 사용 중인 표준 패턴 적용
  • 추가로, GET 엔드포인트에서 userId 파라미터를 받지만 메서드 본문(Line 43)에서 사용하지 않음. 파라미터가 필요없다면 제거하거나, 필요하다면 코드에 반영해주세요.
src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetRepositoryImpl.java (1)

17-19: 중복된 리포지토리 필드를 통합하세요.

동일한 타입(SpringDataPetRepository)의 필드가 두 개 선언되어 있습니다:

  • Line 17: springDataPetRepository
  • Line 19: petRepository

이는 혼란을 야기하고 유지보수를 어렵게 만듭니다. 하나의 필드로 통합하고 모든 메서드에서 일관되게 사용해야 합니다.

🔎 제안하는 수정
 	private final SpringDataPetRepository springDataPetRepository;
-
-	private final SpringDataPetRepository petRepository;

 	@Override
 	public PetEntity save(PetEntity pet) {
 		return springDataPetRepository.save(pet);
 	}

 	public List<PetEntity> findAllPetsByUserId(Long userId) {
-		return petRepository.findAllByUser_UserId(userId);
+		return springDataPetRepository.findAllByUser_UserId(userId);
 	}

 	@Override
 	public Optional<PetEntity> findById(Long petId) {
 		return springDataPetRepository.findById(petId);
 	}
🧹 Nitpick comments (12)
src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/withdraw/GoogleWithdrawService.java (1)

34-39: 예외 처리 개선을 권장합니다.

현재 구현은 모든 예외를 Exception으로 포괄적으로 처리하고 있으며, 원본 예외 정보가 손실됩니다. 다음 개선 사항을 고려해보세요:

  1. 원본 예외를 cause로 전달하여 디버깅 시 스택 트레이스 보존
  2. 예외 발생 시 로깅 추가로 모니터링 개선
  3. 가능하다면 더 구체적인 예외 타입 처리
🔎 개선된 예외 처리 제안
 		HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
 		try{
 			restTemplate.postForEntity(revokeUri,request, Void.class);
 		}
 		catch(Exception e){
-			throw new AuthBusinessException(AuthErrorCode.SOCIAL_WITHDRAW_FAIL);
+			// 원본 예외를 cause로 전달하여 스택 트레이스 보존
+			throw new AuthBusinessException(AuthErrorCode.SOCIAL_WITHDRAW_FAIL, e);
 		}

참고: AuthBusinessException 생성자가 cause를 지원하지 않는 경우, 생성자 추가를 고려하거나 최소한 로깅을 추가하는 것을 권장합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java (1)

109-112: ExpiredJwtException에 대해 더 적절한 에러 코드 사용을 고려해 주세요.

토큰 만료(ExpiredJwtException)와 서명 오류(SignatureException)는 다른 실패 유형입니다. 현재 둘 다 TOKEN_SIGNATURE_INVALID로 매핑되어 있는데, 클라이언트가 만료와 서명 오류를 구분하여 적절한 대응(예: 토큰 갱신 vs 재로그인)을 할 수 있도록 별도의 에러 코드(예: TOKEN_EXPIRED) 사용을 검토해 보세요.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserQueryFacade.java (1)

4-6: 미사용 import 제거를 권장합니다.

UpdateUserInfoCommandUserEntity를 import하고 있지만 파일 내에서 사용되지 않습니다. 코드 정리를 위해 제거하는 것이 좋습니다.

🔎 제안하는 수정
 import org.sopt.pawkey.backendapi.domain.user.api.dto.response.UserInfoResponseDto;
-import org.sopt.pawkey.backendapi.domain.user.application.dto.request.UpdateUserInfoCommand;
 import org.sopt.pawkey.backendapi.domain.user.application.service.UserQueryService;
-import org.sopt.pawkey.backendapi.domain.user.infra.persistence.entity.UserEntity;
 import org.springframework.stereotype.Service;
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java (1)

1-1: 패키지 구조 일관성 검토 필요

UpdateUserInfoFacadefacade 루트 패키지에 위치해 있지만, UserPetCommandFacadefacade.command에, UserQueryFacadefacade.query에 위치합니다. PR 설명에서도 Facade 네이밍 통일이 제안되었으므로, Command/Query 분리 원칙에 따라 facade.command 패키지로 이동하는 것이 좋겠습니다.

-package org.sopt.pawkey.backendapi.domain.user.application.facade;
+package org.sopt.pawkey.backendapi.domain.user.application.facade.command;
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/UpdatePetRequestDto.java (1)

17-18: gender 필드에 대한 유효성 검증 강화 권장

현재 @NotBlank만 적용되어 있어 "M", "F" 외의 값도 허용됩니다. @Pattern 어노테이션을 추가하거나 enum 타입을 사용하여 유효한 값만 허용하도록 개선할 수 있습니다.

🔎 제안: Pattern 어노테이션 추가
 @Schema(description = "반려견 성별 (M/F)", example = "M")
+@Pattern(regexp = "^[MF]$", message = "성별은 M 또는 F만 허용됩니다.")
 @NotBlank String gender,
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserPetQueryService.java (1)

29-34: getPetProfile 메서드의 소유권 검증 부재 - 설계 개선 권장

getPetProfile(Long petId) 메서드는 petId만을 기반으로 반려견 정보를 조회하며 소유권 검증을 수행하지 않습니다.

현재 코드에서는 UserPetCommandFacade.updatePetInfo 내에서 petService.updatePetInfo(userId, petId, command)가 먼저 호출되어 소유권을 검증한 후 getPetProfile(petId)을 호출하므로 실제 보안 문제는 없습니다. 그러나 이 메서드가 다른 곳에서 재사용되거나 향후에 독립적으로 호출될 수 있으므로, 방어 심층(defense in depth) 원칙 관점에서 userId 파라미터를 추가하여 메서드 레벨에서 소유권을 검증하는 것을 권장합니다.

비교 참고: UserPetQueryFacade.getUserPetsuserService.findById(userId)getPetProfiles(user) 패턴으로 검증 후 조회를 수행합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java (1)

29-31: 이메일이 없는 경우 빈 문자열 대신 null 반환을 고려하세요.

현재 소셜 계정이 없는 경우 빈 문자열("")을 반환합니다. 이는 데이터가 실제로 없는 상황을 숨길 수 있습니다. null을 반환하거나 UserInfoResponseDto에서 Optional<String> 또는 nullable 필드를 사용하는 것이 더 명시적일 수 있습니다.

String email = socialAccountRepository.findByUser_UserId(userId)
    .map(SocialAccountEntity::getPrimaryEmail)
    .orElse(null);  // 또는 Optional<String>을 고려
src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/controller/PetController.java (1)

14-18: 사용되지 않는 컨트롤러와 필드를 제거하세요.

PetController는 class-level @RequestMapping 애노테이션만 있고 실제 핸들러 메서드가 없으며, petQueryService 필드도 사용되지 않습니다. Pet 관련 엔드포인트는 UserController에 이미 구현되어 있습니다(/me/pets, /me/pets/{petId}). 이 컨트롤러는 불필요한 dead code이므로 제거를 권장합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetProfileResponseDto.java (2)

41-48: convertGender에서 예외 발생 시 API 응답 실패 가능성 검토 필요

DB에 저장된 성별 값이 "M" 또는 "F"가 아닌 경우 IllegalArgumentException이 발생하여 API 응답이 실패합니다. 입력 시 validation이 있더라도, 기존 데이터나 마이그레이션 이슈로 인한 데이터 정합성 문제가 있을 수 있습니다.

UserInfoResponseDto.parseGender처럼 기본값 반환 방식을 고려해보세요.

🔎 제안: 방어적 처리 적용
 private static String convertGender(String gender) {
-	if ("M".equals(gender)) {
-		return "남아";
-	} else if ("F".equals(gender)) {
-		return "여아";
-	}
-	throw new IllegalArgumentException("올바르지 않은 성별 값입니다: " + gender);
+	return switch (gender) {
+		case "M" -> "남아";
+		case "F" -> "여아";
+		default -> gender;
+	};
 }

35-39: calculateAgeInMonths의 null 체크와 엔티티 정의 간 불일치

PetEntity.birth 컬럼은 nullable = false로 정의되어 있지만, 여기서는 null 체크를 수행합니다. 방어적 코딩으로는 좋으나, 실제로 null이 들어올 수 없다면 이 체크는 불필요할 수 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java (1)

42-44: PetService에서 UserBusinessException 사용 검토

PetService에서 UserBusinessException을 사용하는 것은 도메인 경계를 넘는 의존성입니다. PetErrorCodeUNAUTHORIZED_ACCESS를 추가하고 PetBusinessException을 사용하는 것이 도메인 응집도 측면에서 더 적절할 수 있습니다.

관련 코드 스니펫에서 PetBusinessExceptionPetErrorCode만 받도록 설계되어 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserService.java (1)

34-44: 시그니처 변경은 모두 반영되었으나, 메서드 이름의 명확성 개선 권장

saveUser 메서드 호출 검증 결과, UserRegisterFacade.java의 유일한 호출 위치에서 새로운 시그니처(userId 매개변수 포함)가 올바르게 반영되어 있습니다. 시그니처 변경으로 인한 호출 위치 누락 문제는 없습니다.

다만, 메서드 이름 개선은 여전히 검토할 가치가 있습니다. 현재 saveUser는 기존 사용자의 프로필을 업데이트하는 메서드이며, 사용자 등록 플로우(OAuth로 최소 정보 생성 → 프로필 완성)의 후반부에 위치합니다. 메서드 의도를 더 명확하게 드러내기 위해 updateUserProfile, completeUserRegistration 또는 saveUserProfile과 같이 메서드명을 변경하는 것이 코드 가독성을 향상시킬 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 621d851 and 27c439b.

📒 Files selected for processing (63)
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostFilterController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostLikeController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/entity/PostImageEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostLikeRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/SpringDataPostLikeRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/auth/api/controller/AuthController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/withdraw/GoogleWithdrawService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/auth/infra/persistence/entity/AppleRefreshTokenEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/controller/PetController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/PetRequestDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/UpdatePetRequestDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetProfileResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryListResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/SelectedOptionForPetTraitCategory.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/request/CreatePetCommand.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/request/UpdatePetCommand.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/response/PetTraitCategoryResult.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetQueryService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitOptionRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitSelectedRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/exception/PetErrorCode.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitCategoryEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitOptionEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitSelectedEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetTraitOptionRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetTraitSelectedRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitCategoryRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitOptionRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitSelectedRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/region/api/controller/RegionController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/review/api/controller/ReviewController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/routes/api/controller/RouteController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/CreateUserRequestDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/UpdateUserInfoRequestDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/response/UserInfoResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/response/UserRegisterResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/dto/request/CreateUserCommand.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/dto/request/UpdateUserInfoCommand.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserLoginFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserRegisterFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserWithdrawFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/command/UserPetCommandFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserQueryFacade.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/SocialAccountService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserPetQueryService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/domain/repository/SocialAccountRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/exception/UserErrorCode.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/SocialAccountRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/SpringDataSocialAccountRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/entity/UserEntity.java
💤 Files with no reviewable changes (20)
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/SpringDataPostLikeRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryListResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/repository/PostLikeRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitOptionRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/infra/persistence/entity/PostImageEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitOptionRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/Post/domain/repository/PostLikeRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitCategoryEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitSelectedEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/SelectedOptionForPetTraitCategory.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetTraitOptionRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitSelectedRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetTraitSelectedRepositoryImpl.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/auth/infra/persistence/entity/AppleRefreshTokenEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetTraitCategoryResponseDto.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetTraitSelectedRepository.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetTraitOptionEntity.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetQueryService.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/response/PetTraitCategoryResult.java
  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetTraitCategoryRepository.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-13T12:32:16.414Z
Learnt from: daeun-han
Repo: PAW-KEY/PAWKEY-Backend PR: 55
File: src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java:26-71
Timestamp: 2025-07-13T12:32:16.414Z
Learning: PostEntity에서 postImageEntityList를 List에서 Set으로 변경한 것은 JPA/Hibernate의 MultipleBagFetchException을 피하기 위한 설계입니다. 여러 List 컬렉션을 동시에 fetch join할 수 없으므로 Set을 사용하거나 별도 쿼리로 분리해야 합니다.

Applied to files:

  • src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetEntity.java
📚 Learning: 2025-07-13T11:54:29.024Z
Learnt from: daeun-han
Repo: PAW-KEY/PAWKEY-Backend PR: 55
File: src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserLikedPostQueryFacade.java:55-55
Timestamp: 2025-07-13T11:54:29.024Z
Learning: 좋아요한 게시물 목록을 조회하는 API에서 isLiked 필드를 true로 하드코딩하는 것은 성능상 합리적입니다. PostLikeEntity 리스트가 이미 좋아요를 누른 게시물들만 포함하므로 별도 쿼리로 좋아요 상태를 확인하는 것은 중복이고 비효율적입니다.

Applied to files:

  • src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java
🧬 Code graph analysis (7)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java (3)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserService.java (1)
  • Service (21-103)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/query/UserQueryFacade.java (1)
  • Service (12-22)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java (1)
  • Service (16-36)
src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java (2)
src/main/java/org/sopt/pawkey/backendapi/domain/auth/exception/AuthBusinessException.java (1)
  • AuthBusinessException (6-12)
src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleAuthKeyService.java (1)
  • Slf4j (39-196)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/command/UserPetCommandFacade.java (2)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java (1)
  • Service (17-54)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserPetQueryService.java (1)
  • Service (15-35)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserPetQueryService.java (1)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/exception/PetBusinessException.java (1)
  • PetBusinessException (5-9)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java (3)
src/main/java/org/sopt/pawkey/backendapi/domain/pet/exception/PetBusinessException.java (1)
  • PetBusinessException (5-9)
src/main/java/org/sopt/pawkey/backendapi/domain/user/exception/UserBusinessException.java (1)
  • UserBusinessException (6-10)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/command/UserPetCommandFacade.java (1)
  • Service (12-24)
src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java (2)
src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostController.java (1)
  • RestController (36-90)
src/main/java/org/sopt/pawkey/backendapi/domain/routes/api/controller/RouteController.java (1)
  • RestController (33-97)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java (2)
src/main/java/org/sopt/pawkey/backendapi/domain/user/exception/UserBusinessException.java (1)
  • UserBusinessException (6-10)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java (1)
  • Service (12-24)
🔇 Additional comments (47)
src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserWithdrawFacade.java (2)

24-39: 탈퇴 로직 구현 — 문제 없음

withdraw 메서드의 로직은 적절하게 구현되었습니다:

  • 토큰 조회 → 소셜 연동 해제 → 세션 revoke → 사용자 삭제 순서가 명확합니다
  • 소셜 연동 해제 실패 시 예외 처리로 내부 탈퇴를 계속 진행하는 것이 올바릅니다

3-4: import 경로 변경 — 유효성 확인 완료

TokenServiceSocialWithdrawServiceFactory의 import 경로가 domain.auth 패키지로 올바르게 변경되었습니다. 검증 결과, 클래스 정의 위치가 다음과 같이 import 경로와 일치합니다:

  • TokenService: src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/token/TokenService.java
  • SocialWithdrawServiceFactory: src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/withdraw/SocialWithdrawServiceFactory.java

이 패키지 이동은 codebase 전체에서 일관성 있게 반영되었으며, 기존 domain.user 경로의 import는 더 이상 사용되지 않습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/withdraw/GoogleWithdrawService.java (1)

5-5: LGTM! 중복 import 제거 완료.

중복된 import 문을 제거하고 코드 정리를 수행한 변경사항입니다. 런타임 동작에 영향을 주지 않으며 코드 가독성을 개선합니다.

Also applies to: 13-13

src/main/java/org/sopt/pawkey/backendapi/domain/routes/api/controller/RouteController.java (1)

5-5: import 정리가 적절합니다.

UserId import를 상단으로 통합하고 중복을 제거하여 코드 일관성이 개선되었습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/region/api/controller/RegionController.java (1)

5-5: import 정리가 적절합니다.

UserId import 중복 제거 및 배치 통일로 코드 일관성이 향상되었습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostLikeController.java (1)

6-6: import 순서 정리가 적절합니다.

import 순서를 조정하여 일관성을 확보했으며 동작에는 영향이 없습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/review/api/controller/ReviewController.java (1)

5-5: import 정리가 적절합니다.

UserId import를 단일 위치로 통합하여 중복을 제거했습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostController.java (1)

7-7: import 정리가 적절합니다.

UserId import를 상단으로 이동하고 중복을 제거하여 여러 컨트롤러 간 일관성이 확보되었습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/Post/api/controller/PostFilterController.java (1)

5-5: LGTM!

@UserId 어노테이션 사용을 위한 import 추가가 적절합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserLoginFacade.java (1)

5-11: LGTM!

Import 정리가 깔끔하게 되었으며, 추가된 모든 import가 클래스 내에서 실제로 사용되고 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/auth/api/controller/AuthController.java (1)

6-15: LGTM!

Import 문 재정렬만 수행되었으며, 기존 기능에 영향이 없습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/auth/application/service/login/verifier/apple/AppleTokenVerifier.java (1)

3-31: Import 정리 및 의존성 구성이 적절합니다.

AppleAuthKeyService를 통한 공개 키 조회와 예외 처리를 위한 import가 잘 구성되어 있습니다. 관련 코드 스니펫에서 확인되는 AppleAuthKeyService.getPublicKey() 메서드와의 연동이 올바르게 설정되어 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/exception/UserErrorCode.java (1)

14-15: 새로운 권한 오류 코드 추가 확인 완료

UNAUTHORIZED_ACCESS 에러 코드가 적절하게 추가되었습니다. HTTP 403 상태와 명확한 메시지가 사용되었으며, 에러 코드 패턴도 일관성 있게 유지되고 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/SocialAccountService.java (1)

3-3: Import 정리 확인

Import 구문이 적절하게 정리되었습니다. 기능적 변경 사항은 없습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/SpringDataPetRepository.java (1)

11-13: EntityGraph 단순화가 적절합니다.

반려견 특성(trait) 관련 엔티티 제거에 맞춰 @EntityGraphattributePaths를 "profileImage"만 포함하도록 올바르게 단순화했습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/dto/request/CreateUserCommand.java (1)

3-14: 나이 기반에서 생년월일 기반으로의 전환이 올바르게 구현되었습니다.

age 필드를 LocalDate birth로 변경하고 팩토리 메서드도 그에 맞춰 적절히 업데이트했습니다. PR의 목표와 일치하는 변경사항입니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/SocialAccountRepositoryImpl.java (1)

31-34: userId 기반 조회 메서드가 올바르게 구현되었습니다.

findByUser_UserId 메서드가 표준 리포지토리 패턴에 따라 JPA 리포지토리에 적절히 위임하고 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/SpringDataSocialAccountRepository.java (1)

13-13: Spring Data JPA 쿼리 메서드가 올바르게 추가되었습니다.

findByUser_UserId 메서드는 Spring Data JPA 네이밍 규칙을 따르며, 사용자 ID로 소셜 계정을 조회하는 기능을 적절히 제공합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/command/UserPetCommandFacade.java (1)

12-23: LGTM! Command-Query 분리 패턴이 잘 적용되었습니다.

Facade가 수정(Command) 후 조회(Query)를 호출하여 최신 상태를 반환하는 설계가 적절합니다. PetService.updatePetInfo에서 이미 소유권 검증을 수행하므로 getPetProfile 호출 시 별도 검증이 필요하지 않습니다.

@Transactional이 메서드 레벨에 적용되어 있고, PetService.updatePetInfo@Transactional을 가지고 있지만 기본 전파 수준(REQUIRED)으로 동일 트랜잭션 내에서 실행되므로 문제없습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UpdateUserInfoFacade.java (1)

19-23: LGTM! 수정 후 조회 패턴이 올바르게 구현되었습니다.

userService.updateUserInfo 호출 후 userQueryService.getUserInfo를 통해 최신 정보를 반환하는 흐름이 적절합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/exception/PetErrorCode.java (1)

6-8: LGTM! Pet trait 기능 제거에 따른 적절한 에러 코드 변경입니다.

CATEGORY_OPTION_NOT_FOUNDPET_NOT_FOUND로 변경되었으며, 코드베이스 전반에서 pet trait 관련 기능이 제거됨에 따라 적절한 수정입니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/domain/repository/PetRepository.java (1)

14-15: LGTM! Repository 인터페이스가 적절하게 확장되었습니다.

findById 메서드가 Optional<PetEntity>를 반환하도록 정의되어 있어 null-safe한 조회가 가능합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/facade/UserRegisterFacade.java (1)

25-31: LGTM! 이미지 처리 로직 제거로 코드가 간결해졌습니다.

MultipartFile 파라미터와 관련 이미지 처리 로직이 제거되어 Facade의 책임이 명확해졌습니다. 사용자 등록 흐름이 Region 조회 → User 저장 → Pet 저장 → 응답 반환으로 단순화되었습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/repository/PetRepositoryImpl.java (1)

30-33: 새로운 findById 메서드가 올바르게 구현되었습니다.

Optional<PetEntity>를 반환하는 findById 메서드가 적절하게 추가되었으며, Spring Data JPA의 표준 패턴을 따르고 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/response/UserRegisterResponseDto.java (1)

6-16: 응답 DTO 단순화가 적절하게 구현되었습니다.

응답 DTO가 userIdpetId만 포함하도록 단순화되었으며, 팩토리 메서드도 올바르게 업데이트되었습니다. 이는 민감한 정보(loginId) 노출을 줄이고 응답 페이로드를 최소화하는 좋은 설계입니다.

참고: 이 변경은 기존 API 소비자에게 영향을 미치는 breaking change이므로, API 버전 관리 및 문서화가 적절히 업데이트되었는지 확인하세요.

src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/UpdateUserInfoRequestDto.java (1)

22-24: toCommand() 변환 메서드가 올바르게 구현되었습니다.

Request DTO에서 Command로의 변환 로직이 명확하고 간결하게 구현되었습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserQueryService.java (2)

18-18: 클래스 레벨 @transactional 어노테이션이 적절합니다.

Query 서비스에 @Transactional(readOnly = true)를 추가하여 읽기 전용 트랜잭션임을 명시했습니다. 이는 성능 최적화와 의도 명확화에 도움이 됩니다.


24-35: 이메일 조회 로직이 올바르게 통합되었습니다.

SocialAccountRepository를 통한 이메일 조회가 적절하게 구현되었으며, Optional 체이닝을 사용한 안전한 처리가 돋보입니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/controller/PetController.java (1)

13-13: API 경로 변경은 breaking change입니다.

RequestMapping이 /pets/traits에서 /pets로 변경되었습니다. 이는 기존 API 소비자에게 영향을 미칠 수 있으므로, 다음을 확인하세요:

  • API 문서(Swagger) 업데이트 완료
  • 클라이언트 팀에 변경사항 전달
  • API 버전 관리 전략 적용 여부
src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/response/UserInfoResponseDto.java (2)

7-20: LGTM!

email을 별도 파라미터로 받아 처리하고, birth 기반으로 변경된 구조가 적절합니다. 팩토리 메서드 패턴 사용도 깔끔합니다.


22-30: LGTM!

parseGender 헬퍼 메서드가 null 처리와 대소문자 변환을 적절히 수행합니다. toUpperCase() 사용으로 대소문자 혼용 입력에도 안전합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/request/PetRequestDto.java (1)

13-24: LGTM!

age 기반에서 birth 기반으로의 마이그레이션이 적절히 구현되었습니다. @JsonFormat@NotNull 조합으로 날짜 형식 검증이 잘 되어 있습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/api/dto/response/PetProfileResponseDto.java (1)

21-33: LGTM!

from(PetEntity) 팩토리 메서드로 엔티티에서 DTO로의 변환이 깔끔하게 구현되었습니다. imageUrl null 처리도 적절합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/dto/request/CreatePetCommand.java (1)

5-20: LGTM!

age 기반에서 birth 기반으로의 전환이 깔끔하게 구현되었습니다. Command 객체로서 적절한 구조입니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/application/service/PetService.java (2)

37-53: LGTM!

@Transactional 내에서 소유권 검증 후 업데이트하는 패턴이 적절합니다. dirty checking을 통해 자동으로 변경사항이 반영됩니다.


23-35: savePet 메서드에 @Transactional 누락 확인 필요

savePet 메서드에 @Transactional 어노테이션이 없습니다. 호출하는 상위 레이어(Facade 등)에서 트랜잭션이 관리되고 있는지 확인이 필요합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/api/dto/request/CreateUserRequestDto.java (1)

15-34: LGTM!

birth 기반 사용자 등록 DTO가 적절히 구현되었습니다. @Valid를 통한 중첩 객체 검증과 @JsonFormat 날짜 형식 지정이 올바릅니다.

src/main/java/org/sopt/pawkey/backendapi/domain/pet/infra/persistence/entity/PetEntity.java (2)

42-44: LGTM!

@PastOrPresent 검증 어노테이션으로 미래 날짜 입력을 방지합니다. nullable = false로 데이터 무결성도 보장됩니다.


63-64: LGTM!

dbti 필드 추가가 적절합니다. nullable로 설정되어 DBTI 검사 미완료 상태를 허용합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/application/service/UserService.java (2)

28-32: 읽기 전용 트랜잭션 어노테이션 추가 승인

조회 메서드에 @Transactional(readOnly = true)를 추가한 것은 적절합니다. 성능 최적화와 의도 명확화에 도움이 됩니다.


92-102: 새로운 updateUserInfo 메서드 구현 승인

사용자 정보 업데이트를 위한 새로운 메서드가 적절하게 구현되었습니다. 트랜잭션 처리가 올바르게 적용되었고, 명확한 책임 분리가 이루어졌습니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/infra/persistence/entity/UserEntity.java (2)

41-41: @DynamicUpdate 어노테이션 추가 승인

@DynamicUpdate 어노테이션을 추가한 것은 좋은 선택입니다. 이 어노테이션은 변경된 컬럼만 포함하는 UPDATE SQL을 생성하여, 부분 업데이트가 빈번한 사용자 프로필 엔티티에 적합합니다.


119-123: updateProfile 메서드 구현 승인

프로필 업데이트 메서드가 명확하고 간결하게 구현되었습니다. @DynamicUpdate와 함께 사용하여 효율적인 부분 업데이트가 가능합니다.

src/main/java/org/sopt/pawkey/backendapi/domain/user/api/controller/UserController.java (4)

65-74: createUser 엔드포인트 업데이트 승인

@UserId 어노테이션을 통한 인증된 사용자 ID 주입과 @Parameter(hidden = true)를 통한 Swagger 문서 숨김 처리가 적절하게 구현되었습니다. 다른 컨트롤러(RouteController, PostController)와 일관된 패턴을 따릅니다.


76-88: 사용자 정보 조회 엔드포인트 구현 승인

마이페이지를 위한 사용자 정보 조회 엔드포인트가 깔끔하게 구현되었습니다. RESTful 패턴을 잘 따르고 있으며, Swagger 문서화도 적절합니다.


90-103: 사용자 정보 수정 엔드포인트 구현 승인

사용자 정보 수정 API가 적절하게 구현되었습니다. 입력 검증, Command 패턴 적용, 수정 후 최신 데이터 반환 등 모범 사례를 잘 따르고 있습니다.


164-178: 반려견 정보 수정 엔드포인트 구현 승인

반려견 정보 수정 API가 잘 구현되었습니다.

참고: 이슈 #200에서 언급된 경로는 /petInfo/{petId}였으나, PR에서는 /pets/{petId}를 사용하고 있습니다. 이는 GET /me/pets 엔드포인트와의 일관성을 위한 의도적인 변경으로 보이며, 더 RESTful한 설계입니다.

구현 장점:

  • @PathVariable을 통한 petId 추출
  • @Valid를 통한 입력 검증
  • 권한 검증 (403 에러 응답 문서화)
  • 수정 후 최신 데이터 반환으로 좋은 UX 제공

Copy link
Contributor

@dltnals317 dltnals317 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다...! 👍🏻

@daeun-han
Copy link
Member Author

현재 제안 주신 2가지 부분 적용하여 수정했습니다!

  1. PetEntity에서 walkCount 제외하였으며,
  2. User 도메인에 있던 petQueryService 코드를 Pet 도메인으로 이동하였습니다. Facade는 현재 마이페이지 관련 api임을 감안하여 user 도메인에 위치시켰으며 서비스 코드만 pet 도메인으로 이동한 점 감안 부탁드려요

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.

[FEATURE] 마이페이지 내 유저 정보 및 반려견 정보 수정 API

3 participants