Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feat: 모집 중인 방 리스트 API 구현(#30) #68

Merged
merged 6 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions backend/src/main/java/corea/DataInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package corea;

import corea.matching.domain.Participation;
import corea.matching.repository.ParticipationRepository;
import corea.member.domain.Member;
import corea.member.repository.MemberRepository;
import corea.room.domain.Classification;
import corea.room.domain.Room;
import corea.room.domain.RoomStatus;
import corea.room.repository.RoomRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Profile("test")
@Component
@Transactional
@RequiredArgsConstructor
public class DataInitializer implements ApplicationRunner {
Comment on lines +20 to +24
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트를 위한 초기 데이터를 저장하는 클래스입니다.

해당 클래스는 테스트 패키지로 분리해도 좋다고 느껴졌지만,
나중에 프로덕션 코드에서도 활용 가능성이 있다 판단하여 그냥 두었습니다.😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에, 데이터 쌓이면
context.sql 로 데이터 넣고 clear 안하고 거기에 넣어도 괜찮을듯용


private final MemberRepository memberRepository;
private final RoomRepository roomRepository;
private final ParticipationRepository participationRepository;

@Override
public void run(ApplicationArguments args) {
Member member1 = memberRepository.save(
new Member("jcoding-play", null, "조경찬",
"[email protected]", true, 5f));
Member member2 = memberRepository.save(
new Member("ashsty", null, "박민아",
null, false, 1.5f));
Member member3 = memberRepository.save(
new Member("youngsu5582", null, "이영수",
null, false, 4f));
Member member4 = memberRepository.save(
new Member("hjk0761", null, "김현중",
null, true, 3f));
Member member5 = memberRepository.save(
new Member("chlwlstlf", null, "최진실",
null, true, 2f));
Member member6 = memberRepository.save(
new Member("00kang", null, "강다빈",
null, true, 1f));
Member member7 = memberRepository.save(
new Member("pp449", null, "이상엽",
"[email protected]", true, 4.8f));

Room room1 = roomRepository.save(
new Room("방 제목 1", "방 설명 1", 3,
null, null, "TDD",
1, 20, member1,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.BACKEND, RoomStatus.OPENED));
Room room2 = roomRepository.save(
new Room("방 제목 2", "방 설명 2", 3,
null, null, "TDD",
1, 20, member2,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.BACKEND, RoomStatus.OPENED));
Room room3 = roomRepository.save(
new Room("방 제목 3", "방 설명 3", 3,
null, null, "TDD",
1, 20, member3,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.ANDROID, RoomStatus.OPENED));
Room room4 = roomRepository.save(
new Room("방 제목 4", "방 설명 4", 3,
null, null, "TDD",
1, 20, member4,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.ANDROID, RoomStatus.OPENED));
Room room5 = roomRepository.save(
new Room("방 제목 5", "방 설명 5", 3,
null, null, "TDD",
1, 20, member5,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.FRONTEND, RoomStatus.OPENED));
Room room6 = roomRepository.save(
new Room("방 제목 6", "방 설명 6", 3,
null, null, "TDD",
1, 20, member6,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.FRONTEND, RoomStatus.OPENED));
Room room7 = roomRepository.save(
new Room("방 제목 7", "방 설명 7", 3,
null, null, "TDD",
1, 20, member7,
LocalDateTime.of(2024, 12, 25, 12, 0),
LocalDateTime.of(2024, 12, 30, 12, 0),
Classification.FRONTEND, RoomStatus.OPENED));

participationRepository.save(new Participation(room1.getId(), member2.getId()));
participationRepository.save(new Participation(room1.getId(), member3.getId()));

participationRepository.save(new Participation(room2.getId(), member3.getId()));
participationRepository.save(new Participation(room2.getId(), member4.getId()));

participationRepository.save(new Participation(room3.getId(), member4.getId()));
participationRepository.save(new Participation(room3.getId(), member5.getId()));

participationRepository.save(new Participation(room4.getId(), member5.getId()));
participationRepository.save(new Participation(room4.getId(), member6.getId()));

participationRepository.save(new Participation(room5.getId(), member6.getId()));
participationRepository.save(new Participation(room5.getId(), member7.getId()));

participationRepository.save(new Participation(room6.getId(), member1.getId()));
participationRepository.save(new Participation(room6.getId(), member7.getId()));

participationRepository.save(new Participation(room7.getId(), member1.getId()));
participationRepository.save(new Participation(room7.getId(), member2.getId()));
}
}
3 changes: 3 additions & 0 deletions backend/src/main/java/corea/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package corea;

import corea.auth.resolver.AccessedMemberArgumentResolver;
import corea.auth.resolver.LoginMemberArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
Expand All @@ -14,6 +15,7 @@
public class WebConfig implements WebMvcConfigurer {

private final LoginMemberArgumentResolver loginMemberArgumentResolver;
private final AccessedMemberArgumentResolver accessedMemberArgumentResolver;

@Override
public void addCorsMappings(CorsRegistry registry) {
Expand All @@ -27,5 +29,6 @@ public void addCorsMappings(CorsRegistry registry) {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginMemberArgumentResolver);
resolvers.add(accessedMemberArgumentResolver);
}
}
14 changes: 14 additions & 0 deletions backend/src/main/java/corea/auth/annotation/AccessedMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package corea.auth.annotation;

import io.swagger.v3.oas.annotations.Hidden;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Hidden()
public @interface AccessedMember {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package corea.auth.resolver;

import corea.auth.RequestHandler;
import corea.auth.annotation.AccessedMember;
import corea.member.domain.Member;
import corea.member.repository.MemberRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class AccessedMemberArgumentResolver implements HandlerMethodArgumentResolver {

private final RequestHandler requestHandler;
private final MemberRepository memberRepository;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(AccessedMember.class);
}

@Override
public Member resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();

return memberRepository.findByEmail(requestHandler.extract(request))
.orElse(null);
}
Comment on lines +28 to +35
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그인하지 않은 사용자도 방 리스트를 조회할 수 있기 때문에 findByEmail로 찾을 수 없는 경우 null을 반환하도록 두었습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이용

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

애초에 로그인했을 때/ 안 했을 때 구현 뷰가 달라지니까 필요한 부분이 맞긴 한듯요?

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ public boolean supportsParameter(MethodParameter parameter) {
}

@Override
public AuthInfo resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
public AuthInfo resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
Member member = memberRepository.findByEmail(requestHandler.extract(request))
.orElseThrow(()-> new CoreaException(ExceptionType.AUTHORIZATION_ERROR));

return AuthInfo.from(member);
}
}
19 changes: 15 additions & 4 deletions backend/src/main/java/corea/room/controller/RoomController.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package corea.room.controller;

import corea.auth.annotation.AccessedMember;
import corea.auth.annotation.LoginMember;
import corea.auth.domain.AuthInfo;
import corea.member.domain.Member;
import corea.room.dto.RoomResponse;
import corea.room.dto.RoomResponses;
import corea.room.service.RoomService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/rooms")
Expand All @@ -36,4 +35,16 @@ public ResponseEntity<RoomResponses> participatedRooms(@LoginMember AuthInfo aut
RoomResponses response = roomService.findParticipatedRooms(authInfo.getId());
return ResponseEntity.ok(response);
}

@GetMapping("/opened")
public ResponseEntity<RoomResponses> openedRooms(@AccessedMember Member member,
@RequestParam(value = "classification", defaultValue = "all") String expression,
@RequestParam(defaultValue = "0") int page) {
if (member == null) {
RoomResponses response = roomService.findOpenedRoomsWithoutMember(expression, page);
return ResponseEntity.ok(response);
}
RoomResponses response = roomService.findOpenedRoomsWithMember(member.getId(), expression, page);
return ResponseEntity.ok(response);
Comment on lines +39 to +48
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member가 null일 가능성이 있는데 Service layer까지 전달하기 싫어서 일단 controller에서 분기 처리하였습니다.

가독성이 안 좋은 것 같아 Service layer까지 전달하려고 생각 해보았는데, 모집 종료된 방 리스트 조회하는 기능까지 구현한 다음 중복 코드 제거하면서 더 고민해볼게요.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선은 controller 에서 잡아서 분기 처리하는게 베스트인듯?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분은 모집 종료 방 리스트 조회 기능 구현 이후 팔로업하겠습니당 👍

}
}
38 changes: 38 additions & 0 deletions backend/src/main/java/corea/room/domain/Classification.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package corea.room.domain;

import corea.exception.CoreaException;
import corea.exception.ExceptionType;

import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;

public enum Classification {

ALL("all"),
ANDROID("an"),
BACKEND("be"),
FRONTEND("fe");

private static final Map<String, Classification> CACHED_CLASSIFICATIONS = Arrays.stream(values())
.collect(toMap(classification -> classification.expression, Function.identity()));
Comment on lines +12 to +20
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classification을 조회하려는 경우가 많을 것 같아 미리 캐싱하여 사용하도록 해보았는데, 괜츈나요??

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

누군가의 발표가 생각나네요 👍


private final String expression;

Classification(String expression) {
this.expression = expression;
}

public static Classification from(String expression) {
if (CACHED_CLASSIFICATIONS.containsKey(expression)) {
return CACHED_CLASSIFICATIONS.get(expression);
}
throw new CoreaException(ExceptionType.NOT_FOUND_ERROR);
}

public boolean isAll() {
return this == ALL;
}
}
20 changes: 17 additions & 3 deletions backend/src/main/java/corea/room/domain/Room.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,29 @@ public class Room {
private int limitedParticipantsSize;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "manager_id",foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@JoinColumn(name = "manager_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Member manager;

private LocalDateTime recruitmentDeadline;

private LocalDateTime reviewDeadline;

public Room(String title, String content, int matchingSize, String repositoryLink, String thumbnailLink, String keyword, int currentParticipantsSize, int limitedParticipantsSize, Member manager, LocalDateTime recruitmentDeadline, LocalDateTime reviewDeadline) {
this(null, title, content, matchingSize, repositoryLink, thumbnailLink, keyword, currentParticipantsSize, limitedParticipantsSize, manager, recruitmentDeadline, reviewDeadline);
@Enumerated(value = EnumType.STRING)
private Classification classification;

/**
* RoomStatus가 변경될 수 있는 경우 (OPENED -> CLOSED)
* 1. 방장이 모집 마감을 한 경우
* 2. 제한 인원이 다 찼을 경우 (방에 참여할 때 같이 검증)
* 3. 모집 기간이 끝난 경우
*
* 1, 2의 경우 때문에 방 상태를 가지는 필드를 가져야 될듯.
* **/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Enumerated(value = EnumType.STRING)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

괜찮은듯.
방장이 시간 전 종료하고 싶을 수도 있으니

private RoomStatus status;

public Room(String title, String content, int matchingSize, String repositoryLink, String thumbnailLink, String keyword, int currentParticipantsSize, int limitedParticipantsSize, Member manager, LocalDateTime recruitmentDeadline, LocalDateTime reviewDeadline, Classification classification, RoomStatus status) {
this(null, title, content, matchingSize, repositoryLink, thumbnailLink, keyword, currentParticipantsSize, limitedParticipantsSize, manager, recruitmentDeadline, reviewDeadline, classification, status);
}
}

6 changes: 6 additions & 0 deletions backend/src/main/java/corea/room/domain/RoomStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package corea.room.domain;

public enum RoomStatus {

OPENED, CLOSED
}
9 changes: 7 additions & 2 deletions backend/src/main/java/corea/room/dto/RoomCreateRequest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package corea.room.dto;

import corea.member.domain.Member;
import corea.room.domain.Classification;
import corea.room.domain.Room;
import corea.room.domain.RoomStatus;

import java.time.LocalDateTime;

Expand All @@ -16,13 +18,16 @@ public record RoomCreateRequest(
int currentParticipantsSize,
int limitedParticipantsSize,
LocalDateTime recruitmentDeadline,
LocalDateTime reviewDeadline
LocalDateTime reviewDeadline,
Classification classification,
RoomStatus status
) {

public Room toEntity() {
return new Room(title, content, matchingSize,
repositoryLink, thumbnailLink, keyword,
currentParticipantsSize, limitedParticipantsSize, manager,
recruitmentDeadline, reviewDeadline);
recruitmentDeadline, reviewDeadline, classification,
status);
}
}
5 changes: 5 additions & 0 deletions backend/src/main/java/corea/room/dto/RoomResponses.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package corea.room.dto;

import corea.room.domain.Room;
import org.springframework.data.domain.Page;

import java.util.List;

Expand All @@ -14,4 +15,8 @@ public static RoomResponses from(List<Room> rooms) {
.map(RoomResponse::from)
.collect(collectingAndThen(toList(), RoomResponses::new));
}

public static RoomResponses from(Page<Room> roomsWithPage) {
return from(roomsWithPage.getContent());
}
}
Loading
Loading