스프링을 이야기 할때, 의존성을 제외하고 말할 수는 없다.

그렇다면 도대체 스프링에서 얘기하는 의존성이 뭘까?

프로그래밍에서 의존성이라고 하면, 예를 들어 A라는 객체를 만들 때 A의 원형태라고 생각하면 쉽다. 즉, A 객체는 A 클래스에 의존하여 생성되는 것이다.

Car myCar = new Hyndai();

위 코드에서는 myCar가 Hyndai에 의존한다는 것을 알 수 있다.

프로그램을 만들고, 이후에 코드를 업데이트 해야하는 경우가 발생하면, A와 연관된 클래스를 모두 수정해야 하는 일이 발생한다. 즉 의존성을 일일이 모두 변경해줘야 하는 것이다.

만약 내가 현대차에서 Kia차로 바꾼다면 어떻게 해야할 까? myCar가 사용되는 모든 곳에 대해 Hyundai를 Kia로 바꿔줘야 하는 상황이 발생하는 것이다.

Car myCar = new Kia();

Car myCar = new Hyundai();
...

class Hyundai extends Car{
    ...
}

class Car implements Vehicle{
    ...
}

interface Vehicle{
    ...
}

기존에는 위와 같이 코드를 작성한다 할 때, myCar를 만드는 순서는 다음과 같다.

Hyundai -> Car -> Vehicle

Class Diagram으로 설명하면, 위 코드는 다음과 같은 그림을 갖는다.

즉 myCar를 만들기 위해 Hyundai를 만들고, Hyundai를 만들기 위해 Car, Car를 위해 Vehicle이 만들어 지는 것이다.

위처럼 Car myCar = new Hyundai()처럼 직접 객체를 만들고 의존하는 방식을 Composition has a 라고 하는데, 우리는 연결형, 즉 Association has a를 만들려고 한다.

Composition has a는 위에서 언급 했듯이, Kia차로 차를 바꾼다면 모든 myCar를 Kia로 바꿔줘야 하기 때문이다.

Association has a를 한 코드로 예를 들면, 다음과 같다.

...
class Car{
    Car car;
    ...
    void setCar(Car car){
        this.myCar = car;
    }
}

...

Hyundai hyundai = new Hyundai();
Car myCar;
myCar.setCar(hyundai);

...

Car를 연결하는 Hyundai를 따로 만들고, 이를 setter 등을 이용하여 myCar의 Car에 연결하는 것이다. 이렇게 되면 myCar를 바꿀 필요 없이 Hyundai의 선언만 Kia로 바꿔주면 된다.


하지만 스프링은 이를 xml 등을 이용하여 소스를 직접 수정하지않고, 구성 파일만 수정함으로써 의존성을 변경할 수 있게 해준다.

이를 의존성을 주입한다고 하는데, 영어로는 Dependency Injection다.


Inversion of Control ?

갑자기 Inversion of Control이 나와서 이게 뭘까 싶을 수 있다. IoC는 자바의 DI를 이해하기 위해서 알아야 하는 속성인데, 구글 번역기를 돌려보면 "제어 반전"이라는 해석이 나온다. 제어??? 반전??? 도대체 무슨말이야?? 

위에 Class Diagram으로 Composition has a의 순서와 반대로 생성되는 것이다.

Vehicle -> Car -> Hyundai

이 순서로 생성되는 것인데, 이는 이미 스프링의 어떤 xml 파일에서 beans로 정의되어 있기 때문에 가능한 것이다.

xml에 정의되어있는 어떤 컨트롤을 내 소스에 주입 하는 것인데, 쉽게 말하면 만들어진 Hyundai, Kia를 내 myCar.setCar(hyundai) 혹은 myCar.setCar(kia)와 같은 방식으로 자동으로 설정되게 하는 것이다.

이를 이용하면 소스파일의 수정 없이 의존성을 주입할 수 있다.


직접 주입해보자!

이러한 스프링 xml파일을 만들기 위해서는 springframework의 spring-context를 이용하여야 한다. 이를 pom.xml에 넣어주자. 이는 maven을 이용한다면, maven에서 dependency를 추가해주면되고, 그렇지 않다면 직접 코드를 추가해주면 된다. 나의 경우 maven repository에서 추가했고, 추가된 코드는 다음과 같다.

  <dependencies>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-context</artifactId>
  		<version>5.3.3</version>
  	</dependency>
  </dependencies>

이후에 src폴더에 xml만 모아놓은 폴더여도 좋고, DI를 이용하는 소스가 있는 폴더에 넣어도 좋다. 개인적으로는 DI xml을 넣는 폴더를 따로 만들어 관리하기를 추천한다.

이클립스에서 새로 추가하기 -> Other -> Spring -> Spring Bean Configuration File을 추가해 주고 이름을 적절히 설정하면 name.xml파일이 생성된다.

나는 myCar.xml이라 하겠다.

우선, Car 클래스와 관련된 소스는 아래와 같다.

public interface Vehicle {
	int getWheels();
	String getNames();

}

public class Car implements Vehicle {
	String name;
	int wheels;
	
	public Car(String name, int wheels) {
		this.name = name;
		this.wheels = wheels;
	}
	
	@Override
	public int getWheels() { return wheels; }
	@Override
	public String getNames() { return this.name; }
	
	public void setWheels(int wheels) { this.wheels = wheels; }
	public void setName(String name) {	this.name = name;	}
	@Override
	public String toString() {
		return "Car [name=" + name + ", wheels=" + wheels + "]";
	}
}

public class Hyundai implements CarUI {
	private Car car;
	
	public Hyundai() {
		
	}
	
	@Override
	public void print() {
		System.out.println("*************");
		System.out.println("HYUNDAI " + car.toString());
		System.out.println("*************");
	}
	
	public void setCar(Car car) {
		this.car = car;
	}
}

public class Kia implements CarUI {
	private Car car;
	
	public Kia() {
		
	}
	
	@Override
	public void print() {
		System.out.println("=============");
		System.out.println("KIA " + car.toString());
		System.out.println("=============");
	}
	
	public void setCar(Car car) {
		this.car = car;
	}
}	

public interface CarUI {
	void print();
}

Spring을 사용하지 않을때 myCar를 설정한다면, 다음과 같이 해야 했다.

public class MyOwnCar {
	public static void main(String[] args) {
		Car hyundai = new Car("SUV", 4);
		Hyundai myCar = new Hyundai();
		myCar.setCar(hyundai);
		myCar.print();
	}
}

즉, 내 차가 Kia차로 바뀐다면, 아래와 같이 바뀌어야 했다.

public class MyOwnCar {
	public static void main(String[] args) {
    /*
		Car hyundai = new Car("SUV", 4);
		Hyundai myCar = new Hyundai();
    */
		Car kia = new Car("SUV", 4);
		Kia myCar = new Kia();
		myCar.setCar(kia);
		myCar.print();
	}
}

이제는 myCar.xml을 통해 이러한 행동을 하지 않으려고 한다.


스프링에서는 xml과 같은 DI 지시서를 이용하기 위해서는 Spring context를 이용하여야 하는데, 코드로 다음과 같이 작성한다.

ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/myCar.xml");

ApplicationContext를 xml로부터 가져오는데, "spring/di/myCar.xml"이 내가 불러오고자 하는 DI 지시서의 경로인 것이다.

이후 context.getBean을 통해 해당 DI 지시서에 등록되어있는 beans를 이용할 수 있다.

bean을 이용하는 방법은 다음과 같다.

<bean id="의존성 주입할 객체의 변수명" class="해당 객체의 형태를 나타내는 class경로" />

즉, 내 myCar의 경우에는

<bean id="myCar" class="spring.di.entity.Car" />

이 된다.

만약 의존성을 주입할 때 그냥 설정만 하는거라면 우리가 일일이 변수를 다 변경해줘야 하고, 그렇다면 스프링은 지금처럼 많이 쓰이지 않았을 것이다. 한마디로 스프링은 변수와 같은 속성 설정도 지원하는데, 다음과 같은 속성을 사용할 수 있다.

1. index      : n번째 index를 설정한다.
2. name      : name과 동일한 이름을 갖는 변수를 설정한다.
3. p            : p:"NAME" 에서 "NAME"과 동일한 변수를 설정한다.
                  이 때, xmlns:p="http://www.springframework.org/schema/p" 를 추가하여야 한다.
4. setter를 직접 사용한다.

각각 사용하는 방법은 다음과 같다.

<bean id="myCar" class="spring.di.entity.Car">
    <!-- 1번. index를 이용하는 경우 -->
	<constructor-arg index="0" value="SUV" />
	<constructor-arg index="1" value="4" />
    <!-- 
    만약 생성자가 2개 이상이고, 자료형만 다른 parameter를 전달 받는다면 type을 이용해 구분할 수 있다.
    <constructor-arg index="0" type="int" value="4" />
    <constructor-arg index="0" type="String" value="SUV" />
    -->
	
    <!-- 2번 name을 이용하는 경우 -->
	<property name="name" value="SUV" />  
	<property name="wheels" value="4" />  	 
    
    <!-- 3번 p 속성을 이용하는 경우 -->
    <bean id="myCar" class="spring.di.entity.Car" p:name="SUV" p:wheels="4" />
    
    <!-- 4번. 직접 setter를 이용하는 경우 -->
    myCar.setName("SUV");
</bean>

나머지 경우는 모두 직접 해보시길 바라고, 나는 2번 name을 이용하였을 때의 결과를 보여주겠다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="myCar" class="spring.di.entity.Car"  >
		<constructor-arg name="name" value="SUV" />
		<constructor-arg name="wheels" value="4" />
	</bean> 
    
    <!-- myCar는 Hyundai가 된다. -->
	<bean id="console" class="spring.di.ui.Hyundai">
		<property name="car" ref="myCar" /> 
	</bean>

</beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.di.entity.Car;
import spring.di.ui.CarUI;

public class MyOwnCar {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/myCar.xml");
		Car myCar = context.getBean(Car.class);

		CarUI console = (CarUI) context.getBean("console");
		console.print();
	}
}

실행 결과. Hyundai의 print가 실행됐다.

 

'웹서버 > Spring' 카테고리의 다른 글

[설치] Spring Framework 이클립스에 설치하기  (0) 2021.01.19

Spring Framework를 이클립스에 설치하는 방법을 알아보려고 한다.

웹서버 프레임워크를 만들면서 웹서버의 기초를 공부하고 있고, 실제 환경(프레임워크 등)에서는 어떻게 동작하는지, 어떤식으로 문제를 해결했는지 공부하기 위해 스프링 프레임워크에 대해 알아보려고 한다.

Spring Framework는 어떤 기업을 가든 안쓰는곳이 없을 정도로 널리 쓰이는 프레임워크이다.

스프링 프레임워크가 뭐고 어쩌고 하는거는 따로 다루지 않겠다.

다만 Spring Framework가 왜 잘 쓰이는지, 어떤 장점을 갖는지는 간단하게 적어보려고 한다.

MVC, Dependency Injection, Transaction (AOP) 등.

MVC: Model, View, Controller로 Model <-> Controller <-> View와 같은 디자인 패턴이다.

Dependency Injection: xml, annotation과 같은 데이터를 활용하여 의존성을 낮출 수 있다.

Transaction (AOP): 핵심코드와 부가기능 분리


Spring Framework 설치

Spring Framework는 http://spring.io 에서 다운로드를 하여 이클립스에서 사용하거나,

 

Spring makes Java simple.

Level up your Java code and explore what Spring can do for you.

spring.io

이클립스 마켓플레이스에서 Spring Tools Suite (STS)를 검색함으로써 설치할 수 있다.

마켓플레이스에서 STS를 검색하고, 빨간줄로 표시된 것과 같은것을 설치하면 된다.

성공적으로 설치가 되면, 이클립스에서 새로운 프로젝트를 생성할 때 Spring 항목이 있어야 한다.

따로 링크에서 설치를 하더라도, 똑같이 떠야한다.

'웹서버 > Spring' 카테고리의 다른 글

[Dependency] Spring Dependency / 스프링 의존성  (0) 2021.01.19

+ Recent posts