JAVA SPRING BOOT 공부중
테스트코드 적을 때 주석으로 해놓고 하면 좋음
given : 주어진상황
when : 이걸 실행했을 때
then : 결과값
설명 : given 주어진 상황에서 when 해당 테스트 코드를 실행했을 때 then 결과값이 이렇게 나와야해
assertThrows(IllegalStateException.class, () -> memberService.join(member2))
// 두번째 파라미터를 할 텐데 첫번째 예외가 터질 것을 기대하는 코드
assertEquals(member.getName(), findMember.getName());
// 첫번째와 두번째 파라미터가 동일한지 확인하는 메소드
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
// 값이 동일한지 확인하는 메소드
인텔리제이에서 사용하는 단축키가 사람들이 알려주는 단축키와 달라서 곤란했음. 알고보니 키맵에서 이클립스로 설정해놔서 그런거였음.

설정(Ctrl + Alt + s) - 키맵 - 상단을 window로 변경하면됨.
db연결 전임.
package hello.hello_spring.domain;
public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package hello.hello_spring.repository;
import hello.hello_spring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
package hello.hello_spring.repository;
import hello.hello_spring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@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();
}
}
package hello.hello_spring.service;
import hello.hello_spring.domain.Member;
import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원 가입
public Long join(Member member) {
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();
}
// id로 회원 조회
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
아래는 테스트 코드 (window기준 단축키 Ctrl + Shift + t 쓰면 쉽게 테스트 코드 틀을 만들 수 있음.
package hello.hello_spring.service;
import hello.hello_spring.domain.Member;
import hello.hello_spring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach() { // 각 테스트 전에 실행됨. DI 의존성 주입하기
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() { // 각 테스트 후에 실행. -> repository 비우기
memberRepository.clearStore();
}
@Test
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("hello");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복회원예외() throws Exception{
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//when
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
//then
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
package hello.hello_spring.repository;
import hello.hello_spring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
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();
assertThat(result).isEqualTo(member);
}
@Test
public void findByName() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
Member result = repository.findByName("spring1").get();
assertThat(result).isEqualTo(member1);
}
@Test
public void findAll() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring1");
repository.save(member2);
List<Member> result = repository.findAll();
assertThat(result.size()).isEqualTo(2);
}
}
package hello.hello_spring.controller;
import hello.hello_spring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
memberController가 memberSerivce를 통해서 데이터를 받고 비즈니스를 수행: 의존성이 있다고 표현
@Controller 라는 어노테이션을 두면 스프링 컨테이너가 controller를 관리를 함.
@Controller, @Service, @Repository 어노테이션을 적어주면 스프링이 이를 인식해서 스프링빈에 등록시킴.
Controller 생성자 위에 @Autowired를 붙이면 자동으로 service를 묶어줌. 그럼 스프링 컨테이너에 있던 멤버서비스를 가지고와서 의존성을 넣어줌.
Service도 동일하게 Service 생성자에 @Autowired를 붙여주면 컨테이너에 있던 Repository를 Service에 넣어줌.
@Controller, @Service, @Repository를 넣는 작업 ( 어노테이션 상위로 가면 Component으로 나와있음)
컴포넌트관련 어노테이션이 있으면 스프링이 객체로 각각 스프링 컨테이너에 스프링 빈으로 등록함. 오토와이어드는 각 계층끼리 연결시켜줌.
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때 기본적으로 싱글톤으로 등록. (유일하게 하나만 등록해서 공유함) 따라서 같은 스프링 빈이면 모두 같은 인스턴스임.
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용.
패키지 상위폴더에 config 파일을 만들고, 코드를 적어줘야함.
정형화되지않거나 상황에 따라 구현 클래스를 변경해야하면 설정을 통해 스프링 빈으로 등록.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
생성자 주입 : 생성자를 통해 주입 (가장 좋음 : 의존관계가 실행 중 동적으로 변하는 경우는 거의 없으므로 생성자 주입 권장)
필드 주입: 필드에 @Autowired 하는 것 (별로안좋음)
setter 주입 : 아무나 호출될 수 있어.