Search
Duplicate

스프링 핵심 원리 이해1 - 예제만들기

목차

Previous

스프링의 핵심 원리 이해를 위해 순수 자바로 예제를 만들어본다.

프로젝트 생성

준비물

JDK: Java 11 +
IDE: IntelliJ or Eclipse
⇒ 여기서는 IntelliJ를 기본으로 한다.
아래 방법에 대해 이미 숙지가 끝났고, 더 간결하게 하고싶다면 아래 git 프로젝트를 사용하자.

프로젝트 생성

IntelliJ 사용시
: new Project로 프로젝트생성 위자드를 띄운 후 다음과 같이 설정.
공식 홈페이지 사용시 :공식 홈페이지 접속후 아래와 같이 설정(Gradle, 2.4.x, jar, java 11) Dependencies는 선택하지 않는다.
이렇게 프로젝트를 생성해서 IDE로 실행하도록 하자. Gradle Dependency를 다운받느라 시간이 좀 걸릴 수 있다. 다 되었다면 src/main/java/hello/core/CoreApplication.java에 들어가 실행시켜보자.
이렇게 동작하면 정상적으로 동작한 것이다.

Tip. IntelliJ Gradle 대신 자바 직접 실행

Build툴에서 기본적으로 Gradle을 통해서 실행하는게 IntelliJ의 기본설정인데 이를 java로 변경하면 더 빠르다.
Preferences → Build, Execution, Deployment → Build Tools → Gradle
Build and run using → Gradle IntelliJ IDEA
Run tests using → Gradle IntelliJ IDEA

비즈니스 요구사항과 설계

회원

회원을 가입하고 조회할 수 있다.
회원은 일반과 VIP 두 가지 등급이 있다.
회원 데이터는 자체 DB를 구축할 수 있고 외부 시스템과 연동할 수 있다.(미확정)

주문과 할인 정책

회원은 상품을 주문할 수 있다.
할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라.(변경가능)
할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 있다.(미확정)
이런 요구사항을 보면 회원 데이터나 할인 정책등은 확정난게 없어 결정이 어렵다.
하지만 결정이 될 때까지 개발을 기다릴 수도 없기에 객체 지향 설계를 통해 개발을 시작해본다.
참고로 스프링이 없는 순수 자바로 개발을 진행한다.

회원 도메인 설계

회원 도메인 협력 관계

: 우선 회원 도메인의 협력 관계에 대해 설계해보자.
회원을 가입하고 조회할 수 있다.
⇒ 회원 가입과 회원 조회기능을 제공하는 회원 서비스를 만듭니다.
회원은 일반과 VIP 두 가지 등급이 있다.
⇒이는 회원 도메인 계층에서 설계하도록 합니다.
회원 데이터는 자체 DB를 구축할 수 있고 외부 시스템과 연동할 수 있다.(미확정)
⇒ 회원 데이터를 조회한다는 책임(역할)을 다하기 위해서 구현하는 방식은 DB, 기타 외부 시스템 연동등 다양합니다. 이렇게 미확정이기에 우선 개발을 위해 간단한 메모리 저장소로 역할을 구현하도록 합니다.

회원 클래스 다이어그램

: 협력관계가 설계되었다면 이제 도메인에 대한 실제 구현레벨에서 클래스 다이어그램을 그려봅니다.
회원 서비스 역할은 MemberService라는 interface가 되고 MemberServiceImpl에서 해당 역할을 구현합니다. ⇒ 이처럼 구현체가 하나인경우 접미사로 Impl을 붙혀줘 가독성을 높혀줍니다.
회원 저장소 역할은 MemberRepository라는 interface가 되고 이를 구현하는 방법은 DB 회원저장소를 구현하는 DbMemberRepository나 기 타외부 저장소 API가 될 수 있지만 미확정이고 아직 구현할 수 없기에 테스트를 위한 MemoryMemberRepository에서 구현합니다.

회원 객체 다이어그램

: 실제 서버에 올라가면 객체간의 참조가 어떻게 되는지를 그리면 위와 같이 된다.
클라이언트는 회원서비스를 바라보며 서비스를 호출하고, 서비스는 회원 저장소를 참조하여 로직을 수행합니다.

회원 도메인 개발

이제 실제로 코딩을 통해 회원 클래스 다이어그램으로 작성한 인터페이스와 구현체를 작성해보도록 하자.
완성된 내용은 포스팅 최상단의 git 주소를 참고하자.
도메인
서비스
리포지토리

회원 도메인 실행과 테스트

이제 작성한 코드들이 정상적으로 동작하는지 서비스계층부터 테스트 코드를 작성해서 확인해보자.
테스트 방식은 BDD방식을 사용했는데 이는 기존의 Given-When-Then방식만을 사용하는 것이 아닌 Describe-Context-It으로 테스트 대상을 좀 더 세밀하게 설명하도록 했다.
BDD의 대한 더 자세한글은 기계인간님의 글을 참고하도록 한다.
MemberServiceTest
위와같이 BDD방식으로 테스트를 작성하여 실행하면 아래와 같은 결과가 나온다.
좀 더 테스트를 서술형으로 알아보기 쉽게 작성할 수 있다.
(하지만, 작성에 들이는 비용이 더 크기에 그때그때 상황에 맞춰 사용하면 될 것 같다.)

그런데 여기까지 설계하며 문제점을 파악하지 못했는가?

테스트 코드를 실행하기까지 코드를 작성했다면 이제 다시 되짚어서 생각해 볼 필요가 있다. 어째서 이렇게 예제 코드를 작성했는가. 우리는 객체지향의 지켜져야 할 원칙 5가지 SOLID 중에서 두 가지 OCP, DIP를 동시에 만족시키기 위한 학습을 하고 있었다.
그렇다면, 지금까지의 코드에서 해당 원칙들이 잘 지켜졌는가?
회원 저장소로 MemberRepository를 사용할때 현재 미확정이기에 Db가아닌 Memory를 사용하는 Repository를 사용했는데 이를 다시 변경할 때 OCP원칙이 지켜질 수 있는가?
DIP가 잘 지켜지고 있는가?
의존관계가 인터페이스 뿐 아니라 구현까지 모두 의존하는 문제가 있다.
계속해서 설계를 해가며 이런 문제를 어떻게 해결할 지 생각해보자.

주문과 할인 도메인 설계

주문 도메인 협력, 역할, 책임

주문과 할인 정책

회원은 상품을 주문할 수 있다.
할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라.(변경가능)
할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 있다.(미확정)
주문과 할인데 대한 정책과 요구사항을 분석해보면 회원은 주문을 할 수 있는데, 회원의 등급에 따라 상품에 할인이 적용될 수 있다. 그렇다면 주문을 하는 과정에 있어서 필요한 과정은 다음과 같다.
⇒ 1. 주문 생성: 클라이언트는 주문 서비스에 주문 생성을 요청한다.
⇒ 2. 회원 조회: 할인을 위해서 회원 등급을 알아야하기에 회원을 조회한다.
⇒ 3. 할인 적용: 회원 등급에 따른 할인여부와 할인 금액을 알기 위해 해당 책임을 할인 정책 역할에 위임한다.
⇒ 4. 주문 결과 반환: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.

주문 도메인 전체

저장소와 정책의 구현체를 통해 확장및 변경이 용이해졌다.
이렇게 설계가 됨으로써 회원 저장소 역할에서 회원 저장소를 메모리, DB, 외부 API등등 자유롭게 변경이 가능해졌고, 할인 정책 동일하기에 오픈 직전까지도 변경가능성이 있고 미확정된 정책들에 대해서 유연하게 대처가 가능하다.

주문 도메인 클래스 다이어그램

주문 도메인 전체를 클래스 다이어그램으로 그리면 위와 같다. 각각의 역할을 맡을 인터페이스와 이를 구현할 구현체들의 관계이다.

주문 도메인 객체 다이어그램 2가지

회원을 메모리(혹은DB)에서 조회하고 할인정책을 정액(정률)을 사용해도 주문 서비스를 변경할 필요가 없다.
각각의 역할간의 협력관계는 그대로 유지 된다.

주문과 할인 도메인 개발

이제 주문과 할인정책 관련하여 설계한 역할(인터페이스)부터 차례대로 생성하여 구현체까지 작성해보도록 하자.
주문 도메인 작성
주문 서비스 역할 작성
할인 정책 서비스 역할 작성
이렇게 역할을 생성하고 해당 역할에서 해야할 시나리오(Method)들에 대해 선언을 해줬다.
이제 이를 통해 테스트 케이스를 작성해보자. 해당 테스트 케이스는 회원도메인 설계와 동일하게 BDD방식으로 설계했다.
OrderServiceTest
테스트케이스를 돌려보면 녹색불이 아닌 빨간불이 켜지며 테스트 통과가 되지 않을 것이다.
왜냐하면 아직 구현체를 통해 로직을 구현하지 않았기 때문이다. 그러니 이제 구현체를 작성하자.
FixDiscountPolicy
OrderServiceImpl
이제 다시 테스트케이스를 작동시켜보자.
이렇게 녹색불이 들어온다면 정상이다.
하지만, 아직 위에서도 언급했던 OCP, DIP원칙은 지켜지지 않고 있다. 계속 학습해보자.

이전챕터로

다음 챕터로