본문 바로가기

스프링

[Spring] 서비스를 사용하는 이유(서비스 추상화2)

1.     Com.springsource.junit : junit

2.     Commons-logging : Spring-context가 사용

3.     Mysql-connector : Mysql JDBC

4.     Spring-aop : 스프링 기능 자체의 aop

5.     Spring-bean : 스프링 빈을 활용하는 경우 필요. 스프링의 XML 설정파일과 자바 애노테이션을 파싱 하는데 필요한 클래스 포함

6.     Spring-context : 스프링 코어를 확장한 많은 클래스가 들어 있는데 모든 클래스는 EJB, JNDI(Java Naming Directory Interface), JMX용 클래스와 연동하는데 applicationcontext기능을 사용해야 하며 스프링 리모팅 클래스, 동적 스크립팅 언어(제이루비, 그루비등)와 연동하는 클래스, 빈 유효성검증(JSR-303) API, 스케줄링을 하는 클래스도 포함되어 있다.

7.     Spring-core : 모든 스프링 모듈에서 필요한 모듈. 다른 스프링 모듈에서 사용하는 공통 클래스가 포함됨.

8.     Spring-dao : EmpltyResultDataAccessException 등 사용을 위한 jar

9.     Spring-expression : 스프링 표현언어(SpEL) 지원 클래스 포함.

10.   Spring-jdbc : 스프링이 지원하는 jdbc.

11.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

12.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

 

Enum을 모르겠으면 자바 enum편 참조

String으로 DB에 저장해도 되지만 이런식으로 규칙이 있는 변수는 숫자로 저장해주는게 저장공간의 효율을 늘릴 수 있다. 이럴 때 enum을 사용하면 좋음

 

userServieceuserDaoDI받도록 프로퍼티 추가, userService 아이디로 빈 추가.

 

JDBC외에 Hibernate, JDO, JDA 같은 데이터액세스 API를 사용시 확장을 위한 interface

 

 

 

사용자 레벨 관리 로직은 어디다 두는 것이 좋을까?

DAO는 데이터를 어떻게 가져오고 조작할지 다루는 곳이지 비즈니스 로직을 두는 곳이 아니므로. 비즈니스 로직 서비스를 제공한다는 의미에서 UserService 생성

10~13. UserServiceUserDao의 구현클래스가 바뀌어도(Hibernate 등의 다른 데이터엑세스 DAO로 변경되어도) 영향을 받아서는 안되므로 DI적용

 

UserService의 문제점 > 아래에서 문제점 개선

for루프 안에 if/else 블록들이 읽기 불편하고 코드가 깔끔하지 않다.

이렇게 깔끔해 보이지 않는 이유는 다른 여러가지 로직이 한데 섞여 있다.

-       18.user.getLevel()==Level.BASIC 레벨이 무엇인지 파악하는 로직

-       19.user.getLogin()>=50 업그레이드 조건을 담은 로직

-       20. Chaged = true : 의미 없고 30의 작업을 위함

레벨이 늘어나면 for 루프안에 if 조건이 계속 추가되야함(지저분해짐)

변화에 취약하고 다루기 힘든 코드가됨

 

UserDao를 위한 테스트 클래스

 

 

 

Update 테스트 : id를 제외한 사용자 정보를 변경 후 변경한 오브젝트와 비교.

수정하면 다른 테스트와 꼬이지 않을까???

39. setup 메소드에 의해 항상 초기화 되므로 괜찬음.

128. 만약 update 쿼리에 where절이 빠져있다면?? 전부 바뀐다 김진광으로 이런 테스트의 오류를 막기 위한 방법은??

> JdbcTemplateupdate()가 돌려주는 리턴값을 확인. 리턴값은 영향받은 로우의 개수를 리턴한다. (1이상이면 이상이 있는것으로 파악하면 됨. 1개의 로우가 변하기 때문)

> 138 ~ 139처럼 원하는 사용자 외의 정보는 변경되지 않음을 확인하는 방법이있다.

 

 

 

UserService를 위한 테스트 클래스

UserSerice는 컨테이너가 관리하는 스프링 빈이므로 @Autowired를 통해 주입받을 수 있다.

28. upgradeLevels() 테스트 : GOLD를 제외한 나머지(BASIC, SILVER)는 업그레이드 되는 경우, 안되는 경우가 있을 수 있으므로 최소한 다섯 종류의 사용자 정보를 등록해야한다. 테스트 픽스처 개수가 많아졌으니 리스트로 사용함.

29. Arrays.asList : 배열을 리스트로 만들어주는 편리한 메소드

30~34. 업그레이드 경계가 되는 값을 사용하는 것이 좋다.

44~48. 각 사용자별로 업그레이드 후의 예상 레벨을 검증.

 

 

63. DB에서 사용자 정보를 가져와 레벨을 확인하는 코드가 중복되므로 메서드 분리.

51. 처음 가입하는 사용자는 기본적으로 BASIC 레벨이어야한다. 어디에 로직을 넣어야할까??

> UserDaoJdbc는 적절하지 않다. Dao는 오브젝트를 DB에 정보를 넣고 읽는 방법에만 관심을 가져야지 비즈니스적인 의미를 지닌 정보를 설정하는 책임을 지는 것은 바람직 하지 않다.

> User 클래스에 아예 level 필드를 BASIC으로 초기화하는 방법도 나쁘지 않지만, 처음 가입할 때를 제외하면 무의미한 정보인데 단지 로직을 담기 위해 클래스에서 직접 초기화하는 것은 좀 문제가 있어 보인다.

> Service 비즈니스 로직에서 초기화를 하는건 어떨까? DAO DB에 넣어주는 역할만 하고 비즈니스 로직은 Service에게 맡긴다.

55. 레벨이 정해진 경우와 레벨이 비어있는 경우를 놓고 테스트 진행하면됨.

 

 

UserService 개선

 

 

 

 

 

 

14~21. 추상적인 로직 : 모든 사용자 정보를 가져와 한명씩 레벨 업그레이드. 참 간단하다.

18. upgradeLevel() : 어떤 작업을 하는지 쉽게 이해할 수 있다.

28~41. 자주 변경될 가능성이 있는 구체적인 로직

28. canUpgradeLevel() : 주어진 user에 대해 업그레이드가 가능하면 true, 그렇지 않으면 false. 역할과 책임이 명료해진다. GOLD 같은 경우 항상 업그레이드가 불가능하니 false, 그외에는 사용자 정보를 기준으로 조건을 넣으면 된다. 처리할 수 없는 레벨인 경우에 예외를 던져주면 새로운 레벨이 추가되었을 때 예외가 발생할 테니 쉽게 확인할 수 있다.

37. upgradeLevel() : 다음 레벨로 바꿔주고 DB에 업데이트를 한다. 물론 나중에 업그레이드 안내메일을 보낸다거나, 로그를 남기거나, 관리자에게 통보를 해주는 작업이 추가될 수도 있다. 이때 메소드를 분리해두면 나중에 작업이 추가되더라도 어느 곳을 수정해야 하는지 명확해진다는 장점이 있다.

하지만, 37. upgradeLevel()에 다음 레벨이 무엇인지 너무 노골적으로 들어가 있고, 예외 상황에 대한 처리가 없으며, GOLD레벨 사용자가 업그레이드시 DAO의 업데이트 메소드만 실행될 것이다. 또한 레벨이 늘어나면 if문이 점점 길어질 것이고, 레벨 변경거나 레벨이 늘어나면 사용자 오브젝트에서 if문이 복잡해지고 늘어날 것이다.