일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자바
- oracle
- cursor문
- 메소드오버로딩
- 집합_SET
- 참조형변수
- 객체 비교
- 환경설정
- 한국건설관리시스템
- 컬렉션 타입
- GRANT VIEW
- 어윈 사용법
- 예외미루기
- 정수형타입
- 인터페이스
- 컬렉션프레임워크
- 대덕인재개발원
- EnhancedFor
- 다형성
- Java
- 제네릭
- 추상메서드
- 오라클
- exception
- 예외처리
- abstract
- 생성자오버로드
- 사용자예외클래스생성
- 자동차수리시스템
- NestedFor
- Today
- Total
거니의 velog
(2) 스프링 의존성 주입과 제어 역전 기능 1 본문
* 자바와 같은 객체 지향 프로그래밍 언어에서 클래스는 특정 기능을 수행하는 부품 역할을 한다. 우리가 살아가는 현실 세계에서도 어떤 부품을 사용하다가 이상이 있거나 오래 되면 다른 부품으로 교체해서 사용한다. 마찬가지로 애플리케이션에서도 사용자의 요구 사항에 따라 클래스 기능을 변경하거나 다른 클래스 기능으로 대체해야 하는 경우가 자주 생긴다. 이러한 상황에서 좀 더 수월하게 대체할 수 있게 도입된 기능이 바로 의존성 주입(DI)과 제어 역행(IoC)이다.
1. 의존성 주입하기
* 지금까지 우리가 프로그래밍을 할 때는 어떤 한 클래스가 다른 클래스의 기능을 사용하려면 당연히 개발자가 직접 코드에서 사용할 클래스의 생성자를 호출해서 사용했다. 즉, 사용할 클래스와 사용될 클래스의 관계는 개발자에 의해 직접 코드에서 부여된다.
* 의존성 주입이란 이런 연관 관계를 개발자가 직접 코딩을 통해 컴포넌트(클래스)에 부여하는 것이 아니라 컨테이너가 연관 관계를 직접 규정하는 것이다. 그러면 코드에서 직접적인 연관 관계가 발생하지 않으므로 각 클래스들의 변경이 자유로워 진다(loosely coupled, 약한 결합).
<강한 결합과 약한 결합>
* 현실에서 우리는 자동차의 에어컨이 고장나면 당연히 에어컨만 수리하거나 교체하면 된다.
하지만 만약 에어컨 기능이 자동차 엔진과 관련 있게 설계되었다면 어떨까? 에어컨에 작은 문제라도 생기면
자동차 엔진까지 손을 봐야 하는 상황이 된다. 난감할 것이다. 즉, 자동차의 부품은 같은 기능끼리는 강하게
결합하고, 큰 관련이 없는 기능과는 서로 영향을 주지 않게 만들어야 좋은 자동차라 할 수 있다.
* 프로그램도 마찬가지이다. 프로그램은 각각의 독립적인 기능들로 구성되어 있다.
쇼핑몰의 경우 크게 상품 관리, 주문 관리, 회원 관리, 게시판 관리 등으로 구성된다. 각 기능들은 또 세부
기능을 하는 여러 클래스들로 이루어진다. 그런데 부품 기능을 하는 클래스에 변경 사항이 발생했을 때
그 클래스의 기능과 관련 없는 다른 클래스까지 손봐야 한다면 자동차의 예처럼 여러 가지 문제가 발생할 수 있다.
* 따라서 서로 관련이 있는 기능들은 강하게 결합(tightly coupled)하고, 관련이 없는 기능들은 약하게 결합
(loosely coupled)해야 좋은 프로그램이다. 그 반대가 되면 안 된다.
* 전체 애플리케이션은 각각의 기능을 담당하는 컴포넌트들로 이루어진다. 그리고 각 컴포넌트들은 다시 세부 기능을 수행하는 클래스들로 이루어진다. 그런데 컴포넌트를 이루는 클래스들이 다른 클래스의 기능을 사용하려면 어떻게 해야 할까? 소스 코드에서 다른 클래스의 생성자를 호출해서 사용할 경우 기능을 구현하는 과정에서 다른 변경 사항이 발생하면 빠르게 대처하기가 어렵다. 또다시 관련이 있는 모든 클래스들의 소스 코드를 수정해 주어야 하기 때문이다.
* 따라서 스프링 프레임워크는 각 클래스들의 연관 관계를 클래스들 사이에서 맺는 것이 아니라 스프링 프레임워크에서 설정을 통해 맺어줌으로써 클래스들이 연관 관계를 갖지 않게 구현했다. 이론보다는 실제 예제를 통해 더 확실히 알아보도록 하자.
(1) 의존성 주입을 사용하기 전 게시판 기능
* 다음 코드는 이전에 실습한 게시판의 소스 코드이다. 각 클래스들의 기능을 보면 다른 클래스의 기능을 사용하기 위해 소스 코드에서 직접 다른 클래스 객체를 생성한 후 메서드를 호출하여 연동한다.
...
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
private static String ARTICLE_IMAGE_REPO = "C:\\board\\article_image";
BoardService boardService;
ArticleVO articleVO;
public void init(ServletConfig config) throws ServletException {
boardService = new BoardService(); // BoardService 객체를 코드에서 직접 생성해 사용한다.
}
...
...
public class BoardService {
BoardDAO boardDAO;
public BoardService() {
boardDAO = new BoardDAO(); // BoardDAO 객체를 코드에서 직접 생성해 데이터베이스와 연동한다.
}
...
public class BoardDAO {
private DataSource dataFactory;
Connection conn;
PreparedStatement pstmt;
public BoardDAO() {
try {
Context ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:/comp/env");
dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
} catch (Exception e) {
e.printStackTrace();
}
}
...
* 현재 BoardDAO 클래스에서는 오라클과 연동해 게시판 기능을 구현하고 있다. 그런데 만약 중간에 오라클에서 MySQL로 데이터베이스를 변경한다고 가정해 보자. 지금과 같은 경우는 BoardDAO 클래스의 기능을 일일이 변경해 주어야 한다. 그뿐만 아니라 경우에 따라서는 BoardDAO 클래스를 사용하는 BoardService 클래스의 기능도 변경해야 할 수도 있다.
* 이제까지는 클래스를 사용하려면 자바에서 배웠듯이 자바 코드에서 클래스 생성자를 호출해 객체를 생성했다. 하지만 지금처럼 프로젝트 규모가 점점 커지는 상황에서 이렇게 자바 코드에서 직접 객체를 생성해서 사용하는 것(tightly coupled)은 복잡한 문제를 일으킬 수도 있다. 다른 클래스의 변경 사항이 연속적으로 다른 부분에 영향을 미친다면 이 방법(자바 코드에서 직접 객체를 생성해서 사용하는 것)은 좋은 방법이 아니다.
(2) 인터페이스를 적용한 게시판 기능
* 그럼 이번에는 앞에서 다룬 게시판 기능 구현 시의 문제점을 인터페이스를 사용해 해결해 보자.
* 다음 그림은 게시판 기능과 관련된 클래스들의 계층 구조를 나타낸 것이다. 각각의 클래스가 인터페이스를 구현하는 구조를 이룬다.
* 다음 코드는 각 클래스들이 상위 인터페이스를 구현한 후 오라클 데이터베이스와 연동하는 기능을 구현하는 소스이다.
public class BoardServiceImpl implements BoardService {
BoardDAO boardDAO;
public BoardService() {
boardDAO = new BoardOracleDAOImpl(); // 인터페이스를 이용해 하위 클래스 객체를 생성한 후 오라클 데이터베이스와 연동한다.
}
...
public class BoardOracleDAOImpl implements BoardDAO {
private DataSource dataFactory;
Connection conn;
PreparedStatement pstmt;
public BoardDAO() {
try {
Context ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:/comp/env");
dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
} catch (Exception e) {
e.printStackTrace();
}
}
...
* 이번에는 개발 중에 MySQL과 연동하는 기능이 생겼다고 가정해 보자. 지금처럼 인터페이스로 구현한 경우에는 기존의 BoardOracleDAOImpl 클래스를 변경할 필요가 없다.
* 다음 그림처럼 BoardDAO 인터페이스를 구현한 또 다른 BoardMySqlDAOImpl 클래스를 구현한 후 다음 코드처럼 BoardServiceImpl에서 사용하면 된다.
public class BoardServiceImpl implements BoardService {
BoardDAO boardDAO;
public BoardService() {
// boardDAO = new BoardOracleDAOImpl();
boardDAO = new BoardMySqlDAOImpl(); // 인터페이스를 이용해 하위 클래스 객체를 생성한 후 MySQL 데이터베이스와 연동한다.
}
...
* 인터페이스를 이용해 각 클래스를 구현한 후 각 클래스의 객체를 사용할 때는 인터페이스 타입으로 선언한 참조 변수로 접근해서 사용하면 된다. 그러면 완전하지는 않아도 앞의 경우보다 훨씬 클래스들 간 의존 관계가 약해진다. 그러나 인터페이스를 사용해도 BoardServiceImpl 클래스 자체는 여전히 소스 코드에서 직접 수정해야 한다.
(3) 의존성 주입을 적용한 게시판 기능
* 앞의 게시판 예제를 통해 클래스들의 의존 관계가 강하게 결합되어 있으면 여러 가지 문제가 발생할 수 있음을 알았다. 이번에는 스프링의 의존성 주입 기능을 이용해 각 클래스들 사이의 의존 관계를 완전히 분리하는 작업을 실습해 보자.
* 다음은 의존성 주입을 적용했을 때 얻을 수 있는 장점들이다.
- 클래스들 간의 의존 관계를 최소화하여 코드를 단순화할 수 있다.
- 애플리케이션을 더 쉽게 유지 및 관리할 수 있다.
- 기존 구현 방법은 개발자가 직접 코드 안에서 객체의 생성과 소멸을 제어했다.
하지만 의존성 주입은 객체의 생성, 소멸과 객체 간의 의존 관계를 컨테이너가 제어한다.
* 스프링에서 의존성 주입(이하, DI)을 구현하려면 XML이나 애너테이션을 이용해 객체를 주입하여 객체들의 의존 관계를 맺어주면 된다. 즉, DI를 사용하여 각 객체들 간의 의존 관계를 최소화함으로써 코드를 단순화하고 유지보수를 쉽게 할 수 있다.
* DI는 객체의 생성, 소멸, 의존 관계를 개발자가 직접 설정하는 것이 아니라 XML이나 애너테이션 설정을 통해 경량 컨테이너에 해당하는 스프링 프레임워크가 제어한다. 따라서 기존 코드에서는 개발자가 직접 객체를 제어했지만 스프링 프레임워크에서는 객체의 제어를 스프링이 직접 담당하므로 제어의 역전(이하 IoC)이라고 하는 것이다. IoC의 종류도 여러 가지이며, 일반적으로 스프링에서는 DI로 IoC의 기능을 구현하므로 IoC보다는 DI라는 용어를 더 많이 사용한다.
* 다음 코드는 DI를 적용해 게시판 기능을 구현한 것이다
public class BoardServiceImpl implements BoardService {
private BoardDAO boardDAO;
public BoardServiceImpl(BoardDAO boardDAO) {
this.boardDAO = boardDAO;
} // DI 적용 예시...
...
* BoardServiceImpl 클래스는 의존하는 BoardDAOImpl 객체를 전달받기 위해 new 키워드를 사용해 객체를 생성하지 않고 생성자를 호출할 때 외부에서 객체를 주입 받아 사용했다. 이것이 바로 DI를 적용한 예이다. 소스 코드에서 new를 사용해 객체를 생성하는 것이 아니라 BoardServiceImpl 생성자를 호출할 때 컨테이너에 의해 주입되는 객체로 boardDAO 변수를 초기화한 것이다.
* DI를 사용하면 객체의 의존 관계를 외부에서 설정한다고 했다. 외부 설정에 의해 각 의존 객체가 어떻게 주입되는지 그 과정을 다음 그림으로 나타내었다.
<외부 설정에 의한 의존 객체 주입>
* 위 그림처럼 스프링에서는 의존(dependency)하는 객체를 컨테이너 실행 시 주입(inject)하기 때문에 DI(Dependency Injection)라고 부른다. 여기서 각 클래스 객체를 bean(이하 빈)이라고 부르는데, 이는 의존 관계를 설정하는 외부 XML 파일에서 각각의 객체를 <bean> 태그로 표시하기 때문이다.
* 이번에는 스프링에서 의존 객체를 주입하는 방식을 알아보자. 스프링의 의존 객체 주입 방식은 생성자를 이용해서 주입하는 방식과 setter를 이용해서 주입하는 방식이 있다.
* 앞의 코드는 생성자를 이용한 주입 방식이고, 다음 코드는 setter를 이용한 의존 객체 주입 방식이다.
public class BoardServiceImpl implements BoardService {
private BoardDAO boardDAO;
public void setBoardDAO(BoardDAO boardDAO) {
this.boardDAO = boardDAO;
} // setter를 이용해 컨테이너에서 생성된 BoardDAOImpl 객체를 주입
...
'Java_Spring Framework part1' 카테고리의 다른 글
(6) 스프링 MVC 기능 1 (0) | 2023.10.03 |
---|---|
(5) 스프링 AOP 기능 (0) | 2023.10.03 |
(4) 스프링 의존성 주입과 제어 역전 기능 3 (0) | 2023.10.03 |
(3) 스프링 의존성 주입과 제어 역전 기능 2 (0) | 2023.10.02 |
(1) 스프링 프레임워크 시작하기 (0) | 2023.09.27 |