본문 바로가기
Project/깃터디 (gitudy)

[깃터디/Member] 스터디장 가입 신청 승인/거부 api 구현

by J-rain 2024. 5. 11.
💡 .java를 클릭시 관련 커밋으로 이동💡

 

 회고 

      	// 신청대기중인 유저가 아닌경우 예외처리
        if (applyMember.getStatus() != StudyMemberStatus.STUDY_WAITING) {
            log.warn(">>>> {} : {} <<<<", applyUserId, ExceptionMessage.USER_NOT_STUDY_MEMBER);
            throw new MemberException(ExceptionMessage.USER_NOT_STUDY_MEMBER);
        }

Java에서는 enum 유형을 비교할 때 == 연산자나 != 연산자를 사용할 수 있다. 이는 enum 인스턴스가 고유하며 싱글턴처럼 동작하기 때문인데 즉, StudyMemberStatus.STUDY_WAITINGStudyMemberStatus 열거형에서 유일한 인스턴스이기 때문이다.

 

그럼 equals 메서드는 언제쓸까??

equals 메소드는 객체 내용의 동등성을 비교할 때 사용된다. 두 객체가 같은 타입이며, 그 내용이 동일한지 확인할 때 equals()를 사용한다. 예를 들어, 두 개의 String 객체가 동일한 문자열을 담고 있는지 확인하거나, 두 개의 컬렉션 객체가 같은 요소를 담고 있는지 확인할 때 equals()를 사용한다. 결론적으로, equals() 메소드는 주로 객체의 내용이 같은지를 비교할 때 사용되며, 주로 복잡한 데이터 타입이나 사용자 정의 클래스 객체 사이의 비교에 사용된다. 반면, enum 같은 경우는 ==로 비교하는 것이 일반적!!

 

 

StudyMemberController.java

    // 스터디장의 가입 신청 승인/거부
    @ApiResponse(responseCode = "200", description = "스터디 가입 승인/거부 성공")
    @PatchMapping("/{studyInfoId}/apply/{applyUserId}")
    public JsonResult<?> leaderApproveRefuseMember(@AuthenticationPrincipal User user,
                                                   @PathVariable(name = "studyInfoId") Long studyInfoId,
                                                   @PathVariable(name = "applyUserId") Long applyUserId,
                                                   @RequestParam(name = "approve", defaultValue = "false") boolean approve) {

        studyMemberService.isValidateStudyLeader(user, studyInfoId);

        studyMemberService.leaderApproveRefuseMember(studyInfoId, applyUserId, approve);

        return JsonResult.successOf("Apply Approve or Refuse StudyMember Success");
    }

가입 신청 승인/거부 컨트롤러이다. 일단 isValidateStudyLeader 메서드를 통해 로그인한 유저가 해당 study의 스터디장 인지 판별한다.

 

 

StudyMemberService.java

    // 스터디장의 가입 신청 승인/거부 메서드
    @Transactional
    public void leaderApproveRefuseMember(Long studyInfoId, Long applyUserId, boolean approve) {

        // 승인/거부할 스터디원 조회
        StudyMember applyMember = studyMemberRepository.findByStudyInfoIdAndUserId(studyInfoId, applyUserId).orElseThrow(() -> {
            log.warn(">>>> {} : {} <<<<", applyUserId, ExceptionMessage.USER_NOT_STUDY_MEMBER);
            return new MemberException(ExceptionMessage.USER_NOT_STUDY_MEMBER);
        });

        // 신청대기중인 유저가 아닌경우 예외처리
        if (applyMember.getStatus() != StudyMemberStatus.STUDY_WAITING) {
            log.warn(">>>> {} : {} <<<<", applyUserId, ExceptionMessage.USER_NOT_STUDY_MEMBER);
            throw new MemberException(ExceptionMessage.USER_NOT_STUDY_MEMBER);
        }

        if (approve) {
            applyMember.updateStudyMemberStatus(StudyMemberStatus.STUDY_ACTIVE);

            /*
                알림 메서드 추가
            */

        } else {
            applyMember.updateStudyMemberStatus(StudyMemberStatus.STUDY_REFUSED);

            /*
                알림 메서드 추가
             */
        }
    }
}

아직 알림메서드가 구현되지 않아서 남겨두었지만 @RequestParam 으로 받은 approve를 통해 true이면 STUDY_ACTIVE 로 활동중인 멤버로 상태를 변경해주고 false 일경우 STUDY_REFUSED 로 거부된 멤버로 상태를 변경해주는 Service이다.

 


테스트

 

StudyMemberControllerTest.java

    @Test
    public void 스터디장의_가입_신청_승인_거부_테스트() throws Exception {
        // given
        User savedUser = userRepository.save(generateAuthUser());

        Map<String, String> map = TokenUtil.createTokenMap(savedUser);
        String accessToken = jwtService.generateAccessToken(map, savedUser);
        String refreshToken = jwtService.generateRefreshToken(map, savedUser);

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(savedUser.getId());
        studyInfoRepository.save(studyInfo);

        when(studyMemberService.isValidateStudyLeader(any(User.class), any(Long.class))).thenReturn(UserInfoResponse.of(savedUser));
        doNothing().when(studyMemberService).leaderApproveRefuseMember(any(Long.class), any(Long.class), any(boolean.class));

        // when, then
        mockMvc.perform(patch("/member/" + studyInfo.getId() + "/apply/" + 1L)
                        .contentType(MediaType.APPLICATION_JSON)
                        .header(AUTHORIZATION, createAuthorizationHeader(accessToken, refreshToken)))

                // then
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.res_code").value(200))
                .andExpect(jsonPath("$.res_obj").value("Apply Approve or Refuse StudyMember Success"));

    }

컨트롤러 동작 확인을 위한 간단한 테스트이다.

 

 

StudyMemberServiceTest.java

    @Test
    @DisplayName("스터디장의 가입신청 승인 테스트")
    public void leaderApplyApproveTest() {
        // given
        boolean approve = true;

        User leader = UserFixture.generateAuthUser();
        User user1 = UserFixture.generateGoogleUser();
        userRepository.saveAll(List.of(leader, user1));

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(leader.getId());
        studyInfoRepository.save(studyInfo);

        StudyMember waitingMember = StudyMemberFixture.createStudyMemberWaiting(user1.getId(), studyInfo.getId());  // 승인 대기중 멤버 생성
        studyMemberRepository.save(waitingMember);

        // when
        studyMemberService.leaderApproveRefuseMember(studyInfo.getId(), waitingMember.getUserId(), approve);
        Optional<StudyMember> findStudyMember = studyMemberRepository.findByStudyInfoIdAndUserId(studyInfo.getId(), waitingMember.getUserId());

        // then
        assertTrue(findStudyMember.isPresent());
        assertEquals(findStudyMember.get().getStatus(), StudyMemberStatus.STUDY_ACTIVE);  // 활동 상태로 변경
    }

    @Test
    @DisplayName("스터디장의 가입신청 거부 테스트")
    public void leaderApplyRefuseTest() {
        // given
        boolean approve = false;

        User leader = UserFixture.generateAuthUser();
        User user1 = UserFixture.generateGoogleUser();
        userRepository.saveAll(List.of(leader, user1));

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(leader.getId());
        studyInfoRepository.save(studyInfo);

        StudyMember waitingMember = StudyMemberFixture.createStudyMemberWaiting(user1.getId(), studyInfo.getId());  // 승인 대기중 멤버 생성
        studyMemberRepository.save(waitingMember);

        // when
        studyMemberService.leaderApproveRefuseMember(studyInfo.getId(), waitingMember.getUserId(), approve);
        Optional<StudyMember> findStudyMember = studyMemberRepository.findByStudyInfoIdAndUserId(studyInfo.getId(), waitingMember.getUserId());

        // then
        assertTrue(findStudyMember.isPresent());
        assertEquals(findStudyMember.get().getStatus(), StudyMemberStatus.STUDY_REFUSED);  // 거부 상태로 변경
    }

    @Test
    @DisplayName("스터디장의 가입신청 테스트 - 대기중인 유저가 아닐때")
    public void leaderApplyRefuseTest_waiting() {
        // given
        boolean approve = true;

        User leader = UserFixture.generateAuthUser();
        User user1 = UserFixture.generateGoogleUser();
        userRepository.saveAll(List.of(leader, user1));

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(leader.getId());
        studyInfoRepository.save(studyInfo);

        StudyMember refusedMember = StudyMemberFixture.createStudyMemberRefused(user1.getId(), studyInfo.getId());  // 거부된 멤버 생성
        studyMemberRepository.save(refusedMember);

        // then
        MemberException em = assertThrows(MemberException.class, () -> {
            studyMemberService.leaderApproveRefuseMember(studyInfo.getId(), refusedMember.getUserId(), approve);
        });

        assertEquals(ExceptionMessage.USER_NOT_STUDY_MEMBER.getText(), em.getMessage());
    }

Service 테스트는 경우를 나누어서 진행했다.

  • 스터디장이 승인했을 경우
  • 스터디장이 거부했을 경우
  • 대기중인 유저가 아닐때 테스트

댓글