Spring 삼각형

 

 

Spring 삼각형

 

위의 Spring 삼각형 그림은 Spring의 핵심 개념들을 모두 표현한 모형이다.

 

POJO는 다른 핵심 개념들에 둘러 싸여져 있다.

POJO는 Ioc/DI, AOP, PSA를 통해서 달성할 수 있다는 것을 의미한다.

 

 

 

❑ POJO(Plain Old Java Object)

 

➤ POJO란?

JO’는 java object라는 뜻이다.

자바는 객체 끼리 관계를 맺을 수 밖에 없는 객체 지향 프로그래밍이다.

그렇다면 ‘PO’는 무엇일까?

P’는 plain 이란 의미다.

플레인은 보통 아무것도 들어가지 않은 순수한 상태를 의미한다. (플레인 요거트를 떠올려보자)

따라서 POJO는 자바로 생성하는 순수한 객체를 의미한다.

 

➤ POJO 프로그래밍이란?

POJO를 이용해서 프로그래밍 코드를 작성하는 것

그러나 단순히 순수한 자바 객체를 사용해서 코드를 작성한다고해서 POJO 프로그래밍이라고 볼 수는 없다.

 

※ 아래의 두가지 규칙을 지켜야 POJO 프로그래밍 이라고 부를 수 있다.

1. Java나 Java의 스펙(사양)에 정의된 것 이외에는 다른 기술이나 규약에 얽매이지 않아야 한다.

특정 기술을 상속해서 코드를 작성하게 되면 나중에 애플리케이션의 요구사항이 변경되어 다른 기술로 변경하려면 클래스를 명시적으로 사용했던 부분을 전부 다 일일이 제거하거나 수정해야한다.

 

2. 특정 환경에 종속적이지 않아야 한다.

 

 

➤ POJO 프로그래밍이 필요한 이유

  • 특정 환경이나 기술에 종속적이지 않으면 재사용 가능하고, 확장 가능한 유연한 코드를 작성할 수 있다.
  • 저수준 레벨의 기술과 환경에 종속적인 코드를 애플리케이션 코드에서 제거함으로써 코드가 깔끔해진다.
  • 코드가 깔끔해지기 때문에 디버깅하기도 상대적으로 쉽다.
  • 특정 기술이나 환경에 종속적이지 않기 때문에 테스트가 단순해진다.
  • 객체지향적인 설계를 제한없이 적용할 수 있다.

 

 

 

➤ POJO와 Spring의 관계

 

Spring은 POJO 프로그래밍을 지향하는 Framework이다.

그리고 최대한 다른 환경이나 기술에 종속적이지 않도록 작성하기 위해 IoC/DI, AOP, PSA의 3가지 기술을 지원하고 있다.

 

애플리케이션 프로그래밍 코드를 작성할 때 항상 내가 작성한 코드가 객체지향스러운가에 대한 고민을 하는 습관을 가져야 한다. 

 

 

 

 

❑ IoC(Inversion Of Control)/DI(Depedency Injection)

 

Library는 애플리케이션 흐름의 주도권이 개발자에게 있고 Framework는 애플리케이션 흐름의 주도권이 Framework에 있다.

여기서 말하는 애플리케이션 흐름의 주도권뒤바뀐 것IoC(Inversion of Control)이라고 한다.

 

 

▶️ Java 콘솔 애플리케이션의 일반적인 제어권

순수 자바 코드만으로 콘솔에 메시지를 출력하는 프로그램을 만들었다고 가정해보자.

일반적으로 자바 콘솔 애플리케이션이 실행되면 main() 메소드가 가장먼저 호출된다.

그리고 순서대로 System 클래스를 통해서 static 멤버 변수인 out의 println()이 호출된다.

이렇게 개발자가 작성한 코드를 순차적으로 실행하는게 일반적인 애플리케이션의 제어 흐름이다.

 

 

▶️ Java 웹 애플리케이션에서 IoC가 적용된다면?

 

서블릿 컨테이너의 서블릿 호출 모형

 

웹에서 동작하는 애플리케이션의 경우 클라이언트가 외부에서 접속해서 사용하는 서비스이기 때문에 main() 메서드가 종료되지 않아야 한다.

그런데 서블릿 컨테이너에는 서블릿 사양에 맞게 작성된 서블릿 클래스만 존재하고 별도의 main()메서드는 존재하지 않는다.

main()메서드처럼 애플리케이션의 시작되는 지점엔트리 포인트(Entry point)라고 부른다.

main()메서드가 없이 어떻게 애플리케이션이 실행되는 것일까?

클라이언트의 요청이 들어올 때마다 서블릿 컨테이너 내의 컨테이너 로직(service() 메서드)이 서블릿을 직접 실행시켜주기 때문에 main()메서드가 필요없는 것이다.

서블릿 컨테이너가 서블릿을 제어하고 있기 때문에 애플리케이션의 주도권은 서블릿 컨테이너에 있다.

여기서 서블릿과 웹 애플리케이션 간에 IoC의 개념이 적용되어 있는 것이다.

 

그렇다면 Spring에는 IoC의 개념이 어떻게 적용되어 있을까?

Spring에서 IoC는 DI(Dependency Injection)을 통해 적용된다.

 

 

 

➤ DI(Dependency Injection)란?

 

IoC 개념을 조금 더 구체화 시킨 것

Depencency는 ‘의존하는, 종속하는’ 이라는 의미이고 injection은 ‘주입’이라는 의미이다.

따라서 DI는 직역하면 '의존성 주입' 이라는 뜻이다.

 

 

 

▶️ 의존성 주입은 무엇일까?

 

객체지향프로그래밍에서 의존성은 대부분 객체 간의 의존성을 의미한다.

예를 들어 A, B의 두개의 클래스가 있다.

A클래스가 B클래스를 호출하는 다이어그램

A클래스는 B클래스 내부의 메서드를 호출하여 사용하도록 작성하였다.

이 때 ‘A클래스는 B클래스에 의존한다’라고 한다.

쉽게 말해 ‘A 클래스의 프로그래밍 로직 완성을 위해 B클래스에 도움을 요청한다’ 고 볼 수 있다.

 

※ 다이어그램

클래스간의 관계를 나타내는 다이어그램은 애플리케이션 설계를 위해 자주 사용된다. (애플리케이션 설계에 있어 중요한 역할!)

도구 : VisualParadigm

 

 

A클래스가 B클래스를 의존하는 관계를 코드로 작성해보았다.

public class EnergyController {
    public static void main(String[] args) {
        MonsterDrink monsterDrink = new MonsterDrink();
        int energy = monsterDrink.getCaffeine();
    }
}
public class MonsterDrink {
    public int getCaffeine() {
        return 10;
    }
}

 

위 코드에서 EnergyController 클래스는 클라이언트로부터 요청을 받는 엔드포인트(Endpoint) 역할을 한다.

그리고 MonsterDrink 클래스는 EnergyController 클래스가 전달받은 클라이언트의 요청을 처리하는 역할을 한다.

클라이언트 측면에서 서버의 엔드포인트란 클라이언트가 서버의 자원(리소스)를 이용하기 위한 끝 지점을 의미한다.

 

EnergyController에서 에너지드링크인 MonsterDrink 객체를 생성한 후 MonsterDrink의 getCaffeine() 메소드를 호출하고 있다.

이처럼 객체를 생성해서 참조하게 되면 의존 관계가 성립하게 된다.

 

 

그러나 두 클래스간의 의존 관계가 성립됐지만 아직 의존성 주입은 이루어지지 않았다.

의존성 주입을 하기 위해 코드를 수정해보자.

 

public class EnergyController {
    public static void main(String[] args) {
        MonsterDrink monsterDrink = new MonsterDrink();
        PresentEnergy presentEnergy = new PresentEnergy(monsterDrink);
        int energy = monsterDrink.getCaffeine();
    }
}
public class MonsterDrink {
    public int getCaffeine() {
        return 10;
    }
}
public class PresentEnergy {
    private MonsterDrink monster;
    public PresentEnergy(MonsterDrink monster) {
        this.monster = monster;
    }
}

 

의존성 주입은 생성자를 통해서 어떤 클래스의 객체를 전달 받는 것이다.

생성자의 매개변수로 객체를 전달하는 것을 외부에서 객체를 주입한다고 표현하는 것이다.

그렇다면 여기서 외부는 어디일까?

객체를 매개변수로 전달하고 있는 클래스가 외부이다.

 

클래스의 생성자로 객체를 전달받는 코드가 있다면 객체를 외부에서 주입받고 있어서 의존성 주입이 이루어지고 있다고 볼 수 있다

 

 

 

▶️ 왜 의존성 주입이 필요한가?

 

자바에서 생성자를 통해 객체를 전달하는 일은 아주 흔한 일이다.

하지만 의존성 주입을 사용할 때 클래스 내부에서 외부 클래스의 객체를 생성하기 위한 new 키워드를 사용을 지양해야 한다.

객체를 생성할 땐 당연히 new 키워드를 쓰지만 *Reflection이라는 기법을 이용해서 Runtime시에 동적으로 생성할 수 있는 방법이 있다.

new 키워드를 사용해서 의존 객체를 생성할 때, 클래스들 간의 강하게 결합(Tight Coupling)되어 있다고 한다.

하지만 자주 변경하려면 클래스들 간의 강한 결합은 피하는 것이 시간을 절약할 수 있다.

따라서 강한 결합보다는 느슨한 결합(Loose Coupling)을 사용하는 것이 좋다.

애플리케이션의 요구사항은 언제나 바뀔 수 있기 때문에 코드를 작성할 때 이부분을 고려하는 것이 좋다.

 

 

▶️ 느슨한 의존성 주입은 어떻게 할까?

 

클래스 간의 관계를 느슨하게 하는 대표적인 방법은 바로 인터페이스(Interface)를 사용하는 것이다.

어떤 클래스가 인터페이스 같이 일반화된 구성 요소에 의존하고 있을 때, 클래스들 간의 느슨하게 결합되어 있다고 한다.

예시로 작성했던 코드는 전달할 객체를 생성할 때 new 연산자를 사용했기 때문에 강하게 결합되어 있다.

그런데 여기서 에너지드링크를 monster가 아닌 hotsix로 바꾼다고 하면 코드를 수정할 부분이 많아진다.

하지만 monster와 hotsix의 일반적인 개념인 EnergyDrink라는 인터페이스를 만들어서 monster를 사용한 부분에 EnergyDrink를 작성한다면 어떨까?

moster와 hotsix 중 전달하고 싶은것을 생성할 때 작성하기만 하고 나머지는 수정하지 않아도 된다.

위의 내용을 코드로 구현하면 아래와 같다.

 

public class EnergyController {
    public static void main(String[] args) {
        EnergyDrink energyDrink = new MonsterDrink();
        PresentEnergy presentEnergy = new PresentEnergy(energyDrink);
        int energy = energyDrink.getCaffeine();
    }
}
public interface EnergyDrink {
    public int getCaffeine();
}
public class MonsterDrink implements EnergyDrink {
    @Override
    public int getCaffeine() {
        return 10;
    }
}
public class Hotsix implements EnergyDrink {
    @Override
    public int getCaffeine() {
        return 7;
    }
}
public class PresentEnergy {
    private EnergyDrink energyDrink;
    public PresentEnergy(EnergyDrink energyDrink) {
        this.energyDrink = energyDrink;
    }
}

 

하지만 여전히 EnergyController 클래스 내에서 객체를 생성할 때 new 연산자를 사용하고 있다.

여기서 이 new 연산자는 어떻게 없앨 수 있을까?

 

 

▶️ Spring 에서 의존성 주입을 누가 해주는가?

 

Spring에서 new 키워드로 객체를 생성하지 않고 Spring이 그 역할을 대신 할 수 있다.

new 키워드를 없애려면 Config라는 클래스를 이용하면 된다.

Config에 대해선 차후에 자세히 학습할 예정이기 때문에 지금은 어떤 역할인지만 기억하고 넘어가자.

Config클래스는 객체 생성을 미리 정의해 둔다.

그래서 Spring의 도움을 받아 new 키워드 마저도 없앨 수 있다!

하지만 Config클래스 객체를 new 키워드로 생성한거면 이것도 문제이지 않을까??

아니다. Config 클래스는 단순한 클래스가 아니라 Spring Framework의 영역에 해당하는 것이고 Config 클래스가 실제 애플리케이션의 핵심 로직에 관여하지 않고 있기 때문에 문제가 되지 않는다.

 

Spring 기반의 애플리케이션에서는 Spring이 의존 객체들을 주입해주기 때문에 애플리케이션 코드를 유연하게 구성할 수 있다.

 

 

 

더 공부가 필요한 내용

  • Java Reflection
  • Config