토비의 스프링 5장 서비스 추상화를 읽고 읽기모임을 하며 나눈 것, 배운 것들을 정리합니다.
1. 나의 나눔
5장이 ‘서비스 추상화’라는 주제이긴 하지만 책 곳곳에 좋은 설계와 테스트를 위한 가이드들이 많아서 열심히 메모하며 읽었던 것 같습니다. 특히 5장 초반에 비지니스 로직을 처음으로 구현한 일종의 초난감 if-else 체인 코드가 등장하는데 이걸 개선해 나가는 과정을 코드로 읽으니 너무 좋았습니다. 책을 읽기 전에 나는 어떻게 고칠 수 있는지 먼저 고쳐보고 책의 코드를 봤는데 토비님이 한 것과 제가 한 것이 많이 달라서 배울점이 많았습니다. 책을 좀 급하게 읽느라 코드를 자세히 곱씹어 보지는 못했는데 시간을 내서 이번 장에 나온 코드는 좀 곱씹어 봐야 겠다는 생각이 들었습니다.
그리고 테스트 대역에 대해 설명해 주시는 부분에서 Stub이랑 Mock 오브젝트를 구분해서 설명해 주시는데 다른 책에서 볼 때는 굳이 저걸 왜 나누는 건지 이해가 잘 안되었는데 Mock 오브젝트 사용하는 예제에서 테스트 대상 객체가 외부의존 객체에 전달한 정보를 확인하기 위한 목적으로 Mock 오브젝트를 사용하는 예제를 보며 두 개가 잘 구분이 되더라구요.
그 외에도 소소하게 배운게 많았는데, 지난장에서 컴파일이 안되는 코드 조차 먼저 작성하는 TDD 기법을 보고 따라해 보고 있는데 이번장 읽으면서 IDE에서 컴파일이 안되는 코드를 자동 완성해 준다는 걸 처음 알게 되었습니다. 소소한 팁까지 책에 적어주셔서 감사드립니다.
2. 모임 중 기억에 남는 말들
트랜잭션 경계는 비지니스 로직이 시작되는 지점에서 보통 시작한다. (그렇지는 않은 경우도 있습니다) 비지니스 로직에서 트랜잭션을 걸지 않으면 DAO에서 하나의 쿼리에 대해 트랜잭션 경계가 기본적으로 설정된다.
비지니스 로직에서 트랜잭션 관련 코드가 등장하고 있는데 단일책임원칙을 달성했다고 할 수 있는가? (답변) AOP로 넘어가면 완전히 분리되는 걸 볼 수있는데 그 전 단계인 이번 장에서는 데이터 접근 기술(JDBC, JPA 등)이 변경되어도 서비스 코드에서의 트랜잭션 코드가 영향을 받지 않는 다는 관점으로 생각할 수 있다.
개인적으로 트랜잭션을 거는 로직도 특정 작업이 함께 변경되어야 한다는 비지니스 로직에 포함된다고도 볼 수 있다고 생각한다.
HibernateTransactionManager와 DataSourceTransactionManager 트랜잭션(JDBC Connection 객체 사용해서 트랜잭션 설정)을 같이 쓸 수 있다. (책 2부에 더 자세히 나온다)
테스트에서 경계값 사용에 대한 설명에서 숫자일 때만 유용한 것인지 궁금했다. 문자열 같은 경우에는 경계값이 어떻게 되는지 궁금했다.
특정 성공 케이스만 테스트하는 것이 아니라 여러 발생할 수 있는 케이스를 동등분할(Equivalence Partitioning)해서 확인하는 것이 중요하다.
12 자리 문자열 비밀번호 기능을 테스트 한다면, 1자리, 11자리, 12자리, 12자리 이상 등을 테스트 하는게 경계값 테스트로 볼 수 있지 않을까?
스프링이 접근했던 방법을 책에서 소개해준다. 여러분도 이것을 확장해 가길 추천한다.
트랜잭션 동기화(쓰레드 로컬 사용)로 인해 Connection 객체를 모든 메서드의 파라미터로 물고 넘어가야 했던 불편함이 제거 되었다.
DB 뿐 아니라 메일 메시징 기능에도 트랜잭션을 적용하는 두 가지 방안이 인상적이었다.
현재 JTA를 쓰는 회사는 잘 없을 거라 예상된다.
JDBC 코드를 까보면 서비스 레이어와 트랜잭션 동기화를 위해 진짜 연결을 닫을지 유지할지 확인하는 코드가 있어서 신기했다.
외부 서비스를 의존한체 테스트를 진행하면 테스트 결과를 항상 일관되게 가져갈 수 어렵죠. 어느 계층 단계에서 모킹하느냐에 따라 다양한 접근법이 존재한다. 어쨋든 테스트 결과를 일관되게 유지할 수 있어야 한다.
서비스에서 테스트 서버를 제공하는 경우가 있다. 100을 입력하면 성공, 50이면 실패를 주겠다고 명시하는 서비스도 있었다.
스프링 싱글톤빈에는 상태를 저장해 둬서는 안된다. 이런걸 찾아내는 툴을 도입하기도 했다.
코파일럿까지 IDE에 연동해서 사용하면 얘가 똑똑해서 테스트코드도 거의 제가 의도하는대로 비슷하게 잘만들어줍니다.
PSA(Portable Service Abstraction)라는 용어 보다 그냥 서비스 추상화라고 표현하는게 좋을 것 같다.
DB에 데이터가 들어가면 DB 리팩토링이 어렵죠. 데이터를 한 번에 다 바꿔주는 것인 아니라면 말이죠.
같은 쓰레드 내에서 일반적인 객체를 공유하는 것과 ThreadLocal에 담아서 공유하는 것의 차이가 뭐지?
코파일럿 연동 (3개월 Trial 버전 설정 완료)
동등분할 (Equivalence Partitioning)
“같은 결과를 내는 값의 범위를 구분해서 각 대표 값으로 테스트를 하는 방법을 말한다. 어떤 작업의 결과의 종류가 true, false, 예외발생 세 가지라면 각 결과를 내는 입력 값이나 상황의 조합을 만들어 모든 경우에 대한 테스트를 해보는 것이 좋다.” (토비의 스프링 2장 p206)
커스텀 트랜잭션 구현하기
PlatformTransactionManager 인터페이스(getTransaction, commit, rollback)를 직접 구현하면 우리가 구현한 코드도 DB 트랜잭션과 함께 묶을 수도 있다.
4. 참고
MSA 환경에서 여러 DB 트랜잭션 관리할 때 axonframework 많이 쓰는 것 같다.