diff --git a/README.assets/architecture.png b/README.assets/architecture.png
new file mode 100644
index 00000000..31619c9a
Binary files /dev/null and b/README.assets/architecture.png differ
diff --git a/README.assets/erd.png b/README.assets/erd.png
new file mode 100644
index 00000000..5301dfbb
Binary files /dev/null and b/README.assets/erd.png differ
diff --git a/README.assets/ggumtle.png b/README.assets/ggumtle.png
new file mode 100644
index 00000000..bb1fec7f
Binary files /dev/null and b/README.assets/ggumtle.png differ
diff --git a/README.assets/ggumtle1.png b/README.assets/ggumtle1.png
new file mode 100644
index 00000000..83decc9d
Binary files /dev/null and b/README.assets/ggumtle1.png differ
diff --git a/README.assets/ggumtle2.png b/README.assets/ggumtle2.png
new file mode 100644
index 00000000..72afc838
Binary files /dev/null and b/README.assets/ggumtle2.png differ
diff --git a/README.assets/ggumtle3.png b/README.assets/ggumtle3.png
new file mode 100644
index 00000000..6fcf02f5
Binary files /dev/null and b/README.assets/ggumtle3.png differ
diff --git a/README.assets/ggumtle4.png b/README.assets/ggumtle4.png
new file mode 100644
index 00000000..d207d0aa
Binary files /dev/null and b/README.assets/ggumtle4.png differ
diff --git a/README.assets/ggumtle5.png b/README.assets/ggumtle5.png
new file mode 100644
index 00000000..19dc374c
Binary files /dev/null and b/README.assets/ggumtle5.png differ
diff --git a/README.assets/ggumtle6.png b/README.assets/ggumtle6.png
new file mode 100644
index 00000000..d693e1af
Binary files /dev/null and b/README.assets/ggumtle6.png differ
diff --git a/README.assets/ggumtle7.png b/README.assets/ggumtle7.png
new file mode 100644
index 00000000..fbcc2906
Binary files /dev/null and b/README.assets/ggumtle7.png differ
diff --git a/README.assets/title.gif b/README.assets/title.gif
new file mode 100644
index 00000000..c5b444b0
Binary files /dev/null and b/README.assets/title.gif differ
diff --git a/README.md b/README.md
index 9413b2e7..0f681116 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,81 @@
-# ggumtle
-아자아자! 잘하자!
\ No newline at end of file
+# 꿈 : 틀 소개
+![Alt text](/README.assets/title.gif)
+
+ 꿈, 이제는 정말 이뤄나갈 시간.
+ 마음에만 담아두지 말고 꿈틀에 담아보세요.
+
+ 다른 사람들은 가슴 한켠에 어떤 꿈을 품은 채 살아갈까요?
+ 나와 같은 꿈을 먼저 이룩한 선배 꿈꾼이는 어떤 여정을 겪었을까요?
+
+ 꿈틀에서 다채롭고 감동적인 꿈들을 만나보세요.
+ 꿈나라로 초대합니다. 꿈틀에서 만나요.
+
+
+# 서비스 주요 기능
+
+#### 1. 친밀도에 따른 레이다 조회 및 꿈 카테고리 별 레이다 조회
+#### 2. 설정주기에 따른 꿈 리마인드
+#### 3. 유저간 상호작용(팔로우, 댓글, 리액션)
+#### 4. 검색 기능으로 사용자, 꿈틀, 후기 목록 조회
+#### 5. 필터 기능을 적용한 유저 타임라인 조회
+#### 6. SSE를 활용한 상호작용 알람
+#### 7. 꿈 달성시 타임 캡슐 확인 및 소셜 공유 기능
+
+
+
+# 서비스 시연 동영상
+
+
+
+# 개발환경
+
+### Backend
+
+
+
+
+
+
+
+
+### Frontend
+
+
+
+
+
+
+
+
+
+
+
+### CI/CD
+
+
+
+
+### 협업 툴
+
+
+
+
+
+
+
+# 서비스 아키텍쳐
+![Alt text](/README.assets/architecture.png)
+
+
+# ER Diagram
+![Alt text](/README.assets/erd.png)
+
+
+
+# 팀 소개
+|이우성|이원주|전지환|서준호|신창엽|조인화|
+|:---:|:---:|:---:|:---:|:---:|:---:|
+|||||||
+|`FE` `QA`
[@leewooseong](https://github.com/leewooseong)|`FE 리더`
[@3o14](https://github.com/3o14)|`BE` `PM`
[@DarkBlackRice](https://github.com/DarkBlackRice)|`BE` `INFRA`
[@ho97s](https://github.com/ho97s)|`BE 리더`
[@404](https://github.com/404-not-foundl)|`BE` `QA`
[@jjjoina](https://github.com/jjjoina)|
+
+
diff --git a/backend/src/main/java/com/ggums/ggumtle/common/handler/AlarmHandler.java b/backend/src/main/java/com/ggums/ggumtle/common/handler/AlarmHandler.java
index c346f315..77d27f68 100644
--- a/backend/src/main/java/com/ggums/ggumtle/common/handler/AlarmHandler.java
+++ b/backend/src/main/java/com/ggums/ggumtle/common/handler/AlarmHandler.java
@@ -2,10 +2,12 @@
import com.ggums.ggumtle.common.exception.CustomException;
import com.ggums.ggumtle.common.exception.ExceptionType;
+import com.ggums.ggumtle.dto.response.model.AlarmDto;
import com.ggums.ggumtle.entity.*;
import com.ggums.ggumtle.repository.AlarmRepository;
import com.ggums.ggumtle.repository.BucketRepository;
import com.ggums.ggumtle.repository.UserRepository;
+import com.ggums.ggumtle.service.AlarmService;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
@@ -32,7 +34,7 @@ public class AlarmHandler {
public final Map userEmitters = new ConcurrentHashMap<>();
private final AlarmRepository alarmRepository;
private final BucketRepository bucketRepository;
- private final UserRepository userRepository;
+ private final AlarmService alarmService;
// Giving connection signals to client
@Async
@@ -61,14 +63,14 @@ public void createUserAlarm(User receiver, User sender, AlarmType type){
if (!receiver.getAlarm()) {
return;
}
- Alarm alarm = Alarm.builder()
+ Alarm alarm = Alarm.builder()
.receiver(receiver)
.sender(sender)
.type(type)
.dataId(sender != null ? sender.getId() : null)
.build();
alarmRepository.save(alarm);
- sendEventToUser(receiver.getId());
+ sendEventToUser(receiver.getId(), alarm);
}
/**
@@ -91,7 +93,7 @@ public void createBucketAlarm(User receiver, User sender, AlarmType type, Bucket
.dataId(bucket.getId())
.build();
alarmRepository.save(alarm);
- sendEventToUser(receiver.getId());
+ sendEventToUser(receiver.getId(), alarm);
}
/**
@@ -114,7 +116,7 @@ public void createReviewAlarm(User receiver, User sender, AlarmType type, Review
.dataId(review.getId())
.build();
alarmRepository.save(alarm);
- sendEventToUser(receiver.getId());
+ sendEventToUser(receiver.getId(), alarm);
}
/**
@@ -147,7 +149,7 @@ public void createCommentAlarm(User receiver, User sender, AlarmType type, Strin
}
alarmRepository.save(alarm);
- sendEventToUser(receiver.getId());
+ sendEventToUser(receiver.getId(), alarm);
}
/**
@@ -164,7 +166,7 @@ public void createReminderAlarm(User receiver, Bucket bucket){
.dataId(bucket.getId())
.build();
alarmRepository.save(alarm);
- sendEventToUser(receiver.getId());
+ sendEventToUser(receiver.getId(), alarm);
}
@Scheduled(cron = "0 0 12 * * ?")
@@ -198,11 +200,11 @@ private boolean shouldSendReminder(Bucket bucket, LocalDate today) {
}
@Async
- protected void sendEventToUser(Long userId) {
+ protected void sendEventToUser(Long userId, Alarm alarm) {
SseEmitter emitter = userEmitters.get(userId);
if (emitter != null) {
try {
- emitter.send(SseEmitter.event().name("serverEvent").data("readAlarm"));
+ emitter.send(SseEmitter.event().name("serverEvent").data(alarmService.convertToAlarmResponseDto(alarm)));
} catch (IOException e) {
emitter.completeWithError(e);
userEmitters.remove(userId);
diff --git a/backend/src/main/java/com/ggums/ggumtle/controller/AlarmController.java b/backend/src/main/java/com/ggums/ggumtle/controller/AlarmController.java
index e27467f7..aaf02507 100644
--- a/backend/src/main/java/com/ggums/ggumtle/controller/AlarmController.java
+++ b/backend/src/main/java/com/ggums/ggumtle/controller/AlarmController.java
@@ -127,4 +127,14 @@ public Response readAllAlarm(@AuthenticationPrincipal User user){
return new Response("message", alarmService.readAllAlarm(user));
}
+ @PostMapping("/send-reminder")
+ @Operation(summary = "리마인더 알람 발송", description = "리마인더 알람 전송")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "sent")
+ })
+ public Response sendReminder(){
+ alarmHandler.remindBucketAlarm();
+ return new Response("message", "sent");
+ }
+
}
diff --git a/backend/src/main/java/com/ggums/ggumtle/dto/response/AlarmResponseDto.java b/backend/src/main/java/com/ggums/ggumtle/dto/response/AlarmResponseDto.java
index 69e6d1c7..3a8eb30c 100644
--- a/backend/src/main/java/com/ggums/ggumtle/dto/response/AlarmResponseDto.java
+++ b/backend/src/main/java/com/ggums/ggumtle/dto/response/AlarmResponseDto.java
@@ -1,7 +1,6 @@
package com.ggums.ggumtle.dto.response;
-import com.ggums.ggumtle.dto.response.model.AlarmListDto;
-import com.ggums.ggumtle.entity.AlarmType;
+import com.ggums.ggumtle.dto.response.model.AlarmDto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -10,5 +9,5 @@
@Getter @Setter @Builder
public class AlarmResponseDto {
- private Page alarmList;
+ private Page alarmList;
}
diff --git a/backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmListDto.java b/backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmDto.java
similarity index 97%
rename from backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmListDto.java
rename to backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmDto.java
index 9aedf315..b7fb4ccd 100644
--- a/backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmListDto.java
+++ b/backend/src/main/java/com/ggums/ggumtle/dto/response/model/AlarmDto.java
@@ -10,7 +10,7 @@
@Getter
@Setter
@Builder
-public class AlarmListDto {
+public class AlarmDto {
@Schema(description = "알람 id", example = "1")
private Long alarmId;
diff --git a/backend/src/main/java/com/ggums/ggumtle/service/AlarmService.java b/backend/src/main/java/com/ggums/ggumtle/service/AlarmService.java
index 060d3e2b..eb7e66b6 100644
--- a/backend/src/main/java/com/ggums/ggumtle/service/AlarmService.java
+++ b/backend/src/main/java/com/ggums/ggumtle/service/AlarmService.java
@@ -3,9 +3,7 @@
import com.ggums.ggumtle.common.exception.CustomException;
import com.ggums.ggumtle.common.exception.ExceptionType;
import com.ggums.ggumtle.dto.response.AlarmResponseDto;
-import com.ggums.ggumtle.dto.response.BucketSearchResponseDto;
-import com.ggums.ggumtle.dto.response.model.AlarmListDto;
-import com.ggums.ggumtle.dto.response.model.BucketSearchListDto;
+import com.ggums.ggumtle.dto.response.model.AlarmDto;
import com.ggums.ggumtle.entity.Alarm;
import com.ggums.ggumtle.entity.User;
import com.ggums.ggumtle.repository.AlarmRepository;
@@ -13,6 +11,8 @@
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,12 +50,14 @@ public String alarmRead(User user, Long alarmId){
// getting list of alarm
public AlarmResponseDto alarmList(User user, Pageable pageable){
- Page alarms = alarmRepository.findByReceiver(user, pageable);
- Page alarmList = alarms.map(this::convertToAlarmResponseDto);
+ Pageable sortedPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by("createdDate").descending());
+
+ Page alarms = alarmRepository.findByReceiver(user, sortedPageable);
+ Page alarmList = alarms.map(this::convertToAlarmResponseDto);
return AlarmResponseDto.builder().alarmList(alarmList).build();
}
- private AlarmListDto convertToAlarmResponseDto(Alarm alarm){
+ public AlarmDto convertToAlarmResponseDto(Alarm alarm){
String timeUnit;
long time;
@@ -79,7 +81,7 @@ private AlarmListDto convertToAlarmResponseDto(Alarm alarm){
time = ChronoUnit.MINUTES.between(alarm.getCreatedDate(), LocalDateTime.now());
}
- return AlarmListDto.builder()
+ return AlarmDto.builder()
.alarmId(alarm.getId())
.sender(alarm.getSender() != null ? alarm.getSender().getUserNickname() : null)
.senderProfileImage(alarm.getSender() != null ? alarm.getSender().getUserProfileImage() : null)
diff --git "a/exec/1. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf" "b/exec/1-2. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf"
similarity index 71%
rename from "exec/1. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf"
rename to "exec/1-2. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf"
index 72264233..a109f98a 100644
Binary files "a/exec/1. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf" and "b/exec/1-2. Gitlab \354\206\214\354\212\244 \355\201\264\353\241\240 \354\235\264\355\233\204 \353\271\214\353\223\234 \353\260\217 \353\260\260\355\217\254 \352\264\200\353\240\250 \353\254\270\354\204\234.pdf" differ
diff --git "a/exec/3. DB \353\215\244\355\224\204 \355\214\214\354\235\274.zip" "b/exec/3. DB \353\215\244\355\224\204 \355\214\214\354\235\274.zip"
new file mode 100644
index 00000000..4aecceb4
Binary files /dev/null and "b/exec/3. DB \353\215\244\355\224\204 \355\214\214\354\235\274.zip" differ
diff --git a/frontend/dev-dist/sw.js b/frontend/dev-dist/sw.js
index 8d4f8fc3..b0950571 100644
--- a/frontend/dev-dist/sw.js
+++ b/frontend/dev-dist/sw.js
@@ -13,80 +13,87 @@
// If the loader is already loaded, just stop.
if (!self.define) {
- let registry = {};
+ let registry = {}
- // Used for `eval` and `importScripts` where we can't get script URL by other means.
- // In both cases, it's safe to use a global var because those functions are synchronous.
- let nextDefineUri;
+ // Used for `eval` and `importScripts` where we can't get script URL by other means.
+ // In both cases, it's safe to use a global var because those functions are synchronous.
+ let nextDefineUri
- const singleRequire = (uri, parentUri) => {
- uri = new URL(uri + ".js", parentUri).href;
- return registry[uri] || (
-
- new Promise(resolve => {
- if ("document" in self) {
- const script = document.createElement("script");
- script.src = uri;
- script.onload = resolve;
- document.head.appendChild(script);
- } else {
- nextDefineUri = uri;
- importScripts(uri);
- resolve();
- }
- })
-
- .then(() => {
- let promise = registry[uri];
- if (!promise) {
- throw new Error(`Module ${uri} didn’t register its module`);
- }
- return promise;
- })
- );
- };
+ const singleRequire = (uri, parentUri) => {
+ uri = new URL(uri + '.js', parentUri).href
+ return (
+ registry[uri] ||
+ new Promise((resolve) => {
+ if ('document' in self) {
+ const script = document.createElement('script')
+ script.src = uri
+ script.onload = resolve
+ document.head.appendChild(script)
+ } else {
+ nextDefineUri = uri
+ importScripts(uri)
+ resolve()
+ }
+ }).then(() => {
+ let promise = registry[uri]
+ if (!promise) {
+ throw new Error(`Module ${uri} didn’t register its module`)
+ }
+ return promise
+ })
+ )
+ }
- self.define = (depsNames, factory) => {
- const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href;
- if (registry[uri]) {
- // Module is already loading or loaded.
- return;
- }
- let exports = {};
- const require = depUri => singleRequire(depUri, uri);
- const specialDeps = {
- module: { uri },
- exports,
- require
- };
- registry[uri] = Promise.all(depsNames.map(
- depName => specialDeps[depName] || require(depName)
- )).then(deps => {
- factory(...deps);
- return exports;
- });
- };
+ self.define = (depsNames, factory) => {
+ const uri =
+ nextDefineUri || ('document' in self ? document.currentScript.src : '') || location.href
+ if (registry[uri]) {
+ // Module is already loading or loaded.
+ return
+ }
+ let exports = {}
+ const require = (depUri) => singleRequire(depUri, uri)
+ const specialDeps = {
+ module: { uri },
+ exports,
+ require,
+ }
+ registry[uri] = Promise.all(
+ depsNames.map((depName) => specialDeps[depName] || require(depName))
+ ).then((deps) => {
+ factory(...deps)
+ return exports
+ })
+ }
}
-define(['./workbox-b5f7729d'], (function (workbox) { 'use strict';
+define(['./workbox-b5f7729d'], function (workbox) {
+ 'use strict'
- self.skipWaiting();
- workbox.clientsClaim();
+ self.skipWaiting()
+ workbox.clientsClaim()
- /**
- * The precacheAndRoute() method efficiently caches and responds to
- * requests for URLs in the manifest.
- * See https://goo.gl/S9QRab
- */
- workbox.precacheAndRoute([{
- "url": "registerSW.js",
- "revision": "3ca0b8505b4bec776b69afdba2768812"
- }, {
- "url": "index.html",
- "revision": "0.vlken83r8o8"
- }], {});
- workbox.cleanupOutdatedCaches();
- workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
- allowlist: [/^\/$/]
- }));
-
-}));
+ /**
+ * The precacheAndRoute() method efficiently caches and responds to
+ * requests for URLs in the manifest.
+ * See https://goo.gl/S9QRab
+ */
+ workbox.precacheAndRoute(
+ [
+ {
+ url: 'registerSW.js',
+ revision: '3ca0b8505b4bec776b69afdba2768812',
+ },
+ {
+ url: 'index.html',
+ revision: '0.fptplrrmgcg',
+ },
+ ],
+ {}
+ )
+ workbox.cleanupOutdatedCaches()
+ workbox.registerRoute(
+ new workbox.NavigationRoute(workbox.createHandlerBoundToURL('index.html'), {
+ allowlist: [/^\/$/],
+ })
+ )
+})
diff --git a/frontend/index.html b/frontend/index.html
index cc6a8b8e..7f0a21ce 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -9,7 +9,8 @@
-
+
+