Skip to content

Commit 86fb601

Browse files
authored
Merge pull request #112 from prgrms-web-devcourse-final-project/dev
Dev
2 parents 09fc7ad + 1e422b5 commit 86fb601

5 files changed

Lines changed: 157 additions & 122 deletions

File tree

src/main/java/com/grepp/teamnotfound/app/model/board/ArticleService.java

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.grepp.teamnotfound.app.model.board.code.ProfileBoardType;
1111
import com.grepp.teamnotfound.app.model.board.code.SortType;
1212
import com.grepp.teamnotfound.app.model.board.dto.ArticleListDto;
13+
import com.grepp.teamnotfound.app.model.board.dto.LikeCheckDto;
1314
import com.grepp.teamnotfound.app.model.board.dto.UserArticleListDto;
1415
import com.grepp.teamnotfound.app.model.board.entity.Article;
1516
import com.grepp.teamnotfound.app.model.board.entity.ArticleImg;
@@ -37,13 +38,17 @@
3738
import java.util.List;
3839
import java.util.Optional;
3940
import lombok.RequiredArgsConstructor;
41+
import lombok.extern.slf4j.Slf4j;
4042
import org.springframework.data.domain.Page;
43+
import org.springframework.data.redis.RedisConnectionFailureException;
44+
import org.springframework.data.redis.RedisSystemException;
4145
import org.springframework.stereotype.Service;
4246
import org.springframework.transaction.annotation.Transactional;
4347
import org.springframework.web.multipart.MultipartFile;
4448

4549
@Service
4650
@RequiredArgsConstructor
51+
@Slf4j
4752
public class ArticleService {
4853

4954
private final ArticleRepository articleRepository;
@@ -125,8 +130,8 @@ public ArticleDetailResponse findByArticleIdAndUserId(Long articleId, Long userI
125130
ArticleDetailResponse response = articleRepository.findDetailById(articleId, userId);
126131

127132
// Redis 카운터 캐시 값으로 덮어씀
128-
Integer redisLikeCount = getArticleLikeCount(articleId);
129-
response.setLikes(redisLikeCount);
133+
response.setLikes(getArticleLikeCount(articleId));
134+
response.setIsLiked(getUserLiked(articleId, userId));
130135

131136
articleRepository.plusViewById(articleId);
132137
response.setViews(response.getViews() + 1);
@@ -162,7 +167,6 @@ public void updateArticle(Long articleId, ArticleRequest request, List<Multipart
162167
beforeArticle.setUpdatedAt(OffsetDateTime.now());
163168
Article savedArticle = articleRepository.save(beforeArticle);
164169

165-
// TODO 스토리지에 저장된 불필요한 사진 파일들에 대한 배치 스케줄러 도입 필요
166170
articleImgRepository.softDeleteByArticleId(articleId, OffsetDateTime.now());
167171
uploadAndSaveImgs(images, savedArticle);
168172
}
@@ -203,7 +207,6 @@ private void uploadAndSaveImgs(List<MultipartFile> images, Article targetArticle
203207
.stream()
204208
.map(fileDto -> {
205209
ImgType type = ImgType.DESC;
206-
// NOTE 게시글을 작성할 때 썸네일을 설정할 수 있게? 아니면 첫번째 이미지?
207210
if (imgList.getFirst().originName().equals(fileDto.originName())) {
208211
type = ImgType.THUMBNAIL;
209212
}
@@ -274,22 +277,37 @@ public LikeResponse likeWithRedis(Long articleId, Long userId) {
274277
Article article = articleRepository.findById(articleId)
275278
.orElseThrow(() -> new BoardException(BoardErrorCode.ARTICLE_NOT_FOUND));
276279

277-
// Redis 캐시를 우선적으로 확인(빠른 응답)
278-
// 중복 요청이면 무시
279-
boolean isLikedInRedis = redisLikeService.isUserLikedInRedis(articleId, userId);
280-
if (isLikedInRedis) {
280+
try {
281+
boolean isLikedInRedis = getUserLiked(articleId, userId);
282+
if (isLikedInRedis) { // 이미 좋아요 상태
283+
Integer totalCount = getArticleLikeCount(articleId);
284+
return new LikeResponse(articleId, totalCount, true);
285+
}
286+
287+
redisLikeService.addLikeRequest(articleId, userId);
288+
redisLikeService.setUserLikedStatus(articleId, userId, true);
289+
redisLikeService.incrementArticleLikesCount(articleId); // 게시글 총 좋아요 카운터 증가
290+
281291
Integer totalCount = getArticleLikeCount(articleId);
292+
boolean isLiked = getUserLiked(articleId, userId);
293+
294+
return new LikeResponse(articleId, totalCount, isLiked);
295+
} catch (RedisConnectionFailureException | RedisSystemException e) {
296+
log.warn("[Redis fallback] Like Request - articleId: {}, userId: {}", articleId, userId, e);
297+
298+
boolean alreadyLiked = articleLikeRepository.existsByArticle_ArticleIdAndUser_UserId(articleId, userId);
299+
if (!alreadyLiked) { // 중복 확인
300+
ArticleLike like = ArticleLike.builder()
301+
.article(article)
302+
.user(user)
303+
.createdAt(OffsetDateTime.now())
304+
.build();
305+
articleLikeRepository.save(like);
306+
}
307+
308+
Integer totalCount = articleLikeRepository.countByArticle_ArticleId(articleId);
282309
return new LikeResponse(articleId, totalCount, true);
283310
}
284-
285-
redisLikeService.addLikeRequest(articleId, userId);
286-
redisLikeService.setUserLikedStatus(articleId, userId, true);
287-
redisLikeService.incrementArticleLikesCount(articleId); // 게시글 총 좋아요 카운터 증가
288-
289-
Integer totalCount = getArticleLikeCount(articleId);
290-
boolean isLiked = redisLikeService.isUserLikedInRedis(articleId, userId);
291-
292-
return new LikeResponse(articleId, totalCount, isLiked);
293311
}
294312

295313
@Transactional
@@ -299,27 +317,34 @@ public LikeResponse unlikeWithRedis(Long articleId, Long userId) {
299317
Article article = articleRepository.findById(articleId)
300318
.orElseThrow(() -> new BoardException(BoardErrorCode.ARTICLE_NOT_FOUND));
301319

302-
// Redis 캐시를 우선적으로 확인(빠른 응답)
303-
// 중복 요청이면 무시
304-
boolean isLikedInRedis = redisLikeService.isUserLikedInRedis(articleId, userId);
305-
if (!isLikedInRedis) {
320+
try {
321+
boolean isLikedInRedis = getUserLiked(articleId, userId);
322+
if (!isLikedInRedis) { // 이미 좋아요 취소
323+
Integer totalCount = getArticleLikeCount(articleId);
324+
return new LikeResponse(articleId, totalCount, false);
325+
}
326+
327+
redisLikeService.addUnlikeRequest(articleId, userId);
328+
redisLikeService.setUserLikedStatus(articleId, userId, false);
329+
redisLikeService.decrementArticleLikesCount(articleId);
330+
306331
Integer totalCount = getArticleLikeCount(articleId);
307-
return new LikeResponse(articleId, totalCount, false);
308-
}
332+
boolean isLiked = getUserLiked(articleId, userId);
309333

310-
redisLikeService.addUnlikeRequest(articleId, userId);
311-
redisLikeService.setUserLikedStatus(articleId, userId, false);
312-
redisLikeService.decrementArticleLikesCount(articleId);
334+
return new LikeResponse(articleId, totalCount, isLiked);
335+
} catch (RedisConnectionFailureException | RedisSystemException e) {
336+
log.warn("[Redis fallback] Unlike Request - articleId: {}, userId: {}", articleId, userId);
313337

314-
Integer totalCount = getArticleLikeCount(articleId);
315-
boolean isLiked = redisLikeService.isUserLikedInRedis(articleId, userId);
338+
Optional<ArticleLike> like = articleLikeRepository.findByArticle_ArticleIdAndUser_UserId(articleId, userId);
339+
like.ifPresent(articleLikeRepository::delete);
316340

317-
return new LikeResponse(articleId, totalCount, isLiked);
341+
Integer totalCount = articleLikeRepository.countByArticle_ArticleId(articleId);
342+
return new LikeResponse(articleId, totalCount, false);
343+
}
318344
}
319345

320346
// 캐시에서 좋아요 수 먼저 조회
321347
private Integer getArticleLikeCount(Long articleId) {
322-
// NOTE 게시글 리스트 조회에서도 실시간 좋아요 수가 필요할까?
323348
Long count = redisLikeService.getArticleLikesCount(articleId);
324349

325350
if (count == null) {
@@ -333,6 +358,16 @@ private Integer getArticleLikeCount(Long articleId) {
333358
return count.intValue();
334359
}
335360

361+
// 캐시에서 좋아요 상태 먼저 조회
362+
private boolean getUserLiked(Long articleId, Long userId) {
363+
if (userId == null) return false;
364+
LikeCheckDto checkDto = redisLikeService.isUserLikedInRedis(articleId, userId);
365+
if (!checkDto.isRedisAvailable()) {
366+
return articleLikeRepository.existsByArticle_ArticleIdAndUser_UserId(articleId, userId);
367+
}
368+
return checkDto.isLiked();
369+
}
370+
336371
// DB 에서 댓글 수 조회
337372
@Transactional(readOnly = true)
338373
public Integer getReplyCount(Long articleId) {
@@ -365,7 +400,7 @@ public LikeResponse getNewestLikeCount(Long articleId, Long userId) {
365400
likesCount = redisLikeService.getArticleLikesCount(articleId);
366401

367402
if (userId != null) {
368-
isLiked = redisLikeService.isUserLikedInRedis(articleId, userId);
403+
isLiked = getUserLiked(articleId, userId);
369404
}
370405

371406
// DB 에서 조회가 필요한 경우

0 commit comments

Comments
 (0)