TIL of Java Spring

본 내용은 JAVA 기초 학습 이후 백앤드 스프링 기초를 배우기 위해 김영한 교수님의 “스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술” 의 내용 중 기억할 내용들을 메모하는 포스팅이다.

백앤드.. 배우려면 열심히 해야지. 취업까지 한 고지다.


회원 관리 예제 - 백엔드 개발

회원 레포지터리 테스트 케이스 작성

  • main 메서드에서 실행하거나
  • 웹 어플리케이션의 컨트롤러를 통해 기능 실행 여부를 판단이 가능하나 이는 시간도 걸리고 반복실행이 어렵다.
  • 이에 자바는 JUnit이라는 프레임 워크로 테스트를 실행해서 이런 문제를 해결한다.
  • 해당 방식이로 할 때 주의점은 전체 테스트를 돌릴 때, 과거의 테스트에 사용한 변수나, 데이터를 그대로 접근하게 되는 경우가 발생한다. 따라서 테스트가 하나 끝날 때 데이터를 초기화해주는 작업을 각 테스트별로 넣는게 용이하다. 해당 내용이 @AfterEach를 설정하는 것이다.
  • 이렇게 테스트 케이스들을 전부 넣어서 test 폴더에 저장해두고, 폴더에서 한꺼번에 하거나 gradle 의 테스트 항목을 실행시켜 정상 작동 여부를 체크할 수 있다.
// src/test/java
package hello.hellospring.repository;  
  
import hello.hellospring.domain.Member;  
  
import org.junit.jupiter.api.AfterEach;  
import org.junit.jupiter.api.Test;  
  
import java.util.List;  
  
import static org.assertj.core.api.AssertionsForClassTypes.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();  
	// Assertions.assertEquals(member, result);  
	assertThat(result).isEqualTo(member);  
	}  
  
	@Test  
	public void findByName() {  
		Member member1 = new Member();  
		member1.setName("spring1");  
		repository.save(member1);  
	  
		Member member2 = new Member(); // shift + F6 하면 일괄 수정 가능  
		member2.setName("spring2");  
		repository.save(member2);  
  
		Member result = repository.findByName("spring2").get();  
  
		assertThat(result).isEqualTo(member2);  
	}  
  
	@Test  
	public void findAll() {  
		Member member1 = new Member();  
		member1.setName("spring1");  
		repository.save(member1);  
	  
		Member member2 = new Member(); // shift + F6 하면 일괄 수정 가능  
		member2.setName("spring2");  
		repository.save(member2);  
  
		List<Member> result = repository.findAll();  
	  
		assertThat(result.size()).isEqualTo(2);  
	}  
}

회원 서비스 개발

  • 서비스 패키지의 경우 역할 자체가 비즈니스쪽에 가까우니, 단어 선택 면에서도 비즈니스적 방향성을 잃지 말자.
  • 레포지토리와 같은 것들은 별개다.
//src/main/java/hello.hellospring.service 
package hello.hellospring.service;  
  
import hello.hellospring.domain.Member;  
import hello.hellospring.repository.MemberRepository;  
import hello.hellospring.repository.MemoryMemberRepository;  
  
import java.util.List;  
import java.util.Optional;  
  
public class MemberService {  
  
private final MemberRepository memberRepository = new MemoryMemberRepository();  
  
/**  
* 회원가입  
* @param member 멤버 구조  
* @return  
*/  
	public Long join(Member member) {  
		validateDuplicateMember(member);  
		memberRepository.save(member);  
	return member.getId();  
	}  
  
	private void validateDuplicateMember(Member member) {  
		memberRepository.findByName(member.getName()) // optional 이 감싸지면 NULL 인 경우를 optional로 표현하므로 사용성이 변화함.  
			.ifPresent(m->{  
			throw new IllegalStateException("이미 존재하는 회원입니다.");  
		});  
// 단 optional 객체를 반환하는 것은 권장되지 않음.  
// 차라리 체크하는 메서드를 호출하고, 거기 뒤에 추가 메서드를 호출한다.  
// 만약 이런 내용이 특정 메서드 내부에 추가로 쓰여지게 된다면 하위 메서드로 만들면 된다. cmd+opt+m  
  
	}  
	  
/**  
* 전체 인원 조회  
* @return  
*/  
// 서비스 클래스 쪽은 비즈니스쪽 용어에 가깝게 쓰는 것이 좋다.  
	public List<Member> findMembers() {  
		return memberRepository.findAll();  
	}  
  
	public Optional<Member> findOne(Long memberId) {  
		return memberRepository.findById(memberId);  
	}  
}

회원 서비스 테스트

  • 쉽게 테스트 만드는 방법! : cmd + shitf + t 를 누르고 테스트를 만들면 된다. 만들게 되면 이전 시간에 했던 것처럼 패키지, 클래스와 같은 위치의 test 폴더 아래에 껍데기를 만들어준다.
  • 참고로 테스트, 실제코드가 아니라면 한글명칭으로 적어서 하는 것도 과감하게 적어도 된다.
  • 추가로 빌드될 때 테스트 코드들은 포함되지 않는다.
//test/java service 
package hello.hellospring.service;  
  
import hello.hellospring.domain.Member;  
import hello.hellospring.repository.MemoryMemberRepository;  
import org.assertj.core.internal.bytebuddy.asm.Advice;  
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.*;  
  
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() throws Exception {  
		//추천하는 검증하는 방식  
		//given  
		Member member = new Member();  
		member.setName("hello");  
  
	//when  
		Long saveId = memberService.join(member);  
  
	//then  
// assertThat(saveId).isEqualTo(member.getId());  
		Member findMember = memberService.findOne(saveId).get();  
		assertThat(member.getName()).isEqualTo(findMember.getName());  
	}  
// 이 테스트는 사실 반쪽짜리에 가깝다. 왜냐하면 실제 테스트 검증은 잘 되는 케이스만을 요구받는게 아니기 때문이다.  
  
	@Test  
	public void 중복_회원_예외() throws Exception{  
		//given  
		Member member1 = new Member();  
		member1.setName("spring");  
	  
		Member member2 = new Member();  
		member2.setName("spring");  
  
		memberService.join(member1);  
				IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));  
		  
		assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");  
		  
	}  
  
	@Test  
	void findMembers() {  
	}  
  
	@Test  
	void findOne() {  
	}  
}
//수정된 member service 패키지 
package hello.hellospring.service;  
  
import hello.hellospring.domain.Member;  
import hello.hellospring.repository.MemberRepository;  
import hello.hellospring.repository.MemoryMemberRepository;  
  
	import java.util.List; 
import java.util.Optional;  
  
public class MemberService {  
  
private final MemberRepository memberRepository;  
  
	public MemberService(MemoryMemberRepository memberRepository) {  
		this.memberRepository = memberRepository;  
	} //dependency injection  
  
/**  
* 회원가입  
* @param member 멤버 구조  
* @return  
*/  
		public Long join(Member member) {  
	validateDuplicateMember(member);  ㅂ
memberRepository.save(member);  
return member.getId();  
}  
  
private void validateDuplicateMember(Member member) {  
memberRepository.findByName(member.getName()) // optional 이 감싸지면 NULL 인 경우를 optional로 표현하므로 사용성이 변화함.  
.ifPresent(m->{  
throw new IllegalStateException("이미 존재하는 회원입니다.");  
});  
// 단 optional 객체를 반환하는 것은 권장되지 않음.  
// 차라리 체크하는 메서드를 호출하고, 거기 뒤에 추가 메서드를 호출한다.  
// 만약 이런 내용이 특정 메서드 내부에 추가로 쓰여지게 된다면 하위 메서드로 만들면 된다. cmd+opt+m  
  
}  
  
/**  
* 전체 인원 조회  
* @return  
*/  
// 서비스 클래스 쪽은 비즈니스쪽 용어에 가깝게 쓰는 것이 좋다.  
public List<Member> findMembers() {  
return memberRepository.findAll();  
}  
  
public Optional<Member> findOne(Long memberId) {  
return memberRepository.findById(memberId);  
}  
}