Skip to content

Commit 3f8c493

Browse files
authored
refactor: 소식지 단일 좋아요 여부 확인 API 제거 (#455)
* refactor: 소식지 목록 조회 시 isLike 추가 - 로그인하지 않은 사용자에게는 isLike를 응답하지 않도록 * refactor: 소식지 목록 조회 시 좋아요 여부 확인 로직 추가 - 로그인한 사용자와 로그인하지 않은 사용자를 구분 - @Authorizeduser(required = false) 활용 - Set을 활용하여 n + 1 문제 해결 * test: 소식지 관련 fixture 추가 * test: 소식지 조회 서비스 테스트 추가 * refactor: 사용하지 않는 api 제거 * refactor: 쿼리 2 -> 1로 개선 - jpql 프로젝션 활용 * style: isLike -> isLiked로 변경 * test: 검증 구체화 - 사이즈가 아닌 실제 id가 존재하는지 확인하도록 * refactor: from -> of로 변경
1 parent f4d7fd8 commit 3f8c493

File tree

12 files changed

+186
-75
lines changed

12 files changed

+186
-75
lines changed

src/main/java/com/example/solidconnection/news/controller/NewsController.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.example.solidconnection.news.controller;
22

33
import com.example.solidconnection.common.resolver.AuthorizedUser;
4-
import com.example.solidconnection.news.dto.LikedNewsResponse;
54
import com.example.solidconnection.news.dto.NewsCommandResponse;
65
import com.example.solidconnection.news.dto.NewsCreateRequest;
76
import com.example.solidconnection.news.dto.NewsListResponse;
@@ -37,9 +36,10 @@ public class NewsController {
3736
// todo: 추후 Slice 적용
3837
@GetMapping
3938
public ResponseEntity<NewsListResponse> findNewsBySiteUserId(
40-
@RequestParam(value = "site-user-id") Long siteUserId
39+
@AuthorizedUser(required = false) Long siteUserId,
40+
@RequestParam(value = "author-id") Long authorId
4141
) {
42-
NewsListResponse newsListResponse = newsQueryService.findNewsBySiteUserId(siteUserId);
42+
NewsListResponse newsListResponse = newsQueryService.findNewsByAuthorId(siteUserId, authorId);
4343
return ResponseEntity.ok(newsListResponse);
4444
}
4545

@@ -80,15 +80,6 @@ public ResponseEntity<NewsCommandResponse> deleteNewsById(
8080
return ResponseEntity.ok(newsCommandResponse);
8181
}
8282

83-
@GetMapping("/{news-id}/like")
84-
public ResponseEntity<LikedNewsResponse> isNewsLiked(
85-
@AuthorizedUser long siteUserId,
86-
@PathVariable("news-id") Long newsId
87-
) {
88-
LikedNewsResponse likedNewsResponse = newsLikeService.isNewsLiked(siteUserId, newsId);
89-
return ResponseEntity.ok(likedNewsResponse);
90-
}
91-
9283
@PostMapping("/{news-id}/like")
9384
public ResponseEntity<Void> addNewsLike(
9485
@AuthorizedUser long siteUserId,

src/main/java/com/example/solidconnection/news/dto/LikedNewsResponse.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/main/java/com/example/solidconnection/news/dto/NewsResponse.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.example.solidconnection.news.dto;
22

3+
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
4+
35
import com.example.solidconnection.news.domain.News;
6+
import com.fasterxml.jackson.annotation.JsonInclude;
47
import java.time.ZonedDateTime;
58

69
public record NewsResponse(
@@ -9,16 +12,21 @@ public record NewsResponse(
912
String description,
1013
String thumbnailUrl,
1114
String url,
15+
16+
@JsonInclude(NON_NULL)
17+
Boolean isLiked,
18+
1219
ZonedDateTime updatedAt
1320
) {
1421

15-
public static NewsResponse from(News news) {
22+
public static NewsResponse of(News news, Boolean isLiked) {
1623
return new NewsResponse(
1724
news.getId(),
1825
news.getTitle(),
1926
news.getDescription(),
2027
news.getThumbnailUrl(),
2128
news.getUrl(),
29+
isLiked,
2230
news.getUpdatedAt()
2331
);
2432
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.example.solidconnection.news.repository;
22

33
import com.example.solidconnection.news.domain.News;
4+
import com.example.solidconnection.news.repository.custom.NewsCustomRepository;
45
import java.util.List;
56
import org.springframework.data.jpa.repository.JpaRepository;
67

7-
public interface NewsRepository extends JpaRepository<News, Long> {
8+
public interface NewsRepository extends JpaRepository<News, Long>, NewsCustomRepository {
89

910
List<News> findAllBySiteUserIdOrderByUpdatedAtDesc(long siteUserId);
1011
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.example.solidconnection.news.repository.custom;
2+
3+
import com.example.solidconnection.news.dto.NewsResponse;
4+
import java.util.List;
5+
6+
public interface NewsCustomRepository {
7+
8+
List<NewsResponse> findNewsByAuthorIdWithLikeStatus(long authorId, Long siteUserId);
9+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.example.solidconnection.news.repository.custom;
2+
3+
import com.example.solidconnection.news.dto.NewsResponse;
4+
import jakarta.persistence.EntityManager;
5+
import java.util.List;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.stereotype.Repository;
8+
9+
@Repository
10+
@RequiredArgsConstructor
11+
public class NewsCustomRepositoryImpl implements NewsCustomRepository {
12+
13+
private final EntityManager entityManager;
14+
15+
@Override
16+
public List<NewsResponse> findNewsByAuthorIdWithLikeStatus(long authorId, Long siteUserId) {
17+
String jpql = """
18+
SELECT new com.example.solidconnection.news.dto.NewsResponse(
19+
n.id,
20+
n.title,
21+
n.description,
22+
n.thumbnailUrl,
23+
n.url,
24+
CASE WHEN ln.id IS NOT NULL THEN true ELSE false END,
25+
n.updatedAt
26+
)
27+
FROM News n
28+
LEFT JOIN LikedNews ln ON n.id = ln.newsId AND ln.siteUserId = :siteUserId
29+
WHERE n.siteUserId = :authorId
30+
ORDER BY n.updatedAt DESC
31+
""";
32+
33+
return entityManager.createQuery(jpql, NewsResponse.class)
34+
.setParameter("authorId", authorId)
35+
.setParameter("siteUserId", siteUserId)
36+
.getResultList();
37+
}
38+
}

src/main/java/com/example/solidconnection/news/service/NewsLikeService.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import com.example.solidconnection.common.exception.CustomException;
88
import com.example.solidconnection.news.domain.LikedNews;
9-
import com.example.solidconnection.news.dto.LikedNewsResponse;
109
import com.example.solidconnection.news.repository.LikedNewsRepository;
1110
import com.example.solidconnection.news.repository.NewsRepository;
1211
import lombok.RequiredArgsConstructor;
@@ -20,15 +19,6 @@ public class NewsLikeService {
2019
private final NewsRepository newsRepository;
2120
private final LikedNewsRepository likedNewsRepository;
2221

23-
@Transactional(readOnly = true)
24-
public LikedNewsResponse isNewsLiked(long siteUserId, long newsId) {
25-
if (!newsRepository.existsById(newsId)) {
26-
throw new CustomException(NEWS_NOT_FOUND);
27-
}
28-
boolean isLike = likedNewsRepository.existsByNewsIdAndSiteUserId(newsId, siteUserId);
29-
return LikedNewsResponse.of(isLike);
30-
}
31-
3222
@Transactional
3323
public void addNewsLike(long siteUserId, long newsId) {
3424
if (!newsRepository.existsById(newsId)) {

src/main/java/com/example/solidconnection/news/service/NewsQueryService.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.example.solidconnection.news.service;
22

3-
import com.example.solidconnection.news.domain.News;
43
import com.example.solidconnection.news.dto.NewsListResponse;
54
import com.example.solidconnection.news.dto.NewsResponse;
65
import com.example.solidconnection.news.repository.NewsRepository;
@@ -16,11 +15,19 @@ public class NewsQueryService {
1615
private final NewsRepository newsRepository;
1716

1817
@Transactional(readOnly = true)
19-
public NewsListResponse findNewsBySiteUserId(long siteUserId) {
20-
List<News> newsList = newsRepository.findAllBySiteUserIdOrderByUpdatedAtDesc(siteUserId);
21-
List<NewsResponse> newsResponseList = newsList.stream()
22-
.map(NewsResponse::from)
23-
.toList();
18+
public NewsListResponse findNewsByAuthorId(Long siteUserId, long authorId) {
19+
// 로그인하지 않은 경우
20+
if (siteUserId == null) {
21+
List<NewsResponse> newsResponseList = newsRepository.findAllBySiteUserIdOrderByUpdatedAtDesc(authorId)
22+
.stream()
23+
.map(news -> NewsResponse.of(news, null))
24+
.toList();
25+
return NewsListResponse.from(newsResponseList);
26+
}
27+
28+
// 로그인한 경우
29+
List<NewsResponse> newsResponseList = newsRepository.findNewsByAuthorIdWithLikeStatus(authorId, siteUserId);
30+
2431
return NewsListResponse.from(newsResponseList);
2532
}
2633
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.solidconnection.news.fixture;
2+
3+
import com.example.solidconnection.news.domain.LikedNews;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.boot.test.context.TestComponent;
6+
7+
@TestComponent
8+
@RequiredArgsConstructor
9+
public class LikedNewsFixture {
10+
11+
private final LikedNewsFixtureBuilder likedNewsFixtureBuilder;
12+
13+
public LikedNews 소식지_좋아요(long newsId, long siteUserId) {
14+
return likedNewsFixtureBuilder.likedNews()
15+
.newsId(newsId)
16+
.siteUserId(siteUserId)
17+
.create();
18+
}
19+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.example.solidconnection.news.fixture;
2+
3+
import com.example.solidconnection.news.domain.LikedNews;
4+
import com.example.solidconnection.news.repository.LikedNewsRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.boot.test.context.TestComponent;
7+
8+
@TestComponent
9+
@RequiredArgsConstructor
10+
public class LikedNewsFixtureBuilder {
11+
12+
private final LikedNewsRepository likedNewsRepository;
13+
14+
private long newsId;
15+
16+
private long siteUserId;
17+
18+
public LikedNewsFixtureBuilder likedNews() {
19+
return new LikedNewsFixtureBuilder(likedNewsRepository);
20+
}
21+
22+
public LikedNewsFixtureBuilder newsId(long newsId) {
23+
this.newsId = newsId;
24+
return this;
25+
}
26+
27+
public LikedNewsFixtureBuilder siteUserId(long siteUserId) {
28+
this.siteUserId = siteUserId;
29+
return this;
30+
}
31+
32+
public LikedNews create() {
33+
LikedNews likedNews = new LikedNews(newsId, siteUserId);
34+
return likedNewsRepository.save(likedNews);
35+
}
36+
}

0 commit comments

Comments
 (0)