월루를 꿈꾸는 대학생
[Spring Boot 입문] 회원 관리 본문
계층 구조
- 컨트롤러 : 제어역할
- 서비스 : 핵심 로직
- 리포지토리 : 디비 접근 관리
- 도메인 : 비즈니스 도메인 객체 ? : 멤버 , 쿠폰 등
클래스 의존 관계
- 인터페이스로 구현 클래스를 만든다
- 디비 나중에 바꿔끼우기 위해서 인터페이스로 만듦
리포지토리
- 저장소
- 회원 객체를 저장하는 저장소 역할
- 인터페이스에다가 저장 , 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);
}
테스트 할 때마다 그 전에 서비스랑 리포지토리 만들어서 넣어주네
출처
'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 |