실습 코드 참조
moonhy7/SpringFramework: Spring Framework 실습 코드 정리 (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 객체를 적절하게 생성해서 넘겨줌
댓글