public class HelloServlet extends HttpServlet {
public HelloServlet() {
System.out.println("HelloServlet 객체 생성")
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doGet() 메소드 호출")
}
}
이클립스를 이용하여 서블릿 클래스를 개발한다면 작성된 Servlet 클래스는 web.xml 파일에 자동으로 등록된다.
<web-app>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>hello.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
위 설정은 브라우저에서 /hello.do라는 URL 요청을 전송하면, hello라는 이름으로 등록된 hello.HelloServlet 클래를 찾아 객체를 생성하고 실행한다는 설정이다. 이제 작성된 HelloServlet 프로그램을 실행하면 다음과 같은 메시지가 콘솔에 출력되며, web.xml 설정대로 객체가 생성되고 실행되는 것을 확인할 수 있다.
서블릿은 자바로 만들어진 클래스이다. 따라서 반드시 객체 생성을 해야 객체가 가지고 있는 메소드도 호출 할 수 있다.
위 자바 코드에서는 doGet() 호출하는 부분이 없는 지만, "서블릿 컨테이너"가 호출 해준다.
서블릿 컨테이너는 WEB-INF/web.xml 파일을 로딩하여 구동되고 > 브라우저로 부터 /hello.do 요청 수신을 받고, > hello.HelloServlet 클래스를 찾아 객체를 생성하고 doGet()메소드를 호출하며 > doGet() 메소드 실행 결과를 클라이언트 브라우저로 전송한다.
이렇듯 컨테이너는 자신이 관리할 클래스들이 등록된 XML 설정 파일을 로딩하여 구동한다.
그리고 클라이언트의 요청이 들어오는 순간 XML 설정 파일을 참조하여 객체를 생성하고, 객체의 생명주기를 관리한다. 스프링 컨테이너 역시 서블릿 컨테이너와 유사하게 동작하므로 위에서 살펴본 요소들과 비슷한 요소들이 존재한다.
제어의 역행(IoC)은 결합도와 관련된 개념으로 이해할 수 있다.
기존에 자바 기반으로 애플리케이션을 개발할 때, 객체를 생성하고 객체들 사이의 의존관계를 처리하는 것에 대한 책임은 전적으로 개발자에게 있었다. 즉, 개발자가 어떤 객체를 생성할지 판단하고 객체 간의 의존관계 역시 소스코드로 표현 해야 했다.
하지만 제어의 역행이라는 것은 이런 일련의 작업들을 소스코드로 처리하지 않고 컨테이너로 처리하는 것을 의미한다. 따라서 제어의 역행을 이용하면 소스에서 객체 생성과 의존관계에 대한 코드가 사라져 결과적으로 낮은 결합도의 컴포넌트를 구현할 수 있게 한다.
결합도란 하나의 클래스가 다른 클래스와 얼마나 많이 연결되어 있는지를 나타내는 표현이며, 결합도가 높은 프로그램은 유지보수가 어렵다.
package polymorphism;
public class SamsungTV {
public void powerOn(){
System.out.println("SamsungTV Power On")
}
public void powerOff(){
System.out.println("SamsungTV Power Off")
}
public void volumeUp(){
System.out.println("SamsungTV Volume Up")
}
public void volumeDown(){
System.out.println("SamsungTV Volume Down")
}
}
package polymorphism;
public class LgTV {
public void turnOn(){
System.out.println("LgTV Turn On")
}
public void turnOff(){
System.out.println("LgTV Turn Off")
}
public void soundUp(){
System.out.println("LgTV Sound Up")
}
public void soundDown(){
System.out.println("LgTV Sound Down")
}
}
서로다른 TV 클래스가 있고 메소드 이름도 다르다.
package polymorphism;
public class TVUser {
public static void main(String[] args) {
SamsungTV tv = new SamsungTV();
tv.powerOn();
tv.volumeUp();
tv.volumeDown();
tv.powerOff();
}
}
// 위 코드를 아래 코드로 수정
// 객체 이름과 메소드 이름을 수정 해야함
public class TVUser {
public static void main(String[] args) {
LgTV tv = new LgTV();
tv.turnOn();
tv.soundUp();
tv.soundDown();
tv.turnOff();
}
}
SamsungTV와 LgTV는 메소드 시그니처(signagture)가 다르므로 TVUser 코드 대부분을 수정해야 TV를 교체할 수 있다.
현재 상태에서는 두 TV 클래스가 같은 메소드를 가지게끔 강제할 어떤 수단도 없다.
만약 TVUser와 같은 클라이언트 프로그램이 하나가 아니라 여러 개라면 유지보수는 더욱더 힘들 것이며, TV교체를 결정하기가 쉽지 않을 것이다.
결합도를 낮추기 위해서는 다양한 방법을 사용할 수 있겠지만, 가장 쉽게 생각할 수 있는 것이 객체지향 언어의 핵심 개념인 다형성을 이용하는 것이다.
다형성을 이용하려면 상속과 메소드 재정의(Overriding), 그리고 형변환이 필요하며, 자바 같은 객체지향 언어는 이를 문법으로 지원한다.
결합도를 낮추기 위한 또다른 방법으로 디자인 패턴을 이용하는 방법이 있다.
앞에서 살펴본 다형성을 이용하는 방법은 메소드를 호출할 때 인터페이스를 ㅣ용함으로써 좀 더 쉽게 TV를 교체할 수 있었다. 하지만 이 방법 역시 TV를 변경하고자 할 때, TV 클래스 객체를 생성하는 소스를 수정해야만 한다.
TV를 교체할 때, 클라이언트 소스를 수정하지 않고 TV를 교체할 수만 있다면 유지보수는 더욱 편리해질 것이다. 이를 위해서 Factory패턴을 적용해야 하는데, Factory 패턴은 클라이언트에서 사용할 객체 생성을 캡슐화하여 TVUser와 TV사이를 느슨한 결합 상태로 만들어준다.
'WebProgramming > SPRING' 카테고리의 다른 글
[New Lecture - Spring] 3강 - DI(Dependency Injection) (0) | 2020.05.28 |
---|---|
[New Lecture - Spring] 느슨한 결합력과 인터페이스 (0) | 2020.05.28 |
[Spring Boot]2. 환경설정 (0) | 2020.05.19 |
[Spring Boot]1.Spring Boot? (0) | 2020.05.19 |
SPRING FRAMEWORK - MODEL 2 & SPRING MVC (0) | 2019.05.09 |
댓글