728x90
1. 웹 어플리케이션 계층 구조 및 비지니스 요구사항
1) 웹 어플리케이션 계층 구조
- 컨트롤러 : 웹 MVC 의 컨트롤러 역할
- 서비스 : 비지니스 도메인 객체를 이용하여 핵심 비지니스 로직 구현
- 중복 가입 불가
- 리포지토리 : DB 에 접근 , 도메인 객체를 DB에 저장하고 관리
- 도메인 : 비지니스 도메인 객체
- 회원, 주문 , 쿠폰, 등등 주로 DB에 저장하고 관리되는 객체
2) 비지니스 요구사항 : 데이터, 기능, 데이터 저장소
- 데이터 : 이름, 회원ID, 패스워드
- 기능 : 회원 등록, 조회
- 데이터 저장소(DB) : 강의에서는 따로 선정되지 않은 상황 → 추후 공부하면서 JPA, JDB사용 예정
3) 클래스 의존 관계
- 개발은 DB가 선정되지 않았기 때문에 초기 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용
- 추후 인터페이스로 구현 클래스를 변경할 수 있도록 설계
- DB 는 RDB, NoSQ 등 다양한 저장소로 고민 중인 상황으로 가정 ⇒ 추후 JPA, JDB 등 변경 예정
2. 회원 도메인과 리포지토리(임시 DB) 생성
- 회원 도메인 생성
package HJproject.Hellospring.domain;
public class Member {
private Long code; // 시스템에서 저장 & 식별 구분 하기 위한 code
private String name; // 고객 이름
private String id; // 고객 id
private String passwd; // 고객 passwd
public Long getCode() {
return code;
}
public void setCode(Long code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
- 멤버 리포지토리 MemberRepository : 가입 저장을 위해 사용되는 인터페이스
package HJproject.Hellospring.repository;
import HJproject.Hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findByCode(Long code);
Optional<Member> findByName(String name);
List<Member> findAll(); // 모든 회원 리스트 반환
}
- 메모리 멤버 리포지토리 MemoryMemberRepository : 메모리에 저장하기 위해 사용되는 클래스
package HJproject.Hellospring.repository;
import HJproject.Hellospring.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.setCode(++sequence); // store 에 저장 전 member 값에 sequence 값을 하나 올려서 넣음
store.put(member.getCode(), member); // store 에 member와 code와 함께 저장
return member;
}
@Override
public Optional<Member> findByCode(Long code) {
return Optional.ofNullable(store.get(code));
// optional.ofnullable 를 사용하면 null 이 반환되더라도 감쌀 수 있음
// 추후 클라이언트에서 활용 가능
}
@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());
}
}
3. 테스트 케이스 작성
- 테스트 케이스를 따로 작성해서 사용하는 이유는 기존의 main 클래스를 사용해서 실행하거나 컨트롤러를 통해 실행하면 준비하는데 시간도 오래걸리고, 여러 테스트를 한번에 실행하기 어렵다는 단점이 존재한다.
- 이에 따로 테스트 케이스 클래스를 만들어서 해당 파일을 실행하여 테스트하는 방식을 취하는게 효율적이다.
1) 테스트 케이스 코드 기본 : MomoryMemberRepositoryTest ⇒ 테스트 결과가 테스트 순서에 종속됨
- 저장이 제대로 되는지 확인하는 테스트 코드
- Name 을 기준으로 저장된 member 이 불러와지는지 테스트하는 코드 && 기준없이 저장된 내용 전체가 불러와지는지 테스트하는 코드
import HJproject.Hellospring.domain.Member;
import HJproject.Hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
class MomoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@Test // junit 관련
public void save(){ // 저장이 잘 되는지 확인
Member member = new Member();
// Member member2 = new Member();
member.setName("pray");
// member2.setName("하늘");
repository.save(member);
// repository.save(member2);
// System.out.println(member.getCode());
// System.out.println(member.getName());
Member result = repository.findByCode(member.getCode()).get();
// repository 안에 저장된 memeberCode 를 가져와서 result 에 저장
// 기대값과 실제값을 확인하기 위해 Assertions 의 assertEquals 라는 메서드 활용
// 검증 방법 1. Assertions.assertEquals(기대값, 실제값) => junit
// Assertions.assertEquals(member, result);
// 검증 방법 2. Assertions.assertThat(실제값).isEqualTo(기대값) => assertj
assertThat(result).isEqualTo(member); // static import 상태
}
@Test // 이름 기준으로 불러오기
public void findByName(){
Member member1 = new Member();
member1.setName("pray");
repository.save(member1);
Member member2 = new Member();
member2.setName("Anne");
repository.save(member2);
Member result = repository.findByName("pray").get();
assertThat(result).isEqualTo(member1);
}
@Test // 전체 내용 가져오기
public void findAll(){
Member memeber1 = new Member();
Member member1 = new Member();
member1.setName("pray");
repository.save(member1);
Member member2 = new Member();
member2.setName("Anne");
repository.save(member2);
List<Member> result = repository.findAll();
assertThat(result.size()).isEqualTo(3);
}
}
2) 테스트 케이스 코드 MomoryMemberRepositoryTest 2 : 테스트 케이스 실행 시 순서에 상관없이 결과를 확인하도록 하기위한 코드 ⇒ 테스트 결과가 테스트 순서에 독립적
- 그런데 위에 있는 것처럼 코드를 짜게 되면 문제가 발생한다!!
- 각각의 테스트를 개별로 테스트 하면 문제가 없지만 한꺼번에 전체 클래스 단위에서 테스트 하게 되면 하나의 테스트 뒤에 결과가 초기화 되지 않은 채 계속해서 진행되기 때문에 하나의 테스트 뒤에 메모리 데이터를 초기화 한 뒤 다시 테스트 하게 만들어야 한다.
- 이런 문제를 해결하기 위해 두 가지 파일에서 각각 아래에 해당하는 내용을 넣어준다.
- MemoryMemberRepository
public void clearStore(){
store.clear();
}
- MomoryMemberRepositoryTest
@AfterEach // 하나의 메서드가 끝나면 AfterEach 메서드 동작
public void AfterEach(){
repository.clearStore();
}
3) 테스트 케이스 작성에 관하여
- 개발에 있어서 테스트 케이스를 작성하는 것은 굉장히 중요한 부분이다. 어느 부분에서 제대로 동작하는지 그렇지 않은지, 원하는 결과가 발생하는지 아닌지, 어떻게 고쳐야하는지를 확인할 수 있기 때문이다.
- 테스트 케이스에 따른 개발 방법은 크게 2가지가 있다.
- 첫 번째는 이번처럼 먼저 동작하는 코드(구현 클래스)를 개발하고 그 다음 테스트 케이스를 만들어서 검증하는 방법이다.
- 두 번째는 테스트 주도 개발(TDD, Test Driven Development) 라는 것으로 테스트 케이스 틀을 먼저 만들고 그것에 맞춰 동작하는 코드(구현 클래스)를 개발해가는 방법이다
- 테스트 케이스 전체를 실행해보려면 test 디렉토리안에 기본으로 만들어진 클래스 파일을 실행하면 된다.
4. 기존에 코드에 추가해보기 : 내 맘대로 수정하기!!
- 기존의 내용에 내가 원하는 부분들을 수정해서 넣어보도록 하겠다.
- 기존 : Code(회원가입 번호) , Name(이름)
- 추가 : ID(아이디), passwd(패스워드)
- 추가된 2가지를 갖고 제대로 저장이 되는지 , 찾기(불러오기)가 제대로 되는지 확인할 수 있는 테스트 코드를 만들도록 하겠다.
1) MemberRepository 수정
import HJproject.Hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findByCode(Long code);
Optional<Member> findByName(String name);
Optional<Member> findById(String Id); // 아이디로 찾기 위해 추가
Optional<Member> findBypasswd(String passwd); // 패스워드로 찾기 위해 추가
List<Member> findAll(); // 모든 회원 리스트 반환
}
2) MemoryMemberRepository
import HJproject.Hellospring.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.setCode(++sequence); // store 에 저장 전 member 값에 sequence 값을 하나 올려서 넣음
store.put(member.getCode(), member); // store 에 member와 code와 함께 저장
return member;
}
@Override
public Optional<Member> findByCode(Long code) {
return Optional.ofNullable(store.get(code));
// optional.ofnullable 를 사용하면 null 이 반환되더라도 감쌀 수 있음
// 추후 클라이언트에서 활용 가능
}
@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());
}
//////// 여기서부터 //////////
@Override
public Optional<Member> findById(String id){ // id로 저장된 값을 찾아옴
return store.values().stream() // store에 저장된 값(value)를 반환
.filter(member -> member.getId().equals(id))
// member 에서 meber.getid 를 통해 id 와 동일한 값을 필터
.findAny(); // 전체에서 검색?
}
@Override
public Optional<Member> findByPasswd(String passwd){
return store.values().stream()
.filter(member -> member.getPasswd().equals(passwd))
.findAny();
}
//////// 여기까지 추가하였다 //////////
public void clearStore(){ // 각각 테스트 진행 시 결과 초기화를 위한 메소드
store.clear();
}
}
3) MemoryMemberRepositoryTest
import HJproject.Hellospring.domain.Member;
import HJproject.Hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
class MomoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@AfterEach // 하나의 메서드가 끝나면 AfterEach 메서드 동작
public void AfterEach(){
repository.clearStore();
}
@Test
public void save(){
Member member = new Member();
// Member member2 = new Member();
member.setName("java");
// member2.setName("하늘");
repository.save(member);
// repository.save(member2);
// System.out.println(member.getCode());
// System.out.println(member.getName());
Member result = repository.findByCode(member.getCode()).get();
// repository 안에 저장된 memeberCode 를 가져와서 result 에 저장
// 기대값과 실제값을 확인하기 위해 Assertions 의 assertEquals 라는 메서드 활용
// 검증 방법 1. Assertions.assertEquals(기대값, 실제값) => junit
// Assertions.assertEquals(member, result);
// 검증 방법 2. Assertions.assertThat(실제값).isEqualTo(기대값) => assertj
assertThat(result).isEqualTo(member); // static import 상태
System.out.println("저장되는 값이 확인되었습니다.");
}
@Test
public void findByName(){
Member member1 = new Member();
member1.setName("pray");
repository.save(member1);
Member member2 = new Member();
member2.setName("Anne");
repository.save(member2);
Member result = repository.findByName("pray").get();
assertThat(result).isEqualTo(member1);
// System.out.println(member1.getCode() + " " + member1.getName());
System.out.println("findByName 정상 동작 확인하였습니다");
}
///// 여기서부터 //////
@Test
public void findById(){ // findById 메소드 동작 테스트
Member member = new Member();
member.setId("java_ID");
repository.save(member);
Member findId = repository.findById("java_ID").get();
assertThat(findId).isEqualTo(member);
System.out.println(member.getId());
System.out.println("findById 정상 동작 확인");
}
@Test
public void findByPasswd(){ // findByPasswd 메소드 동작 테스트
Member member = new Member();
member.setPasswd("java_passWD");
repository.save(member);
Member findPW = repository.findByPasswd("java_passWD").get();
assertThat(findPW).isEqualTo(member); // 여기서 member 은 내가 방금 저장한 member
System.out.println(member.getPasswd());
System.out.println("findByPasswd 정상 작동 확인");
}
///// 여기까지 추가 //////
@Test
public void findAll(){
Member memeber1 = new Member();
Member member1 = new Member();
member1.setName("pray");
repository.save(member1);
Member member2 = new Member();
member2.setName("Anne");
repository.save(member2);
List<Member> result = repository.findAll();
assertThat(result.size()).isEqualTo(2);
System.out.println("findAll 정상 동작 확인하였습니다");
}
}
4) 만든 부분 검증하기
위 3가지 부분을 수정하여 단순히 회원 번호, 회원 이름뿐만 아니라 회원 ID 와 회원 Passwd 도 저장 및 찾기가 가능하도록 만들었다.
'Java - Spring &&n SpringBoot' 카테고리의 다른 글
Spring - 컴포넌트 스캔과 자동 의존 관계 설정, 자바 코드로 스프링 빈 등록하기(feat. Annotation) (0) | 2021.12.14 |
---|---|
자바 Spring 개념 잡기 : IoC 제어의 역전, 의존성 주입(Dependency Injection) 와 컨테이너, 스프링 빈 (0) | 2021.12.13 |
Spring - 스프링 웹 개발 기초(feat. 정적 페이지, MVC, API, Json) (0) | 2021.12.07 |
Spring - Index 페이지 만들기, MVC 맛보기, 빌드 &실행(feat. Thyleaf 템플릿) (1) | 2021.12.04 |
Spring 라이브러리 살펴보기 (0) | 2021.12.04 |
댓글