7장 스프링 삼각형과 설정 정보

Seunghee Ryu·2023년 11월 26일
0

  • 스프링에서 가장 중요하다고 생각되는 세 가지의 개념에 대해 설명한다

IoC/DI - 제어의 역전/의존성 주입

  • 프로그래밍에서 의존성이란?
  • 의사 코드
    - 운전자가 자동차를 생산한다
    - 자동차는 내부적으로 타이어를 생산한다
  • 자바로 표현
new Car();
Car 객체 생성자에서 new Tire();
  • 의존성을 단순하게 정의하면 다음과 같다
    - 의존성은 new다
    - new를 실행하는 Car와 Tire 사이에서 Car가 Tire에 의존한다
// 스프링을 적용하지 않은 기존 방식으로 작성한 예제
public interface Tire {
    String getBrand();
}

public class KoreaTire implements Tire{
    public String getBrand() {
        return "코리아 타이어";
    }
}

public class AmericaTire implements Tire{
    public String getBrand() {
        return "미국 타이어";
    }
}

public class Car {
    Tire tire;

    public Car() {
        tire = new KoreaTire();
    }

    public String getTireBrand() {
        return "장착된 타이어: " + tire.getBrand();
    }
}

public class Driver {
    public static void main(String[] args) {
        Car car = new Car();

        System.out.println(car.getTireBrand());
    }
}

스프링 없이 의존성 주입 - 생성자를 통한 의존성 주입

  • 의사 코드
    - 운전자가 타이어를 생산한다
    • 운전자가 자동차를 생산하면서 타이어를 장착한다
  • 자바로 표현
Tire tire = new KoreaTire();
Car car = new Car(Tire);
  • 주입이란?
    - 외부에서 장착시키는 작업을 주입이라고 한다

public interface Tire {
    String getBrand();
}

public class KoreaTire implements Tire{
    public String getBrand() {
        return "코리아 타이어";
    }
}

public class AmericaTire implements Tire{
    public String getBrand() {
        return "미국 타이어";
    }
}

public class Car {
    Tire tire;

    **public Car(Tire tire) {
        this.tire = tire;
    }**

    public String getTireBrand() {
        return "장착된 타이어: " + tire.getBrand();
    }
}

public class Driver {
    public static void main(String[] args) {
        **Tire tire = new KoreaTire();
        Car car = new Car(tire);**

        System.out.println(car.getTireBrand());
    }
}
  • 기존에는 car가 구체적으로 어떤 tire를 생산할지 결정했지만 지금은 tire를 car에 주입한다
  • 의존성 주입의 이점
    - car는 그저 tire 인터페이스를 구현한 어떤 객체가 들어오기만 하면 정상 작동한다
    - 확장성이 좋아진다
    - 컴파일이 수월해진다

스프링 없이 의존성 주입하기 - 속성을 통한 의존성 주입

  • 의사 코드
    - 운전자가 타이어를 생산한다.
    - 운전자가 자동차를 생산한다.
    - 운전자가 자동차에 타이어를 장착한다.
  • 자바로 표현 - 속성 접근자 메서드 사용
Tire tire = new KoreaTire();
Car car = new Car();
car.setTire(tire);
  • 생성자를 통한 의존성 주입은 교체가 어렵다는 단점이 있다
    - 자동차를 생산할 때 한 번 타이어를 장착하면 타이어를 교체할 방법이 없다
  • 그렇기에 교체가 편리하도록 속성을 통해 의존성을 주입한다

public interface Tire {
    String getBrand();
}

public class KoreaTire implements Tire{
    public String getBrand() {
        return "코리아 타이어";
    }
}

public class AmericaTire implements Tire{
    public String getBrand() {
        return "미국 타이어";
    }
}

public class Car {
    Tire tire;

    public Tire getTire() {
        return tire;
    }

    public void setTire(Tire tire) {
        this.tire = tire;
    }

    public String getTireBrand() {
        return "장착된 타이어: " + tire.getBrand();
    }
}

public class Driver {
    public static void main(String[] args) {
        Tire tire = new KoreaTire();
        Car car = new Car();
        car.setTire(tire);

        System.out.println(car.getTireBrand());
    }
}

스프링을 통한 의존성 주입 - xml 파일 사용

  • 의사 코드
    - 운전자가 종합 쇼핑몰에서 타이어를 구매한다
    - 운전자가 종합 쇼핑몰에서 자동차를 구매한다
    - 운전자가 자동차에 타이어를 장착한다
  • 자바로 표현 - 속성 메서드 사용

ApplicationContext context = new ClassPathXmlApplicationContext(”practice.xml”, Driver.class);
Tire tire = (Tire)context.getBean(”tire”);
Car car = (Car)context.getBean(”car”);
car.setTire(tire);
  • 운전자가 직접 자동차를 생산하지 않고 종합 쇼핑몰을 통해 구매하는 형태로 변경된다

public interface Tire {
    String getBrand();
}

public class KoreaTire implements Tire{
    public String getBrand() {
        return "코리아 타이어";
    }
}

public class AmericaTire implements Tire{
    public String getBrand() {
        return "미국 타이어";
    }
}

public class Car {
    Tire tire;

    public Tire getTire() {
        return tire;
    }

    public void setTire(Tire tire) {
        this.tire = tire;
    }

    public String getTireBrand() {
        return "장착된 타이어: " + tire.getBrand();
    }
}

public class Driver {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("practice.xml");
        Car car = context.getBean("car", Car.class);
        Tire tire = context.getBean("tire", Tire.class);

        car.setTire(tire);
        System.out.println(car.getTireBrand());
    }
}

<?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="tire" class="practice.KoreaTire"></bean>
    <bean id="americaTire" class="practice.AmericaTire"></bean>
    <bean id="car" class="practice.Car"></bean>
</beans>
  • Driver 클래스와 xml 파일이 추가되었다
  • ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("practice.xml"); 이 종합 쇼핑몰에 대한 정보이며
  • 종합 쇼핑몰에 입점된 상품에 대한 정보는 xml 파일에 등록되어 있다
  • 스프링을 도입하면 재컴파일/재배포 없이 xml 파일만 수정하면 프로그램의 실행 결과를 바꿀 수 있다

스프링을 통한 의존성 주입 - 스프링 설정 파일(xml)에서 속성 주입

  • 의사 코드
    - 운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
    - 종합 쇼핑몰은 자동차를 생산한다.
    - 종합 쇼핑몰은 타이어를 생산한다.
    - 종합 쇼핑몰은 자동차에 타이어를 장착한다.
    - 종합 쇼핑몰은 운전자에게 자동차를 전달한다.
  • 자바로 표현

ApplicationContext context = new ClassPathXmlApplicationContext(”practice.xml”)
Car car = context.getBean(”car”, Car.class);
  • xml로 표현

<bean id=KoreaTire" class="practice.KoreaTire"></bean>
<bean id="americaTire" class="practice.AmericaTire"></bean>
<bean id="car" class="practice.Car"></bean>
    <property name=”tire” ref=”koreaTire”></property>
</bean>

import org.apache.catalina.core.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Driver {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("practice.xml");
        Car car = context.getBean("car", Car.class);

        System.out.println(car.getTireBrand());
    }
}

<?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="koreaTire" class="practice.KoreaTire"></bean>
    <bean id="americaTire" class="practice.AmericaTire"></bean>
    <bean id="car" class="practice.Car">
        <property name="tire" ref="koreaTire"></property>
    </bean>
</beans>
  • 변경 사항이 생길 경우 xml파일의 property ref 값만 변경하면 된다

스프링을 통한 의존성 주입 - @Autowired를 통한 속성 주입

  • 의사 코드
    - 운전자가 종합 쇼핑몰에서 자동차를 구매 요청한다.
    - 종합 쇼핑몰은 자동차를 생산한다.
    - 종합 쇼핑몰은 타이어를 생산한다.
    - 종합 쇼핑몰은 자동차에 타이어를 장착한다.
    - 종합 쇼핑몰은 운전자에게 자동차를 전달한다.
// @Autowired 사용 전
Tire tire;

public void setTire(Tire tire) {
	this.tire = tire;
}

// @Autowired 사용 후
import org.springframework.beans.factory.annotation.Autowired;

@Autowired
Tire tire;

// 변경된 설정파일
<?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-3.1.xsd">

    <context:annotation-config />

    <bean id="tire" class="practice.KoreaTire"></bean>
    <bean id="americaTire" class="practice.AmericaTire"></bean>
    <bean id="car" class="practice.Car"></bean>
</beans>
  • @Autowired의 의미
    - 스프링 설정 파일을 보고 자동으로 속성의 setter 메서드 역할을 해주겠다는 의미
    - 그렇기 때문에 xml 파일의 property 태그가 사라진다

import org.springframework.beans.factory.annotation.Autowired;

public class Car {
    @Autowired
    Tire tire;

    public String getTireBrand() {
        return "장착된 타이어: " + tire.getBrand();
    }
}


//변경 전
<bean id="tire" class="practice.KoreaTire"></bean>
<bean id="americaTire" class="practice.AmericaTire"></bean>
<bean id="car" class="practice.Car"></bean>

//변경 후
<bean id="koreaTire" class="practice.KoreaTire"></bean>
<bean id="tire" class="practice.AmericaTire"></bean>
<bean id="car" class="practice.Car"></bean>
  • 만약 koreaTire를 삭제하고 AmericaTire의 id 속성을 삭제하면 실행이 되는가?
    - 실행된다
  • 같은 타입을 구현한 클래스가 여러개 있다면 그 때 bean 태그의 id로 구분해서 매칭한다
  • 따라서 id와 type 중 type 구현에 우선순위가 있다

스프링을 통한 의존성 주입 - @Resource를 통한 속성 주입

  • @Autowired와 동일한 기능을 하는 어노테이션
  • 차이점은 @Autowired는 스프링의 어노테이션이고 @Resource는 자바 표준 어노테이션
  • @Autowired는 type이 id보다 우선순위지만 @Resource는 반대로 id가 type보다 우선순위이다

스프링을 통한 의존성 주입 - @Autowired vs. @Resource vs. property 태그

  • @Autowired와 @Resource를 바꿔서 사용하는 데 크게 차이가 없다
  • 하지만 나중에 스프링이 아닌 다른 프레임워크로 교체되는 경우를 대비하면 자바 표준인 @Resource를 쓰는 것이 유리하다
  • @Resource는 개발 생산성이 더 낫고 property는 유지보수성이 좋다
  • 프로젝트의 규모가 커지면 XML 파일의 규모가 커지기 마련인데 XML 파일도 용도별로 분리할 수 있기에 더더욱 property를 사용하는 것이 편리하다
  • 프로젝트 규모와 팀의 성향에 따라 유연하게 사용하면 된다

DI를 마무리하기 전에 마지막으로 언급할 사항

  • 의존 관계가 new 라고 단순화했던 부분
  • 사실 변수에 값을 할당하는 모든 곳에 의존 관계가 생긴다
  • 즉, 대입 연산자(=)에 의해 변수에 값이 할당되는 순간에 의존이 생긴다
  • DI는 외부에 있는 의존 대상을 주입하는 것을 말한다. 의존 대상을 구현하고 배치할 때 SOLID와 응집도는 높이고 결합도는 낮추라는 기본 원칙에 충실해야 한다
  • 그래야 프로젝트의 구현과 유지보수가 수월해진다

AOP

  • Aspect-Oriented Programming, 관점 지향 프로그래밍
  • 스프링 DI가 의존성에 대한 주입이라면 스프링 AOP는 로직(code) 주입이라고 할 수 있다
  • 로직을 주입할 수 있는 곳은 Around(메서드 전 구역), Before(메서드 시작 직후), After(메서드 종료 직전), AfterReturning(메서드 정상 종료 후), AfterThrowing(메서드에서 예외가 발생 후 종료 후)이다

PSA

  • Portable Service Abstraction
  • PSA란 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조를 말한다
  • 특정 클래스가 추상화된 상위 클래스를 일관되게 바라보며 하위 클래스의 기능을 사용하는 것을 PSA의 기본 개념이다
  • 따라서 PSA가 적용된 코드는 개발자의 기존에 작성된 코드를 수정하지 않으면서 확장할 수 있으며, 어느 특정 기술에 특화되어 있지 않는 코드이다
  • 추상화 계층을 사용하여 어떤 기술을 내부에 숨기고 개발자에게 편의성을 제공해주는 것을 서비스 추상화라고 한다

  • 위 그림은 java 콘솔 애플리케이션에서 클라이언트가 데이터베이스에 연결되기 위해 jdbcConnector를 사용하기 위한 서비스 추상화의 다이어그램이다
  • 그림을 살펴보면 DbClient 클래스는 구현체에 직접적으로 연결해서 얻는 것이 아닌 jdbcConnector 인터페이스를 통해 간접적으로 연결되어 Connection 객체를 얻을 수 있다
  • 또한 Connection을 얻는 방식은 getConnection()로 다른 구현체와 동일하다
  • 즉, 일관된 방식으로 해당 서비스의 기능을 사용할 수 있다
  • 애플리케이션에서 특정 서비스를 이용할 때, 서비스의 기능을 접근하는 방식 자체를 일관되게 유지하면서 기술 자체를 유연하게 사용할 수 있도록 하는 것을 PSA(일관된 서비스 추상화)라고 한다

0개의 댓글