728x90


스프링 프레임워크에서는 시작할때 중요하게 가르치는 것은 IoC이다.

IoC는 스프링 프레임워크의 가장 중요한 철학중 하나이기 때문이다.

IoC는 Inversion of Control의 약자이다. 이를 한국말로 하면 제어의 역전이다.


제어의 역전이라... 말이 좀 어렵다. 아무리 봐도 쉬운 개념은 아니다.

컴퓨터에 대한게 으래 그렇듯이 사실 무슨 개념에 대해서 설명하려고 하면 힘들다.

그걸 쓰다보면 그걸 이해하게된다. 정확히 말하면 컴퓨터에 대한 개념을 현실세계에 정확히 대치하기는 어려운것 같다.


그래도 굳이 설명해야한다. 그런데 사실 설명하기 보다는 보여주는게 더 나을것같다.

백문이 불여일견이라는 말이 괜히 있는것은 아니니 말이다.

아래의 일련의 상황들은 일반적으로 우리가 사용하는 자바빈에 대한 사용 예시이다.


##interface Champ

package champ;

public interface Champ {
public void attack();

public void move();

public void skill1();

public void skill2();

public void skill3();

public void skill4();
}


##class Riven

package champ;

public class Riven implements Champ {
int ad;
int ap;
int speed;
String skill1;
String skill2;
String skill3;
String skill4;

{
ad = 60;
ap = 0;
speed = 340;
skill1 = "부서진 날개";
skill2 = "기 폭팔";
skill3 = "용맹";
skill4 = "추방자의 검";
}

@Override
public void attack() {
System.out.println("Riven.attack : " + ad);
}

@Override
public void move() {
System.out.println("Riven.move : " + speed);
}

@Override
public void skill1() {
System.out.println("Riven.skill1 : " + skill1);
}

@Override
public void skill2() {
System.out.println("Riven.skill2 : " + skill2);
}

@Override
public void skill3() {
System.out.println("Riven.skill3 : " + skill3);
}

@Override
public void skill4() {
System.out.println("Riven.skill4 : " + skill4);
}
}


##class Evelynn

package champ;

public class Evelynn implements Champ {
int ad;
int ap;
int speed;
String skill1;
String skill2;
String skill3;
String skill4;

{
ad = 50;
ap = 30;
speed = 335;
skill1 = "증오의 가시";
skill2 = "황홀한 저주";
skill3 = "채찍유린";
skill4 = "최후의 포옹";
}

@Override
public void attack() {
System.out.println("Evelynn.attack : " + ad);
}

@Override
public void move() {
System.out.println("Evelynn.move : " + speed);
}

@Override
public void skill1() {
System.out.println("Evelynn.skill1 : " + skill1);
}

@Override
public void skill2() {
System.out.println("Evelynn.skill2 : " + skill2);
}

@Override
public void skill3() {
System.out.println("Evelynn.skill3 : " + skill3);
}

@Override
public void skill4() {
System.out.println("Evelynn.skill4 : " + skill4);
}
}


##class BeanFactory

package champ;

public class BeanFactory {
public static BeanFactory instance = new BeanFactory();

private BeanFactory() {
/*pass*/
}

public Object getBean(String beanName) {
if (beanName.equals("riven")) {
return new Riven();
} else if (beanName.equals("evelynn")) {
return new Evelynn();
}
return null;
}

public static BeanFactory getInstance() {
return instance;
}
}


##class Main

package champ;

public class Main {

public static void main(String[] args) {
Champ c = (Champ) BeanFactory.getInstance().getBean("evelynn");
c.attack();
c.move();
c.skill1();
c.skill2();
c.skill3();
c.skill4();
}

}


길어 보이지만 사실 별거 없는 코드이다. 흔하다면 흔하다.

Riven과 Evelynn은 champ를 상속받는다. 그리고 이를 Main에서 쓰려하지만 그냥 쓰면 결합이 강해서 수정시 곤란하다.

따라서 Factory패턴을 사용한다. 인스턴스를 생성하는 클래스의 이름은 BeanFactory이다.

이런식으로 코드를 짜는 일은 흔하다. 그리고 이정도만되도 꽤 객체끼리의 결합은 느슨해져있다.

객체끼리의 결합이 강하면 강할수록 수정시 에로사항이 발생하므로 객체끼리의 결합을 느슨하게 만드는건 당연하다.


그러나 이정도의 결합도 강하다고 생각하는것이 스프링프레임 워크이다.,

왜냐하면 객체를 생성하는 factory가 자바코드이기 때문이다.

이 자바코드는 자바문법을 따르므로 객체의 결합을 아무리 약하게 해도 이 결합부만은 어찌할 수 없는것이다.

예를 들어서 위 코드에서 나는 챔피언을 evelynn으로 만들었는데 도중에 riven으로 교체하고싶다면

우리는 필연적으로 자바코드인 main을 손을 볼 수 밖에 없다.

당연하다고 생각하는 사람이 있을것이다. 그런데 결국 뭔가를 바꾸기위해서 기존의 코드를 변경해야한다는건 융통성을 잃는 일이다.

하지만 기존에 인스턴스를 호출하는 객체가 호출되는 객체에 의존되는건 당연하다.

main이 factory를 호출하는고 factory는 champ인스턴스를 만든다. 즉 main은 factory를 controll하고 factory는 champ를 controll한다.


그러면 이를 거꾸로 돌릴 수 있다면? 오히려 factory가 main을 컨트롤하는 입장이 된다면 어떻게 될까?

위의코드에서 main이 성립하기 위해서 factory가 반드시 존재해야한다. 그렇기에 main이 factory를 컨트롤 해야하는게 당연하다 생각했는데

반대가 될수 있다면? main코드를 가만히 냅두고 factory의 변경만으로도 main의 결과가 바뀌게 된다면

컨트롤 하는 개체의 인과관계가 역전이 되는것이다.


기존에는 Main에서 bean의 교체가 일어나려면 Main클래스의 변경말고는 답이 없었다.

그러나 이제 Main을 가만히 냅두고 오히려 factory에서 변경할 수 있다면 이야기는 조금 달라진다는 것이다.

이는 기존의 자바문법만으로는 해결할 수없다. 그걸 위한 스프링프레임워크이다.

스프링 프레임워크는 리소스를 사용하는 방식으로 이를 해결한다. 즉 관절부를  xml로 만든다면 이 역전을 성공할 수 있다는 것이다.


설명이 조금 길었는 필자가 간단하게 보여줄것을 요약하자면 원래 main클래스에서 지금은 evelynn을 출력하던것을 riven으로 바꾸러면

main클래스의 변경이 필연적이였으나. 그렇지 않게 하기 위해서 IoC를 적용할 것이다.

이번에는 자바클래스를 한번만들고나면 자바클래스는 일절 수정할 필요가 없다. 만약에 나중에 evelynn에서 riven으로 출력을 바꾸고싶어도

자바코드는 조금도 손을 대지않게 만들 것이다.


Inversion(역전) of Controll(제어)



프로젝트의 src->main->resource->new->other을 눌러준다.



그 다음 Spring Bean Configuration File을 선택한다.



이름은 원하는 걸로 지으면 된다. 필자는 applicationContext로 짓겟다.



그러면 해당 xml이 완성된다.

이 xml을 수정할 것이다.

아래와 같이 수정을 하자.


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

<bean id="riven" class="champ.Riven"></bean>
<bean id="evelynn" class="champ.Evelynn"></bean>

</beans>


각각의 클래스들은 bean으로 호출된다. class속성은 반드시 패키지+클래스명으로 해주어야한다.

id는 편한걸로하면된다. 이제 main을 조금 수정하자.


package champ;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Main {

public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
Champ c = (Champ) factory.getBean("riven");
c.attack();
c.move();
c.skill1();
c.skill2();
c.skill3();
c.skill4();
}

}


이제 팩토리를 우리가 만들 필요가 없다. 미리 만들어 놓은 컨테이너를 사용하면된다.

우리가 설정해놓은 xml을 이용해서 컨테이너를 호출하게 될것이다.

이제 실행하면 기존과 같은 결과를 낼것이다.


우리는 메인을 하나도 건드리지 않아도 될만큼 수정하고 싶다. 그러면 xml과 main의 각각 한줄을 고치자.


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

<bean id="riven" name="pickChamp" class="champ.Riven"></bean>
<bean id="evelynn" class="champ.Evelynn"></bean>

</beans>


xml속성중에서 name을 선택할 수 있다. name역시 id처럼 마음대로 지정해줄 수 있다.

name은 일종의 alias로 id로 접근하지않고 name으로 접근할 수도있다. 이로서 유연한 코딩을 보장해준다.


package champ;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Main {

public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
Champ c = (Champ) factory.getBean("pickChamp");
c.attack();
c.move();
c.skill1();
c.skill2();
c.skill3();
c.skill4();
}

}


main에서 속성을 id값으로 접근했던것을 name값으로 접근해보자.

아무 무리없이 접근할 수 있는 것을 알 수있다.


이제 어떤가? 여러분이 만약 등록한 bean들 중에서 처음에 riven을 선택했다가도 도중에 evelynn으로 교체하고 싶다면

기존에는 main을 고쳐야했다. 그러나 이제는 그럴필요가 없다.

xml을 열어서 name속성을 evelynn에게 주면된다. 즉 우리는 자바코드를 손가락 하나 안대고도 수정할 수있다.

이렇게 IoC를 구현할 수 있다.


Bean의 속성

id - 특정 bean을 식별할 고유 문자열

name - id를 대신할 별명 ,와 ; 그리고 공백으로 여러 별명을 지정해줄 수 도있다.

class - 패키지명을 포함한 클래스명


+ Recent posts