Spring

Spring Framework - Inversion of Control (IoC)

728x90

참조한 강의 : Spring & Hibernate For Beginners (www.udemy.com/course/spring-hibernate-tutorial/)

 

 

바로 Inversion of Control 의 개념에 들어가기 전에 먼저

POJO 와 Bean 에 대해서 짚고 가야할 것 같다.

 

 

- POJO, Bean ?

 

1) POJO

: POJO 는 Plain Old Java Object 의 약자로, 직역을 하면 평범하고 오래된 자바 객체 라는 뜻이 되는데, POJO 는 그 어떤 프레임워크에도 참조되거나 종속되지 않고, 별도의 naming convention 도 존재하지 않는 자바 객체를 말한다.

예를들어 다음의 코드가 있다 하면,

public class EmployeePojo {
 
    public String firstName;
    public String lastName;
    private LocalDate startDate;
 
    public EmployeePojo(String firstName, String lastName, LocalDate startDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.startDate = startDate;
    }
 
    public String name() {
        return this.firstName + " " + this.lastName;
    }
 
    public LocalDate getStart() {
        return this.startDate;
    }
}
cs

(코드 출처 : www.baeldung.com/java-pojo-class)

 

이 EmployeePojo 클래스는 세개의 firstName, lastName, startDate 라는 멤버변수 를 가지며,

이런식의 클래스 선언은 어떤 프레임워크를 참조하거나 종속된 형태가 아니라

그냥 순수 자바 코드만으로 사용된 프로그램이다.

또한 어떠한 네이밍 컨벤션을 따르는 형태는 아니다. 

(예를들면, Bean 을 사용한 코드라면, property 들을 private 으로 선언하여, getter, setter 를 이용해서 가져와야 한다)

 

소규모의 1인 개발을 할때는 크게 상관 없겠지만, 별도의 convention 이 없이 그냥 중구난방으로 코드를 작성하면

가장 큰 문제는 다른 개발자들이 알아보기 힘들다는 것이다.

 

그래서 코드를 작성할때 모두가 알아보기 쉬운 어떠한 규칙이 있어야 하는데, 

이 POJO 대신 규칙을 적용시킨게 바로 Bean 이다.

 

2) Java Bean 의 규칙은 다음과 같다

1. 클래스의 멤버 변수를 public 이 아니라 private 으로 선언

2. 클래스의 메소드 getter, setter 를 통해서 멤버변수에 접근해야 하며, 네이밍 컨벤션을 getX, setX 이런식으로 작성한다.

3.  매개변수가 없는 기본 생성자가 필요하다

4. State 를 저장하기 위한 Serializable Interface 가 구현되야 한다.

 

그래서 위의 POJO 코드를 Java Bean 형식으로 바꾸면 대략 이런 모습이 된다.

public class EmployeeBean implements Serializable {
 
    private static final long serialVersionUID = -3760445487636086034L;
    private String firstName;
    private String lastName;
    private LocalDate startDate;
 
    public EmployeeBean() {
    }
 
    public EmployeeBean(String firstName, String lastName, LocalDate startDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.startDate = startDate;
    }
 
    public String getFirstName() {
        return firstName;
    }
 
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    //  additional getters/setters
 
}
cs

(코드 출처 : www.baeldung.com/java-pojo-class)

 

 

Inversion of Control (IoC) 를 하는데 이 이야기를 도대체 왜 했느냐 하면,

앞으로 언급할 Spring Container 가 객체들을 관리할때, Spring Bean 형태로 객체들을 관리하기 때문이다.

(* Java Bean 과 Spring Bean 의 차이점은 아래의 주소를 참조하기 바람)

stackoverflow.com/questions/21866571/difference-between-javabean-and-spring-bean

 

Difference between JavaBean and Spring bean

I am new to Spring MVC and have a little idea of the usage of java beans in Java. What is the basic difference between a Java bean and Spring bean?

stackoverflow.com

 

 

- Inversion of Control

 

www.youtube.com/watch?v=vFzP2SaMyA0

Inversion of Control : https://www.youtube.com/watch?v=vFzP2SaMyA0

(비록 spring 에서의 inversion of control 은 아니지만 이 개념 자체를 이해하는데 도움이 되리라 생각해서 올렸다)

 

Inversion 은 역전, 반전 이란뜻이고, control 은 제어 라는뜻이다.

프레임워크를 사용하지 않고 코드를 작성할때는, 내가 필요할때마다 라이브러리를 가져다가 써서 필요한 기능들을 메꾸는 반면, 프레임워크는 이미 틀을 다 주고, 그 안에서 내가 로직만 잘짜면 굴러가도록 만들어져있다.

전자의 경우 프로그램에 대한 통제의 권한이 전적으로 내 코드에 달려있고,

후자의 경우 프로그램에 대한 통제의 권한이 전적으로 프레임워크에 달려있다.

이런것을 보고 inversion of control (제어 권한이 역전됬다) 라고 부르는것 같다.

프로그램을 통제할 권한이 프레임워크로 넘어갔다는 소리다.

 

 

그럼 spring 에서 Inversion of Control 은 무엇인가?

유데미 강의에서는

"The approach of outsourcing the construction and management of objects" 라고 칭하고 있다.

Spring IoC Container 가 Spring Bean 을 관리하는 것을 말한다

(생성이나 생명주기 등을 관리함)

 

1) - POJO 로 작성된 코드

먼저 4개의 파일을 작성할 것이다.

첫번째로, MyApp.java 라는 이름의 main method 를 담당하는 코드와

두번째로, BaseballCoach.java 라는 이름의 String 을 리턴하는 코드

세번째로, 인터페이스 역할을 할 Coach.java 파일

마지막으로, TrackCoach.java 라는 이름의 String 을 리턴하는 코드 이렇게 4개를 만들어 본다.

 

- MyApp.java

public class MyApp {
 
    public static void main(String[] args) {
        Coach theCoach = new TrackCoach();
        System.out.println(theCoach.getDailyWorkout());
    }
 
}
 
cs

 

- BaseballCoach.java

public class BaseballCoach implements Coach{
    @Override
    public String getDailyWorkout() {
        return "Spend 30 Minutes on batting practices";
    }
}
cs

 

- Coach.java

public interface Coach {
    public String getDailyWorkout();
}
 
cs

 

- TrackCoach.java

public class TrackCoach implements Coach {
 
    @Override
    public String getDailyWorkout() {
        return "Run a hard 5k";
    }
 
}
 
cs

 

TrackCoach.java 의 getDailyWorkout method 와 BaseballCoach.java 의 getDailyWorkout method 가 리턴 값만 다를뿐 

기능적으로 똑같기 때문에, Coach 라는 인터페이스를 선언해서 오버라이드 하는 형식으로 코드를 구성하여 코드를 작성한다.

 

코드 자체의 기능은 단순하기 때문에 일일이 코드를 설명하지는 않겠다.

다만 이 코드를 통해서 생각해 볼수있는 것은,

main 함수에서 BaseballCoach 와 TrackCoach 둘 중 하나를 선택해서 오버라이드된 getDailyWorkout() 함수를 호출하는 부분에 대해 생각해 볼 필요가 있다.

 

지금 메인 함수는 Coach theCoach = new TrackCoach() 같이 특정 클래스를 지정하는 

하드 코딩 방식을 쓰고 있다.

만약 코치가 저 두 개 뿐 아니라 수십개가 있다면 어떨까?

다른 코치로 바꾸고 싶을때 마다, 매번 코드를 일일이 수정해줘야 할 것이다.

또한 갯수가 너무 많아지면 관리하기도 어렵게된다.

지금이야 기능이 단순하게 getDailyWorkout() 만 있을뿐

만약 각 클래스 별로 수십개의 함수가 있다하면, 더 코드를 관리하기 힘들 것이다.

 

 

그래서 이 객체들을 더 효율적으로 관리해주기 위한 뭔가가 필요하며,

그 역할을 spring framework 가 해준다

그 중에서도 spring container 가 이 역할을 담당하고 있다.

 

 

2) - spring framework 를 통한 객체 관리

spring 을 통해서 객체를 관리하는 방식은 다음과 같이 3개의 단계로 요약된다.

 

1 - Spring Bean 설정하기

2 - Spring Container 생성하기

3 - Spring Container 로 부터 Bean 찾아내기

 

먼저 Spring Bean 을 설정하는 방법은 3가지가 있는데

가장 전통적인 방법은 XML 파일에 Bean 을 할당하는 것이고,

다른 두 가지 방법은 Annotation 을 부여하거나, 다른 설정 파일 없이 Java Source code 로 처리하는 방법이 있다

일단 여기서는 XML 파일에 Bean 을 설정하는 방법으로 해본다 (다른 방법들은 나중에 포스팅함)

 

먼저 앞에까지 작성했던 프로젝트의 패키지 밖에 

applicationContext.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" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- Define your beans here -->
    <bean id="myCoach" class="com.luv2code.springdemo.BaseballCoach">
    </bean>
</beans>
cs

 

중요한 부분은 11번 줄이다.

<bean> 태그 부분이 중요한데, id 는 해당 bean 을 구분시켜주기 위한 구분자이고 (html 에서의 id 와 똑같음)

class 는 매칭 시킬 spring bean 의 위치를 찾아줘야 한다.

"패키지위치.해당클래스명" 이런식으로 잡아준다.

 

그리고 나서 Spring Container 를 생성해야 한다.

Spring Container 는 다른 이름으로 ApplicationContext 로도 불리며,

그 종류가 여러개가 있다.

 

Spring IoC Container 는 BeanFactory, ApplicationContext 로 구성된다

(BeanFactory 가 root interface 이고, ApplicationContext 는 BeanFactory 의 하위 인터페이스이다)

 

ApplicationContext 의 예로는, 

- ClassPathXmlApplicationContext

- AnnotationConfigApplicationContext

- GenericWebApplicationContext 등이 있다.

 

여기서는 첫번째인, ClassPathXmlApplicationContext 를 사용한다.

 

이를 위해서 앞의 POJO main 과 대비되도록 HelloSpringApp.java 라는 파일을 만들어서 다음과 같이 작성했다.

 

- HelloSpringApp.java

import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class HelloSpringApp {
    public static void main(String[] args) {
        // load the spring configuration file
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // retrieve bean from spring container
        Coach theCoach = context.getBean("myCoach", Coach.class);
        
        // call methods on the bean
        System.out.println(theCoach.getDailyWorkout());
        
        // close the context
        context.close();
    }
}
 
cs

6번 줄은 Spring Container 를 생성한 부분이고 생성자에 앞서 작성한 xml 파일을 넣는다는것에 주의하자.

 

그리고 Spring Container 로 부터 Spring Bean 을 찾아내는 방법은 

getBean method 를 이용하면 된다.

getBean method 의 첫번째 인자는 앞서 xml 에 설정한 bean 의 id 값이며, 두번째 인자는 매칭시킬 클래스 이다.

마지막으로 context.close() 를 해줘야 한다는것에 주의하자.

 

이렇게 spring 으로 관리된 코드를 그림으로 보면 이런 모습이 된다.

 

 

HelloSpringApp 이라는 메인 함수에서 Spring Container 에게 "myCoach" Bean 를 달라고 요청하고

Spring Container 는 설정파일인 XML 에서 이 값에 매칭 되는 Bean 을 찾아서 BaseballCoach 클래스와

TrackCoach 클래스 중 매칭 되는 클래스를 찾아서 반환시켜주는 구조이다.

 

Spring 에서 객체 관리는 이런식으로 진행이된다.

다음 포스팅은 Dependency Injection(DI) 에 대해 해보려한다.

 

 

 

- references)

1. What is POJO Class ? : www.baeldung.com/java-pojo-class

2. Difference between JavaBean and SpringBean : stackoverflow.com/questions/21866571/difference-between-javabean-and-spring-bean

3. What is Inversion of Control ? : stackoverflow.com/questions/3058/what-is-inversion-of-control?page=1&tab=votes#tab-top

4. Spring Core Technologies : docs.spring.io/spring-framework/docs/current/reference/html/core.html

728x90