From 67cb088b8d98ded778bc7db0caf19521097f5f9a Mon Sep 17 00:00:00 2001 From: dong99u Date: Wed, 30 Jul 2025 17:42:13 +0900 Subject: [PATCH 1/5] [REFACTOR] Update memo retrieval and modification methods for user-specific access - Added `findByIdAndUserId` method to `MemoRepository`. - Applied `@Operation` annotation to enhance memo update controller documentation. - Removed unnecessary `User` validation in `MemoQueryServiceImpl`. - Refactored `Utils` class to prevent instantiation using `@NoArgsConstructor`. --- .../domain/memo/controller/MemoController.java | 4 ++++ .../domain/memo/repository/MemoRepository.java | 4 ++++ .../memo/service/query/MemoQueryServiceImpl.java | 13 +------------ .../com/indayvidual/server/global/util/Utils.java | 7 ++++++- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/indayvidual/server/domain/memo/controller/MemoController.java b/src/main/java/com/indayvidual/server/domain/memo/controller/MemoController.java index eb52ebd..a5138ba 100644 --- a/src/main/java/com/indayvidual/server/domain/memo/controller/MemoController.java +++ b/src/main/java/com/indayvidual/server/domain/memo/controller/MemoController.java @@ -109,6 +109,10 @@ public ApiResponse getMemoDetail( } @PatchMapping("/{memoId}") + @Operation( + summary = "메모 수정", + description = "특정 메모를 수정합니다. 본인이 작성한 메모만 수정이 가능합니다." + ) public ApiResponse updateMemo( @Parameter(description = "메모 ID", required = true, example = "1") @PathVariable Long memoId, diff --git a/src/main/java/com/indayvidual/server/domain/memo/repository/MemoRepository.java b/src/main/java/com/indayvidual/server/domain/memo/repository/MemoRepository.java index 5e52cce..c1ffb41 100644 --- a/src/main/java/com/indayvidual/server/domain/memo/repository/MemoRepository.java +++ b/src/main/java/com/indayvidual/server/domain/memo/repository/MemoRepository.java @@ -1,5 +1,7 @@ package com.indayvidual.server.domain.memo.repository; +import java.util.Optional; + import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,5 +11,7 @@ @Repository public interface MemoRepository extends JpaRepository { + Optional findByIdAndUserId(Long memoId, Long userId); + Slice findByUserIdOrderByCreatedAtDescIdDesc(Long userId, Pageable pageable); } diff --git a/src/main/java/com/indayvidual/server/domain/memo/service/query/MemoQueryServiceImpl.java b/src/main/java/com/indayvidual/server/domain/memo/service/query/MemoQueryServiceImpl.java index 4f3eae7..3a89db5 100644 --- a/src/main/java/com/indayvidual/server/domain/memo/service/query/MemoQueryServiceImpl.java +++ b/src/main/java/com/indayvidual/server/domain/memo/service/query/MemoQueryServiceImpl.java @@ -12,9 +12,6 @@ import com.indayvidual.server.domain.memo.entity.Memo; import com.indayvidual.server.domain.memo.exception.MemoException; import com.indayvidual.server.domain.memo.repository.MemoRepository; -import com.indayvidual.server.domain.user.entity.User; -import com.indayvidual.server.domain.user.exception.UserException; -import com.indayvidual.server.domain.user.repository.UserRepository; import com.indayvidual.server.global.api.code.status.ErrorStatus; import com.indayvidual.server.global.util.Utils; @@ -28,7 +25,6 @@ public class MemoQueryServiceImpl implements MemoQueryService { private final MemoRepository memoRepository; - private final UserRepository userRepository; @Override public MemoSliceResponseDTO getMemosWithSlice(Long userId, Integer page, Integer size) { @@ -44,17 +40,10 @@ public MemoSliceResponseDTO getMemosWithSlice(Long userId, Integer page, Integer @Override public MemoDetailResponseDTO getMemoDetail(Long userId, Long memoId) { - User currentUser = userRepository.findById(userId) - .orElseThrow(() -> new UserException(ErrorStatus.USER_NOT_FOUND)); - Memo memo = memoRepository.findById(memoId) + Memo memo = memoRepository.findByIdAndUserId(memoId, userId) .orElseThrow(() -> new MemoException(ErrorStatus.MEMO_NOT_FOUND)); - // 만약 접근하는 사용자가 메모의 소유자가 아니라면 접근 금지 - if (!memo.getUser().equals(currentUser)) { - throw new MemoException(ErrorStatus.MEMO_OWNER_MISMATCH); - } - return MemoDetailResponseDTO.from(memo); } diff --git a/src/main/java/com/indayvidual/server/global/util/Utils.java b/src/main/java/com/indayvidual/server/global/util/Utils.java index 916dc76..e2cd293 100644 --- a/src/main/java/com/indayvidual/server/global/util/Utils.java +++ b/src/main/java/com/indayvidual/server/global/util/Utils.java @@ -1,9 +1,14 @@ package com.indayvidual.server.global.util; -import com.indayvidual.server.global.config.security.JwtUserPrincipal; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import com.indayvidual.server.global.config.security.JwtUserPrincipal; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class Utils { private static final int DEFAULT_PAGE_SIZE = 20; From 76dbad3b17118a17978cf282208030eb1c54e35d Mon Sep 17 00:00:00 2001 From: dong99u Date: Wed, 30 Jul 2025 17:46:37 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[REFACTOR]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20select=EB=AC=B8=EC=9D=B4=20=EB=82=98=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=EA=B2=83=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/memo/entity/Memo.java | 20 +--------------- .../command/MemoCommandServiceImpl.java | 24 +++++++------------ 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/indayvidual/server/domain/memo/entity/Memo.java b/src/main/java/com/indayvidual/server/domain/memo/entity/Memo.java index bfe216d..c1627ac 100644 --- a/src/main/java/com/indayvidual/server/domain/memo/entity/Memo.java +++ b/src/main/java/com/indayvidual/server/domain/memo/entity/Memo.java @@ -4,9 +4,7 @@ import org.hibernate.annotations.DynamicUpdate; import com.indayvidual.server.common.BaseEntity; -import com.indayvidual.server.domain.memo.exception.MemoException; import com.indayvidual.server.domain.user.entity.User; -import com.indayvidual.server.global.api.code.status.ErrorStatus; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -67,21 +65,5 @@ public void updateContent(String content) { } //== 비즈니스 로직 ==// - public boolean isOwnerBy(User user) { - return this.user.equals(user); - } - - public void ensureOwnership(User user) { - if (!isOwnerBy(user)) { - throw new MemoException(ErrorStatus.MEMO_OWNER_MISMATCH); - } - } - - public boolean canDeleteMemo(User user) { - ensureOwnership(user); - - user.getMemos().remove(this); - - return true; - } + } diff --git a/src/main/java/com/indayvidual/server/domain/memo/service/command/MemoCommandServiceImpl.java b/src/main/java/com/indayvidual/server/domain/memo/service/command/MemoCommandServiceImpl.java index df36ce9..1f91d1a 100644 --- a/src/main/java/com/indayvidual/server/domain/memo/service/command/MemoCommandServiceImpl.java +++ b/src/main/java/com/indayvidual/server/domain/memo/service/command/MemoCommandServiceImpl.java @@ -10,7 +10,6 @@ import com.indayvidual.server.domain.memo.exception.MemoException; import com.indayvidual.server.domain.memo.repository.MemoRepository; import com.indayvidual.server.domain.user.entity.User; -import com.indayvidual.server.domain.user.exception.UserException; import com.indayvidual.server.domain.user.repository.UserRepository; import com.indayvidual.server.global.api.code.status.ErrorStatus; @@ -40,22 +39,17 @@ public MemoDetailResponseDTO createMemo(Long userId, CreateMemoRequestDTO reques @Override public Void deleteMemo(Long userId, Long memoId) { - User currentUser = getCurrentUser(userId); - Memo memo = getMemo(memoId); + Memo memo = getMemoByMemoIdAndUserId(memoId, userId); - if (memo.canDeleteMemo(currentUser)) { - memoRepository.delete(memo); - } + memoRepository.delete(memo); return null; } @Override public MemoDetailResponseDTO updateMemo(Long userId, Long memoId, UpdateMemoRequestDTO request) { - User currentUser = getCurrentUser(userId); - - Memo memo = getMemo(memoId); + Memo memo = getMemoByMemoIdAndUserId(memoId, userId); memo.updateTitle(request.getTitle()); memo.updateContent(request.getContent()); @@ -63,13 +57,13 @@ public MemoDetailResponseDTO updateMemo(Long userId, Long memoId, UpdateMemoRequ return MemoDetailResponseDTO.from(memo); } - private Memo getMemo(Long memoId) { - return memoRepository.findById(memoId) - .orElseThrow(() -> new MemoException(ErrorStatus.MEMO_NOT_FOUND)); - } - private User getCurrentUser(Long userId) { return userRepository.findById(userId) - .orElseThrow(() -> new UserException(ErrorStatus.USER_NOT_FOUND)); + .orElseThrow(() -> new MemoException(ErrorStatus.USER_NOT_FOUND)); + } + + private Memo getMemoByMemoIdAndUserId(Long memoId, Long userId) { + return memoRepository.findById(memoId) + .orElseThrow(() -> new MemoException(ErrorStatus.MEMO_NOT_FOUND)); } } From e83ee2be32e2a2aa79edfc7f72b6bf3461d483e6 Mon Sep 17 00:00:00 2001 From: dong99u Date: Wed, 30 Jul 2025 17:51:14 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[REFACTOR]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20select=EB=AC=B8=EC=9D=B4=20=EB=82=98=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=EA=B2=83=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/habit/service/query/HabitQueryServiceImpl.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/indayvidual/server/domain/habit/service/query/HabitQueryServiceImpl.java b/src/main/java/com/indayvidual/server/domain/habit/service/query/HabitQueryServiceImpl.java index 4bdc0d8..0f64e30 100644 --- a/src/main/java/com/indayvidual/server/domain/habit/service/query/HabitQueryServiceImpl.java +++ b/src/main/java/com/indayvidual/server/domain/habit/service/query/HabitQueryServiceImpl.java @@ -53,7 +53,6 @@ public HabitSliceResponseDTO getHabits(Long userId, Integer page, Integer size) @Override public List getDailyHabitCheckedState(Long userId, LocalDate date) { - User currentUser = getCurrentUser(userId); List habits = habitRepository.findAllHabitsWithLogsOnDate(userId, date); @@ -71,9 +70,8 @@ public List getDailyHabitCheckedState(Long userId, LocalDate d @Override public List getWeeklyChecks(Long userId, LocalDate startDate) { - User currentUser = getCurrentUser(userId); - List allHabitsWithLogsOnDateRange = habitRepository.findAllHabitsWithLogsOnDateRange(currentUser.getId(), + List allHabitsWithLogsOnDateRange = habitRepository.findAllHabitsWithLogsOnDateRange(userId, startDate, startDate.plusDays(6)); return allHabitsWithLogsOnDateRange.stream() @@ -84,7 +82,6 @@ public List getWeeklyChecks(Long userId, LocalDate @Override public List getMonthlyChecks(Long userId, YearMonth yearMonth) { - User currentUser = getCurrentUser(userId); List habits = habitRepository.findAllHabitsWithLogsOnMonth(userId, yearMonth); From 967cfb727ac54c1f3470e2db50f3d7c2cc160d7d Mon Sep 17 00:00:00 2001 From: dong99u Date: Wed, 30 Jul 2025 17:53:25 +0900 Subject: [PATCH 4/5] [REFACTOR] Replace exception in `Utils.getUserId` with `UserException` for better error handling --- src/main/java/com/indayvidual/server/global/util/Utils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/indayvidual/server/global/util/Utils.java b/src/main/java/com/indayvidual/server/global/util/Utils.java index e2cd293..5dbc545 100644 --- a/src/main/java/com/indayvidual/server/global/util/Utils.java +++ b/src/main/java/com/indayvidual/server/global/util/Utils.java @@ -3,6 +3,8 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import com.indayvidual.server.domain.user.exception.UserException; +import com.indayvidual.server.global.api.code.status.ErrorStatus; import com.indayvidual.server.global.config.security.JwtUserPrincipal; import lombok.AccessLevel; @@ -27,7 +29,7 @@ public static int validatePageSize(Integer size) { public static Long getUserId() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !(auth.getPrincipal() instanceof JwtUserPrincipal jwt)) { - throw new IllegalStateException("인증되지 않은 사용자입니다."); + throw new UserException(ErrorStatus._UNAUTHORIZED); } return jwt.userId(); } From c963db1045c075ba219c79bba733bee199ada90b Mon Sep 17 00:00:00 2001 From: dong99u Date: Wed, 30 Jul 2025 22:37:58 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[REFACTOR]=20HabitLog=20=EC=A0=95=EC=A0=81?= =?UTF-8?q?=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?-=20return=20=EA=B0=92=EC=9D=80=20=EC=A7=81=EC=A0=91=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9C=BC=EB=AF=80=EB=A1=9C=20=EB=A6=AC=ED=84=B4?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../indayvidual/server/domain/habitlog/entity/HabitLog.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/indayvidual/server/domain/habitlog/entity/HabitLog.java b/src/main/java/com/indayvidual/server/domain/habitlog/entity/HabitLog.java index 7d49715..9078797 100644 --- a/src/main/java/com/indayvidual/server/domain/habitlog/entity/HabitLog.java +++ b/src/main/java/com/indayvidual/server/domain/habitlog/entity/HabitLog.java @@ -49,14 +49,13 @@ public class HabitLog extends BaseEntity { private LocalDate checkedAt = LocalDate.now(); //== 정적 팩토리 생성 메서드 ==// - public static HabitLog createHabitLog(Habit habit) { + public static void createHabitLog(Habit habit) { HabitLog habitLog = HabitLog.builder() .habit(habit) .build(); habit.getHabitLogs().add(habitLog); - return habitLog; } //== 더티체킹 메서드 ==//