유리쯔의일상 2023. 12. 27. 22:32
반응형

#김영한 #스프링 #Spring #인프런 #인프런수업


본 포스팅은김영한선생님의
스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근 기술
강의를 기반으로 작성되었습니다.

 

 

 

 

 

 

비지니스 요구사항 정리

데이터 : 회원ID, 이름
기능 : 회원 등록, 조회
아직 데이터 저장소가 선정되지 않음(시나리오)

Pasted image 20231224184219.png

 

Pasted image 20231224184304.png

 

 

 

 

회원 도메인과 회원 레포지토리 만들기

  1. 도메인 패키지 생성 후 클래스 생성
    패키지 위치 : src/main/java/hello.hellospring/domain
    클래스 위치/클래스명 : /domain/Member
  2. package hello.hellospring.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; } }

2. 회원 레포지터리 생성 후 클래스별 소스작성

  • 패키지 위치 : src/main/java/hello.hellospring
  • 패키지 명 : repository
  • 인터페이스 명 : MemberRepository
  • 소스
   package hello.hellospring.repository;  
	import hello.hellospring.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();  
	}
  • 동일 패키지에 구현받을 클래스 작성
  • 클래스명 : MemoryMemberRespository
package hello.hellospring.repository;  
  
import hello.hellospring.domain.Member;  
  
import java.util.*;  
  
public class MemoryMemberRespository implements MemberRepository{  
    private static Map<Long, Member> store = new HashMap<>();  
    // sequence 는 번호를 부여해주는 객체라고 보면 됨  
    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());  
    }  
}

 

 

 

 

TEST CASE 작성하기

내가 원하는 데로 동작을 할건지 테스트 코드로 검증한다
개발한 기능을 실행하여 테스트 할때 자바의 Main 메서드를 통해 실행 하거나
웹 애플리케이션의 컨트롤러를 통해 해당 기능을 실행한다
이러한 방법은 준비하고 실행하는데 시간이 오래걸리고 반복 실행하기 어렵기 때문에
여러 테스트를 실행하기 어렵다는 단점이 있다
자바는 JUNIT 프레임워크로 테스트를 실행하여 문제를 해결할 수 있다

테스트 코드를 작성 할 때엔 해당 메서드 위에 어노테이션 명시가 필요하다
@Teat : 테스트코드라는것을 명시
@AfterEach : 메소드 테스트 후 처리로직
테스트 코드를 돌린 뒤에 항상 비워줘야한다

TEST class

@AfterEach
public void afterEach(){
	repository.clearStore();
}


해당 메서드를 본문 코드에 선언해놓는다


본문 코드
public void clearStore(){  
    store.clear();  
}


여기서 store는 본문 소스코드에 멤버변수로 HashMap 자료형 변수이다

assertThat이 나는 되지 않아서 찾아보니
import가 안되어서 그랬다

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

 

 

 

 

서비스패키지와 회원서비스 클래스 만들기

회원 리포지토리랑 도메인을 활용하여 작성한다
용도에 따른 네이밍 짓는 법과 테스트코드 작성 및 리팩터링도 진행해보자

  • 패키지 위치 : src/main/java/hello.hellospring
  • 패키지 명 : service
  • 인터페이스 명 : MemberService
  • 소스
package hello.hellospring.service;  
  
import hello.hellospring.domain.Member;  
import hello.hellospring.repository.MemberRepository;  
import hello.hellospring.repository.MemoryMemberRespository;  
  
import java.util.List;  
import java.util.Optional;  
  
public class MemberService {  
    private final MemberRepository memberRepository = new MemoryMemberRespository();

	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();  
	}  
  
  
	public Optional<Member> findOne(Long memberId){  
	   return memberRepository.findById(memberId);  
	}

해당 소스는 리팩토링 까지 진행한 소스이다
리팩토링된 결과

// 회원 저장 
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 long Join(Member member){  
    // 같은 이름이 있는지 중복회원 가입 불가  
   Optional<Member> result = memberRepository.findByName(member.getName());    
   result.ifPresent(m -> {        
   throw new IllegalStateException("이미 존재하는 회원입니다");  
   });    
   memberRepository.save(member);    
   return member.getId();}

findByName(member.getName()) 리턴값은 확인시 optional로 반환된다
Optional로 객체를 진행하기 때문에 값이 있는 경우 바로 result.ifPresent() 를 진행할 수 있다.
꺼낼때는 get() 다만 바로 꺼내는건 주로 사용하지 않고 is등 여러 메서드를 사용하길 권장한다

 

 

 

✋ 잠깐! ✋
Optional이란건 뭘까요 ?
Java8에서 도입된 컨테이너 클래스이다
변수가 'null’일 가능성이 있는 객체를 감싸는 wrapper이다
이 객체는 어떤 값이 존재 할 수 있고 존재 하지 않을 수 있는 상황(nullpoint예외)을 더 안전하게 다루기 위해 사용된다
보통 if(값이 있는경우, 데이터가있는경우)문을 간편하게 사용한다 보면된다

 

 

 

주요매서드

  • empty() : 비어있는 Optional 객체를 반환
  • - Optional<String> empty = Optional.empty();
  •  
  • Optional<String> opt = Optional.of("value");
  • of(T value) : null이 아닌 값을 가지는 Optional객체 반환
    만약 값이 null 일 경우 NullPointerException을 발생시킴
  • Optional<String> opt = Optional.of("value");
  • ofNullable(T value) : 값ㅣ null 일수 있는 경우 사용한다
    값이 null이면 비어있는 Optional객체를 null이 아니면 해당 값을 가지는 Optional객체를 반환한다
  • Optional<String> opt = Optional.ofNullable(변수);
  • isPresent() : Optional객체 값 여부에 따라 true, false반환
  • if(opt.isPresent()){}
  • get() : Optional객체가 가지고 있는 값을 반환한다, 비어있는 경우 NoSuchElementExceptiondmf 발생시킨다
String value = opt.get(); 
  • ifPresent(Consumer<? super T> consumer) : 값이 존재하면 주어진 cunsumer를 값에 적용한다
opt.ifPresent(value -> System.out.println(value)); 
  • orElse(T other) : Optional 객체가 비어있지 않으면 값을 반환하고 비어있다면 주어진 other 값을 대신 반환한다
String value = opt.orElse("default"); 

그 외에도 다양한 메서드가 있으니 필요할때마다 검색해봐야 할 것 같다

 

 

 

 

 

테스트코드 작성의 중요성 

    1. test코드는 빌드될때 포함되지않는다
    2. 한글로도 작성이 가능하다
    3. given , when, then 문법을 활용하자
    4. 테스트코드는 정상 여무도 중요하지만 예외가 터지는지 예외 처리는
      잘 되는지 검증도 해야한다.
      정상 여부만 확인한다면 반쪽짜리이다
    5. 의존성 주입을 통해 final 객체도 동일한 인스턴스가 유지되도록
      하는 것이 좋다
반응형