본문 바로가기
프로그래밍/Spring

[토비의 스프링 1장] 오브젝트와 의존관계

by Loper Lee 2022. 5. 10.

1장 오브젝트와 의존관계

스프링이 추구하고자 하는 가치

  • 유연함 -자바의 잃어버린 객체지향 언어적 장점을 다시 살릴 수 있도록 도와주는 도구
  • 단순함 - 뛰어난 확장성으로 다른 많은 프레임워크 등과 쉽게 결합하여 사용할 수 있도록 할 것

OOP의 장점을 최대한 살린 것이 스프링이고, 이것을 기반으로한 접근 방법을 사용하면 스프링이 추구하던 아키텍처, 설계의 근간이 흔들릴 일 없이 확장되어 결국은 확장된 코드마저도 스프링과 같이 확장성이 뛰어나고 유지보수가 쉬운 코드가 될 수 있다.

객체지향의 원칙

  • 객체지향의 5원칙
    • SRP(Single Reponsibility Principle) : 단일 책임 원칙
      • 하나의 클래스는 하나의 책임만을 가져야 한다.
    • OCP(Open Closed Principle) : 개방-폐쇄 원칙
      • 클래스는 확장에는 열려있고, 변경에는 닫혀있어야 한다.
    • LSP(Liskov Substitution Principle) : 리스코프 치환 법칙
      • 서브 클래스는 언제나 상위 클래스로 교체할 수 있어야한다.
    • ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
      • 클라이언트는 자신이 사용하지 않는 메소드에 의존 관계를 맺으면 안 된다.
    • DIP(Dependency Inversion Principle) : 의존 역전 원칙
      • 고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다.
      • 추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.
  • 높은 응집도와 낮은 결합도
    • 응집도 : 결합도는 서로 다른 모듈 간에 상호 의존하는 정도 또는 연관된 관계를 의미한다.
    • 결합도 : 응집도는 한 모듈 내부의 처리 요소들이 서로 관련되어 있는 정도를 말한다.
  • 응집도는 높을수록 결합도는 낮을수록 이상적인 모듈화이다.

디자인패턴

디자인패턴이란 뭘까? 소프트웨어를 설계할 때 특정 맥락에서 자주 발생하는 고질적인 문제들이 또 발생했을 때 재사용할 할 수있는 훌륭한 해결책

  • 전략패턴 (Strategy Pattern)
    • 독립적으로 분리된 오브젝트들을 상황에 맞춰 바꿔서 대응할 수 있는 패턴
  • 템플릿 메소드 패턴 (Template Method Pattern)
    • 슈퍼클래스에서 기능의 일부를 abstract method 혹은 overriding 가능한 protected method로 만든 뒤 서브클래스에서 필요에 따라 메소드를 구현하도록 하는 패턴
  • 메서드 팩토리 패턴 (Factory Method Pattern)
    • 템플릿 메소드 패턴과 원리는 같으나, 구체적인 오브젝트 생성 방법에 대해 결정하게 하는 것. 주로 Interface 타입 오브젝트를 반환하므로 서브 클래스는 다양한 방법으로 오브젝트를 생성하는 메소드를 작성 할 수 있다. 이 때 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 팩토리 메소드라고 한다.
  • 싱글턴 패턴 (Singleton Pattern)
    • 생성자의 접근제한자를 private로 두고 객체를 불러오는 static 메서드를 통해서 메모리상에 하나의 오브젝트만 존재하게 하는 패턴

Inversion of Control (IoC)

제어의 역전이라 불리우는 IoC는 객체 생성의 대한 책임을 외부에 맡기는 패턴이다.

  • IoC를 이용하면 얻을 수 있는 장점
    • 관심사의 분리를 통해 SRP 원칙을 지키며 클래스 작성이 가능해진다.
    • 클래스가 수정되어도 주입받는 객체는 생성에는 관심이 없기 때문에 수정의 여파가 적어진다.

스프링의 IoC

  • Bean
    • 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트
    • 오브젝트 단위의 애플리케이션 컴포넌트
  • Bean Factory
    • 스프링에서 빈 생성과 관계설정 같은 제어를 담당하는 오브젝트
    • 빈을 제어하는 IoC 기본 기능에 초점이 맞춰진 것.
  • Application Context
    • 빈 팩토리를 확장한 오브젝트
    • 애플리케이션 콘텐스트는 별도의 정보를 참고해서 빈의 생성, 관계 설정 등의 제어 작업을 총괄한다.
    • 직접 오브젝트를 생성하여 관계를 맺어주는 코드가 없고, 생성정보와 연관관계 정보를 별도의 설정정보를 통해 얻는다.
    • 애플리케이션 컨텍스트를 이용할때의 장점
      • 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
      • 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
      • 애플리케이션 컨텐스트는 빈을 검색하는 다양한 방법을 제공한다.
  • Configuration
    • 애플리케이션 컨텍스트가 IoC를 적용하기 위해 사용하는 메타정보로, 컨테이너의 어떤 기능을 세팅하거나 조정하는 경우에도 사용하지만 보통 IoC 컨테이너에 의해 관리되는 객체를 생성하고 구성할 때 사용된다.
  • Singleton Registry
    • private 생성자 때문에 상속을 시킬 수 없다.
    • 생성자를 통해 객체에 동적으로 정보를 주입할 수 없기 때문에 테스트하기 힘들다.
    • 서버 환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
    • 전역 변수와 같은 문제점을 발생시킬 수 있다. 특히 OOP에서는 더욱 문제가 된다.
    • 때문에 스프링에서 제공하는 싱글톤 레지스트리를 통해서 자연스럽게 문제를 해결할 수 있도록 도와준다.
    • 평범한 자바 클래스를 싱글톤으로 활용하게 해준다.
    • 테스트에서 싱글톤 방식으로 사용될 클래스를 활용할 수 있다.
  • 일반적인 싱글톤 패턴을 이용할 경우 아래와 같은 문제가 발생한다.
  • Bean Scope
    • 싱글톤 스코프 : 일반적인 빈스코프로 단 하나의 빈을 만들어 활용한다.
    • 프로토타입 스코프: 컨테이너 빈을 요청할 때마다 매번 새로운 빈을 만든다.
    • 요청 스코프 : HTTP 요청이 생길 때마다 매번 새로운 빈을 만든다.
    • 세션 스코프 : 웹의 세션과 비슷한 스코프를 갖는다.
  • 빈이 생성되고 존재하고 적용되는 범위를 빈의 스코프라고 한다.

Dependency Injection (DI)

스프링은 흔히 IoC컨테이너라 불리우지만 그것만으로 스플링을 설명하기엔 다소 부족한 면모가 있다. 때문에 우리는 의존성 주입이라는 단어를 통해 스프링에 대한 설명을 완성할 수 있다.

  • 의존관계 설정
    • 의존관계란 방향성이 존재하고, 한 쪽이 변하면 다른 한 쪽도 영향을 받는것을 의미한다.
    • 런타임 의존관계
      • 컴파일 단계에서 이미 정해진 의존관계가 아닌 런타임에서 생기는 의존관계를 의미한다.
  • 의존관계 검색
    • Denedency Lookup
    • 객체를 스스로 검색해서 의존관계를 맺는것을 의미한다.
    • DL에서는 검색을 하는 주체 객체는 꼭 스프링 빈일 필요가 없다
      public UserDao() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class); this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class); }
  • 생성자 주입
    • 생성자 기반 의존성 주입 방식은 생성자의 인수를 사용해 의존성을 주입하는 방식이다.
    • 애너테이션 기반 DI에서 이용하기 적절하다
    • public class UserDao { private ConnectionMaker connectionMaker; public UserDao(ConnectionMaker connectionMaker) { this.connectionMaker = connectionMaker; } }
  • 수정자 주입
    • 수정자 메소드는 외부로부터 제공받은 오브젝트 레퍼런스를 저장해뒀다가 내부의 메소드에서 사용하게 하는 DI방식에서 활용하기 적당하다.
    • Setter 기반의 DI는 XML을 이용한 관계설정에서 이용하기 적절하다.
    • public class UserDao { private ConnectionMaker connectionMaker; ... public void setConnectionMaker(ConnectionMaker connectionMaker) { this.connectionMaker = connectionMaker; } }

XML을 이용한 의존성 설정

자바 코드 설정정보 XML 설정 정보
빈 설정파일 @Conriguration
빈의 이름 @Bean methodName() <bean id=”methodName” ...
빈의 클래스 retrun new BeanClass() class=”a.b.c.....BeanClass”>
<?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="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="dirverClass" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/springbook"/>
        <property name="username" value="spring"/>
        <property name="password" value="book"/>
    </bean>

    <bean id="userDao" class="toby.springtoby01.UserDao">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

--

@Bean
public DataSource dataSource() {
    SimpleDriverDataSource dataSource = new SimpleDriverDataSource();

    dataSource.setDriverClass(com.mysql.jdbc.Driver.class);
    dataSource.setUrl("jdbc:mysql://localhost/springbook");
    dataSource.setUsername("spring");
    dataSource.setPassword("book");

    return dataSource;
}

정리

  • 스프링은 개발자가 보다 객체지향에 집중할 수 있도록 만들어주는 프레임워크다.
  • 스프링 IoC를 통해서 생성과 관계설정에 대한 책임에서 벗어날 수 있다.
  • 스프링의 싱글톤 레지스트리는 기존 싱글톤의 단점을 보완한 방법이다.
  • 관심사의 분리를 통해서 보다 응집률이 높고 SRP를 지킬 수 있는 클래스를 설계할 수 있다.
  • 변경이 잦은 기능들은 인터페이스로 추상화 하여 구현하도록 한다.

프레임워크 vs 라이브러리

  • 프레임워크 - 어플리케이션 코드가 프레임워크에 의해 사용된다.
  • 라이브러리 - 라이브러리를 사용하는 어플리케이션 코드는 어플리케이션 흐름을 직접 제어한다.

즉, 오브젝트를 생성하고 관리하는 책임의 주체의 관점에 따라서 분류가 가능하다.

오브젝트의 동일성과 동등성

  • 동일성 - 두 개의 오브젝트가 완벽하게 동일한지 판별
  • 동등성 - 두 개의 오브젝트 내부의 값이 동일한지 판별