TDD 멤버십 삭제 및 포인트 적립 API 구현

윤현우·2023년 2월 10일
0

TDD공부

목록 보기
7/7
post-thumbnail

멤버십 삭제 요구사항

[요구사항 확인]

  • 나의 멤버십 삭제 API
    • 기능: 나의 멤버십을 삭제합니다.
    • 요청: 사용자 식별값, 멤버십 번호
    • 응답: X

멤버십 삭제를 위해서는 해당 멤버십 사용자가 멤버십 조회 페이지에서 삭제를 할 수 있게 만들어야 하기 때문에 멤버십의 PK만 알면 삭제할 수 있도록 만들어야 한다.

제일 먼저 레파지토리테스트를 해보자.

    @Test
    @DisplayName("멤버십 삭제")
    void deleteMembership() {
        //given
        Member member = Member.builder()
                .userId("userA")
                .userPassword("userAPassword")
                .userName("userA")
                .build();

        Membership KakaoUserA = Membership.builder()
                .memberIdIndex(1L)
                .membershipType(MembershipType.KAKAO)
                .point(10000)
                .build();

        memberRepository.save(member);
        membershipRepository.save(KakaoUserA);

        //when
        membershipRepository.deleteByMembershipId(1L);

        //then
        assertThat(membershipRepository.findByMembershipId(1L)).isNull();
    }

멤버십을 지울 membershipRepository의 deleteByMembershipId가 없으므로 컴파일 오류가 난다.

또한 membershipId를 찾아서 확인할 findByMembershipId도 없다.

membershipRepository에 필요한 것들을 추가 생성한다.

public interface MembershipRepository extends JpaRepository<Membership, Long> {
    Membership findByMemberIdIndexAndMembershipType(Long memberId, MembershipType membershipType);

    List<Membership> findByMemberIdIndex(Long memberIdIndex);

    void deleteByMembershipId(Long membershipId);

    Membership findByMembershipId(Long membershipId);
}

다시 테스트를 돌려보면 테스트에 성공한다.

이제 Service 계층을 만들어보자.

서비스 계층에서는 해당 멤버십 번호로 멤버십을 조회하고, 사용자가 요정자와 일치하는 경우에 삭제를 진행해야 한다.

멤버십 삭제에 대한 실패 케이스는 멤버십이 없는 경우, 멤버십이 있는데 본인이 아닌 경우가 있다.

순서대로 실패 테스트를 먼저 작성하고 성공하는 테스트를 작성하도록 하자.

    @Test
    @DisplayName("멤버십 삭제 실패 / 멤버십이 존재하지 않음")
    @Transactional
    void deleteMembership_NoMembership() {
        //given
        Member member = Member.builder()
                .userId("userA")
                .userPassword("userAPassword")
                .userName("userA")
                .build();

        memberRepository.save(member);

        //when
        Long membershipId = 1L;

        memberService.deleteMembership(membershipId);
        //then

    }

    @Test
    @DisplayName("멤버십 삭제 성공")
    @Transactional
    void deleteMemberhsip() {
        //given
        Member member = Member.builder()
                .userId("userA")
                .userPassword("userAPassword")
                .userName("userA")
                .build();

        Membership userA = Membership.builder()
                .memberIdIndex(1L)
                .membershipType(MembershipType.NAVER)
                .point(10000)
                .build();

        memberRepository.save(member);
        membershipRepository.save(userA);

        //when
        memberService.deleteMembership(1L);

        //then
        assertThat(membershipRepository.findByMembershipId(1L)).isNull();

    }

역시 컴파일 에러가 발생하고, 이를 해결하기 위해 다음과 같은 프로덕션 코드를 추가해야 한다.

 public void deleteMembership(Long membershipId) {
    Membership deleteMembership = membershipRepository.findByMembershipId(membershipId);

    if(deleteMembership == null){
        System.out.println("해당 멤버십이 없습니다.");
    }
    if(deleteMembership != null){
        membershipRepository.deleteByMembershipId(membershipId);
    }
}

일단 프로덕트 코드는 테스트를 모두 성공 시킨 후에 자세하게 변경할 것이다.

멤버십 포인트 적립 요구사항

[ 요구사항 확인 ]

  • 멤버십 포인트 적립 API
    • 기능: 나의 멤버십 포인트를 결제 금액의 1%만큼 적립합니다.
    • 요청: 사용자 식별값, 멤버십 ID, 사용 금액을 입력값으로 받습니다.
    • 응답: X
    • 기타: 고정 금액 적립 방식으로의 확장이 유연하도록 구현합니다.

포인트 적립에 필요한 레파지토리 작성은 따로 없기 때문에 서비스 계층 테스트로 넘어간다.

포인트 적립에 필요한 실패 테스트는 해당 멤버십이 없는 경우인데 이 경우는 이전에 테스트를 했기 때문에 성공 테스트를 작성하고, 서비스 프로덕트 코드를 작성해보자.

    @Test
    @DisplayName("포인트 적립 테스트")
    void savePoint() {
        //given
        Member userA = Member.builder()
                .userId("userAId")
                .userPassword("userAPassword")
                .userName("userA")
                .build();

        Membership userAMembership = Membership.builder()
                .membershipType(MembershipType.NAVER)
                .memberIdIndex(1L)
                .point(10000)
                .build();

        Member member = memberRepository.save(userA);
        Membership membership = membershipRepository.save(userAMembership);

        //when
        Long expectMemberIdIndex = 1L;
        MembershipType expectMembershipType = MembershipType.NAVER;
        int price = 10000;
        memberService.MembershipPoint(expectMemberIdIndex, expectMembershipType, price);

        Membership result = membershipRepository.findByMemberIdIndexAndMembershipType(expectMemberIdIndex, expectMembershipType);

        //then
        Assertions.assertThat(result.getPoint()).isEqualTo(10100);
    }

포인트를 적립할 시 해당 멤버십을 확인 할 변수들과 포인트를 적립할 금액을 파라미터로 받아야한다.

여기서는 ratePointService가 없기 때문에 컴파일 오류가 난다.

멤버십 적립 요구사항을 보면 "고정 금액 적립 방식으로의 확장이 유연하도록 구현합니다."
이라는 요구사항이 있다.

따라서 PointService인터페이스를 만들고, RatePointService라는 구현체를 만들어 사용한다.

나는 미리 고정 금액 적립 방식으로의 확장을 위해 미리 FixPointService라는 구현체도 만들 것이다.

이때 고정 금액 적립 방식의 구현체도 만들어야 하기 때문에 OCP원칙과 DIP원칙을 잘 지켜야 한다.

만들기 전, OCP, DIP원칙을 잘 지키도록 만드는 것이 포인트이다.

memberService에 포인트 적립 메서드를 작성한다.

	public void MembershipPoint(Long memberIdIndex, MembershipType membershipType, Integer price){

        Membership membership = membershipRepository.findByMemberIdIndexAndMembershipType(memberIdIndex, membershipType);
        int point = ratePointService.calculateAmount(price);

        membership.setPoint(point + membership.getPoint());
    }

이 때, 멤버서비스는 RateService 인터페이스를 의존해야한다.

@Autowired
private PointService ratePointService;

지금은 ratePointService를 사용할 것이기 때문에 rataePointService를 의존한다.

public class RatePointService implements PointService {

    private static final int POINT_RATE = 1;

    @Override
    public int calculateAmount(Integer price) {
        int point = price * POINT_RATE / 100 ;
        return point;
    }
}

다시 테스트를 돌려보게 되면 테스트에 성공한다. 이렇게 FixPointService도 작성해보자.

public class FixPointService implements PointService {

    private static final int POINT_RATE = 1000;
    @Override
    public int calculateAmount(Integer price) {

        return POINT_RATE;
    }
}

이렇게 모든 멤버십 기능 API를 완성 시켰다. 이제 컨트롤러를 작성하여 완성해보자.

profile
개발자가 되는 그날까지

0개의 댓글