-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ feat(api): add post recommendation sentence API #517
Conversation
siyeonSon
commented
Oct 17, 2024
- Resolve : [Api] add post recommendation sentence API #514
...reetdrop-api/src/main/java/com/depromeet/domains/recommend/service/PostRecommendService.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
작업하면서 고민했던 부분들을 정리해 보았어요!
public class MemoryUserTimestampRepository implements UserTimestampRepository{ | ||
|
||
private static Map<Long, LocalDateTime> store = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요구사항
- 매일매일 갱신되어야 한다
- 사용자에게 해당 코멘트를 보냈다는 기록이 저장되어야 함
다양한 방법들
- redis: redis에 ttl을 24시간으로 설정하여 기록하면 좋을 듯. 그러나 꺼진 redis를 다시 세팅하는 것은 리소스가 듦
- DB: DB에 값을 저장하는 방법은 안전하지만 굳이 DB에 저장할 만큼 중요한 정보는 아님. 사라져도 괜찮음
- 로컬 메모리: Map 형태로 저장하는 방법. 가장 간단하지만 서버가 꺼지면 데이터가 모두 날아감
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버를 재실행하면 로컬에 저장된 Map<Long, LocalDateTime> store
가 모두 사라지는 문제가 있습니다. 그러나 유실 되어도 크게 상관 없는 로직이라 판단했습니다.
redis에 저장하는 것이 가장 적합하다고 생각했으나, 빠르게 개발히기 위해 redis 보다는 로컬 메모리를 활용했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유저에게 코멘트를 보냈다는 것을 확인하는 것에는 방식에 두가지가 있을 것 같은데,
- 서버에서 유저가 요청했던 시간을 가지고 있는 것
- 클라에서 마지막으로 요청한 시간을 가지고 있는 것
서버 측 방법으로 구현하게되면 시연님이 해주신 방법이 가장 좋을 것 같고
클라측에서 구현한다면, 쿼리 파라미터나 헤더쪽에 Last-sent-time 같은 거를 추가해서, 이 시간이 하루를 넘어갈 경우에만 서버에서 내려주는 방식이 있을 것 같습니다. (Notice와 비슷하게 클라측에서 값을 가지고 있을 때)
private String getRandomSentence() { | ||
var postRecommendSentences = postRecommendSentenceRepository.findAll(); | ||
if (postRecommendSentences.isEmpty()) { | ||
throw new NotFoundException(CommonErrorCode.SENTENCES_NOT_FOUND); | ||
} | ||
PostRecommendSentence randomPostRecommendSentence = RandomProvider.getRandomElement(postRecommendSentences); | ||
return randomPostRecommendSentence.getSentence(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요구사항
- 문장들은 DB에서 관리한다
다양한 방법들
-
DB 단위에서 random() 돌리기
ORDER BY RAND() LIMIT 1
으로 해서 랜덤으로 하나의 문장을 가져올 수 있다.
단점: DB에 의존적이다. DB 연산을 굳이 거쳐할까? -
service에서 random() -> DB에서 하나 조회
문장의 개수(N)를 모두 확인하고 java 코드로 1~N 사이의 임의의 숫자를 하나 정한다.
해당 id로 문장 하나를 조회한다.
단점: 만약에 id가 4, 5, 7 처럼 중간에 하나 사라진다면 Null을 가지게 된다 -
DB에서 모든 데이터 -> service에서 random()
모든findAll()
로 가져와서 service 단에서 하나를 임의로 선정한다
단점: DB의 데이터가 수 천개일 경우, 비효율적일 수 있음
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DB에는 20개의 문장이 저장된다고 가정했습니다. 규모가 작을 때는 어떤 방법을 하든 비슷할 것이에요.
DB에서 연산을 하는 것보다 서버에서 연산하는 것이 성능상 더 좋다고 생각했어요. 따라서 3번째 방법이 가장 적합하다고 생각했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문장 추천의 경우 저희가 직접 넣어주는 방식이다보니, findAll 쪽에 캐시를 걸어두어서, 1일에 한번씩 갱신하도록 해두면 매 조회 쿼리가 나가는 것이 아닌 서비스 측에서 가지게 되는 것인 추후에는 캐시가 들어가는 것도 좋아보입니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findAll()에 캐시를 두는 방법 아주 좋은 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트 코드까지 꼼꼼하게 작성해주셨네요. 감사합니다 👍🏻 💯
import java.util.Random; | ||
|
||
@Component | ||
public final class RandomProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RandomProvider로 제공되는 거 좋은 것 같습니다
private String getRandomSentence() { | ||
var postRecommendSentences = postRecommendSentenceRepository.findAll(); | ||
if (postRecommendSentences.isEmpty()) { | ||
throw new NotFoundException(CommonErrorCode.SENTENCES_NOT_FOUND); | ||
} | ||
PostRecommendSentence randomPostRecommendSentence = RandomProvider.getRandomElement(postRecommendSentences); | ||
return randomPostRecommendSentence.getSentence(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문장 추천의 경우 저희가 직접 넣어주는 방식이다보니, findAll 쪽에 캐시를 걸어두어서, 1일에 한번씩 갱신하도록 해두면 매 조회 쿼리가 나가는 것이 아닌 서비스 측에서 가지게 되는 것인 추후에는 캐시가 들어가는 것도 좋아보입니다
@@ -0,0 +1,6 @@ | |||
package com.depromeet.domains.recommend.repository; | |||
|
|||
public interface UserTimestampRepository { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UserTimestampRepository만 보았을때, 어떤 레포지토리인지 정확하게 이해하는데 다소 한계가 있을 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UserReadRepository ??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UserRecommendSendHistoryRepository 로 변경했습니다
public class MemoryUserTimestampRepository implements UserTimestampRepository{ | ||
|
||
private static Map<Long, LocalDateTime> store = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유저에게 코멘트를 보냈다는 것을 확인하는 것에는 방식에 두가지가 있을 것 같은데,
- 서버에서 유저가 요청했던 시간을 가지고 있는 것
- 클라에서 마지막으로 요청한 시간을 가지고 있는 것
서버 측 방법으로 구현하게되면 시연님이 해주신 방법이 가장 좋을 것 같고
클라측에서 구현한다면, 쿼리 파라미터나 헤더쪽에 Last-sent-time 같은 거를 추가해서, 이 시간이 하루를 넘어갈 경우에만 서버에서 내려주는 방식이 있을 것 같습니다. (Notice와 비슷하게 클라측에서 값을 가지고 있을 때)
@Override | ||
public Boolean isSent(Long userId) { | ||
return Optional.ofNullable(store.get(userId)) | ||
.map(readTime -> !readTime.isBefore(LocalDateTime.now().minusDays(1))) | ||
.orElse(false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하루에 한 번 응답값을 전달합니다.
예: 1일 23시에 읽으면, 2일 00시부터 새로운 문장을 읽을 수 있어요