-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
196 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.goormy.hackathon.handler; | ||
|
||
import org.springframework.cloud.function.adapter.aws.FunctionInvoker; | ||
|
||
public class LikeHandler extends FunctionInvoker{ | ||
|
||
} |
118 changes: 118 additions & 0 deletions
118
src/main/java/com/goormy/hackathon/lambda/LikeFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package com.goormy.hackathon.lambda; | ||
|
||
import com.goormy.hackathon.entity.Like; | ||
import com.goormy.hackathon.entity.Post; | ||
import com.goormy.hackathon.entity.User; | ||
import com.goormy.hackathon.repository.LikeRedisRepository; | ||
import com.goormy.hackathon.repository.LikeRepository; | ||
import com.goormy.hackathon.repository.PostRepository; | ||
import com.goormy.hackathon.repository.UserRepository; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Configuration | ||
@Transactional(readOnly = true) | ||
@RequiredArgsConstructor | ||
public class LikeFunction { | ||
|
||
private final LikeRedisRepository likeRedisRepository; | ||
private final LikeRepository likeRepository; | ||
private final UserRepository userRepository; | ||
private final PostRepository postRepository; | ||
|
||
/** | ||
* @description 좋아요 정보를 Redis 캐시에 업데이트 | ||
* */ | ||
@Transactional | ||
public void addLike(Long postId, Long userId) { | ||
// 캐시에서 좋아요에 대한 정보를 먼저 조회함 | ||
Integer findPostLike = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
|
||
if (findPostLike == null) { | ||
// 캐시에 좋아요에 대한 정보가 없다면, | ||
// Key = postlike:{postId}, Field = {userId}, Value = 1 로 '좋아요' 정보 생성 | ||
likeRedisRepository.update(postId, userId, 1); | ||
}else if (findPostLike == -1) { | ||
// '좋아요 취소' 정보가 있는 상태라면 | ||
// '좋아요'를 다시 누른 것이기 때문에 '취소 정보'를 삭제 | ||
likeRedisRepository.delete(postId,userId); | ||
} | ||
} | ||
|
||
/** | ||
* @description 좋아요 취소 정보를 Redis 캐시에 업데이트 | ||
* */ | ||
@Transactional | ||
public void cancelLike(Long postId, Long userId) { | ||
// 캐시에서 좋아요 정보를 먼저 조회함 | ||
Integer findPostLike = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
|
||
// 캐시에 좋아요에 대한 정보가 없다면, | ||
// Key = postlike:{postId}, Field = {userId}, Value = -1 로 '좋아요 취소' 정보 생성 | ||
if (findPostLike == null) { | ||
likeRedisRepository.update(postId,userId,-1); | ||
}else if (findPostLike == 1) { | ||
// '좋아요'라는 정보가 있는 상태라면 | ||
// '좋아요 취소'를 다시 누른 것이기 때문에 '좋아요' 정보를 삭젳 | ||
likeRedisRepository.delete(postId,userId); | ||
} | ||
} | ||
|
||
/** | ||
* @description 좋아요 정보가 있는지 조회 (1. Redis 조회 2. RDB 조회) | ||
* */ | ||
public boolean findLike(Long postId, Long userId) { | ||
// 1. 캐시로부터 '좋아요'에 대한 정보를 조회함 | ||
Integer value = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
|
||
if (value == null) { // 캐시에 정보가 없다면 DB에서 조회되는지 여부에 따라 true/false 리턴 | ||
return likeRepository.isExistByPostIdAndUserId(postId, userId); | ||
}else if (value == -1) { // 캐시에 '좋아요 삭제' 정보가 있다면 false 리턴 | ||
return false; | ||
}else{ // 캐시에 '좋아요 추가' 정보가 있다면 true 리턴 | ||
return true; | ||
} | ||
} | ||
|
||
/** | ||
* @description Redis에 있는 '좋아요' 정보들을 RDB에 반영하는 함수 | ||
* */ | ||
@Transactional | ||
public void dumpToDB() { | ||
// 1. "postlike:{postId} 형식의 모든 key 목록을 불러옴 | ||
Set<String> postLikeKeySet = likeRedisRepository.findAllKeys(); | ||
|
||
// 2. Key마다 postId, userId, value를 조회하는 과정 | ||
for (String key: postLikeKeySet) { | ||
|
||
// 2-1. Key로 Hash 자료구조를 조회함. field = userId / value = 1 or -1 | ||
Map<Object, Object> result = likeRedisRepository.findByKey(key); | ||
|
||
// 2-2. key를 파싱하여 postId를 구함 | ||
String[] split = key.split(":"); | ||
Long postId = Long.valueOf(split[1]); | ||
|
||
for (Map.Entry<Object, Object> entry : result.entrySet()) { | ||
// 2-3. field를 형변환하여 userId를 구함 | ||
Long userId = Long.valueOf(String.valueOf(entry.getKey())); | ||
// 2-4. value를 형변환하여 1 또는 -1 값을 얻게 됨 | ||
Integer value = Integer.valueOf(String.valueOf(entry.getValue())); | ||
|
||
// 3. value 값에 따라 DB에 어떻게 반영할지 결정하여 처리함 | ||
if (value == 1) { // 3-1. 좋아요를 추가한 상태였다면 RDB에 insert 쿼리 발생 | ||
User user = userRepository.getReferenceById(userId); | ||
Post post = postRepository.getReferenceById(postId); | ||
likeRepository.save(new Like(user, post)); | ||
}else if (value == -1) { // 3-2. 좋아요를 취소한 상태였다면 RDB에 delete 쿼리 발생 | ||
likeRepository.deleteByPostIdAndUserId(postId, userId); | ||
} | ||
} | ||
|
||
// 4. 해당 Key에 대해 RDB에 반영하는 과정을 마쳤으므로, | ||
likeRedisRepository.delete(key); | ||
} | ||
} | ||
} |
140 changes: 38 additions & 102 deletions
140
src/main/java/com/goormy/hackathon/service/LikeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,55 @@ | ||
package com.goormy.hackathon.service; | ||
|
||
import com.goormy.hackathon.entity.Like; | ||
import com.goormy.hackathon.entity.Post; | ||
import com.goormy.hackathon.entity.User; | ||
import com.goormy.hackathon.repository.LikeRedisRepository; | ||
import com.goormy.hackathon.repository.LikeRepository; | ||
import com.goormy.hackathon.repository.PostRepository; | ||
import com.goormy.hackathon.repository.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.goormy.hackathon.lambda.LikeFunction; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import software.amazon.awssdk.services.sqs.SqsClient; | ||
import software.amazon.awssdk.services.sqs.model.SendMessageRequest; | ||
import software.amazon.awssdk.services.sqs.model.SendMessageResponse; | ||
|
||
@Service | ||
@Transactional(readOnly = true) | ||
@RequiredArgsConstructor | ||
public class LikeService { | ||
|
||
private final LikeRedisRepository likeRedisRepository; | ||
private final LikeRepository likeRepository; | ||
private final UserRepository userRepository; | ||
private final PostRepository postRepository; | ||
|
||
/** | ||
* @description 좋아요 정보를 Redis 캐시에 업데이트 | ||
* */ | ||
@Transactional | ||
public void addLike(Long postId, Long userId) { | ||
// 캐시에서 좋아요에 대한 정보를 먼저 조회함 | ||
Integer findPostLike = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
private static final Logger logger = LoggerFactory.getLogger(LikeFunction.class); | ||
|
||
if (findPostLike == null) { | ||
// 캐시에 좋아요에 대한 정보가 없다면, | ||
// Key = postlike:{postId}, Field = {userId}, Value = 1 로 '좋아요' 정보 생성 | ||
likeRedisRepository.update(postId, userId, 1); | ||
}else if (findPostLike == -1) { | ||
// '좋아요 취소' 정보가 있는 상태라면 | ||
// '좋아요'를 다시 누른 것이기 때문에 '취소 정보'를 삭제 | ||
likeRedisRepository.delete(postId,userId); | ||
} | ||
} | ||
@Autowired | ||
private SqsClient sqsClient; | ||
|
||
/** | ||
* @description 좋아요 취소 정보를 Redis 캐시에 업데이트 | ||
* */ | ||
@Transactional | ||
public void cancelLike(Long postId, Long userId) { | ||
// 캐시에서 좋아요 정보를 먼저 조회함 | ||
Integer findPostLike = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
@Value("${spring.cloud.aws.sqs.queue-url}") | ||
private String queueUrl; | ||
|
||
// 캐시에 좋아요에 대한 정보가 없다면, | ||
// Key = postlike:{postId}, Field = {userId}, Value = -1 로 '좋아요 취소' 정보 생성 | ||
if (findPostLike == null) { | ||
likeRedisRepository.update(postId,userId,-1); | ||
}else if (findPostLike == 1) { | ||
// '좋아요'라는 정보가 있는 상태라면 | ||
// '좋아요 취소'를 다시 누른 것이기 때문에 '좋아요' 정보를 삭젳 | ||
likeRedisRepository.delete(postId,userId); | ||
} | ||
public void sendLikeRequest(Long userId, Long postId) { | ||
sendRequest(userId, postId, "like"); | ||
} | ||
|
||
/** | ||
* @description 좋아요 정보가 있는지 조회 (1. Redis 조회 2. RDB 조회) | ||
* */ | ||
public boolean findLike(Long postId, Long userId) { | ||
// 1. 캐시로부터 '좋아요'에 대한 정보를 조회함 | ||
Integer value = likeRedisRepository.findPostLikeFromCache(postId, userId); | ||
|
||
if (value == null) { // 캐시에 정보가 없다면 DB에서 조회되는지 여부에 따라 true/false 리턴 | ||
return likeRepository.isExistByPostIdAndUserId(postId, userId); | ||
}else if (value == -1) { // 캐시에 '좋아요 삭제' 정보가 있다면 false 리턴 | ||
return false; | ||
}else{ // 캐시에 '좋아요 추가' 정보가 있다면 true 리턴 | ||
return true; | ||
} | ||
public void sendCancelLikeRequest(Long userId, Long postId) { | ||
sendRequest(userId, postId, "unlike"); | ||
} | ||
|
||
/** | ||
* @description Redis에 있는 '좋아요' 정보들을 RDB에 반영하는 함수 | ||
* */ | ||
@Transactional | ||
public void dumpToDB() { | ||
// 1. "postlike:{postId} 형식의 모든 key 목록을 불러옴 | ||
Set<String> postLikeKeySet = likeRedisRepository.findAllKeys(); | ||
|
||
// 2. Key마다 postId, userId, value를 조회하는 과정 | ||
for (String key: postLikeKeySet) { | ||
|
||
// 2-1. Key로 Hash 자료구조를 조회함. field = userId / value = 1 or -1 | ||
Map<Object, Object> result = likeRedisRepository.findByKey(key); | ||
|
||
// 2-2. key를 파싱하여 postId를 구함 | ||
String[] split = key.split(":"); | ||
Long postId = Long.valueOf(split[1]); | ||
|
||
for (Map.Entry<Object, Object> entry : result.entrySet()) { | ||
// 2-3. field를 형변환하여 userId를 구함 | ||
Long userId = Long.valueOf(String.valueOf(entry.getKey())); | ||
// 2-4. value를 형변환하여 1 또는 -1 값을 얻게 됨 | ||
Integer value = Integer.valueOf(String.valueOf(entry.getValue())); | ||
|
||
// 3. value 값에 따라 DB에 어떻게 반영할지 결정하여 처리함 | ||
if (value == 1) { // 3-1. 좋아요를 추가한 상태였다면 RDB에 insert 쿼리 발생 | ||
User user = userRepository.getReferenceById(userId); | ||
Post post = postRepository.getReferenceById(postId); | ||
likeRepository.save(new Like(user, post)); | ||
}else if (value == -1) { // 3-2. 좋아요를 취소한 상태였다면 RDB에 delete 쿼리 발생 | ||
likeRepository.deleteByPostIdAndUserId(postId, userId); | ||
} | ||
} | ||
|
||
// 4. 해당 Key에 대해 RDB에 반영하는 과정을 마쳤으므로, | ||
likeRedisRepository.delete(key); | ||
public void sendRequest(Long userId, Long postId, String action) { | ||
try{ | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
String messageBody = objectMapper.writeValueAsString(Map.of( | ||
"userId", userId, | ||
"postId", postId, | ||
"action", action | ||
)); | ||
SendMessageRequest sendMsgRequest = SendMessageRequest.builder() | ||
.queueUrl(queueUrl) | ||
.messageBody(messageBody) | ||
.build(); | ||
|
||
logger.info("메시지 송신 - action: {}, user Id: {}, postId: {}", action, userId, postId); | ||
SendMessageResponse sendMsgResponse = sqsClient.sendMessage(sendMsgRequest); | ||
logger.info("메시지가 전달되었습니다: {}, Message ID: {}, HTTP Status: {}", | ||
messageBody, sendMsgResponse.messageId(), sendMsgResponse.sdkHttpResponse().statusCode()); | ||
} catch (Exception e) { | ||
logger.error("메시지 전송 실패", e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters