본 게시글은 하단 강의를 듣고 학습한 내용을 제 생각으로 요약, 정리한 글입니다.
1. 비즈니스 요구사항 정리 1) 비즈니스 요구사항
2) 일반적인 웹 어플리케이션의 계층 구조 컨트롤러 : 웹 MVC의 컨트롤러 역할 . API 만들 때 컨트롤러 역할.서비스 : 서비스 클래스의 핵심 비즈니스 로직 이 들어가 있음.도메인 : 회원, 주문, 쿠폰처럼 db에 주로 저장되고 관리되는 비즈니스 도메인 객체 .리포지토리 : 비즈니스 도메인 객체를 가지고 핵심 비즈니스 로직이 동작하도록 구현한 계층. 도매인 객체를 DB에 저장하고 관리 .
3) 클래스 의존관계 회원 리포지토리(회원을 저장)는 인터페이스로 설계할 거임 이유) 아직 db가 선정되지 않아서 리포지토리를 인터페이스로 만들고 구현체를 우선은 메모리 구현체로 만들거임(메모리 기반의 데이터 저장소 사용). 향후, 구체적인 db기술 선정되고 나면 이를 바꿔끼울 것임. 바꿔 끼우려면 인터페이스가 필요해서 인터페이스 정의해둔 것임. 정리) 인터페이스로 구현 클래스를 변경할 수 있도록 설계
2. 회원 도메인과 리포지토리 만들기 1) 회원 객체 도메인: 회원, 주문, 쿠폰처럼 db에 주로 저장되고 관리되는 비즈니스 도메인 객체. 패키지 만들기 (hello.hellospring.domain) 패키지 안에 Java 클래스 만들기 (Member) src/main/java/hello.hellospring.domain/Member.java
요구사항 구현 src/main/java/hello.hellospring.domain/Member.java getter, setter → id, name 둘 다 체크해서 만들기
2) 회원 리포지토리 (인터페이스, 구현체) 리포지토리: 도메인 객체를 DB에 저장하고 관리. (리포지토리 뜻: 저장소) 회원 리포지토리 인터페이스 패키지 만들기 (hello.hellospring.repository) 패키지 안에 인터페이스 만들기 (MemberRepository) src/main/java/hello.hellospring.repository/MemberRepository.java
기능 구현 src/main/java/hello.hellospring.repository/MemberRepository.java Optional 자바 8의 기능. findById 가져오는데 없으면 null 반환하는데 이때, null을 그대로 반환하지 않고 optional이란 걸로 감싸서 반환함. 리포지토리의 4가지 기능 구현함 save → 회원이 저장소에 저장됨 findById, findByName → 회원을 저장소에서 찾아옴 findAll → 지금까지 저장된 모든 회원 리스트를 반환함 회원 리포지토리 메모리 구현체 패키지 안에 Java 클래스 만들기 (MemoryMemberRepository) src/main/java/hello.hellospring.repository/MemoryMemberRepository.java
인터페이스를 implements하기 src/main/java/hello.hellospring.repository/MemoryMemberRepository.java implement methods (ctrl + i) → 전부 선택해서 ok하기 구현하기 src/main/java/hello.hellospring.repository/MemoryMemberRepository.java private static Map<Long, Member> store = new HashMap<>();저장할 곳. key는 회원 아이디니까 Long으로 함. 값은 Member. 실무에선 동시성 문제 있을 수 있어서 공유되는 변수일 땐 ConcurrentHashMap을 써야하는데 여기선 예제니까 단순하게 HashMap씀 private static long sequence = 0L;sequence는 0,1,2 같은 key값 생성해주는 애임. 실무에선 동시성 문제 고려해서 long보단 AtomicLong 씀. 람다, arrayList부분 같은 자바 문법 따로 공부하기
3. 회원 리포지토리 테스트 케이스 작성 1) 테스트 코드 쓰는 이유 방금 만든 회원 리포지토리 클래스가 정상 동작하는지 검증하기 위함 원래) 개발한 기능을 실행해서 테스트 할 때 main 메서드를 통해서 실행하거나 컨트롤러로 해당 기능을 실행해볼 수 있음. 문제점) 반복적 실행 어려움. 어려 테스트 한번에 실행하기 어려움 대안) JUnit이라는 프레임워크로 테스트 실행 2) 테스트케이스 작성 Test/java에 패키지 만들기(hello.hellospring.repository) 패키지 안에 java class 만들기(MemoryMemberRepositoryTest) MemberRepository 클래스의 객체 만들기 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java
멤버 저장 기능 확인할 save() 만들기 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java
Run save() 기존에 돌리던 거 빨간 사각형 눌러서 끄고 public void save()옆에 초록 삼각형 눌러서 실행하기 save()로 멤버 저장 기능 잘 동작하는지 테스트하기 + import 꿀팁 + 리포지토리에서 store 출력해서 확인 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java 에러 (import Assertions) import org.junit.jupiter.api.Assertions; 사용해서 assertThat 사용하려하면 에러 났음해결) Assertions를 import할 때 assertj로 하기 잘못 넣은 import문 지우고서 Assertions.assertThat … 이 문장의 Assertions에 대해 alt+enter해서 import해주니 import org.assertj.core.api.Assertions; 가 import되면서 해결됐음정리 import org.junit.jupiter.api.Assertions; 는 assertEquals 사용에 필요했음import org.assertj.core.api.Assertions;는 assertThat 사용에 필요했음assertThat 이 좀 더 편하게 쓸 수 있게 해줌두 개 import 동시에 선언하는 건 불가능. Assertions를 alt + enter 해서 static import하면 다음부턴 assertThat 바로 칠 수 있음 src/main/java/hello.hellospring.repository/MemoryMemberRepository.java (store 출력) findByName()으로 이름으로 멤버 찾는 기능 잘 동작하는지 테스트하기 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java 한 번에 실행 가능 클래스 레벨에서 돌리거나 hello.hellospring을 통째로 실행가능. (테스트 코드 장점) findAll() 테스트하기 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java
세 개를 한 번에 실행했더니 오류 발생 (중요) 원인) 다른 테스트가 findByName테스트에 영향을 미쳐서 발생 테스트 순서는 보장 안됨 (모든 테스트는 순서와 상관 없이 method별로 따로 동작하게 설계해야함) findByName만 단독 실행할 땐 문제 없었지만,, 순서에 의존적으로 설계해서 발생한 문제임 findAll()이 먼저 실행됐더니 findByName했더니 이전에 저장했던 다른 객체가 나와버린 것임. 해결) 테스트 끝날 때마다 데이터를 깔끔하게 clear 해주어야 함 src/test/java/hello.hellospring.repository/MemoryMemberRepositoryTest.java @AfterEach: method실행 끝날 때마다 어떤 동작을 하는 call back method src/main/java/hello.hellospring.repository/MemoryMemberRepository.java 원래였으면 MemoryRepository까지 고쳐야 하는데 테스트에서 만드는 객체의 클래스를 MemoryMemberRepository로 고침으로써 MemoryMemberRepository만 고치면 되게 됨
테스트가 수십, 수백 개면 hello.hellospring run하거나 gradlew 띄우고 테스트하면 됨. 다 자동으로 돌려줌 3) TDD: 테스트 코드 작성 후 개발
4. 회원 서비스 개발 1) 회원 서비스 회원 리포지토리와 도메인을 활용해서 실제 비즈니스 로직을 작성하는 부분 패키지 만들기 (hello.hellospring.service) 패키지 안에 클래스 만들기 (MemberService.js) src/main/java/hello.hellospring.service.MemberService.js
회원 리포지토리 만들기 src/main/java/hello.hellospring.service.MemberService.js
회원 가입 만들기 src/main/java/hello.hellospring.service.MemberService.js 람다, throw
Optional optional이란 거 안에 멤버 객체가 있는 거임. 과거엔 null일 가능성 있으면 if null헀지만, 지금은 optional로 감싸서 반환하면 값 꺼내고 싶으면 result.get 하면 됨. 다만 result.orElseGet() 같은 거 더 많이 사용함. 값이 있으면 꺼내고 값 없으면 default값 넣어 꺼냄 전체 회원 조회 만들기 src/main/java/hello.hellospring.service.MemberService.js
2) 네이밍으로 알아보는 리포지토리와 서비스 차이점 리포지토리: save, findById, findByName, findAll처럼 단순히 저장소에 넣었다 뺐다하는 느낌 듬 서비스: join, findMembers처럼 비즈니스에 가까운 느낌 듬. 5. 회원 서비스 테스트 Member Service 클래스에서 ctrl+shift+t해서 Create new test : 테스트 패키지, 클래스, method 만드는 거 한 번에 하는 법 findMembers()를 public이 아닌 private으로 작성해둬서 안 떴었음 ㅎ,, Member Service클래스와 똑같은 패키지에 만들어짐 src/test/java/hello.hellospring.service/MemberServiceTest.java 테스트 method이름은 한글로 바꿔도 됨. build될 때 실제 코드에 포함 안됨 MemberService클래스의 객체 memberService만들기 src/test/java/hello.hellospring.service/MemberServiceTest.java 테스트 method이름은 한글로 바꿔도 됨. build될 때 실제 코드에 포함 안됨 회원가입 정상 테스트 give, when, then 방법 → 주석 처리해두는 것 테스트는 어떤 상황이 주어졌을 때 이를 실행해서 어떤 결과가 나와야 하는 것임 given: 기반하는 데이터, when: 검증하는 것, then: 검증부 src/test/java/hello.hellospring.service/MemberServiceTest.java 테스트 method이름은 한글로 바꿔도 됨. build될 때 실제 코드에 포함 안됨 store 출력 src/main/java/hello.hellospring.repository/MemoryMemberRepositry.java → store 출력
src/test/java/hello.hellospring.service/MemberServiceTest.java → 회원가입 횟수 추가
결과: 첫 회원가입 전, 후 + 두 번째 회원가입 전, 후 store 내부 모습 회원가입 예외 테스트 src/test/java/hello.hellospring.service/MemberServiceTest.java 테스트 method이름은 한글로 바꿔도 됨. build될 때 실제 코드에 포함 안됨 Clear 넣기 src/test/java/hello.hellospring.service/MemberServiceTest.java
같은 memberRepository 객체 쓰게 만들기(DI) src/test/java/hello.hellospring.service/MemberServiceTest.java → BeforeEach 이용
src/main/java/hello.hellospring.service/MemberService → 생성자 이용 DI: MemberService 입장에서 봤을 때 memberRepository를 직접 new하지 않고 외부에서 넣어주는 것