11package sevenstar .marineleisure .meeting .controller ;
22
33import java .util .List ;
4+ import java .util .Map ;
5+ import java .util .Set ;
46import java .util .stream .Collectors ;
57
68import org .springframework .data .domain .Slice ;
2729import sevenstar .marineleisure .meeting .dto .mapper .CustomSlicePageResponse ;
2830import sevenstar .marineleisure .meeting .dto .request .CreateMeetingRequest ;
2931import sevenstar .marineleisure .meeting .dto .request .UpdateMeetingRequest ;
32+ import sevenstar .marineleisure .meeting .dto .response .GoingMeetingResponse ;
3033import sevenstar .marineleisure .meeting .dto .response .MeetingDetailAndMemberResponse ;
3134import sevenstar .marineleisure .meeting .dto .response .MeetingDetailResponse ;
3235import sevenstar .marineleisure .meeting .dto .response .MeetingListResponse ;
@@ -59,21 +62,53 @@ public ResponseEntity<BaseResponse<CustomSlicePageResponse<MeetingListResponse>>
5962 @ RequestParam (name = "size" , defaultValue = "10" ) Integer size
6063 ) {
6164 Slice <Meeting > not_mapping_result = meetingService .getAllMeetings (cursorId , size );
62- List <MeetingListResponse > dtoList = not_mapping_result .getContent ().stream ()
63- //TODO :: 개선예정
65+ List <Meeting > meetingList = not_mapping_result .getContent ();
66+
67+ // 🚀 Map Batch 최적화로 N+1 문제 해결! (5개 쿼리만)
68+ // 1. 모든 ID 수집
69+ Set <Long > hostIds = meetingList .stream ().map (Meeting ::getHostId ).collect (Collectors .toSet ());
70+ Set <Long > spotIds = meetingList .stream ().map (Meeting ::getSpotId ).collect (Collectors .toSet ());
71+ List <Long > meetingIds = meetingList .stream ().map (Meeting ::getId ).collect (Collectors .toList ());
72+
73+ // 2. Batch 조회 (5개 쿼리만!)
74+ Map <Long , Member > hostMap = memberRepository .findAllById (hostIds )
75+ .stream ().collect (Collectors .toMap (Member ::getId , m -> m ));
76+
77+ Map <Long , OutdoorSpot > spotMap = outdoorSpotRepository .findAllById (spotIds )
78+ .stream ().collect (Collectors .toMap (OutdoorSpot ::getId , s -> s ));
79+
80+ Map <Long , Tag > tagMap = tagRepository .findByMeetingIdIn (meetingIds )
81+ .stream ().collect (Collectors .toMap (Tag ::getMeetingId , t -> t ));
82+
83+ Map <Long , Long > participantCountMap = participantRepository .countByMeetingIdIn (meetingIds )
84+ .stream ().collect (Collectors .toMap (
85+ result -> (Long ) result [0 ], // meetingId
86+ result -> (Long ) result [1 ] // count
87+ ));
88+
89+ // 3. 메모리에서 조합 (추가 쿼리 없음!)
90+ List <MeetingListResponse > dtoList = meetingList .stream ()
6491 .map (meeting -> {
65- Member host = memberRepository .findById (meeting .getHostId ())
66- .orElseThrow (() -> new RuntimeException ("Host not found for meeting id: " + meeting .getId ()));
67- OutdoorSpot spot = outdoorSpotRepository .findById (meeting .getSpotId ())
68- .orElseThrow (() -> new RuntimeException ("Spot not found for meeting id: " + meeting .getId ()));
69- Tag tag = tagRepository .findByMeetingId (meeting .getId ())
70- .orElseThrow (() -> new CustomException (MeetingError .MEETING_NOT_FOUND ));
71- long participantCount = participantRepository .countMeetingId (meeting .getId ())
72- .map (Integer ::longValue )
73- .orElse (0L );
92+ Member host = hostMap .get (meeting .getHostId ());
93+ OutdoorSpot spot = spotMap .get (meeting .getSpotId ());
94+ Tag tag = tagMap .get (meeting .getId ());
95+ Long participantCount = participantCountMap .getOrDefault (meeting .getId (), 0L );
96+
97+ // Null 체크 (기존 예외 처리 유지)
98+ if (host == null ) {
99+ throw new RuntimeException ("Host not found for meeting id: " + meeting .getId ());
100+ }
101+ if (spot == null ) {
102+ throw new RuntimeException ("Spot not found for meeting id: " + meeting .getId ());
103+ }
104+ if (tag == null ) {
105+ throw new CustomException (MeetingError .MEETING_NOT_FOUND );
106+ }
107+
74108 return MeetingListResponse .fromEntity (meeting , host , participantCount , spot , tag );
75109 })
76110 .collect (Collectors .toList ());
111+
77112 Long nextCursorId = null ;
78113 if (not_mapping_result .hasNext () && !not_mapping_result .getContent ().isEmpty ()) {
79114 Meeting lastMeetingInSlice = not_mapping_result .getContent ().get (size - 1 );
@@ -88,6 +123,7 @@ public ResponseEntity<BaseResponse<CustomSlicePageResponse<MeetingListResponse>>
88123 );
89124 return BaseResponse .success (result_Mapping );
90125 }
126+
91127 @ GetMapping ("/meetings/{id}" )
92128 public ResponseEntity <BaseResponse <MeetingDetailResponse >> getMeetingDetail (
93129 @ PathVariable ("id" ) Long meetingId
@@ -97,26 +133,57 @@ public ResponseEntity<BaseResponse<MeetingDetailResponse>> getMeetingDetail(
97133 @ GetMapping ("/meetings/my" )
98134 public ResponseEntity <BaseResponse <CustomSlicePageResponse <MeetingListResponse >>> getStatusListMeeting (
99135 @ RequestParam (name = "status" ,defaultValue = "RECRUITING" ) MeetingStatus status ,
100- @ RequestParam (name = "role" ,defaultValue = "HOST " ) MeetingRole role ,
136+ @ RequestParam (name = "role" ,defaultValue = "GUEST " ) MeetingRole role ,
101137 @ RequestParam (name = "cursorId" , defaultValue = "0" ) Long cursorId ,
102138 @ RequestParam (name = "size" , defaultValue = "10" ) Integer size ,
103139 @ AuthenticationPrincipal UserPrincipal userDetails
104140 ){
105141
106142 Long memberId = userDetails .getId ();
107143 Slice <Meeting > not_mapping_result = meetingService .getStatusMyMeetings_role (memberId ,role ,cursorId ,size ,status );
108- List <MeetingListResponse > dtoList = not_mapping_result .getContent ().stream ()
109- //TODO :: 개선예정
144+ List <Meeting > meetingList = not_mapping_result .getContent ();
145+
146+ // 🚀 Map Batch 최적화로 N+1 문제 해결! (5개 쿼리만)
147+ // 1. 모든 ID 수집
148+ Set <Long > hostIds = meetingList .stream ().map (Meeting ::getHostId ).collect (Collectors .toSet ());
149+ Set <Long > spotIds = meetingList .stream ().map (Meeting ::getSpotId ).collect (Collectors .toSet ());
150+ List <Long > meetingIds = meetingList .stream ().map (Meeting ::getId ).collect (Collectors .toList ());
151+
152+ // 2. Batch 조회 (5개 쿼리만!)
153+ Map <Long , Member > hostMap = memberRepository .findAllById (hostIds )
154+ .stream ().collect (Collectors .toMap (Member ::getId , m -> m ));
155+
156+ Map <Long , OutdoorSpot > spotMap = outdoorSpotRepository .findAllById (spotIds )
157+ .stream ().collect (Collectors .toMap (OutdoorSpot ::getId , s -> s ));
158+
159+ Map <Long , Tag > tagMap = tagRepository .findByMeetingIdIn (meetingIds )
160+ .stream ().collect (Collectors .toMap (Tag ::getMeetingId , t -> t ));
161+
162+ Map <Long , Long > participantCountMap = participantRepository .countByMeetingIdIn (meetingIds )
163+ .stream ().collect (Collectors .toMap (
164+ result -> (Long ) result [0 ], // meetingId
165+ result -> (Long ) result [1 ] // count
166+ ));
167+
168+ // 3. 메모리에서 조합 (추가 쿼리 없음!)
169+ List <MeetingListResponse > dtoList = meetingList .stream ()
110170 .map (meeting -> {
111- Member host = memberRepository .findById (meeting .getHostId ())
112- .orElseThrow (() -> new RuntimeException ("Host not found for meeting id: " + meeting .getId ()));
113- OutdoorSpot spot = outdoorSpotRepository .findById (meeting .getSpotId ())
114- .orElseThrow (() -> new RuntimeException ("Spot not found for meeting id: " + meeting .getId ()));
115- Tag tag = tagRepository .findByMeetingId (meeting .getId ())
116- .orElseThrow (() -> new CustomException (MeetingError .MEETING_NOT_FOUND ));
117- long participantCount = participantRepository .countMeetingId (meeting .getId ())
118- .map (Integer ::longValue )
119- .orElse (0L );
171+ Member host = hostMap .get (meeting .getHostId ());
172+ OutdoorSpot spot = spotMap .get (meeting .getSpotId ());
173+ Tag tag = tagMap .get (meeting .getId ());
174+ Long participantCount = participantCountMap .getOrDefault (meeting .getId (), 0L );
175+
176+ // Null 체크 (기존 예외 처리 유지)
177+ if (host == null ) {
178+ throw new RuntimeException ("Host not found for meeting id: " + meeting .getId ());
179+ }
180+ if (spot == null ) {
181+ throw new RuntimeException ("Spot not found for meeting id: " + meeting .getId ());
182+ }
183+ if (tag == null ) {
184+ throw new CustomException (MeetingError .MEETING_NOT_FOUND );
185+ }
186+
120187 return MeetingListResponse .fromEntity (meeting , host , participantCount , spot , tag );
121188 })
122189 .collect (Collectors .toList ());
@@ -187,5 +254,24 @@ public ResponseEntity<BaseResponse<Long>> updateMeeting(
187254 return BaseResponse .success (meetingService .updateMeeting (meetingId , memberId , request ));
188255 }
189256
257+ @ PostMapping ("/meetings/{id}/going" )
258+ public ResponseEntity <BaseResponse <GoingMeetingResponse >> goingMeeting (
259+ @ PathVariable ("id" ) Long meetingId ,
260+ @ AuthenticationPrincipal UserPrincipal userDetails
261+ ){
262+ Long memberId = userDetails .getId ();
263+ return BaseResponse .success (meetingService .goingMeeting (meetingId , memberId ));
264+ }
265+
266+ @ DeleteMapping ("/meetings/{id}" )
267+ public ResponseEntity <BaseResponse <String >> deleteMeeting (
268+ @ PathVariable ("id" ) Long meetingId ,
269+ @ AuthenticationPrincipal UserPrincipal userDetails
270+ ){
271+ Long memberId = userDetails .getId ();
272+ meetingService .deleteMeeting (memberId , meetingId );
273+ return BaseResponse .success (HttpStatus .NO_CONTENT , "success" );
274+ }
275+
190276
191277}
0 commit comments