Skip to content

Commit

Permalink
Merge pull request #114 from kakao-tech-campus-2nd-step3/weekly/11
Browse files Browse the repository at this point in the history
merge: 11주차 작업 master에 반영
  • Loading branch information
peeerr authored Nov 15, 2024
2 parents 1c54d2b + 0ca413e commit b3f070e
Show file tree
Hide file tree
Showing 22 changed files with 311 additions and 62 deletions.
18 changes: 17 additions & 1 deletion .github/workflows/pr_weekly_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/[email protected]
with:
paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
token: ${{ github.token }}
min-coverage-overall: 70
min-coverage-changed-files: 70
title: '📊 테스트 커버리지 리포트'
149 changes: 147 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,147 @@
# Team21_BE
21조 백엔드
# Every Moment
> 하루를 자동으로 기록하고 공유하는 위치 기반 소셜 다이어리 📝
![image](https://github.com/user-attachments/assets/d2be179d-e5e4-4b14-914f-8d88f5e6f7b5)

<br/>

## 📌 프로젝트 소개
바쁜 일상 속에서 매 순간을 기록하기란 쉽지 않습니다.
Every Moment는 위치 데이터를 기반으로 사용자의 하루를 **자동**으로 기록하고 친구들과 공유할 수 있는 소셜 다이어리 서비스입니다.

### 핵심 기능
- 📍 **위치 기반 자동 기록**: 15분 이상 머문 장소 자동 감지 및 일기로 기록

- 🤝 **소셜 다이어리**: 친구들과 일기 공유 및 소통
- 🔍 **스마트 검색**: 다양한 필터링 옵션으로 원하는 일기 빠른 검색
- 🔔 **실시간 알림**: 친구와의 상호작용(좋아요, 댓글, 친구 요청 등)과 새로운 장소 감지를 실시간으로 알림

<br/>

## ⭐️ 주요 기능
| 자동 일기 기록 | 손쉬운 일기 편집 | 다양한 검색 필터링 | 친구와의 일기 공유 |
|:---:|:---:|:---:|:---:|
| <img src="https://github.com/user-attachments/assets/c7aa92f4-0ab9-44bb-8abf-872eaeeefcbb" width="200px"> | <img src="https://github.com/user-attachments/assets/a3557c70-d183-4929-ab36-5833bacf2e36" width="200px"> | <img src="https://github.com/user-attachments/assets/3448752f-2c20-4d72-b59f-560708a6e037" width="200px"> | <img src="https://github.com/user-attachments/assets/fb29d6e7-8325-4656-8514-e6767bc034fc" width="200px"> |
| 15분동안 머문 장소를<br>자동으로 기록 | 저장된 일기를<br>손쉽게 편집 | 다양한 검색 조건으로<br>손쉬운 일기 찾기 | 원하는 일기를 공유하여<br>친구와 소통 |

<br/>

## 🔍 프로젝트 정보
### 개발 기간
- 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)**

<br/>

## 👥 팀원 소개
저희 팀은 **백엔드 2명, 안드로이드 3명**으로 구성되어 있습니다.

<table align="center">
<tr align="center">
<td><B>최준형<B></td>
<td><B>전혜지<B></td>
<td><B>이아림<B></td>
<td><B>윤채원<B></td>
<td><B>권새일<B></td>
</tr>
<tr align="center">
<td>
<a href="https://github.com/peeerr">
<img src="https://github.com/peeerr.png" style="max-width: 100px">
</a>
<br>
<a href="https://github.com/peeerr"><B>Backend</B></a>
</td>
<td>
<a href="https://github.com/HyeJiJUN11">
<img src="https://github.com/HyeJiJUN11.png" style="max-width: 100px">
</a>
<br>
<a href="https://github.com/HyeJiJUN11"><B>Backend</B></a>
</td>
<td>
<a href="https://github.com/arieum">
<img src="https://github.com/arieum.png" style="max-width: 100px">
</a>
<br>
<a href="https://github.com/arieum"><B>Android</B></a>
</td>
<td>
<a href="https://github.com/settle54">
<img src="https://github.com/settle54.png" style="max-width: 100px">
</a>
<br>
<a href="https://github.com/settle54"><B>Android</B></a>
</td>
<td>
<a href="https://github.com/todlf">
<img src="https://github.com/todlf.png" style="max-width: 100px">
</a>
<br>
<a href="https://github.com/todlf"><B>Android</B></a>
</td>
</tr>
</table>

<br/>

## 🛠 기술 스택
### 💻 Language & Framework
- Java 21
- Spring Boot 3.3
- 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 (인증/인가)

<br/>

## 🗄️ ERD
![erd](https://github.com/user-attachments/assets/72e66248-f217-434a-9f20-d8150abafee4)

<br/>

## 🔍 개발 주안점
- **실시간성 확보**
- FCM을 활용한 즉각적인 푸시 알림 구현

- 댓글, 좋아요, 친구 요청, 자동 일기 작성 등 다양한 이벤트에 대한 실시간 알림

- **CI/CD 파이프라인 구축 및 최적화**
- Github Actions를 활용한 배포 자동화

- PR 단계에서 자동화된 테스트 실행으로 개발 생산성 향상
- 이전에는 배포 환경에서 테스트 실패 시 새로운 PR을 생성하고, 배포하는 과정을 반복해야 했음

- **테스트 작성**
- 70% 이상의 테스트 커버리지 유지
39 changes: 39 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -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'
}
Expand Down Expand Up @@ -51,3 +52,41 @@ dependencies {
tasks.named('test') {
useJUnitPlatform()
}

jacocoTestReport {
dependsOn test // 테스트 실행 후 리포트 생성을 보장

reports {
xml.required = true
html.required = true
}

afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
'**/constant/**',
'**/config/**',
'**/dto/**',
'**/security/**',
'**/exception/**',
'**/*Application.class'
])
}))
}
}

test {
useJUnitPlatform()
finalizedBy jacocoTestReport
}

// 테스트 커버리지 최소 기준 설정
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.70
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public ResponseEntity<SuccessResponse<MyDiariesResponse>> 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 = "페이지 크기")
Expand All @@ -115,6 +117,7 @@ public ResponseEntity<SuccessResponse<MyDiariesResponse>> getMyDiaries(
.from(from)
.until(until)
.isBookmark(bookmark)
.isPublic(isPublic)
.key(key)
.size(size)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -11,6 +12,8 @@
@Getter
public class DiaryManualCreateRequest {

private LocalDate diaryDate;

private List<CategoryRequest> categories;

private LocationPoint locationPoint;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,4 +18,5 @@ public class MyDiaryResponse {
private String content;
private boolean isLiked;
private LocalDateTime createAt;
private LocalDate diaryDate;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.potatocake.everymoment.dto.response;

import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -16,4 +17,5 @@ public class MyDiarySimpleResponse {
private ThumbnailResponse thumbnailResponse;
private String content;
private LocalDateTime createAt;
private LocalDate diaryDate;
}
4 changes: 4 additions & 0 deletions src/main/java/com/potatocake/everymoment/entity/Diary.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down
8 changes: 7 additions & 1 deletion src/main/java/com/potatocake/everymoment/entity/Like.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Like, Long> {

Optional<Like> 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<Like> findByMemberIdAndDiaryIdWithLock(@Param("memberId") Long memberId, @Param("diaryId") Long diaryId);

Long countByDiary(Diary diary);

Expand Down
Original file line number Diff line number Diff line change
@@ -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<Member, Long> {
Expand All @@ -18,7 +16,6 @@ public interface MemberRepository extends JpaRepository<Member, Long> {

Window<Member> 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")
Long findNextAnonymousNumber();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Notification, Long> {
List<Notification> findAllByMemberId(Long memberId);

List<Notification> findAllByMemberId(Long memberId, Sort sort);

}
Loading

0 comments on commit b3f070e

Please sign in to comment.