지난시간에 양방향 연관관계를 없애면서
레포지토리에 직접 쿼리문을 넣을 수 잇는 아주 좋은 기능들을 발견했다.
그래서 오늘? 이번? 기회에 적극 활용해서 까먹지 않도록 노력해보겠다..
유저 아이디 값에 맞는 프로필 정보 반환하기

주석으로 프로필을 파악하는 함수가 어떤 순서대로 움직여야할지 정리했다.
다 만들고나면 저 내용들 정리해서 위에 자바독 주석으로 쓸 예정
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService{
//유저 레포지토리
UserRepository userRepository;
//팔로우 레포지토리
FollowingRepository followingRepository;
@Override
public UserResponseDto getProfile(Long loginUserId, Long UserId) {
//로그인 한 유저가 자신의 프로필에 들어왔을 경우
if(loginUserId.equals(UserId)){
//팔로우, 팔로잉 상태 보여주지 않는다.
// 이름, 상태메시지, 팔로우/팔로워 수, 게시글 목록
}else{ // 다른 사람의 프로필에 들어갔을 경우
//해당 사용자의 프로필이 퍼블릭 상태인지 확인
//팔로우상태인지 확인
// 1. 언팔로우 + 프라이빗 계정
// 볼 수 없다 메시지 + ( 200 ok ) 리턴
// 2. 그 외( 언팔로우 + 퍼블릭 / 팔로우 + 퍼블릭,프라이빗 )
//해당 사용자의 이름, 상태메시지, 팔로우 여부, 팔로우/팔로워 수, 게시글 목록
}
return null;
}
}
여기서 로그인 여부는 컨트롤러에서 파악이 될 것이고,
이번 프로젝트에서는 세션이 아니라 JWT를 사용하기 때문에 로그인 정보에 대해서 어떻게 받아올지도 고민해야 한다.
JWT에서 로그인 한 유저의 아이디만 저장하도록 하기로 했으니
컨트롤러에서 아이디값만 get으로 받아와서 넣어주면되겠다!

보면 아이디, 이름, 소개, 공개/비공개여부, 팔로잉과 팔로워 수 자체는
남의 것을 보던 내 것을 보던 공통적으로 들어가기때문에 final로 생성자에서 주입하도록 해주었고,
남의 것을 볼 때만 생기는 팔로잉 여부는 세터값으로 지정해주었다.
public interface FollowingRepository extends JpaRepository<Following, Long> {
//팔로잉 수 반환
@Query("SELECT COUNT(*) FROM Following WHERE user = :userId")
Long countByFollowingUserByUserId(@Param("userId") Long userId);
//팔로워 수 반환
@Query("SELECT COUNT(*) FROM Following WHERE followingUser = :userId")
Long countByFollowingUserByFollowingUserId(@Param("userId") Long userId);
boolean existsByFollowingUserIdAndUserId(Long userId, Long followingUserId);
}
함수이름이 길긴한데 완전 알아보기쉬운듯
existsByFollowingUserIdAndUserId은 로그인한 사용자가 접근한 유저를 팔로우 했는지 확인하는 함수다
JPA에서 이름토대로 함수만들어주다보니 저 함수를 SQL그대로 풀어보면
SELECT CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END
FROM following
WHERE following_user_id = ? AND user_id = ?;
이런식으로 나오지않을까????
이렇게되면 countByFollowingUserByUserId랑 countByFollowingUserByFollowingUserId도 위에 JPQL안써도될거같음.
//팔로잉 수 반환
Long countByFollowingUserByUser_id(Long userId);
//팔로워 수 반환
Long countByFollowingUserByFollowingUser_id(Long followingUserId);
boolean existsByFollowingUserIdAndUserId(Long userId, Long followingUserId);
짧게 바꿔줬다.
💡 JPA 함수 명명 규칙
원래 함수 이름을
countByFollowingUserByUserId이렇게 짰는데 JPQL형식이 아니라서 오류가 났다.
그대신countByFollowingUserByUser_id이라는 함수는 바로 적용이 된다.
왜 이럴까??작동 원리 :
_를 사용한 필드 접근
- JPA에서 참고해야할 객체 이름이나 컬럼은 객체->필드 순서로 접근하게 된다.
- 지금 By 뒤에 보면
User_id인데 풀어서User객체 안에 있는id를 접근해라. 가 되는 것이다.- JPA는 엔티티를 바탕으로 데이터베이스 컬럼 접근을 하니, 특정 엔티티의 필드와 매핑을 하게끔 만들어 줘야 한다.
여기서도 제약조건이 붙는다
게시글에 is_public이라는 컬럼이있는데
이 컬럼이 false가되면 작성한 본인만 게시글을 확인할 수 있다.
그래서 자신의 프로필을 열었으면 제약 없이 모든 게시글을 불러와야 하고
남의 게시글일 경우 is_public = true인 게시글만 불러온다.
@Query(value = "SELECT Post FROM Post WHERE user = :user_id AND isPublic = true"
+ " ORDER BY updatedAt DESC ")
List<Post> getAllPublicPostsByUser_id(Long userId);
List<Post> getAllByUser_id(Long userId);
해당 조건을 충족시키는 데이터들을 반환하는 메서드들이다
위는 sql문 보면 알겠지만 퍼블릭한 게시글만 불러오는 것이다.
아래는 그냥 다불러오는거고(자신이 작성한 글)
을 빼먹었다.
게시글이나 회원같은 경우는 삭제한다고 바로 delete sql문 쏴버리는게아니라 deleteAt에 날짜 기록한 후에 천천히 삭제해야하니깐
그것을 감안해야한다...
삭제가 안되었으면 deletedAt이 null상태니깐
null인 row라는 기본조건이 생김
//공개 게시글 + 삭제되지 않은 게시글 + 수정일 기준 최신순 정렬
@Query(value = "SELECT Post FROM Post "
+ "WHERE user = :user_id "
+ "AND isPublic = TRUE "
+ "AND deletedAt IS NULL "
+ "ORDER BY updatedAt DESC ")
List<Post> getAllPublicPostsByUser_id(Long userId);
List<Post> getAllByUser_IdAndDeletedAtIsNullOrderByUpdatedAt(Long userId);
// 삭제되지 않은 게시글 + 수정일 기준 최신 정렬 ( 함수이름 가독성이 좋지않아서 따로 함더감쌌음)
default List<PostResponseDto> getAllMyPosts(Long userId){
return getAllByUser_IdAndDeletedAtIsNullOrderByUpdatedAt(userId)
.stream().map(PostResponseDto::new).collect(Collectors.toList());
}
으워하나같이 다 쿼리문이 길어졌다.
위에껀 사실 더 길게만들면 JPQL쓸일이 없긴한데
그럼또 함더 감싸줘야할 것 같아서 그냥 그대로 놔두었다. ㅎ;
그렇게 1차적으로 완성된
프로필 조회하기 함수
/**
* [Service] 프로필 조회 함수
* 1. controller에서 받아온 유저값 검증
* 2. 다른사람의 프로필 + 팔로우 안했음 + 상대방이 프로필 비공개 상태 -> 403
* 3. 자신의 프로필 조회일 경우
* - 비공개 개시글 표시o, 팔로잉 여부 표시x
* 4. 타인의 프로필 조회일 경우
* - 비공개 게시글 표시x, 팔로잉 여부 표시o
* @param loginUserId 현재 로그인 중인 유저 아이디
* @param UserId 프로필 조회할 유저 아이디
* @return UserProfileResponseDto 프로필 조회 내용
* - 해당 사용자의 이름, 상태메시지, 팔로우 여부, 팔로우/팔로워 수, 게시글 목록
* @throws 403 해당 페이지 접근 권한이 없기 때문에 예외 발생
*/
@Transactional(readOnly = true)
@Override
public UserProfileResponseDto getProfile(Long loginUserId, Long UserId) {
User user = userRepository.findByUserIdOrElseThrow(UserId);
if(!loginUserId.equals(UserId)
&& followingRepository.existsByFollowingUserIdAndUserId(loginUserId, UserId)
&& !user.isPublic()){
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
"이 사용자는 프로필 비공개 설정 상태이며, 친구가 아닌 경우 정보 열람이 제한됩니다.");
}
//팔로잉, 팔로워 수 구하기
Long followingCnt = followingRepository.countByUser_id(UserId);
Long followerCnt = followingRepository.countByFollowingUser_id(UserId);
if(loginUserId.equals(UserId)){
//게시글 가져오기 - 자기자신의 프로필이라 isn't public 한 게시글도 다 불러옴
List<PostResponseDto> posts = postRepository.getAllMyPosts(UserId);
return new UserProfileResponseDto(
user.getId(),
user.getName(),
user.getIntroduction(),
user.isPublic(),
followingCnt.intValue(),
followerCnt.intValue(),
posts
);
}else{
List<Post> posts = postRepository.getAllPublicPostsByUser_id(UserId);
return new UserProfileResponseDto(
user.getId(),
user.getName(),
user.getIntroduction(),
user.isPublic(),
followingCnt.intValue(),
followerCnt.intValue(),
followingRepository.existsByFollowingUserIdAndUserId(loginUserId, UserId),
posts.stream().map(PostResponseDto::new).collect(Collectors.toList())
);
}
}