From 110c621b3a67637d30d22980e9b605781623e9cf Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 22:09:59 +0900 Subject: [PATCH 1/9] =?UTF-8?q?chore:=20fcm=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 8b03ef5..98243d9 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,8 @@ dependencies { implementation 'com.amazonaws:aws-java-sdk-s3:1.12.657' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + implementation 'com.google.firebase:firebase-admin:9.2.0' + implementation 'io.jsonwebtoken:jjwt-api:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' From d1b2fd47475be70935a87f69859ddedc768c277a Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 23:35:12 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20FCM=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../everymoment/config/FcmConfig.java | 29 +++++ .../everymoment/controller/FcmController.java | 60 +++++++++ .../dto/request/FcmNotificationRequest.java | 15 +++ .../dto/request/FcmTokenRequest.java | 15 +++ .../everymoment/entity/DeviceToken.java | 48 +++++++ .../everymoment/exception/ErrorCode.java | 6 +- .../repository/DeviceTokenRepository.java | 16 +++ .../everymoment/service/CommentService.java | 20 ++- .../everymoment/service/FcmService.java | 122 ++++++++++++++++++ .../service/FriendRequestService.java | 21 ++- .../everymoment/service/LikeService.java | 14 ++ 12 files changed, 360 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/potatocake/everymoment/config/FcmConfig.java create mode 100644 src/main/java/com/potatocake/everymoment/controller/FcmController.java create mode 100644 src/main/java/com/potatocake/everymoment/dto/request/FcmNotificationRequest.java create mode 100644 src/main/java/com/potatocake/everymoment/dto/request/FcmTokenRequest.java create mode 100644 src/main/java/com/potatocake/everymoment/entity/DeviceToken.java create mode 100644 src/main/java/com/potatocake/everymoment/repository/DeviceTokenRepository.java create mode 100644 src/main/java/com/potatocake/everymoment/service/FcmService.java diff --git a/.gitignore b/.gitignore index 8eb016e..145cd25 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ !**/src/test/**/build/ application-dev.yml +everymoment.json ### STS ### .apt_generated diff --git a/src/main/java/com/potatocake/everymoment/config/FcmConfig.java b/src/main/java/com/potatocake/everymoment/config/FcmConfig.java new file mode 100644 index 0000000..d3a0dca --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/config/FcmConfig.java @@ -0,0 +1,29 @@ +package com.potatocake.everymoment.config; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import java.io.IOException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +@Configuration +public class FcmConfig { + + @Bean + public FirebaseMessaging firebaseMessaging() throws IOException { + GoogleCredentials googleCredentials = GoogleCredentials + .fromStream(new ClassPathResource("everymoment.json").getInputStream()); + + FirebaseOptions firebaseOptions = FirebaseOptions.builder() + .setCredentials(googleCredentials) + .build(); + + FirebaseApp app = FirebaseApp.initializeApp(firebaseOptions); + + return FirebaseMessaging.getInstance(app); + } + +} diff --git a/src/main/java/com/potatocake/everymoment/controller/FcmController.java b/src/main/java/com/potatocake/everymoment/controller/FcmController.java new file mode 100644 index 0000000..b6ccd7f --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/controller/FcmController.java @@ -0,0 +1,60 @@ +package com.potatocake.everymoment.controller; + +import com.potatocake.everymoment.dto.SuccessResponse; +import com.potatocake.everymoment.dto.request.FcmTokenRequest; +import com.potatocake.everymoment.security.MemberDetails; +import com.potatocake.everymoment.service.FcmService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "FCM", description = "FCM 토큰 관리 API") +@RestController +@RequestMapping("/api/fcm") +@RequiredArgsConstructor +public class FcmController { + + private final FcmService fcmService; + + @Operation(summary = "FCM 토큰 등록/갱신", description = "디바이스의 FCM 토큰을 등록하거나 갱신합니다.") + @ApiResponse(responseCode = "200", description = "토큰 등록/갱신 성공") + @PostMapping("/token") + public ResponseEntity registerToken( + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "FCM 토큰 정보", required = true) + @RequestBody @Valid FcmTokenRequest request) { + + fcmService.registerToken(memberDetails.getId(), request.getDeviceId(), request.getFcmToken()); + + return ResponseEntity.ok() + .body(SuccessResponse.ok()); + } + + @Operation(summary = "FCM 토큰 삭제", description = "디바이스의 FCM 토큰을 삭제합니다.") + @ApiResponse(responseCode = "200", description = "토큰 삭제 성공") + @DeleteMapping("/token") + public ResponseEntity removeToken( + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "디바이스 ID", required = true) + @RequestParam String deviceId) { + + fcmService.removeToken(memberDetails.getId(), deviceId); + + return ResponseEntity.ok() + .body(SuccessResponse.ok()); + } + +} diff --git a/src/main/java/com/potatocake/everymoment/dto/request/FcmNotificationRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/FcmNotificationRequest.java new file mode 100644 index 0000000..1726828 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/dto/request/FcmNotificationRequest.java @@ -0,0 +1,15 @@ +package com.potatocake.everymoment.dto.request; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FcmNotificationRequest { + + private String title; + private String body; + private String type; + private Long targetId; + +} diff --git a/src/main/java/com/potatocake/everymoment/dto/request/FcmTokenRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/FcmTokenRequest.java new file mode 100644 index 0000000..f5ff007 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/dto/request/FcmTokenRequest.java @@ -0,0 +1,15 @@ +package com.potatocake.everymoment.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; + +@Getter +public class FcmTokenRequest { + + @NotBlank(message = "FCM 토큰은 필수입니다.") + private String fcmToken; + + @NotBlank(message = "디바이스 ID는 필수입니다.") + private String deviceId; + +} diff --git a/src/main/java/com/potatocake/everymoment/entity/DeviceToken.java b/src/main/java/com/potatocake/everymoment/entity/DeviceToken.java new file mode 100644 index 0000000..ab1ddc6 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/entity/DeviceToken.java @@ -0,0 +1,48 @@ +package com.potatocake.everymoment.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class DeviceToken extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(nullable = false) + private Member member; + + @Column(nullable = false) + private String fcmToken; + + @Column(nullable = false) + @Lob + private String deviceId; + + @Builder + public DeviceToken(Member member, String fcmToken, String deviceId) { + this.member = member; + this.fcmToken = fcmToken; + this.deviceId = deviceId; + } + + public void updateToken(String fcmToken) { + this.fcmToken = fcmToken; + } + +} diff --git a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java index 19b0d7c..4e4c945 100644 --- a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java +++ b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java @@ -60,7 +60,11 @@ public enum ErrorCode { /* FriendRequestService */ FRIEND_REQUEST_ALREADY_EXISTS("이미 친구 요청을 보냈습니다.", CONFLICT), - FRIEND_REQUEST_NOT_FOUND("존재하지 않는 친구 요청입니다.", NOT_FOUND); + FRIEND_REQUEST_NOT_FOUND("존재하지 않는 친구 요청입니다.", NOT_FOUND), + + /* FCM */ + FCM_TOKEN_NOT_FOUND("FCM 토큰이 존재하지 않습니다.", HttpStatus.NOT_FOUND), + FCM_MESSAGE_SEND_FAILED("FCM 메시지 전송에 실패했습니다.", HttpStatus.INTERNAL_SERVER_ERROR); private final String message; private final HttpStatus status; diff --git a/src/main/java/com/potatocake/everymoment/repository/DeviceTokenRepository.java b/src/main/java/com/potatocake/everymoment/repository/DeviceTokenRepository.java new file mode 100644 index 0000000..0668494 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/repository/DeviceTokenRepository.java @@ -0,0 +1,16 @@ +package com.potatocake.everymoment.repository; + +import com.potatocake.everymoment.entity.DeviceToken; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DeviceTokenRepository extends JpaRepository { + + List findAllByMemberId(Long memberId); + + Optional findByMemberIdAndDeviceId(Long memberId, String deviceId); + + void deleteByMemberIdAndDeviceId(Long memberId, String deviceId); + +} diff --git a/src/main/java/com/potatocake/everymoment/service/CommentService.java b/src/main/java/com/potatocake/everymoment/service/CommentService.java index 085ed0c..0ee18e8 100644 --- a/src/main/java/com/potatocake/everymoment/service/CommentService.java +++ b/src/main/java/com/potatocake/everymoment/service/CommentService.java @@ -1,6 +1,7 @@ package com.potatocake.everymoment.service; import com.potatocake.everymoment.dto.request.CommentRequest; +import com.potatocake.everymoment.dto.request.FcmNotificationRequest; import com.potatocake.everymoment.dto.response.CommentFriendResponse; import com.potatocake.everymoment.dto.response.CommentResponse; import com.potatocake.everymoment.dto.response.CommentsResponse; @@ -30,6 +31,7 @@ public class CommentService { private final CommentRepository commentRepository; private final DiaryRepository diaryRepository; private final MemberRepository memberRepository; + private final FcmService fcmService; // 댓글 목록 조회 public CommentsResponse getComments(Long diaryId, int key, int size) { @@ -63,6 +65,16 @@ public void createComment(Long memberId, Long diaryId, CommentRequest commentReq .build(); commentRepository.save(comment); + + // 다이어리 작성자에게 FCM 알림 전송 (자신의 댓글에는 알림 안보냄) + if (!diary.getMember().getId().equals(memberId)) { + fcmService.sendNotification(diary.getMember().getId(), FcmNotificationRequest.builder() + .title("새로운 댓글") + .body(currentMember.getNickname() + "님이 회원님의 일기에 댓글을 남겼습니다.") + .type("COMMENT") + .targetId(diary.getId()) + .build()); + } } // 댓글 수정 @@ -85,7 +97,7 @@ private Comment getExistComment(Long memberId, Long commentId) { Comment comment = commentRepository.findById(commentId) .orElseThrow(() -> new GlobalException(ErrorCode.COMMENT_NOT_FOUND)); - if(!Objects.equals(currentMember.getId(), comment.getMember().getId())){ + if (!Objects.equals(currentMember.getId(), comment.getMember().getId())) { throw new GlobalException(ErrorCode.COMMENT_NOT_FOUND); } @@ -93,7 +105,7 @@ private Comment getExistComment(Long memberId, Long commentId) { } // 친구 프로필 DTO 변환 - private CommentFriendResponse convertToCommentFriendResponseDTO(Member member){ + private CommentFriendResponse convertToCommentFriendResponseDTO(Member member) { return CommentFriendResponse.builder() .id(member.getId()) .nickname(member.getNickname()) @@ -102,7 +114,7 @@ private CommentFriendResponse convertToCommentFriendResponseDTO(Member member){ } // 댓글 DTO 변환 - private CommentResponse convertToCommentResponseDTO(Comment comment){ + private CommentResponse convertToCommentResponseDTO(Comment comment) { return CommentResponse.builder() .id(comment.getId()) .commentFriendResponse(convertToCommentFriendResponseDTO(comment.getMember())) @@ -110,5 +122,5 @@ private CommentResponse convertToCommentResponseDTO(Comment comment){ .createdAt(comment.getCreateAt()) .build(); } -} +} diff --git a/src/main/java/com/potatocake/everymoment/service/FcmService.java b/src/main/java/com/potatocake/everymoment/service/FcmService.java new file mode 100644 index 0000000..328fce0 --- /dev/null +++ b/src/main/java/com/potatocake/everymoment/service/FcmService.java @@ -0,0 +1,122 @@ +package com.potatocake.everymoment.service; + +import com.google.firebase.messaging.BatchResponse; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.MessagingErrorCode; +import com.google.firebase.messaging.Notification; +import com.google.firebase.messaging.SendResponse; +import com.potatocake.everymoment.dto.request.FcmNotificationRequest; +import com.potatocake.everymoment.entity.DeviceToken; +import com.potatocake.everymoment.entity.Member; +import com.potatocake.everymoment.exception.ErrorCode; +import com.potatocake.everymoment.exception.GlobalException; +import com.potatocake.everymoment.repository.DeviceTokenRepository; +import com.potatocake.everymoment.repository.MemberRepository; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class FcmService { + + private final FirebaseMessaging firebaseMessaging; + private final DeviceTokenRepository deviceTokenRepository; + private final MemberRepository memberRepository; + + public void sendNotification(Long targetMemberId, FcmNotificationRequest request) { + List deviceTokens = deviceTokenRepository.findAllByMemberId(targetMemberId); + + if (deviceTokens.isEmpty()) { + throw new GlobalException(ErrorCode.FCM_TOKEN_NOT_FOUND); + } + + List messages = deviceTokens.stream() + .map(token -> Message.builder() + .setToken(token.getFcmToken()) + .setNotification(Notification.builder() + .setTitle(request.getTitle()) + .setBody(request.getBody()) + .build()) + .putData("type", request.getType()) + .putData("targetId", request.getTargetId().toString()) + .build()) + .collect(Collectors.toList()); + + try { + BatchResponse response = firebaseMessaging.sendEach(messages); + handleBatchResponse(response, deviceTokens); + } catch (FirebaseMessagingException e) { + log.error("FCM 메시지 전송 실패 : {}", e.getMessage(), e); + throw new GlobalException(ErrorCode.FCM_MESSAGE_SEND_FAILED); + } + } + + private void handleBatchResponse(BatchResponse response, List deviceTokens) { + List tokensToDelete = new ArrayList<>(); + + for (int i = 0; i < response.getResponses().size(); i++) { + SendResponse sendResponse = response.getResponses().get(i); + DeviceToken deviceToken = deviceTokens.get(i); + + if (!sendResponse.isSuccessful()) { + FirebaseMessagingException exception = sendResponse.getException(); + MessagingErrorCode errorCode = exception.getMessagingErrorCode(); + + log.warn("FCM 토큰 전송 실패: {}. 에러 코드: {}, 메시지: {}", + deviceToken.getFcmToken(), + errorCode, + exception.getMessage()); + + if (shouldDeleteToken(errorCode)) { + tokensToDelete.add(deviceToken); + } + } + } + + if (!tokensToDelete.isEmpty()) { + log.info("유효하지 않은 FCM 토큰 {} 개를 삭제합니다", tokensToDelete.size()); + deviceTokenRepository.deleteAll(tokensToDelete); + } + + log.info("FCM 일괄 전송 결과 - 성공: {}, 실패: {}", + response.getSuccessCount(), + response.getFailureCount()); + } + + private boolean shouldDeleteToken(MessagingErrorCode errorCode) { + return errorCode == MessagingErrorCode.UNREGISTERED || + errorCode == MessagingErrorCode.INVALID_ARGUMENT || + errorCode == MessagingErrorCode.SENDER_ID_MISMATCH || + errorCode == MessagingErrorCode.THIRD_PARTY_AUTH_ERROR; + } + + public void registerToken(Long memberId, String deviceId, String fcmToken) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); + + deviceTokenRepository.findByMemberIdAndDeviceId(memberId, deviceId) + .ifPresentOrElse( + deviceToken -> deviceToken.updateToken(fcmToken), + () -> deviceTokenRepository.save(DeviceToken.builder() + .member(member) + .deviceId(deviceId) + .fcmToken(fcmToken) + .build()) + + ); + } + + public void removeToken(Long memberId, String deviceId) { + deviceTokenRepository.deleteByMemberIdAndDeviceId(memberId, deviceId); + } + +} diff --git a/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java b/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java index 89f99a9..cecdfc5 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java @@ -3,6 +3,7 @@ import static java.util.function.Function.identity; import static org.springframework.data.domain.Sort.Direction.DESC; +import com.potatocake.everymoment.dto.request.FcmNotificationRequest; import com.potatocake.everymoment.dto.response.FriendRequestPageRequest; import com.potatocake.everymoment.dto.response.FriendRequestResponse; import com.potatocake.everymoment.entity.Friend; @@ -34,6 +35,7 @@ public class FriendRequestService { private final MemberRepository memberRepository; private final FriendRepository friendRepository; private final PagingUtil pagingUtil; + private final FcmService fcmService; @Transactional(readOnly = true) public FriendRequestPageRequest getFriendRequests(Long key, int size, Long memberId) { @@ -56,14 +58,21 @@ public void sendFriendRequest(Long senderId, Long receiverId) { Member sender = memberRepository.findById(senderId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); - Member receiver = memberRepository.findById(receiverId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); - friendRequestRepository.save(FriendRequest.builder() + FriendRequest friendRequest = friendRequestRepository.save(FriendRequest.builder() .sender(sender) .receiver(receiver) .build()); + + // 알림 발송 + fcmService.sendNotification(receiverId, FcmNotificationRequest.builder() + .title("새로운 친구 요청") + .body(sender.getNickname() + "님이 친구 요청을 보냈습니다.") + .type("FRIEND_REQUEST") + .targetId(friendRequest.getId()) + .build()); } public void acceptFriendRequest(Long requestId, Long memberId) { @@ -76,6 +85,14 @@ public void acceptFriendRequest(Long requestId, Long memberId) { friendRepository.save(friend2); friendRequestRepository.delete(friendRequest); + + // 알림 발송 + fcmService.sendNotification(friendRequest.getSender().getId(), FcmNotificationRequest.builder() + .title("친구 요청 수락") + .body(friendRequest.getReceiver().getNickname() + "님이 친구 요청을 수락했습니다.") + .type("FRIEND_ACCEPT") + .targetId(friendRequest.getReceiver().getId()) + .build()); } public void rejectFriendRequest(Long requestId, Long memberId) { diff --git a/src/main/java/com/potatocake/everymoment/service/LikeService.java b/src/main/java/com/potatocake/everymoment/service/LikeService.java index 5e9bc3d..89a0380 100644 --- a/src/main/java/com/potatocake/everymoment/service/LikeService.java +++ b/src/main/java/com/potatocake/everymoment/service/LikeService.java @@ -1,5 +1,6 @@ package com.potatocake.everymoment.service; +import com.potatocake.everymoment.dto.request.FcmNotificationRequest; import com.potatocake.everymoment.dto.response.LikeCountResponse; import com.potatocake.everymoment.entity.Diary; import com.potatocake.everymoment.entity.Like; @@ -22,6 +23,7 @@ public class LikeService { private final LikeRepository likeRepository; private final DiaryRepository diaryRepository; private final MemberRepository memberRepository; + private final FcmService fcmService; @Transactional(readOnly = true) public LikeCountResponse getLikeCount(Long diaryId) { @@ -54,6 +56,18 @@ public void toggleLike(Long memberId, Long diaryId) { .build(); likeRepository.save(likeEntity); + + if (!diary.getMember().getId().equals(memberId)) { + // 좋아요를 눌렀을 때만 (취소 제외), 그리고 자신의 게시물이 아닐 때만 알림 발송 + fcmService.sendNotification(diary.getMember().getId(), + FcmNotificationRequest.builder() + .title("새로운 좋아요") + .body(member.getNickname() + "님이 회원님의 일기를 좋아합니다.") + .type("LIKE") + .targetId(diary.getId()) + .build() + ); + } } } From c49dcabc9e0866e9ec4b6155bc92d8871f72d83f Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 23:44:04 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=20=EB=B0=9B=EC=9D=80?= =?UTF-8?q?=20=EC=9D=B4=EB=A0=A5=EC=9D=B4=20=EC=9E=88=EB=8B=A4=EB=A9=B4=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=B9=9C=EA=B5=AC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=95=8C=EB=A6=BC=20=EB=B0=9C=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../everymoment/exception/ErrorCode.java | 5 +- .../service/FriendRequestService.java | 67 +++++++++++++++---- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java index 4e4c945..8904a7d 100644 --- a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java +++ b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java @@ -64,7 +64,10 @@ public enum ErrorCode { /* FCM */ FCM_TOKEN_NOT_FOUND("FCM 토큰이 존재하지 않습니다.", HttpStatus.NOT_FOUND), - FCM_MESSAGE_SEND_FAILED("FCM 메시지 전송에 실패했습니다.", HttpStatus.INTERNAL_SERVER_ERROR); + FCM_MESSAGE_SEND_FAILED("FCM 메시지 전송에 실패했습니다.", HttpStatus.INTERNAL_SERVER_ERROR), + + /* Friend */ + ALREADY_FRIEND("이미 친구 관계입니다.", HttpStatus.CONFLICT); private final String message; private final HttpStatus status; diff --git a/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java b/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java index cecdfc5..4354f69 100644 --- a/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java +++ b/src/main/java/com/potatocake/everymoment/service/FriendRequestService.java @@ -17,6 +17,7 @@ import com.potatocake.everymoment.util.PagingUtil; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -50,9 +51,13 @@ public FriendRequestPageRequest getFriendRequests(Long key, int size, Long membe } public void sendFriendRequest(Long senderId, Long receiverId) { - boolean isAlreadySend = friendRequestRepository.existsBySenderIdAndReceiverId(senderId, receiverId); + // 이미 친구인 경우 체크 + if (friendRepository.existsByMemberIdAndFriendId(senderId, receiverId)) { + throw new GlobalException(ErrorCode.ALREADY_FRIEND); + } - if (isAlreadySend) { + // 이미 친구 요청을 보낸 경우 체크 + if (friendRequestRepository.existsBySenderIdAndReceiverId(senderId, receiverId)) { throw new GlobalException(ErrorCode.FRIEND_REQUEST_ALREADY_EXISTS); } @@ -61,18 +66,37 @@ public void sendFriendRequest(Long senderId, Long receiverId) { Member receiver = memberRepository.findById(receiverId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); - FriendRequest friendRequest = friendRequestRepository.save(FriendRequest.builder() - .sender(sender) - .receiver(receiver) - .build()); - - // 알림 발송 - fcmService.sendNotification(receiverId, FcmNotificationRequest.builder() - .title("새로운 친구 요청") - .body(sender.getNickname() + "님이 친구 요청을 보냈습니다.") - .type("FRIEND_REQUEST") - .targetId(friendRequest.getId()) - .build()); + // 상대방이 나에게 보낸 친구 요청이 있는지 확인 + Optional oppositeRequest = friendRequestRepository + .findBySenderIdAndReceiverId(receiverId, senderId); + + if (oppositeRequest.isPresent()) { + // 상대방이 이미 나에게 친구 요청을 보낸 상태라면 자동으로 수락 처리 + createFriendRelationship(receiver, sender); + friendRequestRepository.delete(oppositeRequest.get()); + + // 상대방에게 친구 수락 알림 발송 + fcmService.sendNotification(receiverId, FcmNotificationRequest.builder() + .title("친구 요청 수락") + .body(sender.getNickname() + "님이 친구 요청을 수락했습니다.") + .type("FRIEND_ACCEPT") + .targetId(sender.getId()) + .build()); + } else { + // 상대방이 보낸 요청이 없다면 새로운 친구 요청 생성 + FriendRequest friendRequest = friendRequestRepository.save(FriendRequest.builder() + .sender(sender) + .receiver(receiver) + .build()); + + // 상대방에게 친구 요청 알림 발송 + fcmService.sendNotification(receiverId, FcmNotificationRequest.builder() + .title("새로운 친구 요청") + .body(sender.getNickname() + "님이 친구 요청을 보냈습니다.") + .type("FRIEND_REQUEST") + .targetId(friendRequest.getId()) + .build()); + } } public void acceptFriendRequest(Long requestId, Long memberId) { @@ -101,6 +125,21 @@ public void rejectFriendRequest(Long requestId, Long memberId) { friendRequestRepository.delete(friendRequest); } + private void createFriendRelationship(Member member1, Member member2) { + Friend friend1 = Friend.builder() + .member(member1) + .friend(member2) + .build(); + + Friend friend2 = Friend.builder() + .member(member2) + .friend(member1) + .build(); + + friendRepository.save(friend1); + friendRepository.save(friend2); + } + private FriendRequest findAndValidateFriendRequest(Long requestId, Long memberId) { FriendRequest friendRequest = friendRequestRepository.findById(requestId) .orElseThrow(() -> new GlobalException(ErrorCode.FRIEND_REQUEST_NOT_FOUND)); From d1e836f62d4177abf626b0949ca9c92206e6c771 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 23:48:21 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20=EC=9D=BC=EA=B8=B0?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/potatocake/everymoment/exception/ErrorCode.java | 1 + .../java/com/potatocake/everymoment/service/LikeService.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java index 8904a7d..043f2ae 100644 --- a/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java +++ b/src/main/java/com/potatocake/everymoment/exception/ErrorCode.java @@ -15,6 +15,7 @@ public enum ErrorCode { /* Diary */ + DIARY_NOT_PUBLIC("비공개 일기입니다.", HttpStatus.FORBIDDEN), DIARY_NOT_FOUND("존재하지 않는 일기입니다.", NOT_FOUND), /* Member */ diff --git a/src/main/java/com/potatocake/everymoment/service/LikeService.java b/src/main/java/com/potatocake/everymoment/service/LikeService.java index 89a0380..8d8a45f 100644 --- a/src/main/java/com/potatocake/everymoment/service/LikeService.java +++ b/src/main/java/com/potatocake/everymoment/service/LikeService.java @@ -40,6 +40,11 @@ public LikeCountResponse getLikeCount(Long diaryId) { public void toggleLike(Long memberId, Long diaryId) { Diary diary = diaryRepository.findById(diaryId) .orElseThrow(() -> new GlobalException(ErrorCode.DIARY_NOT_FOUND)); + + if (!diary.isPublic()) { + throw new GlobalException(ErrorCode.DIARY_NOT_PUBLIC); + } + Member member = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); From 14e8eca1796fc10d99bbba5de6366a94a76f9755 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 23:55:11 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80/=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=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 --- .../everymoment/dto/request/CommentRequest.java | 4 ++++ .../dto/request/DiaryManualCreateRequest.java | 13 +++++++++++++ .../everymoment/service/CommentService.java | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/src/main/java/com/potatocake/everymoment/dto/request/CommentRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/CommentRequest.java index db0252a..5fe38f7 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/CommentRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/CommentRequest.java @@ -1,8 +1,12 @@ package com.potatocake.everymoment.dto.request; +import jakarta.validation.constraints.NotEmpty; import lombok.Getter; @Getter public class CommentRequest { + + @NotEmpty(message = "댓글 내용을 입력해 주세요.") private String content; + } diff --git a/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java index 9df2ed2..2a544e0 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java @@ -1,17 +1,30 @@ package com.potatocake.everymoment.dto.request; import com.potatocake.everymoment.dto.LocationPoint; +import jakarta.validation.constraints.Size; import java.util.List; import lombok.Getter; @Getter public class DiaryManualCreateRequest { + private List categories; + private LocationPoint locationPoint; + + @Size(max = 50, message = "장소명은 50자를 초과할 수 없습니다") private String locationName; + + @Size(max = 200, message = "주소는 200자를 초과할 수 없습니다") private String address; + private boolean isBookmark; private boolean isPublic; + + @Size(max = 10, message = "이모지는 10자를 초과할 수 없습니다") private String emoji; + + @Size(max = 5000, message = "일기 내용은 5000자를 초과할 수 없습니다") private String content; + } diff --git a/src/main/java/com/potatocake/everymoment/service/CommentService.java b/src/main/java/com/potatocake/everymoment/service/CommentService.java index 0ee18e8..a5963ba 100644 --- a/src/main/java/com/potatocake/everymoment/service/CommentService.java +++ b/src/main/java/com/potatocake/everymoment/service/CommentService.java @@ -58,6 +58,10 @@ public void createComment(Long memberId, Long diaryId, CommentRequest commentReq Diary diary = diaryRepository.findById(diaryId) .orElseThrow(() -> new GlobalException(ErrorCode.DIARY_NOT_FOUND)); + if (!diary.isPublic()) { + throw new GlobalException(ErrorCode.DIARY_NOT_PUBLIC); + } + Comment comment = Comment.builder() .content(commentRequest.getContent()) .member(currentMember) From 6893f49d1bc9ba06f01696aeb081333a8016a3b7 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 22 Oct 2024 23:58:37 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=EB=B0=B0=ED=8F=AC=20=EC=8B=9C=20FC?= =?UTF-8?q?M=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B3=84=EC=A0=95=20?= =?UTF-8?q?=ED=82=A4=20=ED=8C=8C=EC=9D=BC=20=EC=83=9D=EC=84=B1=20STEP=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 --- .github/workflows/master_weekly_cicd.yml | 4 ++++ .github/workflows/pr_weekly_ci.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/master_weekly_cicd.yml b/.github/workflows/master_weekly_cicd.yml index 3a04a2c..8d38d13 100644 --- a/.github/workflows/master_weekly_cicd.yml +++ b/.github/workflows/master_weekly_cicd.yml @@ -20,6 +20,10 @@ jobs: distribution: 'corretto' java-version: '21' + - name: FCM 서비스 계정 키 파일 생성 + run: | + echo '${{ secrets.FCM_SERVICE_ACCOUNT_KEY }}' > ./src/main/resources/everymoment.json + - name: AWS S3 관련 정보를 설정 파일에 주입 uses: microsoft/variable-substitution@v1 with: diff --git a/.github/workflows/pr_weekly_ci.yml b/.github/workflows/pr_weekly_ci.yml index 0acf431..b0a7200 100644 --- a/.github/workflows/pr_weekly_ci.yml +++ b/.github/workflows/pr_weekly_ci.yml @@ -23,6 +23,10 @@ jobs: distribution: 'corretto' java-version: '21' + - name: FCM 서비스 계정 키 파일 생성 + run: | + echo '${{ secrets.FCM_SERVICE_ACCOUNT_KEY }}' > ./src/main/resources/everymoment.json + - name: AWS S3 관련 정보를 설정 파일에 주입 uses: microsoft/variable-substitution@v1 with: From a48ae7794ce2d2716a4cd68ebc87d7080af286ea Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 23 Oct 2024 00:49:24 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- .../EverymomentApplicationTests.java | 8 +++++--- .../controller/MemberControllerTest.java | 17 ++++++++++------- .../repository/MemberRepositoryTest.java | 2 ++ .../everymoment/service/MemberServiceTest.java | 2 ++ .../everymoment/util/PagingUtilTest.java | 2 ++ 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 3bf77ba..2523496 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -10,7 +10,7 @@ spring: jdbc: time_zone: Asia/Seoul hibernate: - ddl-auto: update + ddl-auto: none h2: console: diff --git a/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java b/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java index a277c76..e1c7736 100644 --- a/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java +++ b/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java @@ -2,12 +2,14 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +@ActiveProfiles("dev") @SpringBootTest class EverymomentApplicationTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } } diff --git a/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java b/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java index c5ac5df..d24b883 100644 --- a/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java +++ b/src/test/java/com/potatocake/everymoment/controller/MemberControllerTest.java @@ -7,9 +7,11 @@ import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -22,8 +24,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.mock.web.MockMultipartFile; import org.springframework.security.test.context.support.WithMockUser; @@ -32,8 +33,7 @@ import org.springframework.web.multipart.MultipartFile; @WithMockUser -@AutoConfigureMockMvc -@SpringBootTest +@WebMvcTest(MemberController.class) class MemberControllerTest { @Autowired @@ -146,13 +146,15 @@ void should_UpdateMemberInfo_When_ValidInput() throws Exception { @DisplayName("프로필 이미지와 닉네임이 모두 누락되면 예외가 발생한다.") void should_ThrowException_When_ProfileImageAndNicknameAreMissing() throws Exception { // when - ResultActions result = mockMvc.perform(multipart("/api/members")); + ResultActions result = mockMvc.perform(multipart("/api/members") + .with(csrf())); // then result .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(INFO_REQUIRED.getStatus().value())) - .andExpect(jsonPath("$.message").value(INFO_REQUIRED.getMessage())); + .andExpect(jsonPath("$.message").value(INFO_REQUIRED.getMessage())) + .andDo(print()); then(memberService).shouldHaveNoInteractions(); } @@ -180,7 +182,8 @@ private ResultActions performMultipart(String url, MockMultipartFile file, Strin return mockMvc.perform(multipart(url) .file(file) .param("nickname", nickname) - .with(user(memberDetails))); + .with(user(memberDetails)) + .with(csrf())); } } diff --git a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java index 2e72d9e..b20c713 100644 --- a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java @@ -11,7 +11,9 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; +import org.springframework.test.context.ActiveProfiles; +@ActiveProfiles("dev") @DataJpaTest class MemberRepositoryTest { diff --git a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java index df574a6..25da818 100644 --- a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java +++ b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java @@ -30,8 +30,10 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; +import org.springframework.test.context.ActiveProfiles; import org.springframework.web.multipart.MultipartFile; +@ActiveProfiles("dev") @ExtendWith(MockitoExtension.class) class MemberServiceTest { diff --git a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java index 4808d11..bd37717 100644 --- a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java +++ b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java @@ -12,7 +12,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; +import org.springframework.test.context.ActiveProfiles; +@ActiveProfiles("dev") class PagingUtilTest { private PagingUtil pagingUtil; From a21b0d9e5bc06f062a1ec804fc3d641ce3ef6494 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 23 Oct 2024 00:54:20 +0900 Subject: [PATCH 8/9] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/potatocake/everymoment/EverymomentApplicationTests.java | 2 -- .../potatocake/everymoment/repository/MemberRepositoryTest.java | 2 -- .../com/potatocake/everymoment/service/MemberServiceTest.java | 2 -- .../java/com/potatocake/everymoment/util/PagingUtilTest.java | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java b/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java index e1c7736..6705d38 100644 --- a/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java +++ b/src/test/java/com/potatocake/everymoment/EverymomentApplicationTests.java @@ -2,9 +2,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -@ActiveProfiles("dev") @SpringBootTest class EverymomentApplicationTests { diff --git a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java index b20c713..2e72d9e 100644 --- a/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/MemberRepositoryTest.java @@ -11,9 +11,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; -import org.springframework.test.context.ActiveProfiles; -@ActiveProfiles("dev") @DataJpaTest class MemberRepositoryTest { diff --git a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java index 25da818..df574a6 100644 --- a/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java +++ b/src/test/java/com/potatocake/everymoment/service/MemberServiceTest.java @@ -30,10 +30,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; -import org.springframework.test.context.ActiveProfiles; import org.springframework.web.multipart.MultipartFile; -@ActiveProfiles("dev") @ExtendWith(MockitoExtension.class) class MemberServiceTest { diff --git a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java index bd37717..4808d11 100644 --- a/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java +++ b/src/test/java/com/potatocake/everymoment/util/PagingUtilTest.java @@ -12,9 +12,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; -import org.springframework.test.context.ActiveProfiles; -@ActiveProfiles("dev") class PagingUtilTest { private PagingUtil pagingUtil; From 984239097d4e6e3e7461b93b94f72ce81b9a7860 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 23 Oct 2024 00:58:26 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 2523496..3bf77ba 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -10,7 +10,7 @@ spring: jdbc: time_zone: Asia/Seoul hibernate: - ddl-auto: none + ddl-auto: update h2: console: