토비의 스프링 - 5장
이번 장에서는 서비스의 추상화에 대해서 알아보겠습니다.
책에 대한 내용을 정리하기 전에 비지니스 로직에 대해서 알아보겠습니다. 비지니스 로직은 유저에게 우리가 제공하는 서비스 (어플리케이션 혹은 웹 서비스) 에 대한 코드를 의미 합니다. 대표적인 예시로는 특정 쇼핑몰 사이트에서 신규 회원가입 시 할인 쿠폰을 발행 해주는 정책이 있을 때 회원가입 시 할인 쿠폰을 생성해 유저에게 할당하고 쿠폰 발행 여부를 메일, 문자 혹은 카카오 메세지를 통해 전송하는 코드가 비지니스 로직에 해당합니다.
이런 비지니스 로직은 기존의 DAO 에 코드를 넣기에는 역할과 책임에 맞지 않기 때문에 서비스 객체 라는 새로운 객체를 생성해 비지니스 로직에 대한 역할과 책임을 담당하게 됩니다.
위의 예시를 인용해보면 회원가입에 해당하는 유저 생성 로직은 UserDAO 에서 관리하고 쿠폰 생성 로직은 CouponDAO 에서 그리고 생성된 유저와 쿠폰을 이어주는 역할을 UserCouponDAO 라는 임의의 객체가 관리하게 됩니다. 그리고 회원가입을 하는 로직을 호출 했을 때 SignUpService 라는 객체가 UserDAO 와 CouponDAO 를 가져와 각각 유저와 쿠폰을 생성 후 UserCouponDAO 가 각각을 받아 유저와 쿠폰을 이어주고 쿠폰 발급 여부를 메일, 문자 혹은 카카오 메세지를 보내는 Message 객체를 통해 발송하는 객체들을 각자가 하는 역할에 맞게 작업을 요청하는 코드를 서비스 객체가 담당하게 됩니다.
이렇게 각 객체가 각자의 역할과 책임에 충실한 것을 단일 책임 원칙이라고 부르며 각 객체들에게 서로 작업을 요청 하는 부분이 객체 지향 프로그래밍에 있어서 가장 기본이 되는 원리라고 보면 됩니다. 이 경우 장점으로는 각자의 책임만을 담당하기 때문에 코드를 이해하기 쉽고 수정이 필요할 때 어디를 수정해야 하는지 좀 더 명확하게 할 수 있으며 각각을 독립적으로 테스트 하는 것 역시 가능하게 됩니다.
이러한 코드를 작성하고 항상 좋은 코드를 설계하기 위해서는 꾸준한 노력과 계속해서 개선하려고 하는 노력이 필요합니다.
이제 서비스 객체를 알게 되었으니 본격적으로 서비스 객체의 추상화에 대해서 이야기 해보려고 합니다.
위에서는 메세지를 보내는 Message 객체를 가져온다고 했는데 여기서 어떤 메세지를 보낼지를 서비스 객체에서는 해당 내용을 담당하지 않고 메세지를 보낸다는 것만을 알고 있도록 의존성을 분리했습니다.
메세지를 발송 해야 하는 Message 객체도 메세지를 발송 한다는 기능만을 인터페이스로 추상화해 각각 SMTP 를 통한 메일 전송 로직을 구현하거나 카카오 계정이 있다면 카카오 메세지로 메세지를 전송하고 없다면 문자로 발송하는 로직을 구현하는 것처럼 특정 행동을 추상화하고 기능을 구현하고 이를 DI 를 통해 주입해 객체간의 영향을 주지 않고 확장하는 것을 서비스 객체의 추상화라고 합니다.
책에서는 트랜잭션 이라고 하는 더이상 나눌 수 없는 단위 작업을 통해 여러 개의 DB를 사용할 경우에 사용되는 JTA로 트랜잭션 서비스를 추상화 구현하고 있습니다.
트랜잭션은 대표적으로 DB를 예시로 들 수 있습니다.
트랜잭션의 핵심 중에는 원시성이라고 하는 더이상 나눌 수 없는 작은 단위 작업이 있는데 DB 에서는 SQL 단위로 처리하면 트랜잭션을 보장해줄 수 있습니다. 하지만 서비스를 운영 하다보면 이 트랜잭션을 SQL 이 아닌 기능 단위로 설정 해야 할 때가 있습니다.
그전에 용어를 잠시 살펴보겠습니다.
- 경계 : 트랜잭션이 시작되고 종료되는 위치
- 커밋 : 여러 개의 SQL 을 하나의 트랜잭션으로 처리 할 때 모든 SQL 수행이 성공적으로 마무리 되면 DB에 알려 작업을 완료 시키는 것
- 롤백 : 여러 개의 SQL 을 하나의 트랜잭션으로 처리 할 때 이전 SQL 수행이 실패했을 때 이전 SQL 작업을 취소 시키는 것
책의 예시를 인용하면 전체 회원의 등급 업그레이드를 할 경우 중간에 업그레이드에 실패하면 어디까지 회원 등급이 업그레이드 되었는지를 체크 하는게 아닌 하나의 트랜잭션으로 묶어 성공, 실패 여부를 확인하면 됩니다. 다른 예시로는 최근 1개월 가입 유저 중 신규 회원 감사 쿠폰을 배포할 경우에도 이를 하나의 트랜잭션으로 묶어 쿠폰을 생성하고, 발행 메세지를 보내는 것을 하나의 트랜잭션으로 묶으면 DB 에 값을 추가하는 트랜잭션, 추가된 쿠폰 발행 메세지를 보내는 트랜잭션으로 나눌 수 있습니다.
위에 나온 JTA 는 하나 이상의 DB에서 트랜잭션을 만들 때 이를 관리하도록 분산 트랜잭션을 위한 XA 프로토콜을 통해 이를 관리하는 API 입니다.
마지막으로 테스트를 진행할 때 시스템의 다른 부분이 많이 얽혀 있고 의존해 있어 실제 객체를 만들어야 할 때 가짜 객체를 만들어 사용하는 방법으로 Mock 이라는게 있습니다.
이는 따로 객체를 생성해주지 않아도 되기 때문에 테스트 환경 구축이 어려울 때나 특정 경우나 순간에 의존 하는 경우에 크게 도움이 됩니다.
대표적으로는 메일 발송 로직에서 발송 되는 메일의 내용을 미리 볼 수 있도록 하는 경우가 있을 것 같습니다.
마지막으로 테스트가 수행될 때 특정 상태를 가정해 하드코딩 해둔 형태를 테스트 스텁이라고 하고 이런 스텁과 간접적인 출력까지 진행 해주는 목을 각각 목 오브젝트는 행위 검증, 스턱 오브젝트는 상태 검증에 사용합니다.