본문 바로가기
👨‍🏫Study/Spring Study

[Spring] 2장 프레임워크 개요 - 프레임워크의 개념과 장점, 특징 / 결합도를 낮추는 방법

by 코푸는 개발자 2022. 2. 2.
728x90
실습 코드 참조

 

moonhy7/SpringFramework: Spring Framework 실습 코드 정리 (github.com)

 

GitHub - moonhy7/SpringFramework: Spring Framework 실습 코드 정리

Spring Framework 실습 코드 정리. Contribute to moonhy7/SpringFramework development by creating an account on GitHub.

github.com

 

 

2.1절 프레임워크 개념

 

1. 프레임워크의 등장 배경

 

1) 용어 정리

1. 프레임워크 : 틀, 뼈대

2. 스프링 프레임워크 : 웹 어플리케이션 개발에 뼈대, 틀을 제공, jsp, java, 쿼리 문 등으로 살과 근육 붙이는 작업을 진행

4. 컨테이너 : 뼈대에 붙은 근육이나 살을 동작시키는 명령을 내림 (프레임워크를 구동시키는 )

- 스프링 프레임워크에서는 설정 파일로 컨테이너를 조작

 

2. 프레임워크의 장점

 

① 빠른 구현 시간 : 개발자는 살과 근육만 붙이면 되기 때문에 매우 빠른 생산성을 가지게 됨

② 쉬운 관리 : 프레임워크로 개발하게 되면 개발 아키텍처가 동일하기 때문에 소스코드 관리가 편해짐

③ 역량 획일화 : 프레임워크를 이용하면 개발자 역량의 차이를 줄일 수 있다.

④ 아키텍쳐의 재사용과 일관성 유지 : 유지보수 과정에서 아키텍처가 수정되지 않는한 검증없이 소프트웨어 개발 가능

 

3. 자바 기반의 프레임워크

 

- 자바 기반의 프레임워크는 대부분 오픈소스 형태로 제공됨

- 아래 표는 대표적인 자바 기반의 프레임워크들을 나열함

 

 

2.2절 스프링 프레임워크

 

1. 스프링 탄생 배경

 

- WAS가 고가의 장비다 보니까 EJB를 기반으로 하는건 비싸고 시간과 노력이 필요

- EJB보다 훨씬 간단하게 사용할 수 있도록 만들어진게 스프링 프레임워크임

 

2. 스프링 프레임워크의 특징

 

1) 경량화

- JAR 파일 임포트 및 어플리케이션 배포가 간편하고 편함

 

2) 제어의 역행 (Inversion of Control)

- 스프링에서는 컨테이너에서 자동으로 객체를 생성해주고 관리해줌 (원래는 개발자가 객체를 직접 생성하고 사용)

- 개발자는 결합도가 낮은 프로그래밍을 가능하게 해줌

- 어노테이션, 설정파일 빈(Bean)객체를 등록하는 방식

 

3) 의존성 주입(Dependency Injection)

- 객체가 생성될 때 필요한 속성값들이나 필드변수들 자동으로 주입

- 결합도가 낮은 프로그래밍 가능하게 해줌

- 어노테이션, 설정파일에 의존성 주입 설정

 

4) 관점 지향 프로그램(Aspect Oriented Programming, AOP)

- 공통으로 동작하는 로그, 오류, 보안, 트랜잭션과 같은 프로그램을 공통관심으로 묶어서 한 번에 관리

- 개발자는 핵심관심이 비즈니스 로직만 구현하면 됨

 

5) 컨테이너

- 스프링 프레임워크를 구동시켜주는 역할

- 서블릿 컨터이너, 부모컨테이너, 자식컨테이너 3개의 컨테이너가 동작

- 어플리케이션 운용에 필요한 객체를 생성하고 의존관계를 관리

 

-> 스프링을 한 줄로 표현하면 "IoC와 AOP를 지원하는 경량의 컨테이너 프레임워크"

 

 

2.3절 IoC (Inversion of Control) 컨테이너

 

1. 간단한 서블릿 클래스 만들기 실습 ( _001_BoardWeb )

 

① HomeController 삭제

 

② 서블릿 추가

 

- HelloServlet.java 코드

package com.springbook.biz;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class HelloServlet
 */
public class HelloServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloServlet() {
        System.out.println("===> HelloServlet 객체 생성");
        
    }

   /**
    * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    */
   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   System.out.println("doGet() 메소드 호출");   
   }

   /**
    * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    */
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      // TODO Auto-generated method stub
      doGet(request, response);
   }

}

 

③ web.xml 코드 수정

- /hello.do 라는 URL 요청 전송 시 hello라는 이름으로 등록된 hello.HelloServlet 클래스를 찾아 객체를 생성하고 실행

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.springbook.biz.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello.do</url-pattern>
  </servlet-mapping>
</web-app>

 

④ 톰캣 포트번호, 경로 수정

 

 

 실행 결과

 

⑥ 서블릿 컨테이너의 서블릿 객체 관리 구조

- 우리가 작성한 코드에는 객체를 생성하는 코드도, doGet() 메소드 호출 코드도 모두 존재하지 않음

- 바로 서블릿 컨테이너가 Servlet 클래스 객체를 생성하고 운용하기 때문

- 동작 순서

web.xml 파일 로딩하여 구동  ->  브라우저로부터 /hello.do 요청 수신  ->

해당하는 클래스를 찾아 객체 생성하고 doGet() 메소드 호출  ->  메소드 실행 결과를 클라이언트 브라우저로 전송

 

2. 결합도(Coupling)가 높은 프로젝트 실습 ( _002_BoardWeb_Coupling )

 

1) SamsungTV.java

package polymorphism;

public class SamsungTV {
	public void powerOn() {
		System.out.println("SamsungTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("SamsungTV---전원 끈다.");
	}
	
	public void VolumeUp() {
		System.out.println("SamsungTV---소리 올린다.");
	}
	
	public void VolumeDown() {
		System.out.println("SamsungTV---소리 내린다.");
	}
}

 

2) LgTV.java

package polymorphism;

public class LgTV {
	public void turnOn() {
		System.out.println("LgTV---전원 켠다.");
	}
	
	public void turnOff() {
		System.out.println("LgTV---전원 끈다.");
	}
	
	public void soundUp() {
		System.out.println("LgTV---소리 올린다.");
	}
	
	public void soundDown() {
		System.out.println("LgTV---소리 내린다.");
	}
}

 

3) TVUser 클래스 생성

두 TV 클래스를 번갈아 사용하는 프로그램

package polymorphism;

public class TVUser {
	public static void main(String[] args) {
//		SamsungTV tv = new SamsungTV();
//		tv.powerOn();
//		tv.volumeUp();
//		tv.volumeDown();
//		tv.powerOff();
		
		//결합도가 높은 프로그램 : 개발자가 생성하고 관리해야되는 객체들이 많아지는 프로그램
		LgTV tv = new LgTV();
		tv.turnOn();
		tv.soundUp();
		tv.soundDown();
		tv.turnOff();
	}
}

 

4) SamsungTV 객체 생성했을 때 실행 결과

 

5) LgTV 객체 생성했을 때 실행 결과

 

- TV를 교체하기 위해서 TVUser 코드의 대부분을 수정해야함

- 메소드들도 다 다르기 떄문에 교체가 어려움

 

3. 결합도를 낮추는 방법 1 - 인터페이스 다형성 이용하기 ( _003_BoardWeb_CouplingWeak_interface )

 

1) 인터페이스 구조

 

2) TV 인터페이스 추가

 - 모든 TV가 공통으로 가져야 할 메소드들을 추상 메소드로 선언

package polymorphism;

public interface TV {
	public void powerOn();
	public void volumeUp();
	public void volumeDown();
	public void powerOff();
}

 

3) SamsungTV 클래스

- TV 인터페이스에 선언된 추상 메소드들을 모두 재정의할 것 (클래스들끼리 같은 메소드를 같도록 강제함)

package polymorphism;

public class SamsungTV implements TV {
	public void powerOn() {
		System.out.println("SamsungTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("SamsungTV---전원 끈다.");
	}
	
	public void volumeUp() {
		System.out.println("SamsungTV---소리 올린다.");
	}
	
	public void volumeDown() {
		System.out.println("SamsungTV---소리 내린다.");
	}
}

 

4) LgTV 클래스

- TV 인터페이스에 선언된 추상 메소드들을 모두 재정의할 것 (클래스들끼리 같은 메소드를 같도록 강제함)

package polymorphism;

public class LgTV implements TV {
	public void powerOn() {
		System.out.println("LgTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("LgTV---전원 끈다.");
	}
	
	public void volumeUp() {
		System.out.println("LgTV---소리 올린다.");
	}
	
	public void volumeDown() {
		System.out.println("LgTV---소리 내린다.");
	}
}

 

5) TVUser 클래스 수정

- TV 인터페이스 타입의 변수 tv에 SamsungTV 객체를 참조하고 있음 

- 묵시적 형변환이 이루어짐

- Samsung을 Lg로만 바꾸면 참조하는 객체를 변경할 수 있음

- 다형성을 이용하여 최소한의 수정으로 TV 교체를 할 수 있음

- 다형성 이용 방법의 단점 : 클라이언트 소스를 교체해야하는 점

package polymorphism;

public class TVUser {
	public static void main(String[] args) {
		TV tv = new SamsungTV();
		tv.powerOn();
		tv.volumeUp();
		tv.volumeDown();
		tv.powerOff();
	}
}

 

6) 실행 결과

 

3. 결합도를 낮추는 방법 2- 디자인 패턴 이용하기 ( _004_BoardWeb_CouplingWeak_DesignPattern )

 

1) BeanFactory 클래스

- 클라이언트 클래스 소스를 수정하지 않고도 TV 교체가 자동으로 가능하게 만듬

- Factory 패턴을 적용함 (클라이언트에서 사용할 객체 생성을 캡슐화하여 클래스들 사이를 느슨한 결합으로 만듬)

package polymorphism;

public class BeanFactory { //클라이언트에서 자동적으로 TV객체를 생성할 수 있도록 설정
	public Object getBean(String beanName) {
		if(beanName.equals("samsung")) {
			return new SamsungTV();
		} else if(beanName.equals("lg")) {
			return new LgTV();
		}
		return null;
	}
}

 

2) TVUser.java 코드 수정

- BeanFactory 클래스의 getBean() 메소드의 매개변수로 beanName에 해당하는 객체를 생성해서 리턴한다.

package polymorphism;

public class TVUser {
	public static void main(String[] args) {
		BeanFactory factory = new BeanFactory();
		TV tv = (TV)factory.getBean(args[0]);
		tv.powerOn();
		tv.volumeUp();
		tv.volumeDown();
		tv.powerOff();
	}
}

 

3) 매개변수로 실행 방법

- Run As > Run Configurations...

 

- 실행할 프로젝트 추가

 

- 매개변수로 lg 또는 samsung을 입력하고 Run 버튼 클릭하여 실행

 

4) 실행 결과

- 팩토리 패턴을 이용하여 클라이언트 소스의 수정 없이도 TV를 변경할 수 있게 됨

 

5) 구조

- 클라이언트에 해당하는 TVUser가 객체를 '직접' 생성하지 않도록 만듬

- 단지 객체 생성을 BeanFactory에 요청함

- 요청을 받은 BeanFactory가 클라이언트가 사용할 TV 객체를 적절하게 생성해서 넘겨줌

 

728x90

댓글