Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,13 @@ public ApiResponse<List<RecommendCardResponse>> getRecommendedCards(
return ApiResponse.onSuccess(cardService.getRecommendedCardPreviews(user.getId()));
}

@GetMapping("/profile/{userId}")
@Operation(summary = "프로필 화면 피드 미리보기", description = "공유한 카드의 번호와 이미지 url 조회 API")
public ApiResponse<CardResponse.profileCardListDTO> getProfileCardsList(@PathVariable Long userId,

Choose a reason for hiding this comment

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

medium

컨트롤러 메서드의 반환 타입 또한 DTO 클래스 이름에 맞춰 ProfileCardListDTO로 변경해야 합니다. 일관성을 위해 수정해주세요.

Suggested change
public ApiResponse<CardResponse.profileCardListDTO> getProfileCardsList(@PathVariable Long userId,
public ApiResponse<CardResponse.ProfileCardListDTO> getProfileCardsList(@PathVariable Long userId,

@RequestParam(required = false) Long cursor,
@RequestParam(defaultValue = "15") int size) {
return ApiResponse.onSuccess(cardService.getProfileCardList(userId, size, cursor));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,22 @@ public static CardDeleteResponse toCardDeleteResponse(Card card) {
.successMessage("카드 삭제 성공")
.build();
}

public static CardResponse.ProfileCardDTO toProfileCardDto(Card card){
return CardResponse.ProfileCardDTO.builder()
.cardId(card.getId())
.cardImageUrl(card.getCardImageUrl())
.build();
}

public static CardResponse.profileCardListDTO toProfileCardList(Long userId, Slice<Card> cardList) {
return CardResponse.profileCardListDTO.builder()

Choose a reason for hiding this comment

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

medium

컨버터 메서드의 반환 타입과 빌더 호출 부분도 DTO 클래스 이름에 맞춰 ProfileCardListDTO로 변경해야 합니다. 일관성을 위해 수정해주세요.

Suggested change
public static CardResponse.profileCardListDTO toProfileCardList(Long userId, Slice<Card> cardList) {
return CardResponse.profileCardListDTO.builder()
public static CardResponse.ProfileCardListDTO toProfileCardList(Long userId, Slice<Card> cardList) {
return CardResponse.ProfileCardListDTO.builder()

.hasNext(cardList.hasNext())
.nextCursor(cardList.hasNext() ? cardList.getContent().get(cardList.getContent().size() - 1).getId() : null)
.userId(userId)
.cardsList(cardList.getContent().stream()
.map(CardConverter::toProfileCardDto)
.toList())
Comment on lines +174 to +176

Choose a reason for hiding this comment

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

medium

cardList.getContent()가 중복으로 호출되고 있습니다. 169번째 줄에서 이미 list 변수에 저장했으므로, list.stream()을 사용하여 중복 호출을 피하는 것이 좋습니다. 이렇게 하면 코드가 더 효율적이고 깔끔해집니다.

Suggested change
.cardsList(cardList.getContent().stream()
.map(CardConverter::toProfileCardDto)
.toList())
.cardsList(list.stream()
.map(CardConverter::toProfileCardDto)
.toList())

.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,25 @@ public static class CardDeleteResponse {

}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class profileCardListDTO{

Choose a reason for hiding this comment

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

medium

자바 클래스 네이밍 컨벤션에 따라 클래스 이름은 파스칼 케이스(PascalCase)로 작성하는 것이 좋습니다. profileCardListDTOProfileCardListDTO로 변경하여 코드의 일관성과 가독성을 향상시켜 주세요. 이 변경은 관련된 다른 파일들(CardController, CardConverter)에도 적용되어야 합니다.

Suggested change
public static class profileCardListDTO{
public static class ProfileCardListDTO{

private Long userId;
private boolean hasNext;
private Long nextCursor;
private List<ProfileCardDTO> cardsList;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ProfileCardDTO {
private Long cardId;
private String cardImageUrl;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,17 @@ SELECT ch.hashtag.id, COUNT(ch.card.id)
""")
List<Object[]> countCardsByHashtagIds(@Param("hashtagIds") List<Long> hashtagIds);

Slice<Card> findByUserIdAndIsSharedTrueOrderByIdDesc(Long userId, Pageable pageable);
Slice<Card> findByUserIdAndIsSharedTrueAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable);

Choose a reason for hiding this comment

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

high

새로 추가된 프로필 피드 조회용 쿼리 메서드에 isDeleted = false 조건이 누락되어 있습니다. 이로 인해 논리적으로 삭제된 카드가 프로필에 노출될 수 있습니다. 다른 쿼리 메서드들처럼 삭제되지 않은 카드만 조회하도록 IsDeletedFalse 조건을 추가하는 것이 좋습니다. 이 메서드 시그니처를 변경하면 CardServiceImpl에서 호출하는 부분도 함께 수정해야 합니다.

Suggested change
Slice<Card> findByUserIdAndIsSharedTrueOrderByIdDesc(Long userId, Pageable pageable);
Slice<Card> findByUserIdAndIsSharedTrueAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable);
Slice<Card> findByUserIdAndIsDeletedFalseAndIsSharedTrueOrderByIdDesc(Long userId, Pageable pageable);
Slice<Card> findByUserIdAndIsDeletedFalseAndIsSharedTrueAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable);

Choose a reason for hiding this comment

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

high

프로필 카드 목록을 조회하는 쿼리 메서드에 삭제된 카드를 필터링하는 IsDeletedFalse 조건이 누락되었습니다. countByUserIdAndIsDeletedFalseAndIsSharedTrue 메서드에서는 해당 조건이 사용되고 있어 로직의 일관성이 부족하며, 삭제된 카드가 프로필에 노출될 수 있는 버그를 유발할 수 있습니다. 메서드 이름과 쿼리에 IsDeletedFalse 조건을 추가하여 수정하는 것을 권장합니다.

Suggested change
Slice<Card> findByUserIdAndIsSharedTrueOrderByIdDesc(Long userId, Pageable pageable);
Slice<Card> findByUserIdAndIsSharedTrueAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable);
Slice<Card> findByUserIdAndIsSharedTrueAndIsDeletedFalseOrderByIdDesc(Long userId, Pageable pageable);
Slice<Card> findByUserIdAndIsSharedTrueAndIdLessThanAndIsDeletedFalseOrderByIdDesc(Long userId, Long cursor, Pageable pageable);


@Query("""
select count(c)
from Card c
where c.isDeleted = false
and c.isShared = true
and c.user.id = :userId
""")
Long countByUserIdAndIsDeletedFalseAndIsSharedTrue(@Param("userId") Long userId);


}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public interface CardService {
CardDetailResponse updateCard(Long cardId, User user, CardUpdateRequest request);
CardResponse.PagedCardFeedResponseDto getCardFeedByCursor(HttpServletRequest request, Long userId, int size, Long cursor);
List<RecommendCardResponse> getRecommendedCardPreviews(Long userId);
CardResponse.profileCardListDTO getProfileCardList(Long userId, int size, Long cursor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,19 @@ public CardFeedResponse getCardFeed(Long cardId, Long userId) {
);
}

@Override

Choose a reason for hiding this comment

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

medium

getProfileCardList 메서드는 데이터베이스에서 데이터를 읽기만 하는 조회용 메서드입니다. 성능 향상과 일관성을 위해 @Transactional(readOnly = true) 어노테이션을 추가하는 것을 권장합니다. 클래스 내 다른 조회 메서드들에도 적용되어 있습니다.

    @Override
    @Transactional(readOnly = true)

public CardResponse.profileCardListDTO getProfileCardList(Long userId, int size, Long cursor) {
Slice<Card> cardSlice;
Pageable pageable = PageRequest.of(0, size);

if (cursor == null) {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueOrderByIdDesc(userId, pageable);
} else {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIdLessThanOrderByIdDesc(userId, cursor, pageable);
}

Choose a reason for hiding this comment

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

high

이 메서드에서 호출하는 cardRepository의 쿼리 메서드들은 삭제된 카드를 필터링하지 않습니다. CardRepository의 관련 메서드에 IsDeletedFalse 조건을 추가하고, 여기서 수정된 메서드를 호출하도록 변경해야 합니다. 이렇게 해야 삭제된 카드가 프로필 피드에 나타나지 않습니다.

Suggested change
if (cursor == null) {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueOrderByIdDesc(userId, pageable);
} else {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIdLessThanOrderByIdDesc(userId, cursor, pageable);
}
if (cursor == null) {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIsDeletedFalseOrderByIdDesc(userId, pageable);
} else {
cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIdLessThanAndIsDeletedFalseOrderByIdDesc(userId, cursor, pageable);
}

return CardConverter.toProfileCardList(userId, cardSlice);
}

@Override
@Transactional
public CardDeleteResponse deleteCard(Long cardId, Long userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ public ApiResponse<UserResponseDTO.ProfileDto> updateUserIntroduce(
UserResponseDTO.ProfileDto updatedProfile = userService.updateIntroduce(request, introduce, user);
return ApiResponse.onSuccess(updatedProfile);
}

@Operation(summary = "유저 프로필 조회")
@PostMapping("/profile/{userId}")
@Tag(name = "User", description = "사용자 관련 API")
public ApiResponse<UserResponseDTO.DetailProfileDto> getProfile(HttpServletRequest request,
@PathVariable Long userId) {
return ApiResponse.onSuccess(userService.getProfile(request,userId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ public static UserResponseDTO.ProfileDto toProfileIconDto(User user){
.isFollowing(true)
.build();
}
// todo: 두개 비슷함 -> 합치기
public static UserResponseDTO.ProfileDto toProfileDto(User user, Boolean isFollowing){
return UserResponseDTO.ProfileDto.builder()
.userId(user.getId())
Expand All @@ -73,6 +72,25 @@ public static UserResponseDTO.ProfileDto toProfileDto(User user, Boolean isFollo
.isFollowing(isFollowing)
.build();
}
public static UserResponseDTO.DetailProfileDto toDetailProfileDto(
User user,
Boolean isFollowing,
Long totalCard,
Long totalFollower,
Long totalFollowing) {

return UserResponseDTO.DetailProfileDto.builder()
.userId(user.getId())
.profileImageUrl(user.getProfileImageUrl())
.nameId(user.getNameId())
.nickname(user.getNickname())
.isFollowing(isFollowing)
.introduce(user.getIntroduce())
.totalCard(totalCard)
.totalFollower(totalFollower)
.totalFollowing(totalFollowing)
.build();
}

public static ReactionResponseDTO.CardReactionUserListDto toCardReactionUsersListDto(Long cardId, ReactionType reactionType, Page<UserResponseDTO.ProfileDto> profileList){
return ReactionResponseDTO.CardReactionUserListDto.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ public static class UserIconListResponseDto{
private List<ProfileDto> userIconList;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class DetailProfileDto {
@NotNull
private Long userId;
@NotNull
private String profileImageUrl;
@NotNull
private String nameId;
@NotNull
private String nickname;
@NotNull
private Boolean isFollowing;
@NotNull
private String introduce;
@NotNull
private Long totalCard;
@NotNull
private Long totalFollower;
@NotNull
private Long totalFollowing;
}

@Getter
@Builder
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ public interface UserFollowRepository extends JpaRepository<UserFollow,Long> {
List<Long> findFollowingUserIds(@Param("userId") Long userId);

UserFollow findByUserAndTargetUser(User user, User target);

Long countUserFollowByTargetUser(User targetUser);
Long countUserFollowByUser(User user);
Comment on lines +27 to +28

Choose a reason for hiding this comment

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

medium

메서드 이름이 약간 혼동을 줄 수 있습니다. Spring Data JPA의 명명 규칙에 따라 더 명확하고 간결한 이름으로 변경하는 것을 제안합니다. 예를 들어, countByTargetUser는 팔로워 수를, countByUser는 팔로잉 수를 계산하는 것으로 명확하게 이해될 수 있습니다. 이 변경 사항을 적용할 경우 UserServiceImpl에서의 메서드 호출도 함께 수정해야 합니다.

Suggested change
Long countUserFollowByTargetUser(User targetUser);
Long countUserFollowByUser(User user);
Long countByTargetUser(User targetUser);
Long countByUser(User user);

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public interface UserService {
UserResponseDTO.UserActionResponseDto followUser(HttpServletRequest request, Long targetUserId);
UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest request, Long targetUserId);
User getLoginUser(HttpServletRequest request);
UserResponseDTO.DetailProfileDto getProfile(HttpServletRequest request,Long userId);

// 마이페이지 업데이트 관련 서비스
UserResponseDTO.ProfileDto updateUserProfileImage(HttpServletRequest request, MultipartFile profileImage, User user);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package EatPic.spring.domain.user.service;

import EatPic.spring.domain.card.repository.CardRepository;
import EatPic.spring.domain.user.converter.UserConverter;
import EatPic.spring.domain.user.dto.*;
import EatPic.spring.domain.user.dto.request.LoginRequestDTO;
Expand Down Expand Up @@ -44,6 +45,7 @@ public class UserServiceImpl implements UserService{
private final UserFollowRepository userFollowRepository;
private final UserBlockRepository userBlockRepository;
private final UserBadgeService userBadgeService;
private final CardRepository cardRepository;
private final PasswordEncoder passwordEncoder;
private final JwtTokenProvider jwtTokenProvider;

Expand Down Expand Up @@ -189,6 +191,19 @@ public User getLoginUser(HttpServletRequest request) {
.orElseThrow(() -> new ExceptionHandler(ErrorStatus.MEMBER_NOT_FOUND));
}

@Override

Choose a reason for hiding this comment

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

medium

getProfile 메서드는 데이터베이스에서 데이터를 읽기만 하는 조회용 메서드입니다. 성능 향상과 일관성을 위해 @Transactional(readOnly = true) 어노테이션을 추가하는 것을 권장합니다. 클래스 내 다른 조회 메서드들에도 적용되어 있습니다.

    @Override
    @Transactional(readOnly = true)

public UserResponseDTO.DetailProfileDto getProfile(HttpServletRequest request, Long userId) {
User me = getLoginUser(request);

User user = userRepository.findUserById(userId);
Boolean isFollowing = userFollowRepository.existsByUserAndTargetUser(me, user);
Long totalCard = cardRepository.countByUserIdAndIsDeletedFalseAndIsSharedTrue(userId);
Long totalFollower = userFollowRepository.countUserFollowByTargetUser(user);
Long totalFollowing = userFollowRepository.countUserFollowByUser(user);

return UserConverter.toDetailProfileDto(user, isFollowing,totalCard,totalFollower,totalFollowing);
}

@Override
public UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest request, Long targetUserId) {
User user = getLoginUser(request);
Expand Down
Loading