Spring 기초편 강의를 보면서 스프링프레임 워크를 사용해보며 사용법을 알 수 있었다.
기본편을 통해 스프링이 어떤 원리로 돌아가는지, 왜 쓰는지 이해해보자
스프링을 사용하지 않았을 때, 어떤 방법으로 흐름이 흘러가는지 이해해 보자
프로젝트를 생성한다. 개발 환경을 세팅하기에는 많은 시간이 소비되기때문에 스프링 부트 스타터 사이트를 이용한다.
groupId: hello
artifactId: core
IntelliJ의 Preference를 통해 Gradle 대신에 자바 직접 실행으로 변경
-> 실행속도 향상
- 회원이 VIP일 수 있다. 회원은 DB를 사용할 수도 있고, 메모리 데이터를 사용할 수도 있다.
- VIP 회원은 할인을 받는다. 할인은 고정 1000원 할인으로 만든다.
- 이 할인 내용은 추후에 변경이 될 예정이다.
id,name,grade 가 존재한다. grade는 Grade
를 사용한다(getter,setter)
BASIC 과 VIP가 존재한다.
enum으로 생성해준다.
public enum Grade {
BASIC,
VIP
}
여기부터 중요한 내용이다.
우선은 메모리를 이용해 사용하지만 추후에 데이터 베이스를 이용할 예정이다.
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
store를 HashMap을 사용해서 만들어 준다.
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
현재는 저장소를
MemoryMemberRepository
를 사용한다는 사실을 기억해두자.
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
public void join(Member member) {
memberRepository.save(member);
}
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
class MemberServiceTest {
MemberService memberService = new MemberServiceImpl();
@Test
void join() {
//given
Member member = new Member(1L, "memberA", Grade.VIP);
//when
memberService.join(member);
Member findMember = memberService.findMember(1L);
//then
Assertions.assertThat(member).isEqualTo(findMember);
}
}
지금까지의 코드들은 작동 잘 된다. 하지만 이 코드들은
SOLID
원칙을 잘 준수하고 있을까?
1. 다른 저장소로 변경할 때(Repository) OCP 원칙을 잘 준수하나?
- OCP : 개방-폐쇄 원칙
- DIP를 잘 지키나?
- 의존성 역전 원칙
=> 의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제가 있다.
일단은 주문까지 설계하고 다시 돌아보도록 하자
discount(멤버,가격)
=> 할인 금액 return
public interface DiscountPolicy {
int discount(Member member, int price);
}
member가 VIP이면 고정 할인값을 돌려준다
public class FixDiscountPolicy implements DiscountPolicy {
private int discountFixAmount = 1000; //1000원 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) {
return discountFixAmount;
} else {
return 0;
}
}
}
memberId,itemName,itemPrice,discountPrice 가 존재한다.
(Constructor,getter,setter)
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
주문 생성 요청이 오면, 회원 정보를 조회하고, 할인 정책을 적용한 뒤 주문 객체를 생성해서 반환한다.
여기서도 마찬가지로 저장소를
MemoryRepository
와FixDiscountPolicy
를 사용한다는 점을 기억해두자public class OrderServiceImpl implements OrderService { private final MemberRepository memberRepository = new MemoryMemberRepository(); private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
#### 주문 Test
```java
class OrderServiceTest {
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
@Test
void createOrder() {
long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
지금까지 저장소와, 서비스, 엔티티를 만들어 프로젝트를 만들어 보았다.
의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제를 어떻게 해결해줄지 다음 챕터에서 확인해보자