public class Member {
private Long id;
private String name;
//Getter and Setter
}
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
@Repository
public class MemoryMemberRepository implements MemberRepository{
//...
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}
- Optional : null값을 감싼다.
- findAny() : filter()로 찾은 결과 중 아무 값이나 하나를 선택
- id는 중복이 없으나 name은 중복이 발생할 수 있기에 구현 방식이 다르다.
public class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@AfterEach
public void afterEach(){
repository.clearStore();
}
@Test
public void save(){
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
Assertions.assertThat(member).isEqualTo(result); //org.assertj.core.api
}
@Test
public void findByName(){
//member1, 2생성
Member result = repository.findByName("spring1").get();
Assertions.assertThat(result).isEqualTo(member1);
}
@Test
public void findAll(){
//member1, 2생성
List<Member> result = repository.findAll();
Assertions.assertThat(result.size()).isEqualTo(2);
}
}
- @AfterEach : 각 테스트 종료된 후 동작
- @BeforeEach : 각 테스트 실행 전 동작
- Test는 순서와 관계없이 설계가 되어야한다. -> clear의 이유
- TDD : Test를 먼저 작성
- Shift + F6 : Rename
- Ctrl + Alt + V : Extract Variable
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
//회원 가입
public Long join(Member member){
//이름 중복 회원X
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원");
});
}
//전체 회원 조회
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach(){
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach(){
memberRepository.clearStore();
}
@Test
void join() {
//given
Member member = new Member();
member.setName("hihi");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
public void exception(){
//동일 이름 member1, 2생성
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원");
// try{
// memberService.join(member2);
// fail();
// } catch (IllegalStateException e){
// Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원");
// }
}
}
- @Controller : 외부 요청을 받는다.
- @Service : 비즈니스 로직을 만든다.
- @Repository : 데이터를 저장한다.
- @Autowired : 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아 넣어줌(DI), 연결의 의미
- @Component : 스프링 빈으로 자동 등록(@Controller, @Service, @Repository)
@Service-@Autowired, @Repository 제거 상태
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}