Skip to content

Commit 3083664

Browse files
authored
fix : 파티 기능 성능 향상 (#55)
* fix : 파티 기능 캐싱 적용 * fix : 인덱싱 적용 * fix : n+1 문제 해결
1 parent 60927d2 commit 3083664

File tree

4 files changed

+43
-12
lines changed

4 files changed

+43
-12
lines changed

backend/src/main/java/com/back/domain/party/party/entity/PartyMember.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
@Getter
1111
@Setter
1212
@IdClass(PartyMemberId.class)
13+
@Table(name = "party_member", indexes = {
14+
@Index(name = "idx_party_member_composite", columnList = "party_id, member_id"),
15+
@Index(name = "idx_party_status", columnList = "party_id, status")
16+
})
1317
public class PartyMember {
1418

1519
@Id

backend/src/main/java/com/back/domain/party/party/repository/PartyMemberRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ public interface PartyMemberRepository extends JpaRepository<PartyMember, PartyM
2626

2727
// 특정 파티에서 가장 오래된 가입자를 찾는 메서드
2828
Optional<PartyMember> findFirstByParty_IdOrderByJoinedAtAsc(Integer partyId);
29+
30+
// 파티원 수 조회용 메서드 추가
31+
long countByParty_IdAndStatus(Integer partyId, PartyMemberStatus status);
32+
2933
}

backend/src/main/java/com/back/domain/party/party/repository/PartyRepository.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.domain.party.party.repository;
22

33
import com.back.domain.party.party.entity.Party;
4+
import org.springframework.data.jpa.repository.EntityGraph;
45
import org.springframework.data.jpa.repository.JpaRepository;
56
import org.springframework.stereotype.Repository;
67

@@ -17,8 +18,13 @@ public interface PartyRepository extends JpaRepository<Party, Integer> {
1718
Optional<Party> findByLeader_Id(Integer leaderId);
1819

1920
// 공개 파티를 조회하는 메서드
21+
@EntityGraph(attributePaths = "leader")
2022
List<Party> findByIsPublic(boolean isPublic);
2123

24+
@Override
25+
@EntityGraph(attributePaths = {"leader", "partyMembers.member"})
26+
Optional<Party> findById(Integer partyId);
27+
2228
// 특정 미션을 진행하는 파티를 찾는 메서드 (미션 도메인 완성 시 사용)
2329
// Optional<Party> findByMission_Id(Integer missionId);
2430
}

backend/src/main/java/com/back/domain/party/party/service/PartyService.java

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
import com.back.global.exception.CustomException;
1313
import com.back.global.exception.ErrorCode;
1414
import lombok.RequiredArgsConstructor;
15+
import org.springframework.cache.annotation.CacheEvict;
16+
import org.springframework.cache.annotation.Cacheable;
1517
import org.springframework.stereotype.Service;
1618
import org.springframework.transaction.annotation.Transactional;
1719

1820
import java.time.LocalDateTime;
1921
import java.util.Comparator;
2022
import java.util.List;
23+
import java.util.Objects;
2124

2225
@Service
2326
@RequiredArgsConstructor
@@ -29,6 +32,7 @@ public class PartyService {
2932
private final PartyMemberRepository partyMemberRepository;
3033

3134
@Transactional
35+
@CacheEvict(value = {"partyList", "partyDetails"}, allEntries = true)
3236
public Party createParty(PartyRequestDto requestDto, Integer memberId) {
3337
Member leader = memberRepository.findById(memberId)
3438
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
@@ -51,6 +55,7 @@ public Party createParty(PartyRequestDto requestDto, Integer memberId) {
5155
}
5256

5357
@Transactional
58+
@CacheEvict(value = {"partyList", "partyDetails"}, allEntries = true)
5459
public void joinParty(Integer partyId, Integer memberId) {
5560
Party party = partyRepository.findById(partyId)
5661
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
@@ -68,7 +73,8 @@ public void joinParty(Integer partyId, Integer memberId) {
6873
}
6974

7075
// 3. 파티의 정원이 가득 찼는지 확인
71-
if (party.getPartyMembers().stream().filter(pm -> pm.getStatus() == PartyMemberStatus.ACCEPTED).count() >= party.getMaxMembers()) {
76+
long acceptedMembers = partyMemberRepository.countByParty_IdAndStatus(partyId, PartyMemberStatus.ACCEPTED);
77+
if (acceptedMembers >= party.getMaxMembers()) {
7278
throw new CustomException(ErrorCode.CONFLICT, "파티의 정원이 가득 찼습니다.");
7379
}
7480

@@ -82,17 +88,18 @@ public void joinParty(Integer partyId, Integer memberId) {
8288
}
8389

8490
@Transactional
91+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
8592
public void leaveParty(Integer partyId, Integer memberId) {
8693
PartyMember leavingMember = partyMemberRepository.findByParty_IdAndMember_Id(partyId, memberId)
8794
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
8895

8996
Party party = leavingMember.getParty();
9097

9198
// 파티장인지 확인
92-
if (party.getLeader().getId() == memberId) {
99+
if (Objects.equals(party.getLeader().getId(), memberId)) {
93100
// 본인을 제외한 모든 멤버를 가입일 순으로 정렬하여 조회
94101
List<PartyMember> otherMembers = partyMemberRepository.findByParty_Id(partyId);
95-
otherMembers.removeIf(pm -> pm.getMember().getId() == memberId);
102+
otherMembers.removeIf(pm -> Objects.equals(pm.getMember().getId(), memberId));
96103
otherMembers.sort(Comparator.comparing(PartyMember::getJoinedAt));
97104

98105
// 남은 멤버가 있다면 새로운 파티장 위임
@@ -110,12 +117,13 @@ public void leaveParty(Integer partyId, Integer memberId) {
110117
}
111118

112119
@Transactional
120+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
113121
public void updateParty(Integer partyId, PartyUpdateRequestDto requestDto, Integer memberId) {
114122
Party party = partyRepository.findById(partyId)
115123
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
116124

117125
// 요청한 멤버가 파티장이 맞는지 확인
118-
if (party.getLeader().getId() != memberId) {
126+
if (!Objects.equals(party.getLeader().getId(), memberId)) {
119127
throw new CustomException(ErrorCode.UNAUTHORIZED, "파티 수정 권한이 없습니다.");
120128
}
121129

@@ -136,12 +144,13 @@ public void updateParty(Integer partyId, PartyUpdateRequestDto requestDto, Integ
136144
}
137145

138146
@Transactional
147+
@CacheEvict(value = {"partyList", "partyDetails"}, allEntries = true)
139148
public void deleteParty(Integer partyId, Integer memberId) {
140149
Party party = partyRepository.findById(partyId)
141150
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
142151

143152
// 요청한 멤버가 파티장이 맞는지 확인
144-
if (party.getLeader().getId() != memberId) {
153+
if (!Objects.equals(party.getLeader().getId(), memberId)) {
145154
throw new CustomException(ErrorCode.UNAUTHORIZED, "파티 삭제 권한이 없습니다.");
146155
}
147156

@@ -153,27 +162,32 @@ public void deleteParty(Integer partyId, Integer memberId) {
153162
partyRepository.deleteById(partyId);
154163
}
155164

165+
@Cacheable(value = "partyList", key = "'all'")
156166
@Transactional(readOnly = true)
157167
public List<Party> getPartyList() {
158168
return partyRepository.findByIsPublic(true);
159169
}
160170

171+
@Cacheable(value = "partyDetails", key = "#partyId")
161172
@Transactional(readOnly = true)
162173
public Party getPartyDetails(Integer partyId) {
163174
return partyRepository.findById(partyId)
164175
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "해당 파티를 찾을 수 없습니다."));
165176
}
166177

167178
@Transactional
179+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
168180
public void inviteMember(Integer partyId, Integer leaderId, String invitedMemberCode) {
169181
Party party = partyRepository.findById(partyId)
170182
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
171183

172-
if (party.getLeader().getId() != leaderId) {
184+
if (!Objects.equals(party.getLeader().getId(), leaderId)) {
173185
throw new CustomException(ErrorCode.UNAUTHORIZED, "파티 초대 권한이 없습니다.");
174186
}
175187

176-
if (party.getPartyMembers().stream().filter(pm -> pm.getStatus() == PartyMemberStatus.ACCEPTED).count() >= party.getMaxMembers()) {
188+
// 정원 확인 로직을 개선된 쿼리로 변경
189+
long acceptedMembers = partyMemberRepository.countByParty_IdAndStatus(partyId, PartyMemberStatus.ACCEPTED);
190+
if (acceptedMembers >= party.getMaxMembers()) {
177191
throw new CustomException(ErrorCode.CONFLICT, "파티의 정원이 가득 찼습니다.");
178192
}
179193

@@ -194,6 +208,7 @@ public void inviteMember(Integer partyId, Integer leaderId, String invitedMember
194208
}
195209

196210
@Transactional
211+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
197212
public void acceptInvitation(Integer partyId, Integer memberId) {
198213
PartyMember partyMember = partyMemberRepository.findByParty_IdAndMember_Id(partyId, memberId)
199214
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "초대 정보를 찾을 수 없습니다."));
@@ -203,16 +218,16 @@ public void acceptInvitation(Integer partyId, Integer memberId) {
203218
}
204219

205220
// 파티 정원 확인
206-
if (partyMember.getParty().getPartyMembers().stream()
207-
.filter(pm -> pm.getStatus() == PartyMemberStatus.ACCEPTED)
208-
.count() >= partyMember.getParty().getMaxMembers()) {
221+
long acceptedMembers = partyMemberRepository.countByParty_IdAndStatus(partyMember.getParty().getId(), PartyMemberStatus.ACCEPTED);
222+
if (acceptedMembers >= partyMember.getParty().getMaxMembers()) {
209223
throw new CustomException(ErrorCode.CONFLICT, "파티의 정원이 가득 찼습니다.");
210224
}
211225

212226
partyMember.setStatus(PartyMemberStatus.ACCEPTED);
213227
}
214228

215229
@Transactional
230+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
216231
public void rejectInvitation(Integer partyId, Integer memberId) {
217232
PartyMember partyMember = partyMemberRepository.findByParty_IdAndMember_Id(partyId, memberId)
218233
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "초대 정보를 찾을 수 없습니다."));
@@ -225,12 +240,13 @@ public void rejectInvitation(Integer partyId, Integer memberId) {
225240
}
226241

227242
@Transactional
243+
@CacheEvict(value = {"partyList", "partyDetails"}, key = "#partyId")
228244
public void kickMember(Integer partyId, Integer leaderId, Integer kickedMemberId) {
229245
Party party = partyRepository.findById(partyId)
230246
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "파티를 찾을 수 없습니다."));
231247

232248
// 1. 추방을 요청한 멤버가 파티장인지 확인
233-
if (party.getLeader().getId() != leaderId) {
249+
if (!Objects.equals(party.getLeader().getId(), leaderId)) {
234250
throw new CustomException(ErrorCode.UNAUTHORIZED, "파티원 추방 권한이 없습니다.");
235251
}
236252

@@ -252,12 +268,13 @@ public void kickMember(Integer partyId, Integer leaderId, Integer kickedMemberId
252268
partyMemberRepository.delete(kickedMember);
253269
}
254270

271+
@Cacheable(value = "pendingRequests", key = "#partyId")
255272
@Transactional(readOnly = true)
256273
public List<PartyMember> getPendingJoinRequests(Integer partyId, Integer leaderId) {
257274
Party party = partyRepository.findById(partyId)
258275
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "파티를 찾을 수 없습니다."));
259276

260-
if (party.getLeader().getId() != leaderId) {
277+
if (!Objects.equals(party.getLeader().getId(), leaderId)) {
261278
throw new CustomException(ErrorCode.UNAUTHORIZED, "권한이 없습니다.");
262279
}
263280

0 commit comments

Comments
 (0)