티스토리 뷰
# 이 게시글은 "토비의 스프링" 책을 보고 정리를 위해 기록해둔 게시글입니다.
[이전 글]
볶음밥 1장 - 1,2,3: 자바빈, 디자인 패턴(템플릿 메소드, 팩토리 메소드, 전략 패턴), 관심사의 분리, SOLID 및 객체지향 약간
볶음밥 1장 - 4,5,6: 제어의 역전, 프레임워크 vs 라이브러리, 스프링 IoC 및 용어 정리, 싱글톤, 동일성 vs 동등성, 빈의 스코프
볶음밥 1장 - 7: 의존관계 주입(DI), DL, IoC
볶음밥 2장: 테스트, TDD, jUnit
볶음밥 3장: 템플릿/콜백
볶음밥 4장: 예외
[5장] 서비스 추상화
이번 챕터는 코드 작성이 많다
그래서 그런지 앞장에서 글로 개념공부만 할때보다 훨씬 재밌게 읽힌다 ㅎㅎ 코드로 보고 이해하는게 직빵이여 역시..
[트랜잭션] : 쪼갤 수 없는 업무 처리의 최소 단위
- 원자성에 의해 모두 반영(commit)되거나, 모두 반영되지 않아야(rollback) 한다.
- 여러개의 SQL 문장을 하나의 트랜잭션으로 묶는 것도 가능하다 (트랜잭션 동기화)
- ACID 참고
[테스트 대역]
- 테스트 환경을 만들어주기 위해 테스트 대상이 되는 오브젝트의 기능에만 충실하게 수행하면서 빠르게, 자주 테스트를 실행할 수 있도록 해주는 오브젝트를 "테스트 대역(test double)" 이라고 한다
1. 테스트 스텁(Test Stub)
- 테스트 대상 오브젝트의 의존객체로 존재하면서 테스트 동안에 코드가 정상적으로 수행될 수 있도록 돕는 역할 수행
- 테스트 코드 내부에서 간접적으로 사용, DI 등을 통해 미리 의존 오브젝트를 테스트 스텁으로 설정해야함.
- 테스트 중 필요한 정보를 리턴해주는 것 가능 / 강제 예외 발생 가능 / 입력값 지정 가능 -> 기대 출력값 검증 가능
-- 테스트는 대개 어느 시스템에 입력을 넣었을 때 원하는 출력값이 나오는지를 검증하게 된다
2. 목 오브젝트(Mock Object)
- 테스트 대상 오브젝트의 메소드가 돌려주는 결과 뿐 아니라 테스트 오브젝트가 간접적으로 의존 오브젝트에 넘기는 값과 그 행위 자체에 대해서도 검증하고자 할 때 사용
- 테스트 대상 오브젝트와 의존 오브젝트 간의 일도 검증하고자 할 때 사용
- 스텁과 마찬가지로 테스트 오브젝트가 정상 수행되도록 하면서, 테스트 오브젝트와 자신 사이에서 일어나는 커뮤니케이션 내용을 저장해뒀다가 테스트 결과를 검증하는데 활용할 수 있다.
[요약]
1. Enum 타입을 애용하자
2. 객체지향적인 코드는 다른 오브젝트의 데이터를 가져와서 작업하는 대신 데이터를 갖고있는 다른 오브젝트에게 작업을 해달라고 요청하는 것이다. (데이터를 요구하지 말고 작업을 요청하라 - 객체지향 프로그래밍의 기본 원리)
3. 트랜잭션 작업 중간에 예외가 발생하면 그 전까지 변경된 작업은 싹 다 롤백 한다
4. 테스트 대역은 테스트 대상 오브젝트가 원활하게 동작할 수 있도록 도우면서 테스트를 위해 간접적인 정보를 제공하는 역할을 한다.
4-1. 테스트 대역 중 테스트 대상으로부터 전달받은 정보도 검증할 수 있도록 만들어진 것을 목 객체라고 한다
[서비스 추상화 미션]
UserService에 DI를 통해 UserLevelUpgradePolicy를 적용해보자!!
<인터페이스>
public interface UserLevelUpgradePolicy {
boolean canUpgradeLevel(User user);
void upgradeLevel(User user);
}
<구현체 1. NormalUserLevelUpgradePolicy>
public class NormalUserLevelUpgradePolicy implements UserLevelUpgradePolicy {
public static final int MIN_LOGINCOUNT_FOR_SILVER = 50;
public static final int MIN_RECOMMEND_FOR_GOLD = 30;
UserDao userDao; // UserDao를 사용해야 하므로 DI 주입받자
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void upgradeLevel(User user) {
// 상황에 맞는 메소드
}
public boolean canUpgradeLevel(User user) {
// 상황에 맞는 메소드
}
}
<구현체 2. EventUserLevelUpgradePolicy>
public class EventUserLevelUpgradePolicy implements UserLevelUpgradePolicy {
public static final int MIN_LOGINCOUNT_FOR_SILVER = 25;
public static final int MIN_RECOMMEND_FOR_GOLD = 15;
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao; // DI
}
public boolean canUpgradeLevel(User user) {
// 상황에 맞는 메소드
}
public void upgradeLevel(User user) {
// 상황에 맞는 메소드
}
}
<Config 파일>
<bean id="userService" class="springbook.user.service.UserService">
<property name="userDao" ref="userDao"/>
<property name="levelUpgradePolicy" ref="userLevelUpgradePolicy"/>
</bean>
<bean id="userLevelUpgradePolicy" class="springbook.user.service.NormalUserLevelUpgradePolicy">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="springbook.user.dao.UserDaoJdbc">
<property name="dataSource" ref="dataSource"/>
</bean>
uesrService는 스프링 컨테이너로부터 스프링 빈(userDao, userLevelUpgradePolicy)을 DI받는다
userLevelUpgradePolicy는 스프링 컨테이너로부터 스프링 빈(userDao)를 DI받는다
<서비스>
public class UserService {
UserDao userDao;
UserLevelUpgradePolicy levelUpgradePolicy;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setLevelUpgradePolicy(UserLevelUpgradePolicy levelUpgradePolicy) {
this.levelUpgradePolicy = levelUpgradePolicy;
}
public void upgradeLevels() {
List<User> users = userDao.getAll();
for (User user : users) {
if (levelUpgradePolicy.canUpgradeLevel(user)) {
levelUpgradePolicy.upgradeLevel(user);
}
}
}
...
}
덕분에 userService는 UserLevelUpgradePolicy 인터페이스에만 의존관계를 갖지 구현 클래스가 뭔지는 전혀 몰라도 된다.
스프링 컨테이너가 DI 해주는 대로 사용하기만 하면 되는것!!
근데 setter 주입 하는게 너무 거슬린다.. private final 걸고 생성자 주입하는게 익숙해서 그런가 영 어색하다
'웹 > Spring' 카테고리의 다른 글
스프링 볶음밥 - 6장 - AOP (2) (0) | 2022.07.08 |
---|---|
스프링 볶음밥 - 6장 - AOP (0) | 2022.07.06 |
스프링 볶음밥 - 4장 - 예외 (0) | 2022.07.04 |
스프링 볶음밥 - 3장 - 템플릿/콜백 (0) | 2022.07.02 |
스프링 볶음밥 - 2장 - 테스트 (0) | 2022.07.02 |