Spring 핵심 원리 #1- 회원 도메인 설계/개발/테스트
Web/Java (Spring+JSP)

Spring 핵심 원리 #1- 회원 도메인 설계/개발/테스트

# 요구사항

=> 보면 나중에 변경이 가능하거나 미정인 부분이 상당히 많다. 이 때문에 우선 인터페이스를 만들어 구현체를 언제든 갈아끼울수 있도록 설계하는 것이 중요하다.

 

# 회원 도메인 설계 과정

-> 그려본 다이어그램을 확인해보면, DB가 우선 어떻게 될지 결정이 되지 않았기 때문에 저장소를 interface로 구현하고, DB에 따라서 구현체를 만들어 갈아끼우면 확장하기 쉬워진다.

 

1) 우선 회원 서비스를 실제로 실행하는 부분 인 MemberService를 추상화로 구현해둔 뒤에, 이를 직접 구현체인 MemberServiceImpl로 상속받는다. (최대한 확장을 위해 추상화-구현체 형태로 만들자)

 

2) 회원 DB에 접근하는 함수 부분(save,findById)등은 MemberRepository interface를 통해 추상화하며, 각 DB 구현체를 만들어 오버라이딩한다.

 

3) MemberServiceImpl에서 MemberRepository 객체를 사용해 DB에 접근하고, 해당 객체의 멤버함수(save, findById)를 통해 회원 관리 기능(join, findMember)을 구현한다.

 

회원 객체 다이어그램- 객체가 동적으로 맺어지는 연관관계이다.

=> 회원 서비스에는 회원 저장소 객체를 접근해서 사용해야 한다.

-> 구현 이전에 설계를 먼저 잘 해야되는걸 절실히 깨닫고 감...

 

 

# Test 코드 작성법

- given/when/then으로 나누어서 작성하는 것이 중요하다.

@Test
public void TestCode(){
//given (준비)
//테스트를 위해 준비하는 과정이며, 테스트에 사용하는 변수,입력값등을 정의하거나 Mock 객체를 정의함

//when (실행)
//실제로 액션을 하는 테스트를 실행하는 과정이다. 중요한 구문이지만 가장 짧음.

//then (검증)
//테스트를 검증하는 과정으로 예상값, 실제 실행을 통해 나온 값을 검증한다.
}

 

# DIP 법칙 준수 확인

- DIP 법칙은 의존 관계를 맺을 때 변화하기 쉬운 것이 의존하기 보다는 변화하지 않는 것에 의존해야 한다는 것.

  1) 상위 모듈은 하위 모듈에 의존해선 안된다. (부모가 자식에게 의존 X)

  2) 추상화가 세부사항(구현체)에 의존해서는 안된다.

-> 확장하기 편하게 공통 부분을 묶어서 변화하지 않도록 설정하는 것이다!

 

EX) 예를 들어 Payment 시스템을 개발할때,

 1) 개발자 입장에서 pay수단중 하나를 골라 서비스를 제공하며

 2) 나중에 요구사항에 의해서 다른 Pay 수단도 추가될수가 있다고 가정해보자.

-> 만약 SamsungPay를 골랐다고 할 때, 이에 관한 메서드만 구현했다면, 확장이 상당히 불편하다.

    요구사항이 바뀌어 다른 서비스를 제공해야 한다고 하면 그때마다 새로운 메소드를 구현해야 함.

package Solid.dip;

class Applepay{
	String payment(){
    return "samsung";
    }
    }
    
 
 //위를 바탕으로 한 PayService 구현
 package solid.dip;
 
 public class payService{
 	private SamsungPay spay;
    
    public void setPay(final SamsungPay pay){
    this.spay = pay; 
    }
 	
    public String payment(){
    return spay.payment;}
 }

-> 이는 매번 다른 payment에 대한 class를 만들때마다 새로운 메소드를 구현해야 하는 문제가 있다.

 

보완책 : 따라서, 공통 부분을 추상화한 Interface를 만든 후에 이를 상속받은 각종 클래스들을 각각 payment의 구현체로 만든다면 훨씬 확장에 용이함을 알 수 있다.

package solid.dip

public interface Pay{
	String payment();
}

//pay interface를 상속받아 다른 payment 시스템을 쉽게 구현할 수 있다.
package solid.dip

class SamsungPay implements Pay{
	@Override
    public String payment(){
	return "samsung"
    }
    
}

package solid.dip
class KakaoPay implements Pay{
	@Override
    public String payment(){
	return "Kakao"
    }
    
}

//payService 또한 쉽게 바뀌는 각 클래스 구현체에 의존하는게 아닌, pay interface에 의존한다.
//나중에 쉽게 payment 시스템을 확장하여 사용 가능하다.
package solid.dip;
public class PayService{
	public Pay pay;
    
    public void setPay(final Pay pay){
    this.pay=pay; }
    
    public String payment(){
    return pay.payment
    }
}

 

- DIP는 확장성에 용이하며, 객체간의 관계를 최대한 느슨하게 해주어 다양한 설계 방식, 복잡한 시스템 설계가 가능해진다.

 

+ 참조 포스팅)

https://huisam.tistory.com/entry/DIP

 

SOLID - DIP(Dependency Inversion Principle)란 : 의존성 역전 원칙

DIP? DIP는 다음과 같은 정의를 가지고 있습니다 상위 모듈은 하위 모듈에 의존해서는 안된다 추상화는 세부 사항에 의존해서는 안된다 정말 아무리봐도 무슨말인지 모르겠죠? 조금 쉽게 설명하

huisam.tistory.com

 

수업 예시)

회원 도메인을 구현할 때, MemberServiceImpl (서비스 구현클래스) 부분에서 DIP 법칙을 지키지 않음을 확인할 수 있다.

-> memberRepository 변수는 Interface(MemberRepository), Class(MemoryMemberRepository) 즉 추상화와 구현체에 모두 의존하고 있음을 확인할 수 있다 : 추후에 리팩토링 예정

 

 

#assertThat (JUnit)

Unit Test에서 assertThat 함수를 사용하여 코드의 길이를 줄이면서도 잘 읽히는 코드를 작성할 수 있도록 돕는다.

assertThat(T actual,Matcher<? super T>matcher)

-> 두 값을 비교하는 함수로, 첫 번째 파라미터는 비교대상의 실제값을, 두번째 파라미터로는 비교로직이 담긴 Matcher가 사용되게 된다.

@Test
void addTest(){
	Assertions.assertThat(member).isEqualTo(findMember);
    	Assertions.assertThat(result.size()).isEqualTo(2);
    }

 

# Credit - 김영한 팀장님의 인프런 강의 "스프링 핵심 강의-기본편"을 수강한 내용입니다.