🧩
TIL_of_Spring_230507
May 07, 2023
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);
}
}