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

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

게시글에는 책의 설명과 무관한 내용이 섞여있습니다.

오브젝트와 의존관계

1
2
DAO(Data Access Object)는 DB를 사용해 데이터를 조회하거나 조작하는 
기능을 전담하도록 만든 오브젝트를 말한다.

초난감 DAO

현재 초난감 UserDao의 코드는 객체지향적이지 않음. 예제 그대로일 경우 exception이 발생하면 리소스 해제가 되지 않을 것이고 매번 메소드를 호출할 때마다 매번 커넥션을 생성해서 연결해야하는 리소스적 단점도 존재함

잘 동작하는 코드이지만 문제점이 많은 코드이기에 개선이 필요함. 코드를 개선할 때는 개선해서 얻을 수 있는 장점과 편리함을 생각해야함. 스프링을 공부한다는 것은 이런 문제 제기와 의문에 대한 답을 찾아나가는 과정

초난감 DAO를 테스트 하면서 느꼈던 불편함 !

  • 항상 동일한 대상을 등록하기 때문에 여러번 실행하면 실패함
  • 초난감 DAO를 테스트하기 위해서 실제 사용되는 데이터베이스의 값을 수정하는 것이 마음에 걸림

DAO의 분리

  • 소프트웨어는 변함 . 서비스가 종료되기 전까지는 끊임없이 변한다.
  • 개발자는 변화를 어떻게 대응할 건지 생각해야함. 변화에 대응할 수 있는 유연한 설계가 필요
  • 견고한 설계는 번거롭고 시간이 많이 소요됨. 하지만 요구사항이 변경되었을 때 변화를 대비하지 않은 코드보다 코드 수정 비용이 적음
  • 분리와 확장을 고려하여 설계하여라.
  • 관심사가 같은 것은 하나의 객체에 모이게 하고 관심사가 다른 것은 분리하여 서로 영향을 주지 않도록 하기
  • 관심사를 분리하면 한가지 관심사에 대한 변경이 일어났을 때 그 관심에 집중되는 코드만 수정하면 됨 다른 코드가 있는 메서드에는 영향을 주지않고 관심내용이 독립적이므로 수정이 간단함

상속을 통한 확장 (템플릿 메서드 패턴)

  1. 상속을 통해 확장하면 관심사가 다른 코드를 분리해내고 독립적으로 변경, 확장이 가능하지만 두 가지 다른 관심사와의 긴밀한 결합을 허용한다.
  2. 서브 클래스는 슈퍼 클래스의 메서드를 사용할 수 있기 때문에 슈퍼 클래스가 변경될 때 서브 클래스에 영향이 갈 수 있음
  3. 관심사가 새로 추가될 때마다 매번 클래스를 생성해야 한다는 문제점이 있다.
  4. 단순한 기능을 위해 상속을 사용하게되면 다른 목적으로 새로운 클래스를 상속할 수 없다는 것도 문제가 된다. (자바는 다중 상속이 불가능 함)

DAO의 확장

관심사를 나눠 두개의 클래스로 분리

  1. DB 커넥션과 DB 조작 관심사를 분리하여 클래스로 나누면 기능이 변화되지 않고 내부 코드를 개선할 수 있음
  2. 그렇지만 UserDao가 커넥션을 관리하는 클래스와 종속되어 있기 때문에 상속을 사용했을 때처럼 코드 수정없이 DB 커넥션 기능을 변경할 방법이 없음
  3. 구체적으로 사용할 클래스의 정보를 아는 것은 좋지않음. 구체적으로 정보를 알고있으면 그 해당 클래스와 종속되는 문제가 생김

관계설정 책임의 분리

UserDao의 ConnectionMaker를 추상화된 타입으로 변경했지만 UserDao 안에서 new 연산자로 SimpleConnectionMaker를 생성하고 있기 때문에 둘 사이의 강한 연결 관계가 형성된다.

UserDao가 독립적으로 확장 가능한 클래스가 되려면 UserDao에서 사용하는 객체들의 구현을 알지 못해야 한다. 때문에 구현체를 UserDao를 호출하는 클라이언트가 UserDao에게 주입하는 방식으로 결합을 끊어야한다.

UserDao가 느슨한 결합으로 DConnection 오브젝트를 사용하게 하려면 두 클래스의 오브젝트 사이에 사용관계 또는 링크, 또는 의존관계 라고 불리는 관계를 맺어주면 된다.

코드를 변경하기전 초난감 DAO는 OCP를 위배하고 있었지만, 변경된 코드는 OCP 원칙을 잘 따르고 있음

높은 응집도와 낮은 결합도

개방 폐쇄 원칙은 높은 응집도와 낮은 결합도라는 소프트웨어 개발의 고전적인 원리로도 설명이 가능하다. 응집도가 높다는 것은 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어있다는 것을 의미한다.

응집도가 높은 클래스는 요구사항 변경이 일어날 때 해당 클래스 기능의 대부분이 변경된다. 이때 만약 모듈의 일부분만 변경된다면 모듈 전체에서 어떤 부분이 바뀌어야하는지 파악해야 하고, 그 변경으로 인한 사이드이펙트를 모두 체크해야 한다.

낮은 결합도

결합도가 낮은 연결은 관계를 유지하기 위한 최소한의 정보만을 제공하고 나머지는 독립적으로 서로 알지 못하게 해야한다. 하나의 오브젝트가 변경이 일어날 때 관계를 맺고 있는 다른 오브젝트들도 변경이 일어나는 경우 결합도가 높다고 볼 수 있다.

전략 패턴

1
2
전략패턴이란 자신의 기능 맥락에서 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 외부로 분리시키고
이를 구현한 구체적인 알고리즘을 필요에 따라 바꿔서 사용하게 해주는 디자인 패턴

초난감 DAO의 개선된 코드는 스프링의 의존주입을 나타냈다고 느꼈음. 실제로 스프링은 빈 팩토리를 이용하여 의존을 주입하는데 이때 필요에 따라 사용될 구현체를 변경하여 주입할 수 있기 때문에 스프링의 의존 주입이 전략패턴과 같다고 느꼈음.

스프링의 의존을 주입하기 위해 팩토리 패턴, 전략패턴 그리고 또 어떤게 있을까..? 당장은 두 가지가 떠오름

제어의 역전(IoC)

팩토리

보통 객체의 생성방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 것을 팩토리라고 한다. 이때 말하는 팩토리는 디자인 패턴에서 다루는 추상 팩토리 패턴이나 팩토리 메서드 패턴과는 다르니 혼동하지 말아야 한다. 팩토리 메소드는 생성할 타입의 오브젝트를 어떻게 만들고 어떻게 준비시킬지 결정한다.

설계도로서의 팩토리

UserDao와 ConnectioMaker는 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있다. DaoFactory는 애플리캐이션을 구성하는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있다.

DaoFactory를 분리했을 때 얻을 수 있는 장점은 매우 많지만 그중에서도 애플리케이션의 컴포넌트 역할을 하는 오브젝트와 애플르케이션의 구조를 결정하는 오브젝트를 분리했다는 데 가장 의미가 있다.

제어권의 이전을 통한 제어관계 역전

제어의 역전을 간단하게 설명하면 프로그램의 흐름이 뒤바뀌는 것

일반적으로 프로그램의 흐름은 프로그램이 시작되는 지점에서 위에서 아래로 흘러간다. 그리고 각 지점마다 사용할 오브젝트를 직접 생성한다. 반면 IoC는 자신이 사용할 오브젝트를 스스로 선택하지 않고 생성하지도 않는다. 일반적인 프로그래밍 방식처럼 오브젝트가 능동적이지 않고 수동적이다.

왜냐하면 관계 연결에 대한 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다. 프로그램의 시작을 담당하는 main()과 같은 엔트리 포인트를 제외하면 모든 오브젝트는 이렇게 위임받은 제어 권한을 갖는 특별한 오브젝트에 의해 결정되고 만들어진다.

스프링의 IoC

스프링의 핵심을 담당하는 것은 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것이다.

오브젝트 팩토리를 이용한 스프링 IoC

1
스프링 Bean은 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트를 가리키는 말이다. 

애플리케이션 컨텍스트

오브젝트 팩토리에 대응되는 것이 스프링의 애플리케이션 컨텍스트. 애플리케이션 컨텍스트는 IoC 컨테이너라고도 불리고 빈 팩토리라고 부를 수도 있다. 애플리케이션 컨텍스트는 ApplicationContext 인터페이스를 구현하였는데, ApplicationContext 인터페이스가 BeanFactory 인터페이스를 상속했으므로 애플리케이션 컨텍스트는 일종의 빈 팩토리이다.

애플리케이션 컨텍스트는 애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트(Bean)에 대한 생성과 관계설정을 담당한다. ApplicationContext는 코드에서 직접 오브젝트를 생성하고 관계를 연결해주는 코드가 존재하지 않고, 오브젝트간의 관계를 설정정보를 통해 얻는다. 때로는 외부의 오브젝트 팩토리에 그 작업을 위임하고 그 결과를 가져다 사용하기도 한다.

애플리케이션 컨텍스트를 사용했을 때 얻을 수 있는 장점

  1. 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다
    • 오브젝트 팩토리는 클라이언트가 필요한 오브젝트를 가져오려면 구체적인 오브젝트 팩토리를 알아야한다.
    • 오브젝트 팩토리는 새로운 오브젝트 팩토리가 필요할 때마다 오브젝트 팩토리 클래스를 추가해야한다.
  2. 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
    • 빈이 사용할 수 있는 기반기술 서비스나 외부 시스템과의 연동 등을 컨테이너 차원에서 제공해줌
    • 오브젝트 생성과 관계 연결뿐만 아니라 객체의 생명주기, 만들어지는 방식, 후처리 설정 다변화 인터셉팅 등 오브젝트를 효과적으로 사용할 수 있는 기능 제공
  3. 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.
    • 이름으로 빈을 검색할 수 있는 것 뿐만아니라 타입이나 선언된 어노테이션 등으로 빈을 검색할 수 있다.

스프링의 싱글톤 레지스트리

스프링은 싱글톤 패턴처럼 IoC 컨테이너에서 관리하는 빈의 인스턴스를 오직 하나만 생성하여 관리해준다. 이는 개념적으로 싱글톤 패턴과 닮았지만 실제 구현 방법에서는 큰 차이가 있다.

싱글톤 패턴

보통 싱글톤 패턴은 밖에서 오브젝트를 생성하지 못하게 생성자를 private으로 막아둔다. 그리고 인스턴스를 가져오는 메소드를 호출 할 때 인스턴스가 없으면 내부에서 생성하여 넘겨주고 이미 생성되어 있다면 static 필드에 저장해둔 오브젝트를 넘긴다. 이렇게만 보면 큰 문제가 없어보이지만 이렇게 구현된 싱글톤 오브젝트는 아래와 같은 단점이 존재한다.

  1. private 생성자를 사용하고 있기 때문에 상속이 불가능. 다형성을 이용할 수 없음
  2. 싱글톤은 오브젝트를 만드는 방식이 제한적이기 때문에 테스트에서 사용될 때 목 오브젝트 등으로 대체하기가 힘듦
  3. 서버환경에서 싱글톤이 하나만 만들어지는 것을 보장하기가 어려움
  4. 싱글톤의 사용은 전역의 상태를 만들 수 있기 때문에 바람직하지 못함

싱글톤 레지스트리

스프링은 특별한 설정을 하지 않으면 기본적으로 빈을 싱글톤으로 관리한다. 앞서 소개한 것처럼 자바의 싱글톤 패턴은 여러가지의 단점이 있기 때문에 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다. 스프링의 싱글톤 레지스트리는 싱글톤 패턴처럼 비정상적인 방법으로 싱글톤을 제공하는 것이 아닌 평범한 자바 클래스를 싱글톤으로 활용하게 해준다. 때문에 제약없이 클래스를 자유롭게 활용할 수 있고 스프링의 지지하는 객체지향 설계방식을 적용하는데에 아무런 제약이 없다.

싱글톤이기에 조심해야할 점

싱글톤은 자원을 효율적으로 사용할 수 있는 대신에 꼭 조심해야할 부분이 있다. IoC 컨테이너에서 오브젝트를 생성하고 그 오브젝트를 필요로 할 때 이미 생성된 오브젝트를 주입해주기 때문 멀티스레드 환경에서 동시성 문제가 발생할 수 있다. 결국 생성된 오브젝트는 heap 영역에 저장되기 때문에 모든 스레드가 접근할 수 있기 때문이다. 그래서 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 한다. 그러나 읽기 전용의 값이라면 초기화 시점에 인스턴스 변수에 저장하고 공유하는 것은 문제가 되지 않는다.

의존관계 주입

IoC 컨테이너라는 단어만으로는 스프링이 제공하는 기능의 특징을 명확하게 설명하지 못함. IoC 개념이 적용된 템플릿 메소드 패턴을 이용한 프레임워크인지 서블릿 컨테이너처럼 서버에서 동작하는 프레임워크인지 명확하게 알 수 없음. 때문에 스프링이 제공하는 IoC 기능을 명확하게 설명하기 위해서 DI(의존관계 주입)이라는 좀 더 의도가 명확한 이름을 사용하기로 결정

DI의 핵심은 오브젝트 레퍼런스를 외부로부터 제공받아 이를 통해 여타 오브젝트와 동적으로 의존관계를 맺는 것이 핵심

스프링은 런타임시에 의존을 주입해준다. 그렇기 때문에 코드에는 런타임 시점의 의존관계가 들어나지 않는다. 그러기 위해선 인터페이스에만 의존해야한다. 이후 런타임 시점의 의존관계는 컨테이너나 팩토리같은 제3의 존재가 결정한다. 이후 의존관계는 제 3의 존재가 사용할 오브젝트 레퍼런스를 제공해줌으로써 만들어진다.

의존관계 검색과 의존관계 주입의 차이

의존관계 주입은 말 그대로 외부에서 생성자나 메소드로 의존을 주입 받지만 의존관계 검색은 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트 생성을 컨테이너에게 요청하고 이를 가져올 때는 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용한다.

의존관계 검색도 의존관계 주입의 모든 장점을 갖고있지만 코드안에 오브젝트 팩토리나 스프링 API가 나타나는 단점이 있다. 대신 의존관계 검색을 사용하는 오브젝트는 직접 컨테이너에게 요청하기 때문에 스프링의 빈일 필요가 없다. 반면 의존관계 주입은 컨테이너가 주입받을 대상의 정보를 알고있어야 하므로 IoC 컨테이너에서 생성되는 오브젝트(Bean)여야 한다.

메소드를 이용한 의존관계 주입

1. 수정자 메소드를 이용한 주입

수정자 메소드를 이용한 의존관계 주입 방법은 외부로부터 제공받은 오브젝트 레퍼런스를 저장해뒀다가 내부의 메소드에서 사용하게 하는 DI 방식에서 활용하기에 적당하다.

2. 일반 메소드를 이용한 주입

수정자 메소드의 제약이 싫을 경우 일반 메소드를 이용하여 의존을 주입할 수 있다. 이 방식은 적절한 수의 파라미터를 가진 여러개의 초기화 메소드를 만들 수 있다는 장점이 있기 때문에 한번에 모든 필요한 파라미터를 다 받아야하는 생성자 주입보다 사용이 편리할 수 있다.

1장 정리

관심사의 분리

  • 초난감이었던 UserDao의 관심사를 분리하여 리팩토링

변화가 잦은 부분을 추상화

  • 클라이언트가 구체적인 구현체에 의존하지 않고 추상화된 인터페이스에 의존하게 된다면 구현체가 변경되어도 클라이언트는 코드를 수정하지 않아도 됨 (전략패턴)
  • 관심사를 분리할 때 변화가 잦은 부분을 추상화하여 외부에서 구현체를 결정할 수 있도록 하면 (의존주입) 낮은 결합의 의존관계를 맺을 수 있음
  • 결과로 한쪽의 기능 변화가 다른 쪽의 변경을 요구하지 않아도 되게 했고(낮은 결합도) 자신의 책임과 관심사에게만 순수하게 집중 할 수 있도록 함(응집도)

개방 폐쇄 원칙

  • 자신의 책임 자체가 변경되는 경우 외에는 불필요한 변화가 발생하지 않게 막아주고 자신이 사용하는 외부 오브젝트의 기능은 자유롭게 확장하거나 변경할 수 있게 만들었음 (OCP)

제어의 역전 (IoC)

  • 오브젝트가 생성되고 다른 오브젝트와 관계를 맺는 책임을 별도의 팩토리, 컨테이너에게 넘기고 자신이 사용할 오브젝트의 생성과 선택에 대해 자유로워졌음

의존관계 주입 (DI)

  • 설계 시점과 코드에는 인터페이스로 느슨한 의존관계를 만들어놓고 런타임시에 실제 사용할 구체적인 의존 오브젝트를 제 3자(DI 컨테이너)의 도움을 받아서 주입

스프링?

스프링이란 오브젝트가 어떻게 설계되고 만들어지고 관계를 맺고 사용되는지에 관심을 갖는 프레임워크. 스프링의 관심은 오브젝트와 그 관계일 뿐 오브젝트의 설계와 분리, 개선은 개발자의 역할이다. 스프링은 원칙은 잘 따르는 설계를 적용하려고 할 때 번거로운 작업을 도와주는 도구일 뿐 그외는 개발자의 책임이다. 결론은 잘 디자인된 프로젝트를 만들어지려면 스프링에게만 의존하는 것이 아닌 개발자의 노력이 필요하다.

This post is licensed under CC BY 4.0 by the author.

[토비의 스프링] 토비의 스프링 3.1 시작하기

[토비의 스프링] 토비의 스프링 3.1 2장 테스트