From bfd1d16843e29787eaed4f83ce14781892574fe0 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Tue, 12 Nov 2024 20:33:18 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=EB=B9=84=EA=B4=80=EC=A0=81=20?= =?UTF-8?q?=EB=9D=BD=20warning=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../potatocake/everymoment/repository/MemberRepository.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java index 7f318ed..6ddb6c5 100644 --- a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java @@ -1,13 +1,11 @@ package com.potatocake.everymoment.repository; import com.potatocake.everymoment.entity.Member; -import jakarta.persistence.LockModeType; import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; public interface MemberRepository extends JpaRepository { @@ -18,8 +16,7 @@ public interface MemberRepository extends JpaRepository { Window findByNicknameContaining(String nickname, ScrollPosition position, Pageable pageable); - @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("SELECT CASE WHEN MIN(m.number) > 0 OR MIN(m.number) IS NULL THEN -1 ELSE MIN(m.number) - 1 END FROM Member m") + @Query(value = "SELECT CASE WHEN MIN(m.number) > 0 OR MIN(m.number) IS NULL THEN -1 ELSE MIN(m.number) - 1 END FROM member m WHERE m.deleted = 0 FOR UPDATE", nativeQuery = true) Long findNextAnonymousNumber(); } From d1ea11dd8b8db426f72ad77e64a009ae253c6366 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 13 Nov 2024 00:30:41 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=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 --- src/main/resources/application-prod.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0c6cad9..6626737 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -9,6 +9,8 @@ spring: hibernate: jdbc: time_zone: Asia/Seoul + format_sql: true + show-sql: true hibernate: ddl-auto: none @@ -22,6 +24,14 @@ spring: max-request-size: 25MB resolve-lazily: true +logging: + level: + org: + hibernate: + orm: + jdbc: + bind: trace + aws: s3: client: AmazonS3 From 9fdbd1680206f0940b8fcd5267cd89bfd8499a1a Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 13 Nov 2024 00:36:44 +0900 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20=EC=BF=BC=EB=A6=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/potatocake/everymoment/repository/MemberRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java index 6ddb6c5..6e2e1e7 100644 --- a/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/MemberRepository.java @@ -16,7 +16,7 @@ public interface MemberRepository extends JpaRepository { Window findByNicknameContaining(String nickname, ScrollPosition position, Pageable pageable); - @Query(value = "SELECT CASE WHEN MIN(m.number) > 0 OR MIN(m.number) IS NULL THEN -1 ELSE MIN(m.number) - 1 END FROM member m WHERE m.deleted = 0 FOR UPDATE", nativeQuery = true) + @Query("SELECT CASE WHEN MIN(m.number) > 0 OR MIN(m.number) IS NULL THEN -1 ELSE MIN(m.number) - 1 END FROM Member m") Long findNextAnonymousNumber(); } From c3ad8922aa9275e25e2c78d4a96fed5628e3f6d7 Mon Sep 17 00:00:00 2001 From: peeerr Date: Wed, 13 Nov 2024 01:37:12 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=A1=9C=EA=B7=B8=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/controller/MemberController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/potatocake/everymoment/controller/MemberController.java b/src/main/java/com/potatocake/everymoment/controller/MemberController.java index 67cf2ad..fbce531 100644 --- a/src/main/java/com/potatocake/everymoment/controller/MemberController.java +++ b/src/main/java/com/potatocake/everymoment/controller/MemberController.java @@ -31,7 +31,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Tag(name = "Members", description = "회원 관리 API") @RequiredArgsConstructor @RequestMapping("/api/members") @@ -117,6 +119,11 @@ public ResponseEntity> updateMemberInfo( @Parameter(description = "새로운 닉네임") @RequestParam(required = false) String nickname ) { + log.info("프로필 업데이트 요청 들어옴"); + log.info("memberDetails: {}", memberDetails); + log.info("profileImage: {}", profileImage); + log.info("nickname: {}", nickname); + validateProfileUpdate(profileImage, nickname); memberService.updateMemberInfo(memberDetails.getId(), profileImage, nickname); From 1bed0457c4a4e1733a42304abb125c870c703fd2 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Wed, 13 Nov 2024 13:34:27 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../everymoment/controller/MemberController.java | 7 ------- src/main/resources/application-prod.yml | 10 ---------- 2 files changed, 17 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/controller/MemberController.java b/src/main/java/com/potatocake/everymoment/controller/MemberController.java index fbce531..67cf2ad 100644 --- a/src/main/java/com/potatocake/everymoment/controller/MemberController.java +++ b/src/main/java/com/potatocake/everymoment/controller/MemberController.java @@ -31,9 +31,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import lombok.extern.slf4j.Slf4j; -@Slf4j @Tag(name = "Members", description = "회원 관리 API") @RequiredArgsConstructor @RequestMapping("/api/members") @@ -119,11 +117,6 @@ public ResponseEntity> updateMemberInfo( @Parameter(description = "새로운 닉네임") @RequestParam(required = false) String nickname ) { - log.info("프로필 업데이트 요청 들어옴"); - log.info("memberDetails: {}", memberDetails); - log.info("profileImage: {}", profileImage); - log.info("nickname: {}", nickname); - validateProfileUpdate(profileImage, nickname); memberService.updateMemberInfo(memberDetails.getId(), profileImage, nickname); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 6626737..0c6cad9 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -9,8 +9,6 @@ spring: hibernate: jdbc: time_zone: Asia/Seoul - format_sql: true - show-sql: true hibernate: ddl-auto: none @@ -24,14 +22,6 @@ spring: max-request-size: 25MB resolve-lazily: true -logging: - level: - org: - hibernate: - orm: - jdbc: - bind: trace - aws: s3: client: AmazonS3 From 15fd18a17ba585db55f90242096a8d752b833286 Mon Sep 17 00:00:00 2001 From: HyeJiJUN Date: Wed, 13 Nov 2024 21:26:06 +0900 Subject: [PATCH 06/19] =?UTF-8?q?fix:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=97=86=EC=9D=B4=20=EC=A0=80=EC=9E=A5=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8A=94=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 --- .../everymoment/service/DiaryService.java | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/service/DiaryService.java b/src/main/java/com/potatocake/everymoment/service/DiaryService.java index 3378c30..00b1883 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiaryService.java +++ b/src/main/java/com/potatocake/everymoment/service/DiaryService.java @@ -107,22 +107,8 @@ public void createDiaryManual(Long memberId, DiaryManualCreateRequest diaryManua //카테고리 저장 List categoryRequestList = diaryManualCreateRequest.getCategories(); - for (CategoryRequest categoryRequest : categoryRequestList) { - Long categoryId = categoryRequest.getCategoryId(); - - DiaryCategory diaryCategory = DiaryCategory.builder() - .diary(savedDiary) - .category(categoryRepository.findById(categoryId) - .map(category -> { - // Category가 현재 사용자의 소유인지 확인 - category.checkOwner(currentMember.getId()); - return category; - }) - .orElseThrow(() -> new GlobalException(ErrorCode.CATEGORY_NOT_FOUND))) - .build(); - - diaryCategoryRepository.save(diaryCategory); - + if(categoryRequestList != null){ + addDiaryCategory(savedDiary, currentMember.getId(), categoryRequestList); } } @@ -140,7 +126,7 @@ public MyDiariesResponse getMyDiaries(Long memberId, DiaryFilterRequest diaryFil Specification spec; - if (!diaryFilterRequest.hasFilter()) { + if(!diaryFilterRequest.hasFilter()){ LocalDate today = LocalDate.now(); spec = DiarySpecification.filterDiaries( @@ -213,22 +199,7 @@ public void updateDiary(Long memberId, Long diaryId, DiaryPatchRequest diaryPatc if (categoryRequestList != null && !categoryRequestList.isEmpty()) { diaryCategoryRepository.deleteByDiary(existingDiary); - - for (CategoryRequest categoryRequest : categoryRequestList) { - Long categoryId = categoryRequest.getCategoryId(); - - DiaryCategory diaryCategory = DiaryCategory.builder() - .diary(existingDiary) - .category(categoryRepository.findById(categoryId) - .map(category -> { - category.checkOwner(memberId); - return category; - }) - .orElseThrow(() -> new GlobalException(ErrorCode.CATEGORY_NOT_FOUND))) - .build(); - - diaryCategoryRepository.save(diaryCategory); - } + addDiaryCategory(existingDiary, memberId, categoryRequestList); } } @@ -282,6 +253,25 @@ private Diary getExistDiary(Long memberId, Long diaryId) { return diary; } + //다이어리에 카테고리 추가 + private void addDiaryCategory(Diary savedDiary, Long memberId, List categoryRequestList) { + for (CategoryRequest categoryRequest : categoryRequestList) { + Long categoryId = categoryRequest.getCategoryId(); + + DiaryCategory diaryCategory = DiaryCategory.builder() + .diary(savedDiary) + .category(categoryRepository.findById(categoryId) + .map(category -> { + category.checkOwner(memberId); + return category; + }) + .orElseThrow(() -> new GlobalException(ErrorCode.CATEGORY_NOT_FOUND))) + .build(); + + diaryCategoryRepository.save(diaryCategory); + } + } + //상세 조회시 일기DTO 변환 private MyDiaryResponse convertToMyDiaryResponseDto(Diary savedDiary, Long memberId) { // 카테고리 찾음 From 5a9cac8172e6c78300924818b31503426b725dae Mon Sep 17 00:00:00 2001 From: HyeJiJUN Date: Wed, 13 Nov 2024 22:41:02 +0900 Subject: [PATCH 07/19] =?UTF-8?q?feat:=20diary=EC=97=90=20diaryDate=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 --- .../dto/request/DiaryManualCreateRequest.java | 3 +++ .../dto/response/MyDiaryResponse.java | 2 ++ .../dto/response/MyDiarySimpleResponse.java | 2 ++ .../potatocake/everymoment/entity/Diary.java | 4 ++++ .../everymoment/service/DiaryService.java | 3 +++ .../service/DiarySpecification.java | 23 +++++++++++++++++-- 6 files changed, 35 insertions(+), 2 deletions(-) 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 7659c2b..5ed8174 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/DiaryManualCreateRequest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.potatocake.everymoment.dto.LocationPoint; import jakarta.validation.constraints.Size; +import java.time.LocalDate; import java.util.List; import lombok.Builder; import lombok.Getter; @@ -11,6 +12,8 @@ @Getter public class DiaryManualCreateRequest { + private LocalDate diaryDate; + private List categories; private LocationPoint locationPoint; diff --git a/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java b/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java index c8c8110..346a1f1 100644 --- a/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java +++ b/src/main/java/com/potatocake/everymoment/dto/response/MyDiaryResponse.java @@ -1,5 +1,6 @@ package com.potatocake.everymoment.dto.response; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import lombok.Builder; @@ -17,4 +18,5 @@ public class MyDiaryResponse { private String content; private boolean isLiked; private LocalDateTime createAt; + private LocalDate diaryDate; } diff --git a/src/main/java/com/potatocake/everymoment/dto/response/MyDiarySimpleResponse.java b/src/main/java/com/potatocake/everymoment/dto/response/MyDiarySimpleResponse.java index 59e7c70..6e7fa5b 100644 --- a/src/main/java/com/potatocake/everymoment/dto/response/MyDiarySimpleResponse.java +++ b/src/main/java/com/potatocake/everymoment/dto/response/MyDiarySimpleResponse.java @@ -1,5 +1,6 @@ package com.potatocake.everymoment.dto.response; +import java.time.LocalDate; import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; @@ -16,4 +17,5 @@ public class MyDiarySimpleResponse { private ThumbnailResponse thumbnailResponse; private String content; private LocalDateTime createAt; + private LocalDate diaryDate; } diff --git a/src/main/java/com/potatocake/everymoment/entity/Diary.java b/src/main/java/com/potatocake/everymoment/entity/Diary.java index c139ff3..bbdeedd 100644 --- a/src/main/java/com/potatocake/everymoment/entity/Diary.java +++ b/src/main/java/com/potatocake/everymoment/entity/Diary.java @@ -11,6 +11,7 @@ import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; +import java.time.LocalDate; import java.util.HashSet; import java.util.Set; import lombok.AccessLevel; @@ -37,6 +38,9 @@ public class Diary extends BaseTimeEntity { @Column(columnDefinition = "TEXT") private String content; + @Column + private LocalDate diaryDate; + @Column(nullable = false) private Point locationPoint; diff --git a/src/main/java/com/potatocake/everymoment/service/DiaryService.java b/src/main/java/com/potatocake/everymoment/service/DiaryService.java index 00b1883..1bdc4e9 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiaryService.java +++ b/src/main/java/com/potatocake/everymoment/service/DiaryService.java @@ -95,6 +95,7 @@ public void createDiaryManual(Long memberId, DiaryManualCreateRequest diaryManua Diary diary = Diary.builder() .member(currentMember) .content(diaryManualCreateRequest.getContent()) + .diaryDate(diaryManualCreateRequest.getDiaryDate()) .locationPoint(point) .locationName(diaryManualCreateRequest.getLocationName()) .address(diaryManualCreateRequest.getAddress()) @@ -295,6 +296,7 @@ private MyDiaryResponse convertToMyDiaryResponseDto(Diary savedDiary, Long membe .content(savedDiary.getContent()) .isLiked(isLiked) .createAt(savedDiary.getCreateAt()) + .diaryDate(savedDiary.getDiaryDate()) .build(); } @@ -319,6 +321,7 @@ private MyDiarySimpleResponse convertToMyDiarySimpleResponseDto(Diary savedDiary .thumbnailResponse(thumbnailResponse) .content(savedDiary.getContent()) .createAt(savedDiary.getCreateAt()) + .diaryDate(savedDiary.getDiaryDate()) .build(); } } diff --git a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java index 96e8e9d..7eefe26 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java +++ b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java @@ -37,12 +37,31 @@ public static Specification filterDiaries( } if(date != null){ - predicate = builder.and(predicate, builder.equal(root.get("createAt").as(LocalDate.class), date)); + predicate = builder.and(predicate, + builder.or( + builder.and( + builder.isNotNull(root.get("diaryDate")), + builder.equal(root.get("diaryDate"), date) + ), + builder.and( + builder.isNull(root.get("diaryDate")), + builder.equal(root.get("createAt").as(LocalDate.class), date) + ) + )); } if (from != null && until != null) { predicate = builder.and(predicate, - builder.between(root.get("createAt"), from, until.plusDays(1))); + builder.or( + builder.and( + builder.isNotNull(root.get("diaryDate")), + builder.between(root.get("diaryDate"), from, until) + ), + builder.and( + builder.isNull(root.get("diaryDate")), + builder.between(root.get("createAt"), from, until.plusDays(1)) + ) + )); } if (isBookmark != null) { From c5ec931f178085a3c7ca5814ae03fe3d5aecbabe Mon Sep 17 00:00:00 2001 From: HyeJiJUN Date: Wed, 13 Nov 2024 23:27:44 +0900 Subject: [PATCH 08/19] =?UTF-8?q?feat:=20=EC=95=8C=EB=9E=8C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=B5=9C=EC=8B=A0=EC=88=9C=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../everymoment/repository/NotificationRepository.java | 5 ++++- .../potatocake/everymoment/service/NotificationService.java | 4 +++- .../everymoment/repository/NotificationRepositoryTest.java | 5 +++-- .../everymoment/service/NotificationServiceTest.java | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/repository/NotificationRepository.java b/src/main/java/com/potatocake/everymoment/repository/NotificationRepository.java index 19297a5..e752355 100644 --- a/src/main/java/com/potatocake/everymoment/repository/NotificationRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/NotificationRepository.java @@ -2,8 +2,11 @@ import com.potatocake.everymoment.entity.Notification; import java.util.List; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; public interface NotificationRepository extends JpaRepository { - List findAllByMemberId(Long memberId); + + List findAllByMemberId(Long memberId, Sort sort); + } diff --git a/src/main/java/com/potatocake/everymoment/service/NotificationService.java b/src/main/java/com/potatocake/everymoment/service/NotificationService.java index 4f5fd36..bd352dd 100644 --- a/src/main/java/com/potatocake/everymoment/service/NotificationService.java +++ b/src/main/java/com/potatocake/everymoment/service/NotificationService.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -60,7 +61,8 @@ public List getNotifications(Long memberId) { Member currentMember = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); - List notifications = notificationRepository.findAllByMemberId(currentMember.getId()); + List notifications = notificationRepository.findAllByMemberId( + currentMember.getId(), Sort.by(Sort.Direction.DESC, "createAt")); return notifications.stream() .map(this::convertToNotificationResponseDTO) diff --git a/src/test/java/com/potatocake/everymoment/repository/NotificationRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/NotificationRepositoryTest.java index b5924c8..76177f4 100644 --- a/src/test/java/com/potatocake/everymoment/repository/NotificationRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/NotificationRepositoryTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.data.domain.Sort; import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = "spring.jpa.hibernate.ddl-auto=create-drop") @@ -79,7 +80,7 @@ void should_FindNotifications_When_FilteringByMemberId() { // when List foundNotifications = notificationRepository - .findAllByMemberId(savedMember.getId()); + .findAllByMemberId(savedMember.getId(), Sort.by(Sort.Direction.DESC, "createAt")); // then assertThat(foundNotifications).hasSize(2); @@ -113,7 +114,7 @@ void should_DeleteNotification_When_ValidEntity() { // then List remainingNotifications = notificationRepository - .findAllByMemberId(savedMember.getId()); + .findAllByMemberId(savedMember.getId(), Sort.by(Sort.Direction.DESC, "createAt")); assertThat(remainingNotifications).isEmpty(); } diff --git a/src/test/java/com/potatocake/everymoment/service/NotificationServiceTest.java b/src/test/java/com/potatocake/everymoment/service/NotificationServiceTest.java index 4ad4aa0..7d842fa 100644 --- a/src/test/java/com/potatocake/everymoment/service/NotificationServiceTest.java +++ b/src/test/java/com/potatocake/everymoment/service/NotificationServiceTest.java @@ -24,6 +24,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Sort; @ExtendWith(MockitoExtension.class) class NotificationServiceTest { @@ -104,7 +105,7 @@ void should_GetNotifications_When_ValidMemberId() { ); given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - given(notificationRepository.findAllByMemberId(memberId)).willReturn(notifications); + given(notificationRepository.findAllByMemberId(memberId, Sort.by(Sort.Direction.DESC, "createAt"))).willReturn(notifications); // when List responses = notificationService.getNotifications(memberId); From 731a17e1d9120b5a59de758ad04aaa9b31904c93 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 14:37:12 +0900 Subject: [PATCH 09/19] =?UTF-8?q?feat:=20=EC=9C=A0=EB=8B=88=ED=81=AC=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/potatocake/everymoment/entity/Like.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/potatocake/everymoment/entity/Like.java b/src/main/java/com/potatocake/everymoment/entity/Like.java index ff8ecfb..2db2b54 100644 --- a/src/main/java/com/potatocake/everymoment/entity/Like.java +++ b/src/main/java/com/potatocake/everymoment/entity/Like.java @@ -7,12 +7,18 @@ import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -@Table(name = "likes") +@Table( + name = "likes", + uniqueConstraints = { + @UniqueConstraint(columnNames = {"member_id", "diary_id"}) + } +) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity From f2ccd6e8493c8a7ab4bfa181f9022c065e9141ee Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 14:37:33 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20=EB=B9=84=EA=B4=80=EC=A0=81=20?= =?UTF-8?q?=EB=9D=BD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../potatocake/everymoment/repository/LikeRepository.java | 8 +++++++- .../com/potatocake/everymoment/service/LikeService.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/repository/LikeRepository.java b/src/main/java/com/potatocake/everymoment/repository/LikeRepository.java index 367cd00..fa45665 100644 --- a/src/main/java/com/potatocake/everymoment/repository/LikeRepository.java +++ b/src/main/java/com/potatocake/everymoment/repository/LikeRepository.java @@ -2,12 +2,18 @@ import com.potatocake.everymoment.entity.Diary; import com.potatocake.everymoment.entity.Like; +import jakarta.persistence.LockModeType; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface LikeRepository extends JpaRepository { - Optional findByMemberIdAndDiaryId(Long memberId, Long diaryId); + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT l FROM Like l WHERE l.member.id = :memberId AND l.diary.id = :diaryId") + Optional findByMemberIdAndDiaryIdWithLock(@Param("memberId") Long memberId, @Param("diaryId") Long diaryId); Long countByDiary(Diary diary); diff --git a/src/main/java/com/potatocake/everymoment/service/LikeService.java b/src/main/java/com/potatocake/everymoment/service/LikeService.java index d2c5586..95584ad 100644 --- a/src/main/java/com/potatocake/everymoment/service/LikeService.java +++ b/src/main/java/com/potatocake/everymoment/service/LikeService.java @@ -48,7 +48,7 @@ public void toggleLike(Long memberId, Long diaryId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND)); - Optional existingLike = likeRepository.findByMemberIdAndDiaryId(memberId, diaryId); + Optional existingLike = likeRepository.findByMemberIdAndDiaryIdWithLock(memberId, diaryId); if (existingLike.isPresent()) { // 이미 좋아요가 존재하면 삭제 (좋아요 취소) From 2c935a7c089021a10b4d413fbe56ab839e3b5c8c Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 14:58:43 +0900 Subject: [PATCH 11/19] =?UTF-8?q?test:=20=EB=B9=84=EA=B4=80=EC=A0=81=20?= =?UTF-8?q?=EB=9D=BD=20=EC=A0=81=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../everymoment/repository/LikeRepositoryTest.java | 2 +- .../potatocake/everymoment/service/LikeServiceTest.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/potatocake/everymoment/repository/LikeRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/LikeRepositoryTest.java index 87027c4..d3f3b8b 100644 --- a/src/test/java/com/potatocake/everymoment/repository/LikeRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/LikeRepositoryTest.java @@ -96,7 +96,7 @@ void should_FindLike_When_FilteringByMemberAndDiary() { likeRepository.save(like); // when - Optional foundLike = likeRepository.findByMemberIdAndDiaryId(member.getId(), diary.getId()); + Optional foundLike = likeRepository.findByMemberIdAndDiaryIdWithLock(member.getId(), diary.getId()); // then assertThat(foundLike).isPresent(); diff --git a/src/test/java/com/potatocake/everymoment/service/LikeServiceTest.java b/src/test/java/com/potatocake/everymoment/service/LikeServiceTest.java index f645c71..0bd3eb7 100644 --- a/src/test/java/com/potatocake/everymoment/service/LikeServiceTest.java +++ b/src/test/java/com/potatocake/everymoment/service/LikeServiceTest.java @@ -85,7 +85,7 @@ void should_AddLike_When_NotLiked() { given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); given(diaryRepository.findById(diaryId)).willReturn(Optional.of(diary)); - given(likeRepository.findByMemberIdAndDiaryId(memberId, diaryId)) + given(likeRepository.findByMemberIdAndDiaryIdWithLock(memberId, diaryId)) .willReturn(Optional.empty()); // when @@ -122,7 +122,7 @@ void should_RemoveLike_When_AlreadyLiked() { given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); given(diaryRepository.findById(diaryId)).willReturn(Optional.of(diary)); - given(likeRepository.findByMemberIdAndDiaryId(memberId, diaryId)) + given(likeRepository.findByMemberIdAndDiaryIdWithLock(memberId, diaryId)) .willReturn(Optional.of(like)); // when @@ -138,9 +138,7 @@ void should_ThrowException_When_DiaryNotPublic() { // given Long memberId = 1L; Long diaryId = 1L; - Member member = Member.builder() - .id(memberId) - .build(); + Diary diary = Diary.builder() .id(diaryId) .isPublic(false) From 5686e828d26117cb7b3a50f4078c7304d4436a52 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 21:56:37 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=97=90=20=EA=B3=B5=EC=9C=A0=20=EC=97=AC=EB=B6=80=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/controller/DiaryController.java | 3 +++ .../everymoment/dto/request/DiaryFilterRequest.java | 1 + .../potatocake/everymoment/service/DiaryService.java | 10 ++++++---- .../everymoment/service/DiarySpecification.java | 8 ++++++-- .../everymoment/repository/DiaryRepositoryTest.java | 10 ++++++---- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java index 41579ac..afae616 100644 --- a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java +++ b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java @@ -100,6 +100,8 @@ public ResponseEntity> getMyDiaries( @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate until, @Parameter(description = "북마크 여부") @RequestParam(required = false) Boolean bookmark, + @Parameter(description = "공유 여부") + @RequestParam(required = false) Boolean isPublic, @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, @Parameter(description = "페이지 크기") @@ -115,6 +117,7 @@ public ResponseEntity> getMyDiaries( .from(from) .until(until) .isBookmark(bookmark) + .isPublic(isPublic) .key(key) .size(size) .build(); diff --git a/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java b/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java index 29fbb5d..429f0c7 100644 --- a/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java +++ b/src/main/java/com/potatocake/everymoment/dto/request/DiaryFilterRequest.java @@ -18,6 +18,7 @@ public class DiaryFilterRequest { private LocalDate from; private LocalDate until; private Boolean isBookmark; + private Boolean isPublic; private int key; private int size; diff --git a/src/main/java/com/potatocake/everymoment/service/DiaryService.java b/src/main/java/com/potatocake/everymoment/service/DiaryService.java index 1bdc4e9..da0c65d 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiaryService.java +++ b/src/main/java/com/potatocake/everymoment/service/DiaryService.java @@ -108,7 +108,7 @@ public void createDiaryManual(Long memberId, DiaryManualCreateRequest diaryManua //카테고리 저장 List categoryRequestList = diaryManualCreateRequest.getCategories(); - if(categoryRequestList != null){ + if (categoryRequestList != null) { addDiaryCategory(savedDiary, currentMember.getId(), categoryRequestList); } } @@ -127,7 +127,7 @@ public MyDiariesResponse getMyDiaries(Long memberId, DiaryFilterRequest diaryFil Specification spec; - if(!diaryFilterRequest.hasFilter()){ + if (!diaryFilterRequest.hasFilter()) { LocalDate today = LocalDate.now(); spec = DiarySpecification.filterDiaries( @@ -137,7 +137,8 @@ public MyDiariesResponse getMyDiaries(Long memberId, DiaryFilterRequest diaryFil today, diaryFilterRequest.getFrom(), diaryFilterRequest.getUntil(), - diaryFilterRequest.getIsBookmark()) + diaryFilterRequest.getIsBookmark(), + diaryFilterRequest.getIsPublic()) .and((root, query, builder) -> builder.equal(root.get("member"), currentMember)); diaryPage = diaryRepository.findAll(spec, @@ -150,7 +151,8 @@ public MyDiariesResponse getMyDiaries(Long memberId, DiaryFilterRequest diaryFil diaryFilterRequest.getDate(), diaryFilterRequest.getFrom(), diaryFilterRequest.getUntil(), - diaryFilterRequest.getIsBookmark()) + diaryFilterRequest.getIsBookmark(), + diaryFilterRequest.getIsPublic()) .and((root, query, builder) -> builder.equal(root.get("member"), currentMember)); diaryPage = diaryRepository.findAll(spec, diff --git a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java index 7eefe26..01184f3 100644 --- a/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java +++ b/src/main/java/com/potatocake/everymoment/service/DiarySpecification.java @@ -18,7 +18,7 @@ public class DiarySpecification { public static Specification filterDiaries( String keyword, List emojis, List categories, - LocalDate date, LocalDate from, LocalDate until, Boolean isBookmark) { + LocalDate date, LocalDate from, LocalDate until, Boolean isBookmark, Boolean isPublic) { return (Root root, CriteriaQuery query, CriteriaBuilder builder) -> { Predicate predicate = builder.conjunction(); @@ -36,7 +36,7 @@ public static Specification filterDiaries( predicate = builder.and(predicate, categoryJoin.get("categoryName").in(categories)); } - if(date != null){ + if (date != null) { predicate = builder.and(predicate, builder.or( builder.and( @@ -68,6 +68,10 @@ public static Specification filterDiaries( predicate = builder.and(predicate, builder.equal(root.get("isBookmark"), isBookmark)); } + if (isPublic != null) { + predicate = builder.and(predicate, builder.equal(root.get("isPublic"), isPublic)); + } + return predicate; }; } diff --git a/src/test/java/com/potatocake/everymoment/repository/DiaryRepositoryTest.java b/src/test/java/com/potatocake/everymoment/repository/DiaryRepositoryTest.java index 74f276c..37ec8df 100644 --- a/src/test/java/com/potatocake/everymoment/repository/DiaryRepositoryTest.java +++ b/src/test/java/com/potatocake/everymoment/repository/DiaryRepositoryTest.java @@ -93,7 +93,7 @@ void should_FindDiaries_When_FilteringWithSearchCriteria() { .locationName("Busan") .address("Busan Address") .emoji("😍") - .isPublic(true) + .isPublic(false) .isBookmark(false) .build(); @@ -107,7 +107,8 @@ void should_FindDiaries_When_FilteringWithSearchCriteria() { null, // date null, // from null, // until - false // isBookmark + false, // isBookmark + true // isPublic ); Page result = diaryRepository.findAll(spec, PageRequest.of(0, 10)); @@ -132,7 +133,7 @@ void should_FindDiaries_When_FilteringBookmarked() { .locationName("Seoul") .address("Seoul Address") .isBookmark(true) - .isPublic(false) + .isPublic(true) .build(); Diary diary2 = createDiary(member, "Content 2", "Busan", "Busan Address"); @@ -156,7 +157,8 @@ void should_FindDiaries_When_FilteringBookmarked() { null, // date null, // from null, // until - true // isBookmark + true, // isBookmark + true // isPublic ); Page result = diaryRepository.findAll(spec, PageRequest.of(0, 10)); From b8b93d1ec1d85b86551f74820bd0cda345ef3f32 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 22:22:25 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=A4=EB=B2=84=EB=A6=AC=EC=A7=80=20=EC=B8=A1=EC=A0=95=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr_weekly_ci.yml | 16 ++++++++++++++++ build.gradle | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/pr_weekly_ci.yml b/.github/workflows/pr_weekly_ci.yml index f54701f..8714ecc 100644 --- a/.github/workflows/pr_weekly_ci.yml +++ b/.github/workflows/pr_weekly_ci.yml @@ -67,3 +67,19 @@ jobs: with: report_paths: '**/build/test-results/test/TEST-*.xml' token: ${{ github.token }} + + - name: JaCoCo 테스트 커버리지 리포트 업로드 + uses: actions/upload-artifact@v3 + if: always() + with: + name: jacoco-report + path: '**/build/reports/jacoco/' + + - name: JaCoCo 테스트 커버리지 결과를 PR에 코멘트로 등록 + uses: madrapps/jacoco-report@v1.6.1 + with: + paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml + token: ${{ github.token }} + min-coverage-overall: 80 + min-coverage-changed-files: 80 + title: '📊 테스트 커버리지 리포트' diff --git a/build.gradle b/build.gradle index 98243d9..4e12aab 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'jacoco' id 'org.springframework.boot' version '3.3.3' id 'io.spring.dependency-management' version '1.1.6' } @@ -51,3 +52,14 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +jacocoTestReport { + reports { + xml.required = true + html.required = true + } +} + +test { + finalizedBy jacocoTestReport +} From c10dee6263432f635dbbb47a751723cef2c32ab8 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 22:26:03 +0900 Subject: [PATCH 14/19] =?UTF-8?q?fix:=20=EB=93=A4=EC=97=AC=EC=93=B0?= =?UTF-8?q?=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr_weekly_ci.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pr_weekly_ci.yml b/.github/workflows/pr_weekly_ci.yml index 8714ecc..a2fc914 100644 --- a/.github/workflows/pr_weekly_ci.yml +++ b/.github/workflows/pr_weekly_ci.yml @@ -53,7 +53,7 @@ jobs: - name: 빌드 테스트 수행 run: | chmod +x ./gradlew - ./gradlew clean build --build-cache --stacktrace + ./gradlew clean build jacocoTestReport --build-cache --stacktrace - name: 테스트 수행 결과 보고 uses: EnricoMi/publish-unit-test-result-action@v2 @@ -69,17 +69,15 @@ jobs: token: ${{ github.token }} - name: JaCoCo 테스트 커버리지 리포트 업로드 - uses: actions/upload-artifact@v3 - if: always() - with: - name: jacoco-report - path: '**/build/reports/jacoco/' + uses: actions/upload-artifact@v3 + if: always() + with: + name: jacoco-report + path: '**/build/reports/jacoco/' - name: JaCoCo 테스트 커버리지 결과를 PR에 코멘트로 등록 uses: madrapps/jacoco-report@v1.6.1 with: paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml token: ${{ github.token }} - min-coverage-overall: 80 - min-coverage-changed-files: 80 title: '📊 테스트 커버리지 리포트' From e0074dde72e6521869beeb7fc7f293b0e40733e3 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 22:30:46 +0900 Subject: [PATCH 15/19] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=A4=EB=B2=84=EB=A6=AC=EC=A7=80=20=EC=B5=9C=EC=86=8C=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr_weekly_ci.yml | 2 ++ build.gradle | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/pr_weekly_ci.yml b/.github/workflows/pr_weekly_ci.yml index a2fc914..372f0ca 100644 --- a/.github/workflows/pr_weekly_ci.yml +++ b/.github/workflows/pr_weekly_ci.yml @@ -80,4 +80,6 @@ jobs: with: paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml token: ${{ github.token }} + min-coverage-overall: 70 + min-coverage-changed-files: 70 title: '📊 테스트 커버리지 리포트' diff --git a/build.gradle b/build.gradle index 4e12aab..8c803bb 100644 --- a/build.gradle +++ b/build.gradle @@ -54,12 +54,38 @@ tasks.named('test') { } jacocoTestReport { + dependsOn test // 테스트 실행 후 리포트 생성을 보장 + reports { xml.required = true html.required = true } + + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + '**/dto/**', + '**/entity/**', + '**/config/**', + '**/exception/**', + '**/*Application.class' + ]) + })) + } } test { + useJUnitPlatform() finalizedBy jacocoTestReport } + +// 테스트 커버리지 최소 기준 설정 +jacocoTestCoverageVerification { + violationRules { + rule { + limit { + minimum = 0.70 + } + } + } +} From 234bc8f5447607aac5824ea1db8d0a1098ff546f Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Thu, 14 Nov 2024 22:34:21 +0900 Subject: [PATCH 16/19] =?UTF-8?q?feat:=20=EC=B8=A1=EC=A0=95=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=20=EB=8C=80=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8c803bb..ff625e8 100644 --- a/build.gradle +++ b/build.gradle @@ -64,9 +64,10 @@ jacocoTestReport { afterEvaluate { classDirectories.setFrom(files(classDirectories.files.collect { fileTree(dir: it, exclude: [ - '**/dto/**', - '**/entity/**', + '**/constant/**', '**/config/**', + '**/dto/**', + '**/security/**', '**/exception/**', '**/*Application.class' ]) From c225da9720538a08c355420a9e922c289017dfd3 Mon Sep 17 00:00:00 2001 From: peeerr Date: Thu, 14 Nov 2024 23:38:27 +0900 Subject: [PATCH 17/19] =?UTF-8?q?docs:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19ed58e..fdd88bb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,153 @@ -# Team21_BE -21조 백엔드 +# Every Moment +> 하루를 자동으로 기록하고 공유하는 위치 기반 소셜 다이어리 📝 + +![image](https://github.com/user-attachments/assets/d2be179d-e5e4-4b14-914f-8d88f5e6f7b5) + +
+ +## 📌 프로젝트 소개 +매 순간을 기록하고 싶지만, 바쁜 일상 속에서 일기 쓰기는 쉽지 않습니다. +Every Moment는 사용자의 위치 데이터를 기반으로 사용자의 하루를 **자동**으로 일기를 기록하고, 친구들과 공유할 수 있는 소셜 다이어리 서비스입니다. + +## 📌 프로젝트 소개 +바쁜 일상 속에서 매 순간을 기록하기란 쉽지 않습니다. +Every Moment는 위치 데이터를 기반으로 사용자의 하루를 **자동**으로 기록하고 친구들과 공유할 수 있는 소셜 다이어리 서비스입니다. + +### 핵심 기능 +- 📍 **위치 기반 자동 기록**: 15분 이상 머문 장소 자동 감지 및 일기로 기록 + +- 🤝 **소셜 다이어리**: 친구들과 일기 공유 및 소통 +- 🔍 **스마트 검색**: 다양한 필터링 옵션으로 원하는 일기 빠른 검색 +- 🔔 **실시간 알림**: 친구와의 상호작용(좋아요, 댓글, 친구 요청 등)과 새로운 장소 감지를 실시간으로 알림 + +
+ +## ⭐️ 주요 기능 +| 자동 일기 기록 | 손쉬운 일기 편집 | 다양한 검색 필터링 | 친구와의 일기 공유 | +|:---:|:---:|:---:|:---:| +| | | | | +| 15분동안 머문 장소를
자동으로 기록 | 저장된 일기를
손쉽게 편집 | 다양한 검색 조건으로
손쉬운 일기 찾기 | 원하는 일기를 공유하여
친구와 소통 | + +
+ +## 🔍 프로젝트 정보 +### 개발 기간 +- 2024.09 ~ 2024.11 (3개월) + +### 서비스 링크 +- **API 서버**: http://13.125.156.74:8080 + +- **API 문서**: [Swagger UI](http://13.125.156.74:8080/swagger-ui/index.html) | [노션 문서](https://peeerr.notion.site/API-2e575ca8df07493dbc25f3d0e91ca211?pvs=4) + +### 프로젝트 관리 +- **[Backend Repository](https://github.com/kakao-tech-campus-2nd-step3/Team21_BE)** +- **[Android Repository](https://github.com/kakao-tech-campus-2nd-step3/Team21_Android)** + +- **[Git Flow 전략](https://github.com/kakao-tech-campus-2nd-step3/Team21_BE/wiki/Git-Flow-%EC%A0%84%EB%9E%B5)** +- **[코딩 컨벤션](https://github.com/kakao-tech-campus-2nd-step3/Team21_BE/wiki/%EC%BD%94%EB%94%A9-%EC%BB%A8%EB%B2%A4%EC%85%98)** +- **[커밋 컨벤션](https://github.com/kakao-tech-campus-2nd-step3/Team21_BE/wiki/%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98)** + +
+ +## 👥 팀원 소개 +저희 팀은 **백엔드 2명, 안드로이드 3명**으로 구성되어 있습니다. + + + + + + + + + + + + + + + + +
최준형전혜지이아림윤채원권새일
+ + + +
+ Backend +
+ + + +
+ Backend +
+ + + +
+ Android +
+ + + +
+ Android +
+ + + +
+ Android +
+ +
+ +## 🛠 기술 스택 +### 💻 Language & Framework +- Java 21 +- Spring Boot 3.3 +- Spring Security +- Spring Data JPA + +### 📊 Database & Storage +- MySQL +- AWS S3 (파일 저장소) + +### 📱 Communication +- Firebase Cloud Messaging (FCM) + +### 🏗 Infra +- AWS EC2 +- AWS RDS + +### 🔧 Development Tools +- GitHub Actions + +### 📚 Documentation & Testing +- Swagger +- JUnit5 + +### ⚙️ Others +- JWT (인증/인가) +- Lombok + +
+ +## 🗄️ ERD +![erd](https://github.com/user-attachments/assets/72e66248-f217-434a-9f20-d8150abafee4) + +
+ +## 🔍 개발 주안점 +- **실시간성 확보** + - FCM을 활용한 즉각적인 푸시 알림 구현 + + - 댓글, 좋아요, 친구 요청, 자동 일기 작성 등 다양한 이벤트에 대한 실시간 알림 + +- **CI/CD 파이프라인 구축 및 최적화** + - Github Actions를 활용한 배포 자동화 + + - PR 단계에서 자동화된 테스트 실행으로 개발 생산성 향상 + - 이전에는 배포 환경에서 테스트 실패 시 새로운 PR을 생성하고, 배포하는 과정을 반복해야 했음 + +- **테스트 작성** + - 70% 이상의 테스트 커버리지 유지 From 8f35bd24fd94509a898181bfd485d1b8315e7b53 Mon Sep 17 00:00:00 2001 From: peeerr Date: Thu, 14 Nov 2024 23:41:34 +0900 Subject: [PATCH 18/19] =?UTF-8?q?docs:=20=EC=A4=91=EB=B3=B5=EB=90=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index fdd88bb..9d9e634 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,6 @@
-## 📌 프로젝트 소개 -매 순간을 기록하고 싶지만, 바쁜 일상 속에서 일기 쓰기는 쉽지 않습니다. -Every Moment는 사용자의 위치 데이터를 기반으로 사용자의 하루를 **자동**으로 일기를 기록하고, 친구들과 공유할 수 있는 소셜 다이어리 서비스입니다. - ## 📌 프로젝트 소개 바쁜 일상 속에서 매 순간을 기록하기란 쉽지 않습니다. Every Moment는 위치 데이터를 기반으로 사용자의 하루를 **자동**으로 기록하고 친구들과 공유할 수 있는 소셜 다이어리 서비스입니다. @@ -105,7 +101,6 @@ Every Moment는 위치 데이터를 기반으로 사용자의 하루를 **자동 ### 💻 Language & Framework - Java 21 - Spring Boot 3.3 -- Spring Security - Spring Data JPA ### 📊 Database & Storage From 0ca413e399c530f27bff1712ae240145724241f1 Mon Sep 17 00:00:00 2001 From: peeerr Date: Thu, 14 Nov 2024 23:47:03 +0900 Subject: [PATCH 19/19] =?UTF-8?q?docs:=20=EA=B8=B0=EC=88=A0=20=EC=8A=A4?= =?UTF-8?q?=ED=83=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9d9e634..9ef5a47 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ Every Moment는 위치 데이터를 기반으로 사용자의 하루를 **자동 ### ⚙️ Others - JWT (인증/인가) -- Lombok