Toby Spring 6

Toby Spring Boot

토비님의 토비의 스프링 6 - 이해와 원리 강의를 요약한 내용입니다.

Github

MY Project

Intro

JDK

  • SDK Man

    $ sdk # sdk man 터미널 열기
    > sdk list java # java 관련 모든 설치 가능한 버전 확인
    > sdk list java | grep installed # 설치된 버전만 확인
    > sdk install java [jdk name] # 자바 설치
    > sdk use java [jdk name] # 폴더에서 해당 버전 사용
    > sdk env init # 프로젝트 폴더의 사용 JDK 저장
    > sdk env # 프로젝트 폴더의 사용 JDK 복구

API

오브젝트와 의존관계

오브젝트

  • 프로그램을 실행하면 만들어져서 움직이는 것(=객체)

  • 오브젝트는 일을 하며 우리가 원하는 기능을 수행

  • 자바의 오브젝트는 클래스의 인스턴스 또는 배열

클래스

  • 우리가 작성하는 코드

  • 오브젝트를 만들어내기 위해 필요한 상세한 정보(설계도, 프로토타입, 모델 ..)

인스턴스

  • 추상적인 것(ex. 클래스)에 대한 실체

.

의존관계

  • 반드시 두 개 이상의 대상이 존재

    • 하나가 다른 하나를 사용, 호출, 생성, 인스턴스화, 전송 등을 할 때 의존관계에 있다고 이야기

  • 클래스 사이에 의존관계가 있을 때 의존 대상이 변경되면 이를 사용하는 클래스의 코드도 영향을 받는다.

  • 오브젝트 사이에 의존관계는 실행되는 런타임에 의존관계가 만들어지고 실행 기능에 영향을 받지만, 클래스 레벨의 의존관계와는 다를 수 있다.

.

관심사의 분리(Separation of Concerns)

  • 관심사는 동일한 이유로 변경되는 코드의 집합

  • API로 정보를 가져와서 JSON을 오브젝트에 매핑하는 관심과 응답 객체를 준비하는 로직은 관심이 다르다.

  • 변경의 이유와 시점을 살펴보고 이를 분리하자.

관심사의 분리

.

상속을 통한 확장

  • 객체의 변경 없이 정보를 가져오는 방법을 확장하게 만들려면 상속을 이용할 수 있다.

  • 어떻게 객체를 준비할 것이가와 어떻게 정보를 가져올 것인가라는 관심사가 클래스 레벨에서 분리된다.

  • 정보를 담은 오브젝트인 ExRate를 생성하는 책임을 서브 클래스에게 위임하는 방식이다.

    • 객체지향 디자인 패턴에 나오는 팩토리 메소드 패턴을 이용한다.

  • 하지만, 자바는 다중 상속을 허용하지 않으므로 다른 관심사를 분리할 경우 확장을 이용하기 어렵다.

    • 또한 상위 클래스의 변경에 따라 하위 클래스를 모두 변경해야 하므로 상속을 통한 확장은 관심사를 분리하기에 한계가 있다.

상속을 통한 확장

.

클래스의 분리

  • 관심사에 따라 클래스를 분리해서 각각 독립적으로 구성할 수 있다.

    • 결과적으로 클래스 레벨에 사용 의존관계가 만들어지기 때문에 강한 코드 수준의 결합이 생긴다.

    • 실제로 사용할 클래스가 변경되면 이를 이용하는 쪽의 코드도 따라서 변경이 되어야 한다.

  • 상속을 통한 확장처럼 유연한 변경도 불가능해진다.

  • 상속한 것이 아니기 때문에 사용하는 클래스의 메소드 이름과 구조도 제각각일 수 있다.

    • 그래서 클래스가 변경되면 많은 코드가 따라서 변경되어야 한다.

  • 클래스가 다르다는 것을 제외하면 관심사의 분리가 잘 된 방법이 아니다.

클래스의 분리

.

인터페이스 도입

  • 독립적인 인터페이스를 정의하고 서비스가 사용할 메소드 이름을 정해둔다.

    • 이를 각 클래스가 구현하게 만들면 이를 사용하는 쪽에서 의존하는 클래스가 변경되더라도 사용하는 메소드 이름의 변경이 일어나지 않는다.

  • 하지만, 클래스의 인스턴스를 만드는 생성자를 호출하는 코드에는 클래스 이름이 등장하기 때문에 정보를 가져오는 클래스가 변경되면 서비스 코드도 일부분이지만 따라서 변경되어야 한다.

  • 여전히 상속을 통한 확장만큼의 유연성도 가지지 못한다.

인터페이스 도입

.

관계설정 책임의 분리

  • 인터페이스 도입으로 인터페이스를 사용하도록 작성했지만 구현 클래스에 대한 정보를 가지고 있다면 서비스는 여전히 인터페이스를 구현한 특정 클래스에 의존하는 코드가 된다.

    • 자신이 어떤 클래스의 오브젝트를 사용할지를 결정한다면 관계설정 책임을 직접 가지고 있는 것이다.

    • 이 관계설정 책임을 자신을 호출하는 앞의 오브젝트에게 넘길 수 있다.

    • 이렇게 되면 코드 레벨의 의존관계에서 자유로워진다.

  • 이후에는 오직 인터페이스에만 의존하는 코드가 되기 때문에 어떤 구현 클래스의 오브젝트를 사용하게 되더라도 서비스의 코드가 변경되지 않는다.

  • 관계설정 책임을 가진 앞의 클래스(Client)는 생성자를 통해서 어떤 클래스의 오브젝트를 사용할지 결정한 것을 전달해주면 된다.

관계설정 책임의 분리

오브젝트 팩토리

  • Client는 클라이언트로서의 책임과 서비스와 인터페이스 오브젝트 사이의 관계설정 책임을 두 가지를 가지고 있다. → 관심사의 분리가 필요하다.

  • 클라이언트의 관계설정 책임을 가진 코드를 ObjectFactory라는 이름으로 분리한다.

    • ObjectFactory는 사용할 클래스를 선정하고 오브젝트를 만들면서 의존관계가 있다면 이를 생성자에 전달해서 만드는 기능을 담당한다.

오브젝트 팩토리

원칙과 패턴

객체지향 설계원칙과 객체지향 디자인 패턴

개방-폐쇄 원칙(Open-Closed Principle)

  • 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.

  • 클래스가 기능을 확장할 때 클래스의 코드는 변경되지 않아야 한다.

높은 응집도와 낮은 결합도(High Coherence and low coupling)

  • 응집도가 높다는 것은 하나의 모듈이 하나의 책임 또는 관심사에 집중되어 있다는 뜻.

    • 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다.

  • 책임과 관심사가 다른 모듈과는 낮은 결합도, 즉 느슨하게 연결된 형태를 유지하는 것이 바람직하다.

전략 패턴(Strategy Pattern)

  • 자신의 기능 맥락(Context)에서, 필요에 따라서 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라서 바꿔서 사용할 수 있게 하는 디자인 패턴.

  • Collections.sort()는 정렬에 사용할 전략 오브제트를 전달 받아서 사용한다.

제어의 역전(Inversion of Control)

스프링 컨테이너와 의존관계 주입

Dependency Injection

Introduction to the Spring IoC Container and Beans

BeanFactory

  • 스프링의 BeanFactory가 앞에서 만든 ObjectFactory가 제공하던 기능을 대체한다.

  • BeanFactory는 ObjectFactory의 구성 정보를 참고해서 동작하게 만든다.

Bean

  • 스프링 컨테이너는 빈(bean)이라고 불리는 애플리케이션을 구성하는 오브젝트를 관리하는 기능을 담당한다.

의존관계 주입(Dependency Injection)

  • IoC는 스프링의 동작원리를 정확하게 설명하기에는 너무 일반적인 프레임워크 동작원리를 설명하는 용어이다.

  • 그래서 스프링과 같이 오브젝트의 의존관계에 대한 책임을 스프링과 같은 외부 오브젝트가 담당하도록 만드는 것을 설명하는, 의존관계 주입(Dependency Injection) 패턴, 줄여서 DI라고 불리는 용어가 마틴 파울러에 의해서 제안되었고 스프링 개발자들 사이에서, 또 이 원칙을 따라서 프레임워크를 만들거나 개발 방식을 셜명하는 다른 언어와 기술에서도 넓게 사용되고 있다.

  • 스프링이 처음 등장했던 시기에는 IoC라는 용어를 주로 사용했기 때문에 이후에 DI를 사용하면서도 IoC라는 용어도 같이 쓰이기도 한다.

  • 스프링은 IoC/DI 컨테이너라는 식으로 설명하는 문서도 많이 있다.

스프링 컨테이너와 의존관계 주입

컨테이너

  • 애플리케이션을 구성하는 오브젝트를 만들어서 담아두고 필요할 때 사용하도록 제공하는 기능을 담당

  • 보통 오브젝트를 보관하는 것뿐 아니라 생명주기(lifecycle)까지 담당

  • 스프링 컨테이너는 빈이라고 부르는 오브젝트를 생성하고 의존관계를 설정하는 것까지 담당

    • 빈 오브젝트의 생명주기를 담당하는 기능도 제공

구성정보를 가져오는 다른 방법

  • @Configuration, @Bean 애노테이션이 붙은 구성정보 클래스와 메소드를 통해서 만들어질 오브젝트와 의존관계를 정의하는 코드는, 같은 구성정보를 제공 받을 수 있다면 다양한 다른 방법으로 정의할 수 있다.

  • 예전에 많이 사용하던 XML을 이용하는 방법과 @Component 애노테이션이 붙은 클래스를 모두 찾아보는 빈 스캐닝 방식과 생성자 파라미터를 보고 의존 빈 오브젝트를 선택하는데 필요한 타입 정보를 가져오는 방식을 사용할 수도 있다.

  • 빈 정보를 스캐닝에 의해서 동적으로 만들어내는 경우에는 @ComponentScan 애노테이션이 사용된다.

  • 실제로는 빈 스캐닝 방식과 @Configuration/@Bean을 가진 구성정보 클래스 두 가지 방식을 혼합해서 사용한다.

구성정보를 가져오는 다른 방법

싱글톤 레지스트리(Singleton Registry)

싱글톤 패턴은 다음과 같은 단점을 가진다.

  • private 생성자를 가지고 있기 때문에 상속할 수 없다

  • 싱글톤은 테스트하기 힘들다

  • 서버 환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다

  • 싱그톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다

스프링은 하나의 오브젝트만 만들어져야 한다는 필요를 충족하면서도 싱글톤 패턴을 사용해서 만들었을 때의 단점을 가지지않도록 컨테이너 레벨에서 하나의 오브젝트만 만들어지는 것을 보장하는 기능을 제공한다.

  • 이렇게 싱글톤 오브젝트를 만들고 관리하는 스프링 컨테이너를 싱글톤을 등록하고 관리한다는 의미에서 싱글톤 레지스트리라고 부르기도 한다.

  • 스프링의 빈이 생성되고 적용되는 범위를 빈의 스코프(scope)라고 부른다.

  • 스프링은 기본적으로 빈 오브젝트가 싱글톤 스코프를 가지도록 한다.

  • 필요에 따라 여러 개의 빈 오브젝트가 만들어지도록 할 수도 있다.

DI와 디자인 패턴

디자인 패턴을 구분하는 두 가지 방식

  • 사용 목적(purpose)

  • 스코프(scope)

스코프에 의해서 분류하면 확장성을 가진 클래스 패턴과 오브젝트 패턴으로 나눌 수 있다.

  • 클래스 패턴: 상속(inheritance)을 이용

  • 오브젝트 패턴: 합성(composition)을 이용

대부분의 디자인 패턴은 오브젝트 패턴이다.

  • 가능하면 오브젝트 합성을 상속보다 더 선호하라는 디자인 패턴의 기본 객체지향 원리를 따른 것이다.

  • 전략 패턴은 오브젝트 합성을 이용

  • 데코레이터 패턴 : 오브젝트에 부가적인 기능/책임을 동적으로 부여하는 디자인 패턴

design-patterns

의존성 역전 원칙(Dependency Inversion Principle)

  • (1). 상위 수준의 모듈(ex. package)은 하위 수준의 모듈에 의존해서는 안 된다.

    • 둘 모두 추상화에 의존해야 한다.

  • (2). 추상화는 구체적인 사항에 의존해서는 안 된다.

    • 구체적인 사항은 추상화에 의존해야 한다.

DIP는 먼저 인터페이스를 통해서 추상화에 의존하도록 코드를 만들어야 한다.

  • 그리고 인터페이스 소유권의 역전도 필요하다

Separate Interface Pattern

  • 인터페이스와 그 구현을 별개의 패키지에 위치시키는 패턴

  • 인터페이스를 이를 구현한 클래스와 같은 패키지가 아닌 이 인터페이스를 사용하는 클라이언트와 같은 패키지에 위치하게 한다.

  • 만약 이를 사용하는 클래스가 여럿인 경우에는 별개의 패키지로 인터페이스 구분해둘 수 있다.

의존성 역전 원칙을 잘 따르는 코드를 만드는 방법.

(1) 인터페이스를 만들어서 추상화하기.

(2) 모든 코드가 추상화에만 의존하도록 만들기.

(3) 인터페이스를 사용하는 코드가 있는 모듈로 이전(역전)시키기.

의존성 역전 원칙(DIP)

테스트

자동으로 수행되는 테스트(Automated Test)

  • 수동으로 개발한 코드를 테스트하는 방법은 번거롭고 활용하는데 한계

  • 코드로 만들어져 언제든 실행해서 테스트할 수 있는 자동으로 수행되는 테스트가 필요

    • 이를 통해서 지속적인 개선과 점진적인 개발이 가능

개발자가 만드는 테스트

  • 테스트를 코드로 만들고 자동으로 수행되는 테스트를 실행해서 작성한 코드에 대한 피드백

  • 테스트 작성과 실행이 개발을 하는 과정의 일부

  • 테스팅 프레임워크를 이용해서 테스트 작성과 실행 과정을 효율적으로 진행

.

JUnit 테스트

  • JUnit은 켄트 벡과 에릭 감마가 처음 개발한 가장 대표적인 자동화된 테스트 수행 도구

  • 자바 외의 다른 언어로도 유사하게 개발되어지면서 이를 통틀어 xUnit이라고 불림

JUnit 5

  • 스프링 프레임워크 자체 테스트에 사용되고, 스프링을 이용해서 개발하는 프로젝트에서도 가장 많이 사용

  • Junit은 기본적인 동작방식으로 각 테스트를 실행할 때마다 매번 새로운 인스턴스를 생성

    • 모든 테스트가 다른 테스트에 영향을 받지 않고, 독립적으로 실행되도록

JUnit 테스트 작성

.

PaymentService 테스트

  • 자동화된 테스트는 언제든 실행할 수 있고 항상 동일한 테스트 결과를 얻어야 한다.

  • 때로는 외부 시스템에 대한 테스트, 현재 시간과 같이 코드에서 쉽게 제어할 수 없는 값을 이용하는 테스트를 작성해야 하는데, 이런 경우에 일관된 결과를 보장하는 테스트 코드를 작성하기가 쉽지 않다.

PaymentServiceTest

테스트와 DI

테스트 대역(Test Double, Imposter), 스텁(Stub), 목(Mock)

Stub Test

.

스프링 DI를 이용하는 테스트

  • 스프링 컨테이너를 구성하고 여기서 테스트에 필요한 대상과 의존 오브젝트를 설정하거나 테스트에서 참고할 빈 오브젝트를 가져오게 할 수 있다.

  • 스프링은 방대한 양의 테스팅 지원 기술을 제공한다.

.

JUnit에서 스프링 컨테이너를 만들어 테스트를 수행할 때 @ExtendWith@ContextConfiguration을 이용한다.

@ExtendedWith

  • JUnit5 테스트 클래스가 스프링 테스팅 기능을 사용하도록 지정

  • @ContextConfiguration과 결합된 합성 애노테이션인 @SpringJUnitConfig을 이용할 수도 있다.

@ContextConfiguration

Autowired

  • 테스트 코드에 @Autowired가 붙은 인스턴스 변수를 선언하며 스프링 테스트에 의해서 인스턴스 변수의 타입과 일치하는 스프링 컨테이너의 빈 오브젝트를 주입해준다.

  • @Autowired 외에도 스프링에서 지원하는 여러가지 종류의 애노테이션을 지원한다.

Spring Test

.

학습 테스트(Learning Test)

  • 켄트 벡의 테스트주도개발이라는 책에서 소개된 테스트 방법의 한 가지

  • 학습 테스트는 내가 만들지 않은 코드, 라이브러리, API 등에 대한 테스트

  • 학습 테스트의 목적은 사용할 API나 프레임워크의 기능을 테스트로 작성하고 실행해보면서 사용방법을 바르게 이해했는지 확인

  • 학습 테스트는 켄트 벡의 테스트 주도 개발과 로버트 마틴의 클린 코드에서 소개

Learning Test

Clock을 이용한 시간 테스트

.

도메인 모델 아키텍처 패턴

  • 도메인 로직, 비즈니스 로직을 어디에 둘 지 결정하는 패턴

    • (1) 트랜잭션 스크립트 - 서비스 메소드(PaymentService.prepare)

    • (2) 도메인 모델 - 도메인 모델 오브젝트(Payment)

도메인 오브젝트 테스트

.

개발자가 만드는 테스트

  • 개발한 코드에 대한 검증 기능을 코드로 작성

  • 자동으로 테스트를 수행하고 결과를 확인

  • 테스팅 프레임워크를 활용

  • 테스트 작성과 실행도 개발 과정의 일부이다

템플릿

템플릿(Template)

  • 고정된 작업 흐름 안에 변경할 수 있는 코드를 콜백 형태로 전달해서 사용할 수 있도록 만들어진 오브젝트

템플릿 메소드 패턴(Template Method Pattern)

  • 상속을 이용해서 고정된 템플릿과 변경 가능한 훅 메소드 등으로 분리하는 패턴

  • 고정된 틀의 로직을 가진 템플릿 메소드를 슈퍼클래스에 두고, 바뀌는 부분을 서브클래스의 메소드에 두는 구조

  • 하나의 메소드를 가진 인터페이스 타입(SAM)의 오브젝트 또는 람다 오브젝트

콜백(Callback)

  • 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트

  • 파라미터로 전달되지만 값을 참조하기 위한 것이 아니라 특정 로직을 담은 메소드를 실행시키는 것이 목적

  • 템플릿에 메소드 주입 방식으로 전달

메소드 호출 주입(method call injection)

  • DI의 일종으로 컨테이너의 구성 정보에 포함되지 않고 메소드 실행 시점에 의존 오브젝트를 파라미터로 주입하는 방식으로 동작

  • 스프링의 룩업 메소드 주입(lookup method injection)과는 다르고, 런타임 상속을 통해서 메소드의 구현 코드를 직접 주입하는 방식

템플릿 콜백 패턴(Template Callback Pattern)

  • 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분(템플릿)을 자유롭게 변경되는 성질을 가진 부분(콜백)으로부터 독립시켜서 효과적으로 활용할 수 있도록 하는 방법

  • 템플릿과 콜백을 사용하는 방식은 프레임워크에 적용되는 제어의 역전으로 설명 가능

    • 컨텍스트에 전략(콜백)을 교체해서 쓰는 전략 패턴의 특별한 케이스로 볼 수도 있음

    • 콜백은 인터페이스로 만들어지고 대부분 하나의 메소드만 가짐

  • 콜백을 이용해서 확장성을 추구하는 방식은 스프링을 만든 로드 존슨이 쓴 J2EE Design And Development에서 처음 소개

    • 책에서 예로든 템플릿은 JDBC를 다루는 코드에 적용하도록 만들어진 JdbcTemplate이고, 후에 스프링 프레임워크가 만들어질 때 포함

패턴 적용 전 리펙토링

패턴 적용

디폴트 콜백과 템플릿 빈 적용

스프링 제공 템플릿

RestTemplate

RestTemplate 는 HTTP API 요청을 처리하는 템플릿

  • 가장 전형적인 스프링의 템플릿과 콜백 기술을 사용한 기술

  • 스프링이 제공하는 가장 오래된 동기 방식의 REST 클라이언트 기술의 하나

  • GET, POST 메소드를 사용하는 간단한 HTTP API를 호출할 때 사용하기에 편리

  • 다양한 HTTP API 기술을 이용하도록 만들 수 있다.

  • 최근에 스프링에 추가된 RestClient을 이용하면 모던한 API 스타일로 된 HTTP API를 호출하는 코드를 만들 수 있고, 여러가지 콜백 오브젝트를 지원

HTTP Client 라이브러리 확장 : ClientHttpRequestFactory

  • HTTP Client 기술을 사용해서 ClientHttpRequest를 생성하는 전략

    • SimpleClientHttpRequest (HttpURLConnection)

    • JdkClientHttpRequest (HttpClient)

    • NettyClientRequest

    • JettyClientRequest

    • OkHttp3ClientReque

Message Body 변환 전략 : HttpMessageConverter

RestTemplate.doExecute()

protected <T> T doExecute(URI url, @Nullable String uriTemplate, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  • HTTP API 호출 workflow를 가지고 있는 템플릿 메소드로 두 개의 콜백을 받음

  • RequestCallback

    • void doWithRequest㏗ClientHttpRequest request㏘ throws IOException;

  • ResponseExtractor

    • T extractData㏗ClientHttpResponse response㏘ throws IOException;

  • execute(), getForObject(), postForEntity() 등 편리한 메소드 제공

RestTemplate 활용

In addition

JdbcTemplate

  • SQL 쿼리를 수행하거나 등록, 수정, 프로시저 호출을 할 때 사용할 수 있는 템플릿

JdbcClient

TransactionTemplate

  • 스프링의 트랜잭션 추상화 기술과 함께 사용 가능한 데이터 트랜잭션 작업용 템플릿

  • @Transactional이 제공하는 트랜잭션 경계설정 기능을 TransactionTemplate으로도 모두 적용 가능

  • JDBC, JPA, MyBatis, Hibernate 등의 다양한 데이터 기술에 모두 사용 가능

JmsTemplate

HibernateTemplate

SqlSessionTemplate

예외

The Java Tutorials Exceptions

예외는 정상적인 흐름의 진행을 어렵게 만드는 이벤트

  • 예외적인 상황에서만 사용

  • 많은 경우 프로그램 오류, 버그로 발생

  • 재시도, 대안을 통해 예외 상황을 복구해서 정상 흐름으로 전환을 대비

Error

  • 시스템에 비정상적인 상황이 발생했을 때 사용되고 주로 JVM에서 발생

  • 일반적으로 애플리케이션에서 복구를 기대할 수 없는 종류의 예외의 슈퍼 클래스

  • OutOfMemoryError, ThreadDeath

Exception (checked exception)

  • Exception은 애플리케이션이 복구를 기대할 수도 있는 모든 예외의 슈퍼 클래스

    • 복구할 수 없다면 RuntimeException이나 적절한 추상화 레벨의 예외로 전환해서 던지기

  • 언체크 예외/런타임 예외가 아닌 애플리케이션에서 발생되는 예외는 체크 예외로 분류

  • 체크 예외는 catch를 사용해서 잡아내든가, 메소드에서 throws를 정의해서 메소드 밖으로 던져야 함

RuntimeException (unchecked exception)

  • 명시적인 예외처리가 강제되지 않음

  • catch/throws를 쓰지 않아도 문제 없이 컴파일

JPA

자바에서 RDB를 사용하는 모든 기술(JDBC, JPA, MyBatis..)은 DB와의 연결 정보를 관리하는 DataSource를 사용

EmbeddedDatabaseBuilder

  • 애플리케이션 내장형 DB(H2, HSQL, Derby)를 빠르게 셋업하고 DataSource를 생성하는데 사용하는 빌더

LocalContainerEntityManagerFactoryBean

JPA의 EntityManager는 EntityManagerFactory가 있어야 만들 수 있다.

  • Repository, EntityManagerFactory, DataSource 는 Bean으로 등록되어 관리

  • 스프링 부트의 자동 구성 방식을 이용해 JPA 사용 시 EntityManagerFactory, DataSource 빈을 자동으로 등록

JPA를 이용한 Order 저장

EntityManager와 트랜잭션

코드에 의해서 엔티티 매니저를 생성하고 트랜잭션을 가져와 JPA의 기능을 사용하는 기본 코드는 다음과 같은 구조를 가진다.

// Non-managed environment idiom
EntityManager em = emf.createEntityManager();
EntityTransaction tx = null;
try {
    tx = em.getTransaction();
    tx.begin();

    // do some work
    ...

    tx.commit();
}
catch (RuntimeException e) {
    if ( tx != null && tx.isActive() ) tx.rollback();
    throw e; // or display error message
}
finally {
    em.close();
}

Order 리포지토리와 예외

JDBC를 수행중에 발생하는 예외는 SQLException으로 만들어져서 던져진다.

JPA를 이용하는 코드에서 예외가 발생하면 주로 JDBC의 SQLException을 랩핑해서 JPA의 표준 예외로 만들어서 던져진다.

Order 리포지토리와 예외

DataAccessException 추상화

JDBC SQLException

  • JDBC를 기반으로 하는 모든 기술에서 발생하는 예외

    • JDBC, MyBatis, JPA ...

  • DB 에러코드에 의존하거나, 데이터 기술에 의존적인 예외처리

DataAccessException

스프링의 데이터 예외 추상화가 적용되면 JDBC의 SQLException, JPA의 예외 등이 스프링이 정의한 예외 계층구조의 예외로 번역되고, 실제 발생한 예외는 래핑된 형태로 전달

기술의 변화가 예외 처리하는 코드에 영향을 주지 않도록 만들자.

-DataAccessException과 예외 추상화

서비스 추상화

자바 서버 기술(J2Ee/JavaEE)은 여러 계층으로 구분해서 개발하는 레이어 아키텍처를 이용

서비스

스프링 애플리케이션의 빈이 존재하는 계층 구조

.

서비스

  • 클라이언트에게 서비스를 제공해주는 오브젝트나 모듈

  • 서비스는 일반적으로 상태를 가지지 않음(stateless)

    • 상태없는 싱글톤 스프링 빈을 사용하기 적합(@Component, @Service)

  • 보통 애플리케이션의 비즈니스 로직/도메인 로직의 코드가 위치하는 계층을 서비스 계층(Service Layer)이라고 부르고, 그 외에도 다양한 이름으로 불린다.

.

서비스의 종류

  • 애플리케이션 서비스 (application service)

    • 비즈니스 로직을 담당하기 시작하는 시작점, 종료가 되는 경계에 있는 서비스

  • 도메인 서비스 (domain servic)

    • 도메인 모델 패턴에서 비즈니스 로직을 특정 엔티티로는 표현하기 힘든 로직을 담는 서비스

  • 인프라 서비스 (infrastructure service)

    • 도메인/애플리케이션 로직에 참여하지 않고, 기술을 제공하는 서비스

    • 메일, 캐시, 트랜잭션, 메시징..

    • 서비스 추상화의 대상

Application Service

가장 중요한 도메인/애플리케이션/비즈니스 로직 (도메인 오브젝트/엔티티 활용)

  • @Service 빈으로 구성

  • Application/Service 계층에 존재

  • 인프라 서비스의 도움이 필요

    • 인프라 레이어에 존재하는 기술에 가능한 의존하지 않도록 만들어야 함

  • PaymentService - ExRateService에 적용된 DIP

애플리케이션 서비스 도입

기술에 독립적인 서비스

OrderService

  • 데이터 액세스 기술 JPA에 의존

  • JPA를 사용하는 Repository, Transaction Manager 에 의존

Order

  • @Entity가 붙은 JPA 엔티티로 작성

    • 컴파일 시점에만 JPA 라이브러리에 의존

  • 클래스 코드에는 JPA 기술과 관련된 내용이 들어가지 않음

  • JPA를 사용하지 않으면 런타임에는 JPA 라이브러리에 의존하지 않음

Order에서 JPA 메타데이터 분리

  • @Entity은 컴파일타임 라이브러리 의존성만 가짐

  • 엔티티의 동작에는 영향을 주지 않기 때문에 엔티티 클래스를 다른 데이터 기술에서 사용 가능

    • 제거하고 싶다면 외부 XML 디스크립터를 사용

Order에서 JPA 메타데이터 분리

특정 기술(JPA)에 의존하지 않는 애플리케이션 서비스

트랜잭션 서비스 추상화

Transaction은 데이터 기술에 따라 방법이 다르다

  • JDBC

  • JPA

  • MyBatis

  • Jooq

추상화

  • 구현의 복잡함과 디테일을 감추고 중요한 것만 남기는 기법

  • 여러 인프라 서비스 기술의 공통적이고 핵심적인 기능을 인터페이스로 정의

  • 이를 구현하는 어댑터를 만들어 일관된 사용이 가능하게 만드는 것

스프링 트랜잭션 관리 기술의 핵심은 트랜잭션 추상화

데이터 액세스 기술에 상관없이 공통적으로 적용되는 트랜잭션 인터페이스인 PlatformTransactionManager 제공

  • PlatformTransactionManager

    • 트랜잭션 매니저를 인터페이스 형태로 만들어서 트랜잭션을 위한 공통적인 핵심 기능을 추상화

      public interface PlatformTransactionManager extends TransactionManager {
          TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
      
          void commit(TransactionStatus status) throws TransactionException;
      
          void rollback(TransactionStatus status) throws TransactionException;
      }
  • JpaTransactionManager, DataSourceTransactionManager, JtaTransactionManager, HibernateTransactionManager

    • 트랜잭션 사용방법이 다른 기술들을 중간에서 변환해주는 어댑터

트랜잭션 서비스 추상화

JDBC 데이터 액세스 기술

JdbcClient

  • Spring 6.1에서 추가

  • SQL을 사용하는 JDBC 데이터 처리 코드를 유연하게 작성하도록 지원(일종의 템플릿/콜백)

    • SQL을 사용하는 간단한 코드로 안전하고, 트랜잭션 추상화가 적용된 DB 액세스 코드를 작성 가능

  • 스프링의 JdbcTemplate의 대체

JDBC를 직접 사용하는 코드를 이용할 때는 JdbcTransactionManagerTransaction Manager Object로 등록해서 사용

JDBC 데이터 엑세스 기술

트랜잭션 테스트

트랜잭션이 필요한 곳에 정확하게 적용되었는지 테스트하기는 매우 어렵다.

  • 특히 JDBC처럼 자동 커밋이 되거나 SpringDataJPA처럼 기본 리포지토리 구현에서 트랜잭션을 알아서 적용해주는 기술을 사용할 경우

    • 모든 작업이 성공하면 하나의 트랜잭션으로 진행된 것인지 여러개의 트랜잭션으로 쪼개진 것인지 확인이 어려움

  • 트랜잭션 중간에 실패하는 케이스를 만들 수 있다면 롤백 여부로 확인 가능

트랜잭션 테스트

트랜잭션 프록시

스프링의 트랜잭션 프록시는 ProxyFactoryBeanAOP 프록시를 이용해서 만들어진다.

트랜잭션 프록시

@Transactional and AOP

스프링이 만들어주는 트랜잭션 프록시

  • @Tranactional이 붙은 클래스의 메소드가 트랜잭션 안에서 실행되도록 프록시를 생성

Spring Proxy AOP

  • AOP는 스프링에서 그다지 성공하지 못한 핵심 기술 중의 하나

  • 활용 용도가 제한적이면서 막상 사용하기는 매우 어려움

  • 스프링이 만들어 놓은 트랜잭션보안 기술에서는 유용하게 활용

  • 직접 활용하려면 꽤 많은 학습이 필요

  • AOP는 아니더라도 데코레이터/프록시 패턴의 동작원리를 이해하면 필요한 곳에 활용 가능

@Transactional과 AOP

스프링으로 어떻게 개발할 것인가?

스프링 애플리케이션 개발에 필요한 작업

  • 애플리케이션 코드를 설계하고 스프링 빈 선정

  • 구성정보 메타데이터 작성

  • 스프링 컨테이너 준비

스프링 구성정보 메타데이터

  • 스프링 빈의 정의(클래스, 이름, 생성자, 프로퍼티, 오토와이어링)

  • 애노테이션 기반 구성정보 (@Component, @Autowired)

  • 자바 기반 구성정보 (@Configuration, @Bean)

  • 자동 구성정보(SpringBoot @AutoConfiguration)

스프링이 제공하는 인프라 빈 활용

  • 스프링부트의 자동 구성과 프로퍼티 설정 통해 활용 가능

  • 자동 구성에 의해 내부에서 만들어지는 빈의 구조를 이해

  • 프로퍼티 구성 정보를 이용한 커스토마이징

  • @Bean 오버라이딩을 이용한 구성

  • @Enable로 시작하는 기능

스프링의 각 모듈 기술 활용

  • 스프링과 이에 대응되는 스프링 부트의 기능을 함께 학습

  • 테스트

  • 데이터 액세스 (JDBC, JPA)

  • 웹MVC

  • REST Client 4가지

  • 태스크 실행, 스케줄링

  • 캐시

  • 리액티브

스프링 프로젝트 / 에코시스템

  • Spring Boot

  • Spring Data

  • Spring Security

  • Spring Cloud

  • Spring Session

  • Spring Integration

  • Spring Modulith

  • Spring Batch

  • Spring AI

  • ..


번외. 스프링과 JDK 업그레이드

  • 새로운 스프링 부트 프로젝트를 생성 후 참고해서 변경

    • gradle/wrapper/gradle-wrapper.properties

    • build.gradle

    • settings.gradle

  • 변경된 라이브러리 버전 확인

Last updated