Group Study (2023-2024)/Spring 심화

[Spring 심화] 1장 오브젝트와 의존관계

이프로🍑 2023. 11. 6. 23:01
1장 오브젝트와 의존관계

☘️스프링이란 '어떻게 오브젝트가 설계되고, 만들어지고, 어떻게 관계를 맺고 사용되는지 관심을 갖는 프레임워크'이다.

1.1 초난감 DAO

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

사용자 정보를 DB에 저장하고 조회할 수 있는 간단한 DAO를 하나 만들어보자.

public class UserDao {
    public void add(User user) throws ... {
        Class.forName("com.mysql.jdbc.Driver");
        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");

        PreparedStatement ps = c.prepareStatement("insert into users(id, name, password) values(?,?,?)");
        ps.setString(1, user.getId());
        ...

        ps.executeUpdate();

        ps.close();
        c.close();
    }

    public User get(String id) throws ... {
        ...
    }
}

개발팀에서 바로 쫓겨날 수 있는 초난감 DAO 코드를 객체지향 기술의 원리에 충실한 멋진 스프링 스타일의 코드로 개선해보자 !

DAO의 분리

  • 관심사의 분리 Seperation of Concerns

커넥션 만들기의 추출

User DAO의 관심사항

1️⃣ DB와 연결을 위한 커넥션을 어떻게 가져올까.
2️⃣ SQL문장을 담을 Statement를 만들고 실행하는 것.
3️⃣ 작업이 끝나면 공유 리소스를 시스템에 돌려주는 것.

중복 코드의 메소드 추출

중복된 DB 연결 코드를 getConnection()이라는 이름의 독립적인 메소드로 만들자.

💡리팩토링 refactoring
기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술을 말한다.

  • 리팩토링에서의 메소드 추출 기법extract method으로 중복을 제거했다.

DB 커넥션 만들기의 독립

🤔UserDao를 수정하지 않고, DB 커넥션 생성 방식을 바꿀 수 있을까?

상속을 통한 확장

getConnection()을 추상메소드로 만들자. UserDao클래스를 상속하는 NUserDao, DUserDao라는 서브클래스를 만들고 추상메소드 getConnection()을 원하는 방식으로 구현하면 된다. (p.66-67)

💡디자인패턴
소프트웨어 설계 시 특정 상황에서 자주 만나는 문제를 해결하기 위해 사용할 수 있는 재사용 가능한 솔루션을 말한다.

💡템플릿 메소드 패턴 template method pattern
디자인 패턴에서 이렇게 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법.

💡팩토리 메소드 패턴 (factory method pattern)
서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 팩토리 메소드라고 하고, 이 방식을 통해 오브젝트 생성 방법을 나머지 로직, 즉 슈퍼 클래스의 기본 코드에서 독립시키는 방법을 팩토리 메소드 패턴이라고 한다.

▶️ 이 설명을 "UserDao에 팩토리 메소드 패턴을 적용해서 getConnection()을 분리합시다."라는 한마디로 담아낼 수 있다.

🤔 상속의 문제점

1️⃣ 이미 UserDao가 다른 목적을 위해 상속을 사용한다면? 자바는 다중상속을 허용하지 않는다.
2️⃣ 상속을 통한 상하위 클래스의 관계는 생각보다 밀접하다. 여전히 상속관계는 두 가지 다른 관심사에 대해 긴밀한 결합을 허용한다. 서브 클래스는 슈퍼클래스의 기능을 직접 사용할 수 있다.
3️⃣ DB 커넥션을 생성하는 코드를 다른 DAO 클래스에 적용할 수 없다. 다른 DAO클래스들에도 getConnection()의 구현코드가 중복될 것이다.

클래스의 분리

DB 커넥션과 관련된 부분을 서브클래스가 아닌 별도의 클래스에 담고, UserDao가 이용하게 하자.
➡️ SimpleConnectionMaker*라는 새로운 클래스를 만들고 DB 생성 기능을 그 안에 넣는다. 그리고 UserDao는 new 키워드를 사용해 *SimpleConnectionMaker 클래스의 오브젝트를 만들어 두고, 이를 add(), get() 메소드에서 사용하면 된다.

🤔 상속을 통해 커넥션 기능의 확장이 다시 불가능해졌다.

simpleConnectionMaker = new SimpleConnectionMaker();
...
connection c =simpleConnectionMaker.openConnection();

근본적인 원인은 UserDao가 바뀔 수 있는 정보, 즉 DB커넥션을 가져오는 클래스에 대해 너무 많이 알고 있기(종속적이기) 때문이다.

인터페이스의 도입

관심사를 분리하면서 상속의 문제를 해결하기 위한 방법으로는 인터페이스로 해결할 수 있다. 인터페이스를 통해 접근하면 실제 구현 클래스를 바꿔도 신경 쓸 일이 없다.

🤔인터페이스를 이용해서 DB커넥션을 제공하는 클래스에 대한 구체적인 정보는 모두 제거되었지만, 초기에 어떤 클래스의 오브젝트를 사용할 지 결정하는 생성자의 코드가 제거되지 않고 남아 있었다.

관계설정 책임의 분리

💥아직 분리되지 못한 관심사항
UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 관한 관심.
➡️ UserDao의 클라이언트가 구현 클래스를 선택하고, 선택한 클래스의 오브젝트를 생성해서 UserDao와 연결해주도록 하자. 생성자 패러미터를 통해서 연결을 수행할 수 있다.

💡클라이언트 : 사용하는 오브젝트
💡 서비스 : 사용되는 오브젝트

원칙과 패턴

💡개방 폐쇄 원칙 (OCP, Open-Closed Principle)
클래스나 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

💡객체지향 설계 원칙 (SOLID)
객체지향 설계 원칙은 객체지향의 특징을 잘 살릴 수 있는 설계의 특징을 말한다. SOLID는 아래 5가지 원칙의 첫 글자를 따서 만든 단어다.
- SRP(The Single Principle) : 단일 책임 원칙
- OCP(The Open Closed Principle): 개방 폐쇄 원칙
- LSP(The Liskov Subsitution Principle): 리스코프 치환 원칙
- ISP(The Interface Segreation Principle): 인터페이스 분리 원칙
- DIP(The Dependency Inversion Principle) 의존관계 역전 원칙

높은 응집도와 낮은 결합도

개방폐쇄 원칙은 높은 응집도와 낮은 결합도 (high coherence and low coupling) 라는 소프트웨어 개발의 고전적인 원리로도 설명이 가능하다.

  • 높은 응집도
  • 응집도가 높다는 것은 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다는 것으로 설명할 수 있다. 즉 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다면 응집도가 높다고 말할 수 있다.
  • 낮은 결합도
  • 낮은 결합도는 높은 응집도보다 더 민감한 원칙이다. 결합도란 '하나의 오브젝트가 변경이 일어날 떄에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도'라고 설명할 수 있다.

전략 패턴

개선한 UserDaoTest-UserDao-ConnectionMaker 구조를 디자인 패턴의 시각으로 보면 전략 패턴 (Strategy Pattern) 에 해당한다고 볼 수 있다.

💡전략 패턴 (Strategy Pattern)
자신의 기능 맥락(Context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴이다.

1.4 제어의 역전(IoC)

IoC라는 약자로 많이 사용되는 제어의 역전 (Inversion of Control) 이라는 용어에 대해 알아보고 UserDao를 더 개선하자.

오브젝트 팩토리

🤔 UserDaoTest는 원래 테스트를 위해 만든 클래스이다. 떠맡은 구현클래스를 결정하고 연결하는 기능을 분리해보자.

팩토리

💡팩토리 (factory)
객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 역할을 담당하는 오브젝트

1.5 스프링의 IoC

스프링 IoC의 용어 정리

💡빈 bean
빈 또는 빈 오브젝트는 스프링이 IoC 방식으로 관리하는 오브젝트라는 뜻으로, 관리되는 오브젝트managed object라고 부르기도 한다. 스프링 애플리케이션의 모든 오브젝트 중, 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 한다.

💡 빈 팩토리 bean factory
스프링의 IoC를 담당하는 핵심 컨테이너를 가리킨다. 빈을 등록하고, 생성하고, 조회하고 돌려주고, 그 외에 부가적인 빈을 관리하는 기능을 담당한다.

💡애플리케이션 컨텍스트 application context
빈 팩토리를 확장한 IoC 컨테이너이다. 빈을 등록하고 관리하는 기본적인 긴으은 빈 팩토리와 동일하며, 여기에 스프링이 제공하는 각종 부가 서비스를 추가로 제공한다.

💡설정정보/설정 메타정보 configuration metadata
스프링의 설정정보란 애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보를 말한다.

💡컨테이너container 또는 IoC 컨테이너
IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고 한다. 스프링 컨테이너나 맥락에 따라 스프링이 가리키는 말이 될 수 있다.

1.6 싱글톤 레지스트리와 오브젝트 스코프

애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리 singleton resistry이기도 하다. 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.

왜 스프링은 싱글톤으로 빈을 만드는 걸까?

스프링이 주로 적용되는 대상이 대규모 서버 환경이기 때문에 오브젝트 생성으로 인한 부하를 줄이기 위해서!

싱글톤 패턴의 한계

1️⃣ private 생성자를 갖고 있기 때문에 상속할 수 없다.
2️⃣ 싱글톤은 테스트하기 어렵다.
3️⃣ 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
4️⃣ 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.

💡싱글톤 패턴 Singleton Pattern)
싱글톤 패턴은 GoF가 소개한 디자인 패턴 중의 하나로, 어떤 클래스를 애플리케이션 내에서 주로 하나만 존재하도록 강제하는 패턴이다.

싱글톤 레지스트리 Singletone registry

스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데, 이것이 싱글톤 레지스트리이다.

  • 평범한 자바 클래스라도 IoC 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리되게 할 수 있다.
  • 주의할 점
    • 싱글톤이 멀티스테드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 Stateless방식으로 만들어져야 한다.

스프링 빈의 스코프

빈이 생성되고, 존재하고, 적용되는 범위를 빈의 스코프 scope라고 한다.

  • 스프링 빈의 기본 스코프는 싱글톤
    • 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다.
  • 그 외
    • 프로토타입 prototype 스코프 : 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어 준다.
    • 요청 request 스코프: 웹을 통해 새로운 HTTP 요청이 생길 때마다 생성된다.
    • 세션 session 스코프: 웹의 세션과 스코프가 유사하다.

1.7 의존관계 주입(DI)

스프링 IoC 기능의 대표적인 동작원리는 주로 의존관계 주입이라고 불린다.

  • 의존관계 주입은 구체적인 오브젝트와 그것을 사용할 주체, 클라이언트를 런타임 시에 연결해주는 작업을 말한다.
  • 의존관계 주입의 핵심은 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제 3의 존재가 있다는 것이다.
  • 스프링의 애플리케이션 컨텍스트, 빈 팩토리, IoC 컨테이너 등이 모두 외부에서 오브젝트 사이의 런타임 관계를 맺어주는 책임을 지닌 제 3의 존재라고 볼 수 있다.

의존관계 검색과 주입

스프링이 제공하는 IoC 방법에는 의존관계 주입만 있는 것이 아니다. 의존관계를 맺는 방법이 외부로부터의 주입이 아니라 스스로 검색을 이용하기 때문에 의존관계 검색(dependency Lookup)이라고 불리는 것도 있다. 의존관계 검색은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성 작업은 외부 컨테이너에게 IoC로 맡기지만, 이를 가져올 때는 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용한다.

public  UserDao()  {

AnnotationConfigApplicationContext context =

new  AnnotationConfigApplicationContext(DaoFactory.class);

this.connectionMaker = context.getBean("connectionMaker",  ConnectionMaker.class);

}

의존관계 주입의 응용

  • 기능 구현의 교환
  • 다양한 환경에서 해당 빈을 사용하는 내부 구현체만 교체하면 다양한 구현방식을 제공할 수 있다.
  • 부가기능 추가
  • DI의 장점은 관심사의 분리(SOC)를 통해 얻어지는 높은 응집도에서 나온다.

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

  • 수정자 메소드를 이용한 주입
    수정자(Setter) 메소드는 외부에서 오브젝트 내부의 애트리뷰트 값을 변경하려는 용도로 주로 사용한다.
  • 일반 메소드를 이용한 주입
    수정자 메소드처럼 set으로 시작해야 하고 한 번에 한 개의 파라미터만 가질 수 있다는 제약이 싫다면 여러 개의 파라미터를 받을 수 있다는 점이다,

1.8 XML을 이용한 설정

  자바의 코드 설정 정보 XML 설정 정보
빈 설정파일 @Configuration  
빈의 이름 @Bean methodName() <bean id=methodName"
빈의 클래스 return new BeanClass(); class="a.b.c... BeanClass">

 

 

'Group Study (2023-2024) > Spring 심화' 카테고리의 다른 글

[Spring 심화] 4장 예외  (1) 2023.11.27
[Spring 심화] 3장 템플릿  (0) 2023.11.21
[Spring 심화] 2장 테스트  (0) 2023.11.13