월루를 꿈꾸는 대학생

[Spring Boot 입문] 회원 관리 본문

Programing/Spring Boot

[Spring Boot 입문] 회원 관리

하즈시 2023. 1. 11. 00:25
728x90

계층 구조

- 컨트롤러 : 제어역할 

- 서비스 : 핵심 로직 

- 리포지토리 : 디비 접근 관리

- 도메인 : 비즈니스 도메인 객체 ?  : 멤버 , 쿠폰 등 

 

클래스 의존 관계
- 인터페이스로 구현 클래스를 만든다

- 디비 나중에 바꿔끼우기 위해서 인터페이스로 만듦 


 

리포지토리 

- 저장소 

- 회원 객체를 저장하는 저장소 역할 

- 인터페이스에다가 저장 , id로 찾기 , 이름으로 찾기 , 모두 찾기 등의 4가지 메서드를 정의 

// 값이 없으면 null이 반환이 되니까 에러 .. 그럴 경우 Optional로 감싸면 에러 안 난다
Optional<Member> findById(Long id);

 

// member repositry에서 필요한 기능을 정의 해두고 구현체를 바꿔끼우는 식으로 

 

- 처음 인터페이스를 만들고 그 인터페이스에 각 구현체가 가져야할 기능들을 정의 해둠 ... 그리고 Memory든 어떤 DB든 아직 결정이 안 났는데 결정 난 후에 바로 바꿔끼울 수 있는 이유가 인터페이스에다가 미리 정의를 했으니까 그 뒤에 구현체만 바꿔서 끼우면 문제없이 돌아가도록 설계

 

 

구현체 부분 

@Override
public Optional<Member> findByName(String name) {
    // 람다식
    // 스토어에 있는 값들을 전체적으로 돌림 루프를 돌림
    // 필터링 하는데 값들의 member.getName()과 파라미터 name이랑 같은게 있는지
    // 그 중 하나라도 찾으면 반환 ! findAny().
    // 루프 돌리면서 하나라도 찾으면 그거 리턴하고 없으면 null인데 Optinal로 감싸서 넘김 
    return store.values().stream()
            .filter(member -> member.getName().equals(name))
            .findAny();
}

 


테스트 케이스 작성 

 

- 코드를 코드로 검증 

- JUnit 프레임워크를 통해서 테스트 실행 ! 

- @Test 붙여줘야함 

@Test
public void findByName(){
    Member member1 = new Member();
    member1.setName("Spring1");
    repository.save(member1);

    Member member2 = new Member();
    member2.setName("Spring2");
    repository.save(member2);

    // get()이거 쓰면 Optional 한 번 까서 볼 수 있음 그래서 반환타입이 Optional이 없네
    Member result = repository.findByName("Spring1").get();
    assertThat(result).isEqualTo(member1);

}

 

import static org.assertj.core.api.Assertions.*;

 

- rename : shift + f6 


// get()이거 쓰면 Optional 한 번 까서 볼 수 있음 그래서 반환타입이 Optional이 없네
Member result = repository.findByName("Spring1").get();

 

- 테스트는 순서가 보장이 안 됨 그래서 에러가 날 때가 있음 

- 테스트가 끝나고 나면 데이터를 클리어 해줘야함 ->  AfterEach 로 테스트 끝날 때마다 호출되는 메서드를 정의하고 데이터 삭제를 해두기  

// 테스트가 끝날 때마다 호출되는 AfterEach가 콜백
@AfterEach
public void afterEach(){
    // 저장된 거 다 삭제
    repository.clearStore();
}

 

 

 


회원 서비스 개발 

 

// 같은 이름이 있는 중복 회원 x 만약 해당 이름으로 못 찾은 경우를 대비해서 optional로 감쌈
Optional<Member> result = memberRepository.findByName(member.getName());
//ifPresent : result값이 null이 아니라 값이 있으면 동작하는 메서드
// -> 옵셔널로 감싸면 옵셔널 가지고 있는 메서드 사용 가능
// 값 자체를 꺼내고 싶으면 result.get()으로 꺼내기도 가능
result.ifPresent(m-> {
    throw new IllegalStateException("이미 존재하는 회원입니다");
});

 

// 요렇게도 가능 짜피 옵셔널 반환이니까 한번에! 
memberRepository.findByName(member.getName()).ifPresent(m->{
    throw new IllegalStateException("이미 존재하는 회원입니다");
});

 

 

서비스 네이밍은 보다 비즈니스 적으로 적자 

리포지토리는 기계적으로 데이터 넣어 빼 이런 느낌으로 네이밍 

 


회원 서비스 테스트

 

Command + shift + t : 해당 클래스에서 테스트케이스만들기 

 

자동으로 만들어줌 

 

 

정상 테스트도 중요하지만 예외 테스트도 중요

 

테스트를 할 때 3부분으로 나눠서 하면 굳굳 

//given

//when

//then

 

 

예외 처리 경우

- assertThrows 메서드 사용 

// 2번째 실행시 예외가 터져야하는데 그게 앞에 있는 예외가 터지면 성공
assertThrows(IllegalStateException.class, ()-> memberService.join(member2));

IllegalStateException e = assertThrows(IllegalStateException.class, ()->memberService.join(member2));

assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");

 

테스트 할 때마다 초기화! 

- afterEach

@AfterEach
public void afterEach(){
    memberRepository.clearStore();
}

 

 

레파지토리 생성에 대해서 

 

처음에

class MemberServiceTest {

    MemberService memberService = new MemberService();
    // @AfterEach 위해서 생성
    // 이거 보면 다른 쪽에도 리포지토리 생성했는데 또 생성하는 거는 좀 거시기함 .. 서로 new로 만든거니까 다른 리파지토리임
    // 같은 거로 테스트 하는게 좋지
    MemoryMemberRepository memberRepository = new MemoryMemberRepository();

이렇게 new로 새로 만들었음 근데 이건 같은 리파지토리가 아니니까 별로임 

같은 걸 만들도록 수정이 필요 

 

원래 memberService 여기서 이렇게 new로 만들었는데  

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

 

이거를 외부에서 넣어주도록 변경 

public class MemberService {
    // 수정 후
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository){
        this.memberRepository=memberRepository;
    }

 

외부에서 넣어주는 DI

class MemberServiceTest {

    MemberService memberService;
    // @AfterEach 위해서 생성
    // 이거 보면 다른 쪽에도 리포지토리 생성했는데 또 생성하는 거는 좀 거시기함 .. 서로 new로 만든거니까 다른 리파지토리임
    // 같은 거로 테스트 하는게 좋지
    MemoryMemberRepository memberRepository ;

    // 테스트 실행전에 만들어줌 
    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

  테스트 할 때마다 그 전에 서비스랑 리포지토리 만들어서 넣어주네 

 

 

 

출처

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

 

 

728x90

'Programing > Spring Boot' 카테고리의 다른 글

[ Spring 입문] AOP  (0) 2023.01.25
[Spring 입문] 웹 mvc개발  (0) 2023.01.20
[Spring Boot 입문] 스프링 웹 개발 기초  (0) 2023.01.08
[SpringBoot 입문] 환경세팅  (0) 2023.01.07
[Spring Boot] 템플릿 상속  (0) 2023.01.01