From 70e01f81f5c8599e4893ea0e90eb50e2c394c999 Mon Sep 17 00:00:00 2001 From: Juhyeon Lee Date: Tue, 17 Mar 2026 18:28:46 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[FIX]=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80=20+=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=EA=B0=B1=EC=8B=A0=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EC=99=B8=203=EA=B1=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/api/category/controller/CategoryController.java | 2 +- .../java/com/moongeul/backend/api/member/entity/Member.java | 3 +-- .../moongeul/backend/api/member/service/MemberService.java | 4 ++-- src/main/java/com/moongeul/backend/api/post/dto/PostDTO.java | 5 +++++ .../com/moongeul/backend/api/post/service/PostService.java | 3 +++ .../com/moongeul/backend/api/story/service/StoryService.java | 4 ++-- .../backend/common/config/security/SecurityConfig.java | 1 + 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/moongeul/backend/api/category/controller/CategoryController.java b/src/main/java/com/moongeul/backend/api/category/controller/CategoryController.java index 77b1839..d646871 100644 --- a/src/main/java/com/moongeul/backend/api/category/controller/CategoryController.java +++ b/src/main/java/com/moongeul/backend/api/category/controller/CategoryController.java @@ -65,7 +65,7 @@ public ResponseEntity> createCategory(@Authenti @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 카테고리를 찾을 수 없습니다."), }) @GetMapping("/{id}") - public ResponseEntity> getCategoryTitle(@RequestParam Long id) { + public ResponseEntity> getCategoryTitle(@PathVariable Long id) { CategoryResponseDTO response = categoryService.getCategoryTitle(id); return ApiResponse.success(SuccessStatus.GET_CATEGORY_TITLE_SUCCESS, response); diff --git a/src/main/java/com/moongeul/backend/api/member/entity/Member.java b/src/main/java/com/moongeul/backend/api/member/entity/Member.java index d3d9c0a..a89048d 100644 --- a/src/main/java/com/moongeul/backend/api/member/entity/Member.java +++ b/src/main/java/com/moongeul/backend/api/member/entity/Member.java @@ -58,9 +58,8 @@ public String getAuthorityKey() { /** * OAuth2 로그인 시 이름, 사진이 변경될 경우 Entity를 업데이트하는 메서드 */ - public Member update(String name, String picture) { + public Member update(String name) { this.name = name; - this.profileImage = picture; return this; } diff --git a/src/main/java/com/moongeul/backend/api/member/service/MemberService.java b/src/main/java/com/moongeul/backend/api/member/service/MemberService.java index fb46574..987ebf8 100644 --- a/src/main/java/com/moongeul/backend/api/member/service/MemberService.java +++ b/src/main/java/com/moongeul/backend/api/member/service/MemberService.java @@ -95,7 +95,7 @@ public LoginResponseDTO loginWithGoogle(String code, String type){ // 3. DB 처리 (회원가입 또는 로그인) Member member = memberRepository.findBySocialId(socialId) - .map(entity -> entity.update(name, picture)) // 이미 있으면 정보 업데이트 + .map(entity -> entity.update(name)) // 이미 있으면 정보 업데이트 .orElseGet(() -> signUp(socialId, email, name, picture, socialType)); // 없으면 신규 회원가입 // 4. 자체 JWT 토큰 생성 및 반환 @@ -129,7 +129,7 @@ public LoginResponseDTO loginWithKakao(String code, String type){ // 3. DB 처리 (회원가입 또는 로그인) Member member = memberRepository.findBySocialId(socialId) - .map(entity -> entity.update(name, picture)) // 이미 있으면 정보 업데이트 + .map(entity -> entity.update(name)) // 이미 있으면 정보 업데이트 .orElseGet(() -> signUp(socialId, email, name, picture, socialType)); // 없으면 신규 회원가입 // 4. 자체 JWT 토큰 생성 및 반환 diff --git a/src/main/java/com/moongeul/backend/api/post/dto/PostDTO.java b/src/main/java/com/moongeul/backend/api/post/dto/PostDTO.java index 170c98b..e417a85 100644 --- a/src/main/java/com/moongeul/backend/api/post/dto/PostDTO.java +++ b/src/main/java/com/moongeul/backend/api/post/dto/PostDTO.java @@ -1,5 +1,6 @@ package com.moongeul.backend.api.post.dto; +import com.moongeul.backend.api.post.entity.PostVisibility; import com.moongeul.backend.api.readingTaste.entity.ReadingTasteType; import lombok.AllArgsConstructor; import lombok.Builder; @@ -23,7 +24,11 @@ public class PostDTO { private BookInfo bookInfo; // 책 정보 + private PostVisibility postVisibility; // 공개여부 + private Long categoryId; // 카테고리 id + private Double rating; // 별점 + private Integer page; // 페이지 수 private String content; // 감상평 private LocalDate readDate; // 읽은날짜 diff --git a/src/main/java/com/moongeul/backend/api/post/service/PostService.java b/src/main/java/com/moongeul/backend/api/post/service/PostService.java index 84816b2..d30d78e 100644 --- a/src/main/java/com/moongeul/backend/api/post/service/PostService.java +++ b/src/main/java/com/moongeul/backend/api/post/service/PostService.java @@ -234,8 +234,11 @@ private PostDTO convertToPostDTO(Post post, PostDTO.MyLikesStatus myLikesStatus, .pubdate(post.getBook().getPubdate()) .ratingAverage(post.getBook().getRatingAverage()) .build()) + .postVisibility(post.getPostVisibility()) + .categoryId(post.getCategory().getId()) .created(post.getCreatedAt()) .rating(post.getRating()) + .page(post.getPage()) .content(post.getContent()) .readDate(post.getReadDate()) .quotesCnt(quoteDTOList.size()) diff --git a/src/main/java/com/moongeul/backend/api/story/service/StoryService.java b/src/main/java/com/moongeul/backend/api/story/service/StoryService.java index b139bfe..b437ed1 100644 --- a/src/main/java/com/moongeul/backend/api/story/service/StoryService.java +++ b/src/main/java/com/moongeul/backend/api/story/service/StoryService.java @@ -84,8 +84,8 @@ public StoryAllResponseDTO getALLStory(String email, PostVisibility postVisibili Pageable pageable = PageRequest.of(page - 1, size); Page storyPage; - // 현재 시간 기준 24시간 전 계산 - LocalDateTime timeLimit = LocalDateTime.now().minusHours(24); + // 현재 시간 기준 24시간 전 계산 -> 한 달 임시 수정 + LocalDateTime timeLimit = LocalDateTime.now().minusMonths(1); boolean isAnonymous = (email == null || "anonymousUser".equals(email)); diff --git a/src/main/java/com/moongeul/backend/common/config/security/SecurityConfig.java b/src/main/java/com/moongeul/backend/common/config/security/SecurityConfig.java index de42056..d534cbc 100644 --- a/src/main/java/com/moongeul/backend/common/config/security/SecurityConfig.java +++ b/src/main/java/com/moongeul/backend/common/config/security/SecurityConfig.java @@ -37,6 +37,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/v3/api-docs/**", "/api-doc/**", "/swagger-ui/**").permitAll() .requestMatchers("/api/v2/member/google/login", "/api/v2/member/kakao/login", "/api/v2/member/reissue-token").permitAll() .requestMatchers("/api/v2/reading-taste", "/api/v2/reading-taste/total-count").permitAll() + .requestMatchers("/api/v2/book/bestseller").permitAll() .requestMatchers(HttpMethod.GET, "/api/v2/post/**").permitAll() .anyRequest().authenticated() ); From 61c0cf4a76ee81d92d9bd93d1409cbaee8162038 Mon Sep 17 00:00:00 2001 From: Juhyeon Lee Date: Wed, 18 Mar 2026 02:04:56 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[FEAT]=20=ED=8C=94=EB=A1=9C=EC=9E=89/?= =?UTF-8?q?=ED=8C=94=EB=A1=9C=EC=9B=8C=20userId=20=EA=B8=B0=EB=B0=98=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 12 +++-- .../api/member/service/FollowService.java | 53 ++++++++++++++++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/moongeul/backend/api/member/controller/MemberController.java b/src/main/java/com/moongeul/backend/api/member/controller/MemberController.java index 165782e..f3cc028 100644 --- a/src/main/java/com/moongeul/backend/api/member/controller/MemberController.java +++ b/src/main/java/com/moongeul/backend/api/member/controller/MemberController.java @@ -308,6 +308,7 @@ public ResponseEntity> unfollow(@AuthenticationPrincipal UserD @Operation( summary = "팔로잉 사용자 목록 조회 API", description = "내가 팔로우한 사용자(팔로잉)의 목록을 조회합니다." + + "
userId 쿼리파라미터가 없으면 본인, 있으면 해당 사용자의 정보를 조회합니다." + "

[enum] myFollowStatus: 내가 해당 팔로워를 팔로우했는지 확인하는 필드:" + "
- NONE: 팔로우 아님" + "
- PENDING: 요청 대기중" + @@ -319,14 +320,16 @@ public ResponseEntity> unfollow(@AuthenticationPrincipal UserD @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 사용자를 찾을 수 없습니다.") }) @GetMapping("/following") - public ResponseEntity>> getFollowings(@AuthenticationPrincipal UserDetails userDetails){ - List response = followService.getFollowing(userDetails.getUsername()); + public ResponseEntity>> getFollowings(@AuthenticationPrincipal UserDetails userDetails, + @RequestParam(required = false) Long userId){ + List response = followService.getFollowing(userDetails.getUsername(), userId); return ApiResponse.success(SuccessStatus.GET_FOLLOWING_SUCCESS, response); } @Operation( summary = "팔로워 사용자 목록 조회 API", description = "나를 팔로잉한 사용자(팔로워)의 목록을 조회합니다." + + "
userId 쿼리파라미터가 없으면 본인, 있으면 해당 사용자의 정보를 조회합니다." + "

[enum] myFollowStatus: 내가 해당 팔로워를 팔로우했는지 확인하는 필드:" + "
- NONE: 팔로우 아님" + "
- PENDING: 요청 대기중" + @@ -337,8 +340,9 @@ public ResponseEntity>> getFollowings(@Authe @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 사용자를 찾을 수 없습니다.") }) @GetMapping("/follower") - public ResponseEntity>> getFollowers(@AuthenticationPrincipal UserDetails userDetails){ - List response = followService.getFollower(userDetails.getUsername()); + public ResponseEntity>> getFollowers(@AuthenticationPrincipal UserDetails userDetails, + @RequestParam(required = false) Long userId){ + List response = followService.getFollower(userDetails.getUsername(), userId); return ApiResponse.success(SuccessStatus.GET_FOLLOWER_SUCCESS, response); } diff --git a/src/main/java/com/moongeul/backend/api/member/service/FollowService.java b/src/main/java/com/moongeul/backend/api/member/service/FollowService.java index 1754838..523c72a 100644 --- a/src/main/java/com/moongeul/backend/api/member/service/FollowService.java +++ b/src/main/java/com/moongeul/backend/api/member/service/FollowService.java @@ -14,6 +14,7 @@ import com.moongeul.backend.api.notification.repository.NotificationRepository; import com.moongeul.backend.api.notification.service.NotificationTriggerService; import com.moongeul.backend.common.exception.BadRequestException; +import com.moongeul.backend.common.exception.ForbiddenException; import com.moongeul.backend.common.exception.NotFoundException; import com.moongeul.backend.common.response.ErrorStatus; import lombok.RequiredArgsConstructor; @@ -89,10 +90,11 @@ public void unfollow(Long followingId, String email) { // 팔로잉 사용자 목록 조회 @Transactional(readOnly = true) // 생성, 수정, 삭제가 없는 메서드 - public List getFollowing(String email){ - Member me = getMemberByEmail(email); + public List getFollowing(String email, Long userId){ + Member currentMember = getMemberByEmail(email); + Member targetMember = getTargetMember(currentMember, userId); - return followRepository.findByFollowings(me.getId()) + return followRepository.findByFollowings(targetMember.getId()) .stream() .map(follow -> { Member following = follow.getFollowing(); @@ -109,15 +111,16 @@ public List getFollowing(String email){ // 팔로워 사용자 목록 조회 @Transactional(readOnly = true) // 생성, 수정, 삭제가 없는 메서드 - public List getFollower(String email){ - Member me = getMemberByEmail(email); + public List getFollower(String email, Long userId){ + Member currentMember = getMemberByEmail(email); + Member targetMember = getTargetMember(currentMember, userId); // 나를 팔로우하는 사람들(Follower) 목록 조회 (상태: ACCEPTED만) - List followers = followRepository.findByFollowers(me.getId()); + List followers = followRepository.findByFollowers(targetMember.getId()); // 내가 누구를 팔로우하고 있는지(PENDING, ACCEPTED) 전체 목록을 Map으로 만듦 // Map은 서비스 로직 안에서만 '검색용'으로 쓰임 - Map myFollowStatusMap = followRepository.findAllByFollowerId(me.getId()) + Map myFollowStatusMap = followRepository.findAllByFollowerId(targetMember.getId()) .stream() .collect(Collectors.toMap( follow -> follow.getFollowing().getId(), // Key: 상대방 ID @@ -177,4 +180,40 @@ private Member getMemberByEmail(String email) { return memberRepository.findByEmail(email) .orElseThrow(() -> new NotFoundException(ErrorStatus.USER_NOTFOUND_EXCEPTION.getMessage())); } + + private Member getTargetMember(Member currentMember, Long userId) { + Member targetMember = (userId == null) + ? currentMember + : memberRepository.findById(userId) + .orElseThrow(() -> new NotFoundException(ErrorStatus.USER_NOTFOUND_EXCEPTION.getMessage())); + + validatePrivacyAccess(currentMember, targetMember); + return targetMember; + } + + private void validatePrivacyAccess(Member currentMember, Member targetMember) { + if (currentMember.getId().equals(targetMember.getId())) { + return; + } + + PrivacyLevel privacyLevel = targetMember.getPrivacyLevel(); + + if (privacyLevel == null || privacyLevel == PrivacyLevel.PUBLIC) { + return; + } + + if (privacyLevel == PrivacyLevel.PRIVATE) { + throw new ForbiddenException(ErrorStatus.PRIVACY_FORBIDDEN_EXCEPTION.getMessage()); + } + + if (privacyLevel == PrivacyLevel.FOLLOWER_ONLY) { + FollowStatus status = followRepository.findByFollowingIdAndFollowerId(targetMember.getId(), currentMember.getId()) + .map(Follow::getFollowStatus) + .orElse(FollowStatus.NONE); + + if (status != FollowStatus.ACCEPTED) { + throw new ForbiddenException(ErrorStatus.PRIVACY_FORBIDDEN_EXCEPTION.getMessage()); + } + } + } }