From 6ee2b037d5385f6d778e5b9a43ea68d0291a6197 Mon Sep 17 00:00:00 2001 From: paeng <127924700+juuuuone@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:49:49 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat/#49]=20(=EC=96=B8)=ED=8C=94=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0/=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/follow/dao/FollowRepository.java | 4 ++ .../member/api/MemberRestController.java | 37 +++++++++-- .../application/FollowCommandService.java | 10 +++ .../application/FollowCommandServiceImpl.java | 65 +++++++++++++++++++ .../application/GetUserQueryService.java | 4 +- .../application/GetUserQueryServiceImpl.java | 4 +- .../application/ProfileCommandService.java | 4 +- .../ProfileCommandServiceImpl.java | 4 +- .../member/converter/GetUserConverter.java | 6 +- .../member/converter/ProfileConverter.java | 6 +- ...{MemberResponseDTO.java => MemberDTO.java} | 38 +++++++++-- .../exception/annotation/NotFollowMyself.java | 24 +++++++ .../validator/NotFollowMyselfValidator.java | 33 ++++++++++ .../global/error/code/status/ErrorStatus.java | 1 + 14 files changed, 215 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/clokey/server/domain/member/application/FollowCommandService.java create mode 100644 src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java rename src/main/java/com/clokey/server/domain/member/dto/{MemberResponseDTO.java => MemberDTO.java} (63%) create mode 100644 src/main/java/com/clokey/server/domain/member/exception/annotation/NotFollowMyself.java create mode 100644 src/main/java/com/clokey/server/domain/member/exception/validator/NotFollowMyselfValidator.java diff --git a/src/main/java/com/clokey/server/domain/follow/dao/FollowRepository.java b/src/main/java/com/clokey/server/domain/follow/dao/FollowRepository.java index c2225b78..45dd605d 100644 --- a/src/main/java/com/clokey/server/domain/follow/dao/FollowRepository.java +++ b/src/main/java/com/clokey/server/domain/follow/dao/FollowRepository.java @@ -3,8 +3,12 @@ import com.clokey.server.domain.model.mapping.Follow; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface FollowRepository extends JpaRepository { boolean existsByFollowing_IdAndFollowed_Id(Long followingId, Long followedId); + + Optional findByFollowing_IdAndFollowed_Id(Long followingId, Long followedId); } diff --git a/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java b/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java index 0912b9e1..777f740b 100644 --- a/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java +++ b/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java @@ -1,7 +1,8 @@ package com.clokey.server.domain.member.api; +import com.clokey.server.domain.member.application.FollowCommandService; import com.clokey.server.domain.member.application.GetUserQueryService; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; import com.clokey.server.domain.member.application.ProfileCommandService; import com.clokey.server.domain.member.exception.annotation.IdExist; import com.clokey.server.domain.member.exception.annotation.IdValid; @@ -9,7 +10,6 @@ import com.clokey.server.global.error.code.status.SuccessStatus; import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -21,14 +21,15 @@ public class MemberRestController { private final ProfileCommandService profileCommandService; private final GetUserQueryService getUserQueryService; + private final FollowCommandService followCommandService; @Operation(summary = "프로필 수정 API", description = "사용자의 프로필 정보를 수정하는 API입니다.") @PatchMapping("users/{user_id}/profile") - public BaseResponse updateProfile( + public BaseResponse updateProfile( @PathVariable("user_id") Long userId, - @RequestBody @Valid MemberResponseDTO.ProfileRQ request) { + @RequestBody @Valid MemberDTO.ProfileRQ request) { - MemberResponseDTO.ProfileRP response = profileCommandService.updateProfile(userId, request); + MemberDTO.ProfileRP response = profileCommandService.updateProfile(userId, request); return BaseResponse.onSuccess(SuccessStatus.MEMBER_ACTION_SUCCESS, response); } @@ -47,10 +48,34 @@ public BaseResponse checkID( public BaseResponse getUser( @IdValid @PathVariable("clokey_id") String clokeyId) { - MemberResponseDTO.GetUserRP response = getUserQueryService.getUser(clokeyId); + MemberDTO.GetUserRP response = getUserQueryService.getUser(clokeyId); return BaseResponse.onSuccess(SuccessStatus.MEMBER_SUCCESS, response); } + + @Operation(summary = "팔로우 조회 API", description = "내가 다른 사용자를 팔로우하고있는지 확인하는 API입니다.") + @PostMapping("users/follow/check") + public BaseResponse followCheck( + @RequestBody @Valid MemberDTO.FollowRQ request){ + + MemberDTO.FollowRP response= followCommandService.followCheck(request); + + return BaseResponse.onSuccess(SuccessStatus.MEMBER_SUCCESS, response); + } + + + + @Operation(summary = "팔로우 API", description = "다른 사용자를 팔로우/언팔로우하는 API입니다. 호출시마다 기존 상태와 반대로 변경됩니다.") + @PostMapping("users/follow") + public BaseResponse follow( + @RequestBody @Valid MemberDTO.FollowRQ request) { + + followCommandService.follow(request); + + return BaseResponse.onSuccess(SuccessStatus.MEMBER_ACTION_SUCCESS, null); + } + + } diff --git a/src/main/java/com/clokey/server/domain/member/application/FollowCommandService.java b/src/main/java/com/clokey/server/domain/member/application/FollowCommandService.java new file mode 100644 index 00000000..a7b574f1 --- /dev/null +++ b/src/main/java/com/clokey/server/domain/member/application/FollowCommandService.java @@ -0,0 +1,10 @@ +package com.clokey.server.domain.member.application; + +import com.clokey.server.domain.member.dto.MemberDTO; + +public interface FollowCommandService { + void follow(MemberDTO.FollowRQ request); + + MemberDTO.FollowRP followCheck(MemberDTO.FollowRQ request); +} + diff --git a/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java new file mode 100644 index 00000000..2661e7d4 --- /dev/null +++ b/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java @@ -0,0 +1,65 @@ +package com.clokey.server.domain.member.application; + +import com.clokey.server.domain.follow.dao.FollowRepository; +import com.clokey.server.domain.member.dto.MemberDTO; +import com.clokey.server.domain.model.Member; +import com.clokey.server.domain.model.mapping.Follow; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Service +@RequiredArgsConstructor +public class FollowCommandServiceImpl implements FollowCommandService { + + private final MemberRepositoryService memberRepositoryService; + private final FollowRepository followRepository; + + @Override + public MemberDTO.FollowRP followCheck(MemberDTO.FollowRQ request) { + Long myUserId = memberRepositoryService.findMemberByClokeyId(request.getMyClokeyId()).getId(); + Long yourUserId = memberRepositoryService.findMemberByClokeyId(request.getYourClokeyId()).getId(); + + boolean isFollow = followRepository.existsByFollowing_IdAndFollowed_Id(myUserId, yourUserId); + + return new MemberDTO.FollowRP(isFollow); + } + + @Override + @Transactional + public void follow(MemberDTO.FollowRQ request) { + // myClokeyId로 사용자 조회 + Long myUserId = memberRepositoryService.findMemberByClokeyId(request.getMyClokeyId()).getId(); + Long yourUserId = memberRepositoryService.findMemberByClokeyId(request.getYourClokeyId()).getId(); + + validateFollowRequest(request); + + // 팔로우 관계가 존재하는지 확인 + boolean isFollow = followRepository.existsByFollowing_IdAndFollowed_Id(myUserId, yourUserId); + + if (isFollow) { + // 팔로우가 이미 존재하면 언팔로우 처리 + Follow follow = followRepository.findByFollowing_IdAndFollowed_Id(myUserId, yourUserId) + .orElseThrow(() -> new IllegalStateException("팔로우 관계가 존재하지 않습니다.")); + + // 팔로우 삭제 (언팔로우) + followRepository.delete(follow); + } else { + // 팔로우가 존재하지 않으면 팔로우 처리 + Follow follow = Follow.builder() + .following(memberRepositoryService.findMemberById(myUserId)) + .followed(memberRepositoryService.findMemberById(yourUserId)) + .build(); + + // 팔로우 저장 + followRepository.save(follow); + } + } + + private void validateFollowRequest(MemberDTO.FollowRQ request) { + if (request.getMyClokeyId().equals(request.getYourClokeyId())) { + throw new IllegalArgumentException("myClokeyId와 yourClokeyId는 동일할 수 없습니다."); + } + } +} diff --git a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryService.java b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryService.java index 58051c2c..81addbc3 100644 --- a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryService.java +++ b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryService.java @@ -1,9 +1,9 @@ package com.clokey.server.domain.member.application; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; public interface GetUserQueryService { - MemberResponseDTO.GetUserRP getUser(String clokeyId); + MemberDTO.GetUserRP getUser(String clokeyId); } diff --git a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java index 6fbac83e..6e9fa371 100644 --- a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java +++ b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java @@ -1,7 +1,7 @@ package com.clokey.server.domain.member.application; import com.clokey.server.domain.member.converter.GetUserConverter; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; import com.clokey.server.domain.model.Member; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -22,7 +22,7 @@ public class GetUserQueryServiceImpl implements GetUserQueryService { @Override @Transactional - public MemberResponseDTO.GetUserRP getUser(String clokeyId) { + public MemberDTO.GetUserRP getUser(String clokeyId) { Member member = memberRepositoryService.findMemberByClokeyId(clokeyId); Long recordCount = countHistoryByMember(member); diff --git a/src/main/java/com/clokey/server/domain/member/application/ProfileCommandService.java b/src/main/java/com/clokey/server/domain/member/application/ProfileCommandService.java index 57761c95..68db403a 100644 --- a/src/main/java/com/clokey/server/domain/member/application/ProfileCommandService.java +++ b/src/main/java/com/clokey/server/domain/member/application/ProfileCommandService.java @@ -1,9 +1,9 @@ package com.clokey.server.domain.member.application; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; public interface ProfileCommandService { - MemberResponseDTO.ProfileRP updateProfile(Long userId, MemberResponseDTO.ProfileRQ request); + MemberDTO.ProfileRP updateProfile(Long userId, MemberDTO.ProfileRQ request); } diff --git a/src/main/java/com/clokey/server/domain/member/application/ProfileCommandServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/ProfileCommandServiceImpl.java index 980723c6..da0c1155 100644 --- a/src/main/java/com/clokey/server/domain/member/application/ProfileCommandServiceImpl.java +++ b/src/main/java/com/clokey/server/domain/member/application/ProfileCommandServiceImpl.java @@ -1,7 +1,7 @@ package com.clokey.server.domain.member.application; import com.clokey.server.domain.member.converter.ProfileConverter; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; import com.clokey.server.domain.model.Member; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -15,7 +15,7 @@ public class ProfileCommandServiceImpl implements ProfileCommandService { @Override @Transactional - public MemberResponseDTO.ProfileRP updateProfile(Long userId, MemberResponseDTO.ProfileRQ request) { + public MemberDTO.ProfileRP updateProfile(Long userId, MemberDTO.ProfileRQ request) { // 사용자 확인 Member member = memberRepositoryService.findMemberById(userId); diff --git a/src/main/java/com/clokey/server/domain/member/converter/GetUserConverter.java b/src/main/java/com/clokey/server/domain/member/converter/GetUserConverter.java index 76bfaaeb..25a67d42 100644 --- a/src/main/java/com/clokey/server/domain/member/converter/GetUserConverter.java +++ b/src/main/java/com/clokey/server/domain/member/converter/GetUserConverter.java @@ -1,12 +1,12 @@ package com.clokey.server.domain.member.converter; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; import com.clokey.server.domain.model.Member; public class GetUserConverter { - public static MemberResponseDTO.GetUserRP toGetUserResponseDTO(Member member, Long recordCount, Long followerCount, Long followingCount) { - return MemberResponseDTO.GetUserRP.builder() + public static MemberDTO.GetUserRP toGetUserResponseDTO(Member member, Long recordCount, Long followerCount, Long followingCount) { + return MemberDTO.GetUserRP.builder() .clokeyId(member.getClokeyId()) .profileImageUrl(member.getProfileImageUrl()) .recordCount(recordCount) diff --git a/src/main/java/com/clokey/server/domain/member/converter/ProfileConverter.java b/src/main/java/com/clokey/server/domain/member/converter/ProfileConverter.java index 70dfc92f..6f0298fc 100644 --- a/src/main/java/com/clokey/server/domain/member/converter/ProfileConverter.java +++ b/src/main/java/com/clokey/server/domain/member/converter/ProfileConverter.java @@ -1,6 +1,6 @@ package com.clokey.server.domain.member.converter; -import com.clokey.server.domain.member.dto.MemberResponseDTO; +import com.clokey.server.domain.member.dto.MemberDTO; import com.clokey.server.domain.model.Member; import java.time.LocalDateTime; @@ -8,8 +8,8 @@ public class ProfileConverter { - public static MemberResponseDTO.ProfileRP toProfileRPDTO(Member member) { - return MemberResponseDTO.ProfileRP.builder() + public static MemberDTO.ProfileRP toProfileRPDTO(Member member) { + return MemberDTO.ProfileRP.builder() .id(member.getId()) .bio(member.getBio()) .email(member.getEmail()) diff --git a/src/main/java/com/clokey/server/domain/member/dto/MemberResponseDTO.java b/src/main/java/com/clokey/server/domain/member/dto/MemberDTO.java similarity index 63% rename from src/main/java/com/clokey/server/domain/member/dto/MemberResponseDTO.java rename to src/main/java/com/clokey/server/domain/member/dto/MemberDTO.java index 8c38e374..d94d835c 100644 --- a/src/main/java/com/clokey/server/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/com/clokey/server/domain/member/dto/MemberDTO.java @@ -1,6 +1,8 @@ package com.clokey.server.domain.member.dto; import com.clokey.server.domain.member.exception.annotation.EssentialFieldNotNull; +import com.clokey.server.domain.member.exception.annotation.IdValid; +import com.clokey.server.domain.member.exception.annotation.NotFollowMyself; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -8,7 +10,7 @@ import java.time.LocalDateTime; -public class MemberResponseDTO { +public class MemberDTO { @Builder @Getter @@ -33,14 +35,14 @@ public static class GetUserRP { public static class ProfileRQ { @EssentialFieldNotNull - private String nickname; + String nickname; @EssentialFieldNotNull - private String clokeyId; + String clokeyId; - private String profileImageUrl; + String profileImageUrl; - private String bio; + String bio; } @@ -58,4 +60,30 @@ public static class ProfileRP { String profileImageUrl; LocalDateTime updatedAt; } + + + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class FollowRQ{ + + @IdValid + String myClokeyId; + @IdValid + String yourClokeyId; + + } + + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class FollowRP{ + + boolean isFollow; + + } } diff --git a/src/main/java/com/clokey/server/domain/member/exception/annotation/NotFollowMyself.java b/src/main/java/com/clokey/server/domain/member/exception/annotation/NotFollowMyself.java new file mode 100644 index 00000000..8973c554 --- /dev/null +++ b/src/main/java/com/clokey/server/domain/member/exception/annotation/NotFollowMyself.java @@ -0,0 +1,24 @@ +package com.clokey.server.domain.member.exception.annotation; + +import com.clokey.server.domain.member.exception.validator.NotFollowMyselfValidator; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Documented +@Constraint(validatedBy = NotFollowMyselfValidator.class) +@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER , ElementType.TYPE}) // 메소드, 필드, 파라미터에서 사용 가능 +@Retention(RetentionPolicy.RUNTIME) +public @interface NotFollowMyself { + String message() default "myClokeyId와 yourClokeyId는 동일할 수 없습니다."; // 기본 오류 메시지 + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/com/clokey/server/domain/member/exception/validator/NotFollowMyselfValidator.java b/src/main/java/com/clokey/server/domain/member/exception/validator/NotFollowMyselfValidator.java new file mode 100644 index 00000000..17f05f04 --- /dev/null +++ b/src/main/java/com/clokey/server/domain/member/exception/validator/NotFollowMyselfValidator.java @@ -0,0 +1,33 @@ +package com.clokey.server.domain.member.exception.validator; + +import com.clokey.server.domain.member.dto.MemberDTO; +import com.clokey.server.domain.member.exception.annotation.NotFollowMyself; +import com.clokey.server.global.error.code.status.ErrorStatus; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +public class NotFollowMyselfValidator implements ConstraintValidator { + @Override + public void initialize(NotFollowMyself constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(MemberDTO.FollowRQ followRQ, ConstraintValidatorContext constraintValidatorContext) { + if (followRQ == null ) { + constraintValidatorContext.disableDefaultConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.ESSENTIAL_INPUT_REQUIRED.toString()).addConstraintViolation(); + return false; + } + + String myClokeyId = followRQ.getMyClokeyId(); + String yourClokeyId = followRQ.getYourClokeyId(); + + if(myClokeyId.equals(yourClokeyId)){ + constraintValidatorContext.disableDefaultConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.CANNOT_FOLLOW_MYSELF.toString()).addConstraintViolation(); + return false; + } + return true; + } +} diff --git a/src/main/java/com/clokey/server/global/error/code/status/ErrorStatus.java b/src/main/java/com/clokey/server/global/error/code/status/ErrorStatus.java index 3981b78b..984e786e 100644 --- a/src/main/java/com/clokey/server/global/error/code/status/ErrorStatus.java +++ b/src/main/java/com/clokey/server/global/error/code/status/ErrorStatus.java @@ -25,6 +25,7 @@ public enum ErrorStatus implements BaseErrorCode { CLOKEY_ID_INVALID(HttpStatus.BAD_REQUEST,"MEMBER_4004","잘못된 클로키 아이디입니다."), DUPLICATE_CLOKEY_ID(HttpStatus.BAD_REQUEST,"MEMBER_4005","중복된 클로키 아이디입니다."), ESSENTIAL_INPUT_REQUIRED(HttpStatus.BAD_REQUEST,"MEMBER_4006","필수 입력 요소 값이 누락되었습니다."), + CANNOT_FOLLOW_MYSELF(HttpStatus.BAD_REQUEST,"MEMBER_4007", "팔로우 아이디와 팔로잉 아이디가 같을 수 없습니다."), //옷 에러 NO_SUCH_CLOTH(HttpStatus.NOT_FOUND,"CLOTH_4041","존재하지 않는 옷 ID입니다."), From be54661828244e4a7bec9392d531b3000c395ce2 Mon Sep 17 00:00:00 2001 From: paeng <127924700+juuuuone@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:01:47 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[refactor/#49]=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=20transactional=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/application/GetUserQueryServiceImpl.java | 12 ++++++++---- .../application/MemberRepositoryServiceImpl.java | 11 ++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java index 6e9fa371..d4834c28 100644 --- a/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java +++ b/src/main/java/com/clokey/server/domain/member/application/GetUserQueryServiceImpl.java @@ -21,7 +21,7 @@ public class GetUserQueryServiceImpl implements GetUserQueryService { private EntityManager entityManager; @Override - @Transactional + @Transactional(readOnly = true) // 트랜잭션 읽기 전용으로 설정 public MemberDTO.GetUserRP getUser(String clokeyId) { Member member = memberRepositoryService.findMemberByClokeyId(clokeyId); @@ -32,21 +32,24 @@ public MemberDTO.GetUserRP getUser(String clokeyId) { return GetUserConverter.toGetUserResponseDTO(member, recordCount, followerCount, followingCount); } - private Long countHistoryByMember(Member member) { + @Transactional(readOnly = true) // 트랜잭션 읽기 전용으로 설정 + public Long countHistoryByMember(Member member) { String jpql = "SELECT COUNT(h) FROM History h WHERE h.member = :member"; TypedQuery query = entityManager.createQuery(jpql, Long.class); query.setParameter("member", member); return query.getSingleResult(); } - private Long countFollowersByMember(Member member) { + @Transactional(readOnly = true) // 트랜잭션 읽기 전용으로 설정 + public Long countFollowersByMember(Member member) { String jpql = "SELECT COUNT(f) FROM Follow f WHERE f.followed = :member"; TypedQuery query = entityManager.createQuery(jpql, Long.class); query.setParameter("member", member); return query.getSingleResult(); } - private Long countFollowingByMember(Member member) { + @Transactional(readOnly = true) // 트랜잭션 읽기 전용으로 설정 + public Long countFollowingByMember(Member member) { String jpql = "SELECT COUNT(f) FROM Follow f WHERE f.following = :member"; TypedQuery query = entityManager.createQuery(jpql, Long.class); query.setParameter("member", member); @@ -56,3 +59,4 @@ private Long countFollowingByMember(Member member) { + diff --git a/src/main/java/com/clokey/server/domain/member/application/MemberRepositoryServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/MemberRepositoryServiceImpl.java index 0401a630..b61006cb 100644 --- a/src/main/java/com/clokey/server/domain/member/application/MemberRepositoryServiceImpl.java +++ b/src/main/java/com/clokey/server/domain/member/application/MemberRepositoryServiceImpl.java @@ -10,6 +10,7 @@ import jakarta.persistence.TypedQuery; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -23,28 +24,32 @@ public class MemberRepositoryServiceImpl implements MemberRepositoryService { private EntityManager entityManager; @Override + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public boolean memberExist(Long memberId) { return memberRepository.existsById(memberId); } @Override + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public Optional getMember(Long memberId) { return memberRepository.findById(memberId); } - @Override + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(ErrorStatus.NO_SUCH_MEMBER)); } @Override + @Transactional // 쓰기 트랜잭션 public Member saveMember(Member member) { return memberRepository.save(member); } @Override + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public boolean idExist(String clokeyId) { String jpql = "SELECT COUNT(m) > 0 FROM Member m WHERE m.clokeyId = :clokeyId"; TypedQuery query = entityManager.createQuery(jpql, Boolean.class); @@ -53,6 +58,7 @@ public boolean idExist(String clokeyId) { } @Override + @Transactional(readOnly = true) // 읽기 전용 트랜잭션 public Member findMemberByClokeyId(String clokeyId) { String jpql = "SELECT m FROM Member m WHERE m.clokeyId = :clokeyId"; TypedQuery query = entityManager.createQuery(jpql, Member.class); @@ -62,5 +68,4 @@ public Member findMemberByClokeyId(String clokeyId) { .findFirst() .orElseThrow(() -> new IllegalArgumentException("클로키 아이디에 해당하는 사용자가 없습니다.")); } - -} \ No newline at end of file +} From e38d50520618a5f210e563cd29db4185e8dbe4f2 Mon Sep 17 00:00:00 2001 From: paeng <127924700+juuuuone@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:09:58 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[refactor/#49]=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/member/api/MemberRestController.java | 3 ++- .../member/application/FollowCommandServiceImpl.java | 9 +-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java b/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java index 777f740b..70d5f7cd 100644 --- a/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java +++ b/src/main/java/com/clokey/server/domain/member/api/MemberRestController.java @@ -6,6 +6,7 @@ import com.clokey.server.domain.member.application.ProfileCommandService; import com.clokey.server.domain.member.exception.annotation.IdExist; import com.clokey.server.domain.member.exception.annotation.IdValid; +import com.clokey.server.domain.member.exception.annotation.NotFollowMyself; import com.clokey.server.global.common.response.BaseResponse; import com.clokey.server.global.error.code.status.SuccessStatus; import io.swagger.v3.oas.annotations.Operation; @@ -69,7 +70,7 @@ public BaseResponse followCheck( @Operation(summary = "팔로우 API", description = "다른 사용자를 팔로우/언팔로우하는 API입니다. 호출시마다 기존 상태와 반대로 변경됩니다.") @PostMapping("users/follow") public BaseResponse follow( - @RequestBody @Valid MemberDTO.FollowRQ request) { + @NotFollowMyself @RequestBody @Valid MemberDTO.FollowRQ request) { followCommandService.follow(request); diff --git a/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java b/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java index 2661e7d4..f11113ad 100644 --- a/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java +++ b/src/main/java/com/clokey/server/domain/member/application/FollowCommandServiceImpl.java @@ -33,8 +33,6 @@ public void follow(MemberDTO.FollowRQ request) { Long myUserId = memberRepositoryService.findMemberByClokeyId(request.getMyClokeyId()).getId(); Long yourUserId = memberRepositoryService.findMemberByClokeyId(request.getYourClokeyId()).getId(); - validateFollowRequest(request); - // 팔로우 관계가 존재하는지 확인 boolean isFollow = followRepository.existsByFollowing_IdAndFollowed_Id(myUserId, yourUserId); @@ -56,10 +54,5 @@ public void follow(MemberDTO.FollowRQ request) { followRepository.save(follow); } } - - private void validateFollowRequest(MemberDTO.FollowRQ request) { - if (request.getMyClokeyId().equals(request.getYourClokeyId())) { - throw new IllegalArgumentException("myClokeyId와 yourClokeyId는 동일할 수 없습니다."); - } - } + }