diff --git a/build.gradle b/build.gradle index c7517c1..e034941 100644 --- a/build.gradle +++ b/build.gradle @@ -53,8 +53,8 @@ dependencies { //jackson -// implementation 'com.fasterxml.jackson.core:jackson-databind' -// implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingApi.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingApi.java index 788c870..59344bc 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingApi.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingApi.java @@ -5,26 +5,26 @@ 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 team_alcoholic.jumo_server.domain.meeting.dto.MeetingDto; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResponseDto; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingResDto; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResDto; @Tag(name = "모임 API", description = "모임에 대한 CRUD를 수행하는 API입니다.") public interface MeetingApi { @Operation(summary = "id에 해당하는 모임 상세정보를 응답하는 API입니다.") @ApiResponse( - responseCode = "200", - description = "모임 조회 성공", - content = @Content(schema = @Schema(implementation = MeetingDto.class)) + responseCode = "200", + description = "모임 조회 성공", + content = @Content(schema = @Schema(implementation = MeetingResDto.class)) ) - public MeetingDto getMeetingById(Long id); + public MeetingResDto getMeetingById(Long id); @Operation(summary = "모임 목록에 대한 pagination API입니다. 정렬방식, 개수, 커서를 지정할 수 있습니다.") @ApiResponse( responseCode = "200", description = "모임 목록 조회 성공", - content = @Content(schema = @Schema(implementation = MeetingListResponseDto.class)) + content = @Content(schema = @Schema(implementation = MeetingListResDto.class)) ) - public MeetingListResponseDto getLatestMeetingList(String sort, int limit, Long cursor); + public MeetingListResDto getLatestMeetingList(String sort, int limit, Long cursor); } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingController.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingController.java index af4c375..bd687de 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingController.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/controller/MeetingController.java @@ -1,10 +1,9 @@ package team_alcoholic.jumo_server.domain.meeting.controller; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingDto; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResponseDto; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingResDto; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResDto; import team_alcoholic.jumo_server.domain.meeting.service.MeetingService; @RestController @@ -15,12 +14,12 @@ public class MeetingController implements MeetingApi { private final MeetingService meetingService; @GetMapping("{id}") - public MeetingDto getMeetingById(@PathVariable("id") Long id) { + public MeetingResDto getMeetingById(@PathVariable("id") Long id) { return meetingService.findMeetingById(id); } @GetMapping() - public MeetingListResponseDto getLatestMeetingList( + public MeetingListResDto getLatestMeetingList( @RequestParam(required = false, defaultValue = "latest") String sort, @RequestParam(required = false, defaultValue = "30") int limit, @RequestParam(required = false, defaultValue = "0") Long cursor diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/Meeting.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/Meeting.java index e46a268..ab71a84 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/Meeting.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/Meeting.java @@ -2,16 +2,18 @@ import jakarta.persistence.*; import lombok.Getter; +import team_alcoholic.jumo_server.domain.region.domain.Region; import team_alcoholic.jumo_server.global.common.domain.BaseTimeEntity; import java.time.LocalDateTime; +import java.util.List; @Entity @Getter public class Meeting extends BaseTimeEntity { @Id - @GeneratedValue(strategy= GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String uuid; @@ -20,7 +22,11 @@ public class Meeting extends BaseTimeEntity { private String status; private LocalDateTime meetingAt; private LocalDateTime fixAt; - private String region; + + @ManyToOne + @JoinColumn(name = "region", referencedColumnName = "admcd") + private Region region; + private String place; private String liquors; private Integer participatesMin; @@ -34,4 +40,7 @@ public class Meeting extends BaseTimeEntity { private String thumbnailImage; private String externalService; private String externalLink; + + @OneToMany(mappedBy = "meeting") + private List images; } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/MeetingImage.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/MeetingImage.java index 13ae1d5..8508912 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/MeetingImage.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/domain/MeetingImage.java @@ -1,9 +1,6 @@ package team_alcoholic.jumo_server.domain.meeting.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; import lombok.Getter; import team_alcoholic.jumo_server.global.common.domain.BaseTimeEntity; @@ -14,6 +11,8 @@ public class MeetingImage extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private Long meeting; + @ManyToOne + @JoinColumn(name = "meeting", nullable = false) + private Meeting meeting; private String url; } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListDto.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListDto.java index e4b1688..248e890 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListDto.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListDto.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import team_alcoholic.jumo_server.domain.meeting.domain.Meeting; +import team_alcoholic.jumo_server.domain.region.domain.Region; import java.time.LocalDateTime; @@ -14,20 +15,20 @@ public class MeetingListDto { private Long id; private String uuid; -// private String host; + // private String host; private String name; private String status; private LocalDateTime meetingAt; private LocalDateTime fixAt; private String region; -// private String place; + // private String place; private String liquors; private Integer participatesMin; private Integer participatesMax; private Integer payment; -// private String paymentMethod; + // private String paymentMethod; private boolean byob; -// private Integer byobMin; + // private Integer byobMin; // private Integer byobMax; // private String description; private String thumbnail; @@ -41,7 +42,10 @@ public MeetingListDto(Meeting meeting) { this.status = meeting.getStatus(); this.meetingAt = meeting.getMeetingAt(); this.fixAt = meeting.getFixAt(); - this.region = meeting.getRegion(); + if (meeting.getRegion() != null) { + this.region = meeting.getRegion().getAdmnm(); + } + this.liquors = meeting.getLiquors(); this.participatesMin = meeting.getParticipatesMin(); this.participatesMax = meeting.getParticipatesMax(); @@ -50,4 +54,6 @@ public MeetingListDto(Meeting meeting) { this.thumbnail = meeting.getThumbnailImage(); this.externalService = meeting.getExternalService(); } + + } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResponseDto.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResDto.java similarity index 88% rename from src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResponseDto.java rename to src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResDto.java index f5c8b7d..07ba02f 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResponseDto.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingListResDto.java @@ -9,7 +9,7 @@ @Getter @Setter @AllArgsConstructor -public class MeetingListResponseDto { +public class MeetingListResDto { private List meetings; private Long lastId; private boolean eof; diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingDto.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingResDto.java similarity index 84% rename from src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingDto.java rename to src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingResDto.java index c355b5f..6b5e3a1 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingDto.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/dto/MeetingResDto.java @@ -3,13 +3,14 @@ import lombok.Getter; import lombok.Setter; import team_alcoholic.jumo_server.domain.meeting.domain.Meeting; +import team_alcoholic.jumo_server.domain.meeting.domain.MeetingImage; import java.time.LocalDateTime; import java.util.List; @Getter @Setter -public class MeetingDto { +public class MeetingResDto { private Long id; private String uuid; @@ -34,7 +35,8 @@ public class MeetingDto { private String externalLink; private List images; - public MeetingDto(Meeting meeting, List images) { + + public MeetingResDto(Meeting meeting) { this.id = meeting.getId(); this.uuid = meeting.getUuid(); this.host = meeting.getHost(); @@ -42,7 +44,9 @@ public MeetingDto(Meeting meeting, List images) { this.status = meeting.getStatus(); this.meetingAt = meeting.getMeetingAt(); this.fixAt = meeting.getFixAt(); - this.region = meeting.getRegion(); + if (meeting.getRegion() != null) { + this.region = meeting.getRegion().getAdmnm(); + } this.place = meeting.getPlace(); this.liquors = meeting.getLiquors(); this.participatesMin = meeting.getParticipatesMin(); @@ -56,6 +60,6 @@ public MeetingDto(Meeting meeting, List images) { this.thumbnail = meeting.getThumbnailImage(); this.externalService = meeting.getExternalService(); this.externalLink = meeting.getExternalLink(); - this.images = images; + this.images = meeting.getImages().stream().map(MeetingImage::getUrl).toList(); } } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/JpaMeetingRepository.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/JpaMeetingRepository.java deleted file mode 100644 index 8a377b6..0000000 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/JpaMeetingRepository.java +++ /dev/null @@ -1,45 +0,0 @@ -package team_alcoholic.jumo_server.domain.meeting.repository; - -import jakarta.persistence.EntityManager; -import org.springframework.stereotype.Repository; -import team_alcoholic.jumo_server.domain.meeting.domain.Meeting; -import team_alcoholic.jumo_server.domain.meeting.domain.MeetingImage; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingDto; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListDto; - -import java.util.List; -import java.util.stream.Collectors; - -@Repository -public class JpaMeetingRepository implements MeetingRepository{ - - private final EntityManager em; - public JpaMeetingRepository(EntityManager em) { this.em = em;} - - @Override - public MeetingDto findMeetingById(Long id) { - List imageList = em.createQuery("select image from MeetingImage image where image.meeting=:id", MeetingImage.class) - .setParameter("id", id) - .getResultList(); - List images = imageList.stream().map(MeetingImage::getUrl).collect(Collectors.toList()); - Meeting meeting = em.createQuery("select meeting from Meeting meeting where meeting.id=:id", Meeting.class) - .setParameter("id", id) - .getSingleResult(); - return new MeetingDto(meeting, images); - } - - @Override - public List findLatestMeetingList(int limit) { - return em.createQuery("select new team_alcoholic.jumo_server.domain.meeting.dto.MeetingListDto(m.id, m.uuid, m.name, m.status, m.meetingAt, m.fixAt, r.admnm, m.liquors, m.participatesMin, m.participatesMax, m.payment, m.byob, m.thumbnailImage, m.externalService) from Meeting m, Region r where m.region=r.admcd order by m.id desc limit :limit", MeetingListDto.class) - .setParameter("limit", limit) - .getResultList(); - } - - @Override - public List findLatestMeetingListById(int limit, Long cursor) { - return em.createQuery("select new team_alcoholic.jumo_server.domain.meeting.dto.MeetingListDto(m.id, m.uuid, m.name, m.status, m.meetingAt, m.fixAt, r.admnm, m.liquors, m.participatesMin, m.participatesMax, m.payment, m.byob, m.thumbnailImage, m.externalService) from Meeting m, Region r where m.region=r.admcd and m.id<:cursor order by m.id desc limit :limit", MeetingListDto.class) - .setParameter("limit", limit) - .setParameter("cursor", cursor) - .getResultList(); - } -} diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/MeetingRepository.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/MeetingRepository.java index 0c18090..c4be195 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/MeetingRepository.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/repository/MeetingRepository.java @@ -1,12 +1,21 @@ package team_alcoholic.jumo_server.domain.meeting.repository; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingDto; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListDto; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import team_alcoholic.jumo_server.domain.meeting.domain.Meeting; + import java.util.List; -public interface MeetingRepository { - MeetingDto findMeetingById(Long id); - List findLatestMeetingList(int limit); - List findLatestMeetingListById(int limit, Long cursor); +public interface MeetingRepository extends JpaRepository { + + // 특정 ID의 Meeting 조회 + @EntityGraph(attributePaths = "images") + Meeting findMeetingById(Long id); + + // 특정 ID보다 작은 Meeting 목록 조회 + // n+1 문제 해결을 위해 EntityGraph 사용 + @EntityGraph(attributePaths = "region") + List findByIdLessThanOrderByIdDesc(Long cursor, Pageable pageable); } diff --git a/src/main/java/team_alcoholic/jumo_server/domain/meeting/service/MeetingService.java b/src/main/java/team_alcoholic/jumo_server/domain/meeting/service/MeetingService.java index 964559a..ef36d41 100644 --- a/src/main/java/team_alcoholic/jumo_server/domain/meeting/service/MeetingService.java +++ b/src/main/java/team_alcoholic/jumo_server/domain/meeting/service/MeetingService.java @@ -1,14 +1,15 @@ package team_alcoholic.jumo_server.domain.meeting.service; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingDto; +import org.springframework.transaction.annotation.Transactional; +import team_alcoholic.jumo_server.domain.meeting.domain.Meeting; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingResDto; import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListDto; import team_alcoholic.jumo_server.domain.meeting.repository.MeetingRepository; -import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResponseDto; -import team_alcoholic.jumo_server.domain.region.domain.Region; -import team_alcoholic.jumo_server.domain.region.repository.RegionRepository; +import team_alcoholic.jumo_server.domain.meeting.dto.MeetingListResDto; import java.util.List; @@ -17,21 +18,31 @@ public class MeetingService { private final MeetingRepository meetingRepository; - private final RegionRepository regionRepository; - public MeetingDto findMeetingById(Long id) { - MeetingDto meeting = meetingRepository.findMeetingById(id); - Region region = regionRepository.findByAdmcd(meeting.getRegion()); - meeting.setRegion(region.getAdmnm()); - return meeting; + public MeetingResDto findMeetingById(Long id) { + Meeting meeting = meetingRepository.findMeetingById(id); + return new MeetingResDto(meeting); } - public MeetingListResponseDto findLatestMeetingList(int limit, Long cursor) { - List meetings; - if (cursor==0) meetings = meetingRepository.findLatestMeetingList(limit+1); - else meetings = meetingRepository.findLatestMeetingListById(limit+1, cursor); - boolean eof = (meetings.size() meetings; + Pageable pageable = PageRequest.of(0, limit + 1); + + // cursor가 0이면 Long.MAX_VALUE를 사용 + Long effectiveCursor = (cursor == 0) ? Long.MAX_VALUE : cursor; + + meetings = meetingRepository.findByIdLessThanOrderByIdDesc(effectiveCursor, pageable); + + boolean eof = (meetings.size() < limit + 1); + if (!eof) { + meetings.remove(meetings.size() - 1); + } + + List meetingList = meetings.stream().map(MeetingListDto::new).toList(); + return new MeetingListResDto(meetingList, meetings.get(meetings.size() - 1).getId(), eof); } + } + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 15a6d65..28ea9bd 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -3,7 +3,7 @@ - + @@ -16,9 +16,9 @@ - - - + + + ${name}