Skip to content

Commit

Permalink
[DDING-65] Feed 조회 API 구현 및 커서 기반 페이지네이션 적용 (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
KoSeonJe authored Jan 12, 2025
1 parent 15ae86a commit 329b585
Show file tree
Hide file tree
Showing 22 changed files with 692 additions and 324 deletions.
49 changes: 28 additions & 21 deletions src/main/java/ddingdong/ddingdongBE/domain/feed/api/FeedApi.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
package ddingdong.ddingdongBE.domain.feed.api;

import ddingdong.ddingdongBE.domain.feed.controller.dto.response.FeedListResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.ClubFeedPageResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.FeedResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.NewestFeedListResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.NewestFeedPerClubPageResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;

@Tag(name = "Feed - User", description = "Feed API")
@RequestMapping("/server")
public interface FeedApi {

@Operation(summary = "동아리 피드 전체 조회 API")
@ApiResponse(responseCode = "200", description = "동아리 피드 전체 조회 성공",
content = @Content(schema = @Schema(implementation = FeedListResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/clubs/{clubId}/feeds")
List<FeedListResponse> getAllFeedByClubId(@PathVariable Long clubId);
@Operation(summary = "특정 동아리 피드 페이지 조회 API")
@ApiResponse(responseCode = "200", description = "특정 동아리 피드 페이지 조회 성공",
content = @Content(schema = @Schema(implementation = ClubFeedPageResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/clubs/{clubId}/feeds")
ClubFeedPageResponse getFeedPageByClub(
@PathVariable("clubId") Long clubId,
@RequestParam(value = "size", defaultValue = "9") int size,
@RequestParam(value = "currentCursorId", defaultValue = "-1") Long currentCursorId
);

@Operation(summary = "전체 동아리 최신 피드 조회 API")
@ApiResponse(responseCode = "200", description = "전체 동아리 최신 피드 조회 성공",
content = @Content(schema = @Schema(implementation = NewestFeedListResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/feeds")
List<NewestFeedListResponse> getNewestAllFeed();
@Operation(summary = "모든 동아리 최신 피드 페이지 조회 API")
@ApiResponse(responseCode = "200", description = "모든 동아리 최신 피드 페이지 조회 성공",
content = @Content(schema = @Schema(implementation = NewestFeedPerClubPageResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/feeds")
NewestFeedPerClubPageResponse getNewestFeedPerClub(
@RequestParam(value = "size", defaultValue = "9") int size,
@RequestParam(value = "currentCursorId", defaultValue = "-1") Long currentCursorId
);

@Operation(summary = "동아리 피드 상세 조회 API")
@ApiResponse(responseCode = "200", description = "동아리 피드 상세 조회 API",
content = @Content(schema = @Schema(implementation = FeedResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/feeds/{feedId}")
FeedResponse getByFeedId(@PathVariable("feedId") Long feedId);
@Operation(summary = "동아리 피드 상세 조회 API")
@ApiResponse(responseCode = "200", description = "동아리 피드 상세 조회 API",
content = @Content(schema = @Schema(implementation = FeedResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/feeds/{feedId}")
FeedResponse getByFeedId(@PathVariable("feedId") Long feedId);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package ddingdong.ddingdongBE.domain.feed.controller;

import ddingdong.ddingdongBE.domain.feed.api.FeedApi;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.FeedListResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.ClubFeedPageResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.FeedResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.NewestFeedListResponse;
import ddingdong.ddingdongBE.domain.feed.controller.dto.response.NewestFeedPerClubPageResponse;
import ddingdong.ddingdongBE.domain.feed.service.FacadeFeedService;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubFeedPageQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedQuery;
import java.util.List;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.NewestFeedPerClubPageQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -18,19 +18,22 @@ public class FeedController implements FeedApi {
private final FacadeFeedService facadeFeedService;

@Override
public List<FeedListResponse> getAllFeedByClubId(Long clubId) {
List<FeedListQuery> feedListQueries = facadeFeedService.getAllByClubId(clubId);
return feedListQueries.stream()
.map(FeedListResponse::from)
.toList();
public ClubFeedPageResponse getFeedPageByClub(
Long clubId,
int size,
Long currentCursorId
) {
ClubFeedPageQuery clubFeedPageQuery = facadeFeedService.getFeedPageByClub(clubId, size, currentCursorId);
return ClubFeedPageResponse.from(clubFeedPageQuery);
}

@Override
public List<NewestFeedListResponse> getNewestAllFeed() {
List<FeedListQuery> newestFeedListQueries = facadeFeedService.getNewestAll();
return newestFeedListQueries.stream()
.map(NewestFeedListResponse::from)
.toList();
public NewestFeedPerClubPageResponse getNewestFeedPerClub(
int size,
Long currentCursorId
) {
NewestFeedPerClubPageQuery newestFeedPerClubPageQuery = facadeFeedService.getNewestFeedPerClubPage(size, currentCursorId);
return NewestFeedPerClubPageResponse.from(newestFeedPerClubPageQuery);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ddingdong.ddingdongBE.domain.feed.controller.dto.response;

import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubFeedPageQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Builder;

public record ClubFeedPageResponse(
@ArraySchema(schema = @Schema(name = "동아리 피드 정보", implementation = ClubFeedListResponse.class))
List<ClubFeedListResponse> clubFeeds,
@Schema(name = "피드 페이지 정보", implementation = PagingResponse.class)
PagingResponse pagingInfo
) {

public static ClubFeedPageResponse from(ClubFeedPageQuery clubFeedPageQuery) {
List<ClubFeedListResponse> clubFeeds = clubFeedPageQuery.feedListQueries().stream()
.map(ClubFeedListResponse::from)
.toList();
return new ClubFeedPageResponse(clubFeeds, PagingResponse.from(clubFeedPageQuery.pagingQuery()));
}

@Builder
record ClubFeedListResponse(
@Schema(description = "피드 ID", example = "1")
Long id,
@Schema(description = "피드 썸네일 CDN URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailCdnUrl,
@Schema(description = "피드 썸네일 S3 URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailOriginUrl,
@Schema(description = "피드 타입", example = "IMAGE")
String feedType
) {

public static ClubFeedListResponse from(FeedListQuery feedListQuery) {
return ClubFeedListResponse.builder()
.id(feedListQuery.id())
.thumbnailCdnUrl(feedListQuery.thumbnailCdnUrl())
.thumbnailOriginUrl(feedListQuery.thumbnailOriginUrl())
.feedType(feedListQuery.feedType())
.build();
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.ClubProfileQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedFileUrlQuery;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import lombok.Builder;
Expand All @@ -10,44 +11,68 @@
public record FeedResponse(
@Schema(description = "피드 ID", example = "1")
Long id,
@Schema(description = "동아리 정보")
ClubProfileResponse clubProfile,
@Schema(description = "활동 내용", example = "안녕하세요. 카우 피드에요")
String activityContent,
@Schema(description = "CDN URL", example = "https://example.cloudfront.net")
String fileUrl,
@Schema(description = "피드 유형", example = "IMAGE")
String feedType,
@Schema(description = "생성 날짜", example = "2024-08-31")
LocalDate createdDate
) {

@Builder
record ClubProfileResponse(
@Schema(description = "동아리 ID", example = "1")
Long id,
@Schema(description = "동아리 이름", example = "카우")
String name,
@Schema(description = "동아리 프로필 이미지 url", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String profileImageUrl
) {
public static ClubProfileResponse from(ClubProfileQuery query) {
return ClubProfileResponse.builder()
.id(query.id())
.name(query.name())
.profileImageUrl(query.profileImageUrl())
.build();
LocalDate createdDate,
@Schema(description = "URL 정보", implementation = FileUrlResponse.class)
FileUrlResponse fileUrls,
@Schema(description = "동아리 정보")
ClubProfileResponse clubProfile
) {

@Builder
record ClubProfileResponse(
@Schema(description = "동아리 ID", example = "1")
Long id,
@Schema(description = "동아리 이름", example = "카우")
String name,
@Schema(description = "동아리 프로필 이미지 url", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String profileImageOriginUrl,
@Schema(description = "동아리 프로필 이미지 url", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String profileImageCdnUrl

) {

public static ClubProfileResponse from(ClubProfileQuery query) {
return ClubProfileResponse.builder()
.id(query.id())
.name(query.name())
.profileImageCdnUrl(query.profileImageCdnUrl())
.profileImageOriginUrl(query.profileImageOriginUrl())
.build();
}
}

@Builder
record FileUrlResponse(
@Schema(description = "파일 식별자", example = "0192c828-ffce-7ee8-94a8-d9d4c8cdec00")
String id,
@Schema(description = "원본 url", example = "url")
String originUrl,
@Schema(description = "cdn url", example = "url")
String cdnUrl
) {

public static FileUrlResponse from(FeedFileUrlQuery feedFileUrlQuery) {
return FileUrlResponse.builder()
.id(feedFileUrlQuery.id())
.originUrl(feedFileUrlQuery.originUrl())
.cdnUrl(feedFileUrlQuery.cdnUrl())
.build();
}
}

public static FeedResponse from(FeedQuery query) {
return FeedResponse.builder()
.id(query.id())
.clubProfile(ClubProfileResponse.from(query.clubProfileQuery()))
.activityContent(query.activityContent())
.fileUrls(FileUrlResponse.from(query.feedFileUrlQuery()))
.feedType(query.feedType())
.createdDate(query.createdDate())
.build();
}
}

public static FeedResponse from(FeedQuery info) {
return FeedResponse.builder()
.id(info.id())
.clubProfile(ClubProfileResponse.from(info.clubProfileQuery()))
.activityContent(info.activityContent())
.fileUrl(info.fileUrl())
.feedType(info.feedType())
.createdDate(info.createdDate())
.build();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ddingdong.ddingdongBE.domain.feed.controller.dto.response;

import ddingdong.ddingdongBE.domain.feed.service.dto.query.FeedListQuery;
import ddingdong.ddingdongBE.domain.feed.service.dto.query.NewestFeedPerClubPageQuery;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Builder;

public record NewestFeedPerClubPageResponse(
@ArraySchema(schema = @Schema(name = "동아리 최신 피드 정보", implementation = NewestFeedListResponse.class))
List<NewestFeedListResponse> newestFeeds,
@Schema(name = "피드 페이지 정보", implementation = PagingResponse.class)
PagingResponse pagingInfo
) {

public static NewestFeedPerClubPageResponse from(NewestFeedPerClubPageQuery newestFeedPerClubPageQuery) {
List<NewestFeedListResponse> newestFeeds = newestFeedPerClubPageQuery.feedListQueries().stream()
.map(NewestFeedListResponse::from)
.toList();
return new NewestFeedPerClubPageResponse(newestFeeds,
PagingResponse.from(newestFeedPerClubPageQuery.pagingQuery()));
}

@Builder
public record NewestFeedListResponse(
@Schema(description = "피드 ID", example = "1")
Long id,
@Schema(description = "피드 썸네일 CDN URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailCdnUrl,
@Schema(description = "피드 썸네일 S3 URL", example = "https://%s.s3.%s.amazonaws.com/%s/%s/%s")
String thumbnailOriginUrl,
@Schema(description = "피드 타입", example = "IMAGE")
String feedType
) {

public static NewestFeedListResponse from(FeedListQuery query) {
return NewestFeedListResponse.builder()
.id(query.id())
.thumbnailOriginUrl(query.thumbnailOriginUrl())
.thumbnailCdnUrl(query.thumbnailCdnUrl())
.feedType(query.feedType())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ddingdong.ddingdongBE.domain.feed.controller.dto.response;

import ddingdong.ddingdongBE.domain.feed.service.dto.query.PagingQuery;
import io.swagger.v3.oas.annotations.media.Schema;

public record PagingResponse(
@Schema(name = "현재 커서 id", description = "9")
Long currentCursorId,
@Schema(name = "다음 커서 id", description = "10")
Long nextCursorId,
@Schema(name = "다음 커서 존재 여부", description = "true")
boolean hasNext
) {

public static PagingResponse from(PagingQuery pagingQuery) {
return new PagingResponse(pagingQuery.currentCursorId(), pagingQuery.nextCursorId(), pagingQuery.hasNext());
}
}
Loading

0 comments on commit 329b585

Please sign in to comment.