스프링 부트에서 토비 3.1 따라하기 : 1장 오브젝트와 의존관계 - 1.4 제어의 역전 IoC
개발환경 - OS : mac - STS : 4.0.1 - MySQL : Server version: 8.0.13 Homebrew - Frame-Work : 일단 최대한 의존성 없이 운영될 수 있도록 jar로 만 진행 관련 링크 - 스프링 사용자 모임_홈페이지 http://www.ksug.org/ - 스프링 사용자 모임 질의응답 https://groups.google.com/forum/#!topic/ksug/13vB4tCFqrI - 2017 스프링캠프 https://www.youtube.com/playlist?list=PLdHtZnJh1KdZ6NDO9zc9hF4tONDLTSEUV |
1장 오브젝트와 의존관계
1.4 제어의 역전 IoC
Inversion of Control 라는 용어이다. 스프링 이전부터 상당히 오래된 개념이다. 이를 반영하여 UserDao를 조금 더 개선해보자 라고~ 한다.
1.4.0 프리비어스~
지금까지는 초난감 DAO를 리팩토링하는 작업을 진행했다. 그런데 사실 이 과정에서 얼렁뚱땅 넘겨버린 게 하나 있다. 바로 얼마 전에 만든 클라이언트라는 감투를 쓴 UserDaoTest라는 녀석이다. UserDao의 독립을 위해 UserDaoTest가 이전의 수고를 고스란히 물려받았다. 그런데 말입니다.
UserDaoTest main()는 초난감DAO를 만드는 과정에서 테스트를 위해 만든 것이 아니었던 것이었던 것이었습니다.
결국 클라이언트라고 새로 만든 클래스는 클라이언트라는 역할도 하고 UserDao가 잘 돌아가는지 테스트하는 역할도 하게 된 것이죠.
1.4.1 오브젝트 팩토리
그리하여 성격이 다른 책임 또는 관심사를 분리해봅시다. 아마 여기에 IoC가 쓰이나봅니다... 한번 봅시다~
자 그럼, 이번에 분리할 기능은
(1) UserDao와 ConnectionMaker 구현 클래스의 오브젝트를 만드는 것
(2) 그렇게 만들어진 두 개의 오브젝트가 서로 연결되어 사용될 수 있는 관계를 만드는 것 입니다.
1. 두둥, 팩토리 등장
객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 것. (단, 디자인 패턴에서의 추상 팩토리 패턴이나 팩토리 메소드 패턴과는 다르니 혼동하지말자... 라고 하는데 몰라서 아직 혼동이 안된다.. ;> )
분리시킬 기능을 담당할 팩토리 클래스 DaoFactory 생성
1) 코드
(1) DaoFactory
package springbook.user.dao;
import java.sql.SQLException;
import springbook.user.domain.User;
public class UserDaoTest {
/**
* 토비의 스피링 3.1 예제 따라하기
* 1장 - 03 DAO 확장 - 인터페이스 도입 + 런타임 오브젝트 관계를 갖는 구조로 조정
*
* 클라이언트 전용 역할을 하는 class 생성 : UserDao에는 전혀 손대지 않고도
* 모든 고객이 만족스럽게 DB 연결 기능을 확장해서 사용할 수 있게된다.
*
* @since 2019.02.20
* @author 변찬우 http://normalstory.tistory.com
* @param args
* @throws SQLException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException, SQLException {
/**
* 토비의 스피링 3.1 예제 따라하기
* 1장 - 04 제어의 역전 IoC - 클라이언트의 역할 분리
*
* 이전에 있던 ConnectionMaker 코드는 -> DaoFactory로 이전하고
*
* 새로 위임 받은 팩토리 userDao 메소드를 통해
* 초기화된 userDao 오브젝트를 받아 옮
*
* @since 2019.02.21
*/
UserDao dao = new DaoFactory().userDao(); // <- 이 부분만 바뀐거죠 ~~~
User user = new User();
user.setId("whiteship8"); // <- PK에 해당하니까 수시로 변경 필요
user.setName("백기선");
user.setPassword("married");
dao.add(user);
System.out.println(user.getId() + "등록 성공");
User user2 = dao.get(user.getId());
System.out.println(user2.getName());
System.out.println(user2.getPassword());
System.out.println(user2.getId()+"조회성공");
}
}
(2) 새로 만든, 팩토리 역할을 하는,
클래스를 대신해서 UserDaoTestuserDao 오브젝트를 세팅해주는 DaoFactory 클래스의 코드
package springbook.user.dao; // <- 참고로 패키지 선언을 통해 새로만든 클래스 코드의 위치를 알 수 있습니다.
/**
* 토비의 스피링 3.1 예제 따라하기
* 1장 - 04 제어의 역전 IoC - 클라이언트의 역할 분리
*
* @since 2019.02.21
* @author 변찬우 http://normalstory.tistory.com
*/
public class DaoFactory {
/**
* 팩토리 클래스의 메소드는
* userDao 타입의 오브젝트를 어떻게 만들고 어떻게 준비시킬지 결정한다.
* 아래 코드는 UserDaoTest에 있던 코드 그대로~ 입니다. 역할을 위임 받았으니까요,
* @return userDao
*/
public UserDao userDao() {
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao;
}
}
2) 결과 : 썩쎄스 ~
export_1.4.1 오브젝트 팩토리 : study_spring190221_1.zip
2) 혹시, 이전 포스팅 파일 import 하신 분은 이전 프로젝트를 삭제하시거나 이름을 바꾸신 후 다시 import 하셔야 합니다. |
2. 설계도로서의(자격의 의미로서 ; ) 팩토리
1) 구조
2) 해석
(1) UserDao, ConnectionMaker 는 핵심로직 담당 - 실질적 로직을 담당하는 컴포넌트.
DaoFactory는 이런 애플리케이션들을 구성하고 관계를 책임지는 역할 담당 - 컴포넌트의 구조와 관계를 정의한 설계도.
(2) D사에 공급할때 UserDao, ConnectionMaker 와 함께 DaoFactory를 제공한다.
UserDao 핵심 코드는 안전하게 보존하되 DaoFactory는 코드를 제공함으로써 DB연동 방식은 자유로운 확장이 가능하다.
더욱 의미있는 점은 컴포넌트 역할을 하는 오브젝트와 애플리케이션 구조를 결정하는 오브젝트를 분리했다는 점이다.
1.4.2 오브젝트 팩토리의 활용
아직 끝난게 끝난게 아니다.
DaoFactory 의 자유로운 확장으로 인해 번거로운 일이 생길 새로운 불편함의 가능성이 생겼다.
package springbook.user.dao; // <- 참고로 패키지 선언을 통해 새로만든 클래스 코드의 위치를 알 수 있습니다.
/**
* 토비의 스피링 3.1 예제 따라하기
* 1장 - 04 제어의 역전 IoC - 오브젝트 팩토리의 활용
*
* @since 2019.02.21
* @author 변찬우 http://normalstory.tistory.com
*/
public class DaoFactory {
/**
* 팩토리 클래스의 확장성으로 인해 발생한 새로운 과제
*/
public UserDao userDao() {
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao;
}
public AccountDao accountDao() {
return new accountDao(new DConnectionMaker()); // 위에 코드와 동일한 형식의 코드, 한 줄로 줄여쓴 것 뿐이다.
}
public MessageDao messageDao() {
return new messageDao(new DConnectionMaker());
}
}
팩토리 안에 Dao 가 많아 질 수록 new DConnectionMaker()라는 connectionMaker 구현 클래스의 인스턴스 부분이 반복해서 나타난다. (= 코드 중복 발생). 이는 첫 예제 초난감 DAO에서 getConnection() 메소드를 분리하는 과정과 동일한 패턴의 이유이다. 다시말해 확장성으로 인해 아래와 같은 코드가 생성될 수 있게 되었다.
package springbook.user.dao;
/**
* 토비의 스피링 3.1 예제 따라하기
* 1장 - 04 제어의 역전 IoC - 클라이언트의 역할 분리
*
* @since 2019.02.21
* @author 변찬우 http://normalstory.tistory.com
*/
public class DaoFactory {
/**
* 팩토리 클래스의 메소드는
* userDao 타입의 오브젝트를 어떻게 만들고 어떻게 준비시킬지 결정한다.
* @return userDao
*/
public UserDao userDao() {
// ConnectionMaker connectionMaker = new DConnectionMaker();
// (아래) 독립 메소드로 추출
UserDao userDao = new UserDao(connectionMaker()); // 변수를 메소드로 전환
return userDao;
}
/**
* 독립 메소드로 추출 - 팩토리에 생성될 수 있는 다수의 개별 메소드에서 일일이 작성하지 않아도 되도록,
*/
public ConnectionMaker connectionMaker() {
return new DConnectionMaker(); // 분리해서 중복을 제거한 connectionMaker 타입의 오브젝트 생성코드
}
}
1.4.3 제어권의 이전을 통한 제어관계 역전
드디어 나왔다. 역전의 명수 IoC ; D 제어권 이전이란 프로그램 제어 흐름 구조가 뒤바귀는 것이라고 설명할 수 있다.
1. 안 역전
첫 예제 초난감 DAO를 생각해보면 UserDao 안에 있는 테스트용 main() 메소드( 사용자 또는 엔트리포인트 )는 그 안에서 클래스의 오브젝트도 직접 생성하고 그렇게 생성된 오브젝트의 메소드도 사용한다. UserDao 또한 자신이 사용한 ConnectionMaker의 구현 클래스를 자신이 결정하고 그 오브젝트를 필요한 시점에 생성하고 등등 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조이다.
2. 역전
제어의 역전이란 이 모든 것을 뒤집는 것이다. 이 곳에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 물론 오브젝트를 생성하지도 않는다. 또한 자신이 어디서 왔는지 어디로 가는지도 알 수 없다. (마치 인간과 같구나..아..인생 ;>)
모든 제어의 권한을 자신이 아닌 다른 대상에 위임하기 때문이다. 따라서
프로그램 시작을 담당하는 main() 과 같은 엔트리 포인트를 제외하면 모든 프로젝트는 이렇게 위임받은 제어 권한을 갖은 특별한 오브젝트에 의해 결정되고 만들어진다.
사례1. 서블릿의 역할, JSP, EJB처럼 컨테이너 안에서 동작하는 구조에 이미 제어의 역전 개념이 적용되어 있다. 이들 모두 main() 메소드가 있어서 직접 실행 시킬 수 있는 것이 아니라, 이들에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출할 뿐이다.
사례2. 프레임워크도 제어의 역전 개념이 적용한 대표적인 기술이다.
- 잘못된 개념 : 미리 만들어 둔 반 제품, 확장해서 사용할 수 있도록 준비된 추상 라이브러리 집합
- 도움 개념 : 1) 라이브러리 : 애플리케이션 코드가 애플리케이션 흐름을 직접 제어한다. 필요할때만 능동적으로 라이브러리 사용 (능동적)
2) 프레임 워크 : 애플리케이션 코드가 프레임 워크에 의해 사용된다. (수동적)
- 바른 개념 : 분명한 제어의 역전이 적용되어 있다. 어플리케이션 코드는 프레임워크가 짜 놓은 틀에서 수동적으로 동작해야 한다.
사례3. 우리도 이미 했다. 두번째 예제, 추상 UserDao를 상속한 서브클래스를 기억하는가. 이 서브 클래스는 getConnection()을 구현한다. 하지만 이떄 getConnection()는 자신이 언제 사용될지 알지 못한다. 즉 제어권을 상위 템플릿 메소드에 넘기고 자신은 필요할 때 호출되어(수동적) 사용되도록 하는 점이 바로 제어의 역전이다. 서브클래스에서 결정되는 것이 아닌 것이다. 단지 DB커넥션을 만든다- 하는 기능만 구현해놓으면, 슈퍼클래스인 UserDao의 탬플릿 메소드의 add(), get() 등에서 필요할 때 호출해서 가져다 쓰는 것 뿐이다. 따라서 이 예제에서의 템플릿 메소드는 제어의 역전이라는 개념을 활용해 문제를 해결하는 디자인 패턴이라고 할 수 있다.
사례4. 끝이 아니다. DaoFactory 에서도 제어의 역전이 사용되었다. ConnectionMaker의 구현클래스를 결정하고 오브젝트를 만들 수 있는 제어권이 팩토리 클래스 생성 이전에는 UserDao에 있었지만 팩토리 클래스가 만들어지고 나서는 자기(UserDao) 자신뿐만 아니라 자신이 사용한 오브젝트도 DaoFactory 가 생성하고 공급해주는 것을 수동적으로 받아 쓰는 처지?가 되었다.
자연스럽게 관심을 분리하고 책임을 나누고 유연하게 확장 할 수 있는 구조로 만들기 위해 도입했던 과정이 바로 IoC를 적용하는 작업이었다고 볼 수 있다. (난 또 언제 나오나 했네.. 그동안 한게 IoC였구나..)
3. 제어의 역전 이란
제어의 역전을 위해서는 우리?가 생성한 DaoFactory 와 같은 역할을 하는 존재가 필요하다. 굳이 대표적인 IoC 프레임워크라고 하는 스프링이 없어도 디자인 패턴에서도 발견할 수 있는 개념으로도 IoC 스타일의 설계와 코드를 만들어 사용하면 된다. ( 아, 이게 스프링이구나!! )
프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등 관장할 수 있는 존재만 있으면 된다. 이전에 만든 DaoFactory가 오브젝트 수준의 가장 단순한 IoC 컨테이너 내지 IoC 프레임워크 라고 불리울 수 있겠다.
두근두근, 다음 예제부터는 그럼 스프링 IoC를 알아보자 대박이겠지? ;D
'새로워지기 > 서른의 생활코딩' 카테고리의 다른 글
스프링 부트에서 테스트 주도 개발 실습 - 3장. 모두를 위한 평등 (0) | 2019.02.23 |
---|---|
스프링 부트에서 토비 3.1 따라하기 : 1장 - 1.5 스프링의 IoC (0) | 2019.02.22 |
스프링 부트에서 토비 3.1 따라하기 : 1장 - 1.3 DAO 확장 (0) | 2019.02.21 |
스프링 부트에서 토비 3.1 따라하기 : 1장 - 1.2 DAO 분리 (0) | 2019.02.19 |
스프링 부트에서 테스트 주도 개발 실습 - 2장. 타락한 객체 (0) | 2019.02.19 |
댓글