Skip to content
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

Merged
merged 7 commits into from
Oct 21, 2024

Conversation

siyeonSon
Copy link
Member

@siyeonSon siyeonSon added ✨feature New feature or request 🥈d-1 With in 1 Day 😺 API Server Api Domain labels Oct 17, 2024
@siyeonSon siyeonSon self-assigned this Oct 17, 2024
Copy link
Member Author

@siyeonSon siyeonSon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

작업하면서 고민했던 부분들을 정리해 보았어요!

Comment on lines 11 to 13
public class MemoryUserTimestampRepository implements UserTimestampRepository{

private static Map<Long, LocalDateTime> store = new HashMap<>();
Copy link
Member Author

@siyeonSon siyeonSon Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요구사항

  • 매일매일 갱신되어야 한다
  • 사용자에게 해당 코멘트를 보냈다는 기록이 저장되어야 함

다양한 방법들

  1. redis: redis에 ttl을 24시간으로 설정하여 기록하면 좋을 듯. 그러나 꺼진 redis를 다시 세팅하는 것은 리소스가 듦
  2. DB: DB에 값을 저장하는 방법은 안전하지만 굳이 DB에 저장할 만큼 중요한 정보는 아님. 사라져도 괜찮음
  3. 로컬 메모리: Map 형태로 저장하는 방법. 가장 간단하지만 서버가 꺼지면 데이터가 모두 날아감

Copy link
Member Author

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 보다는 로컬 메모리를 활용했습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유저에게 코멘트를 보냈다는 것을 확인하는 것에는 방식에 두가지가 있을 것 같은데,

  1. 서버에서 유저가 요청했던 시간을 가지고 있는 것
  2. 클라에서 마지막으로 요청한 시간을 가지고 있는 것
    서버 측 방법으로 구현하게되면 시연님이 해주신 방법이 가장 좋을 것 같고
    클라측에서 구현한다면, 쿼리 파라미터나 헤더쪽에 Last-sent-time 같은 거를 추가해서, 이 시간이 하루를 넘어갈 경우에만 서버에서 내려주는 방식이 있을 것 같습니다. (Notice와 비슷하게 클라측에서 값을 가지고 있을 때)

Comment on lines +30 to +37
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();
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요구사항

  • 문장들은 DB에서 관리한다

다양한 방법들

  1. DB 단위에서 random() 돌리기
    ORDER BY RAND() LIMIT 1 으로 해서 랜덤으로 하나의 문장을 가져올 수 있다.
    단점: DB에 의존적이다. DB 연산을 굳이 거쳐할까?

  2. service에서 random() -> DB에서 하나 조회
    문장의 개수(N)를 모두 확인하고 java 코드로 1~N 사이의 임의의 숫자를 하나 정한다.
    해당 id로 문장 하나를 조회한다.
    단점: 만약에 id가 4, 5, 7 처럼 중간에 하나 사라진다면 Null을 가지게 된다

  3. DB에서 모든 데이터 -> service에서 random()
    모든 findAll()로 가져와서 service 단에서 하나를 임의로 선정한다
    단점: DB의 데이터가 수 천개일 경우, 비효율적일 수 있음

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DB에는 20개의 문장이 저장된다고 가정했습니다. 규모가 작을 때는 어떤 방법을 하든 비슷할 것이에요.
DB에서 연산을 하는 것보다 서버에서 연산하는 것이 성능상 더 좋다고 생각했어요. 따라서 3번째 방법이 가장 적합하다고 생각했습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문장 추천의 경우 저희가 직접 넣어주는 방식이다보니, findAll 쪽에 캐시를 걸어두어서, 1일에 한번씩 갱신하도록 해두면 매 조회 쿼리가 나가는 것이 아닌 서비스 측에서 가지게 되는 것인 추후에는 캐시가 들어가는 것도 좋아보입니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findAll()에 캐시를 두는 방법 아주 좋은 것 같습니다!

Copy link
Collaborator

@seonghun-dev seonghun-dev left a 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RandomProvider로 제공되는 거 좋은 것 같습니다

Comment on lines +30 to +37
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();
}
Copy link
Collaborator

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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserTimestampRepository만 보았을때, 어떤 레포지토리인지 정확하게 이해하는데 다소 한계가 있을 것 같습니다.

Copy link
Member Author

@siyeonSon siyeonSon Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@siyeonSon siyeonSon Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserRecommendSendHistoryRepository 로 변경했습니다

Comment on lines 11 to 13
public class MemoryUserTimestampRepository implements UserTimestampRepository{

private static Map<Long, LocalDateTime> store = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유저에게 코멘트를 보냈다는 것을 확인하는 것에는 방식에 두가지가 있을 것 같은데,

  1. 서버에서 유저가 요청했던 시간을 가지고 있는 것
  2. 클라에서 마지막으로 요청한 시간을 가지고 있는 것
    서버 측 방법으로 구현하게되면 시연님이 해주신 방법이 가장 좋을 것 같고
    클라측에서 구현한다면, 쿼리 파라미터나 헤더쪽에 Last-sent-time 같은 거를 추가해서, 이 시간이 하루를 넘어갈 경우에만 서버에서 내려주는 방식이 있을 것 같습니다. (Notice와 비슷하게 클라측에서 값을 가지고 있을 때)

Comment on lines 20 to 25
@Override
public Boolean isSent(Long userId) {
return Optional.ofNullable(store.get(userId))
.map(readTime -> !readTime.isBefore(LocalDateTime.now().minusDays(1)))
.orElse(false);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하루에 한 번 응답값을 전달합니다.
예: 1일 23시에 읽으면, 2일 00시부터 새로운 문장을 읽을 수 있어요

@seonghun-dev seonghun-dev self-requested a review October 21, 2024 06:26
@siyeonSon siyeonSon merged commit 698f759 into dev Oct 21, 2024
1 of 2 checks passed
@siyeonSon siyeonSon deleted the feat/post-recommend/random-sentence branch October 21, 2024 06:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
😺 API Server Api Domain 🥈d-1 With in 1 Day ✨feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Api] add post recommendation sentence API
2 participants