토비의 스프링 Vol.1 1장 - 오브젝트와 의존관계

서재·2023년 11월 30일
0
post-thumbnail

해당 시리즈는 토비의 스프링 3.1 |Vol.1| 스프링의 이해와 원리
을 읽고 인상깊었던 점들을 정리한 시리즈입니다.


📌 1.1 초난감 DAO

DAO

Data Access Object의 약자
JDBC API를 통해 DB에 접근하는 객체를 의미한다.

DAO가 JDBC를 이용하여 DB에 접근하기 위해선,
DB로부터 Connection을 얻고,
SQL을 실행하여,
DB를 수정하거나 조회한다.


📌 1.2 DAO의 분리

삽입과 조회를 하는 DAO에 대한 예시가 명시되어 있다.
각 메소드는 DB에 접근하기 위해 Connection을 얻는데,

수정에 닫히고 확장에는 열리도록 작성하기 위한,
리팩토링에 관련된 내용이었다.

그 중 잠깐 디자인 패턴들이 언급되었다.

템플릿 메소드 패턴

DAO의 삽입 메소드와 조회 메소드에서 Connection을 얻는 메소드를 추출하고,
DB의 변경에 유연하도록 DAO를 상속하는 서브 클래스를 추가한다.

이 때, 전체적인 골격이 정의된 슈퍼 클래스의 일부 메소드를 abstractprotected로 명시하여, 서브 클래스가 필요에 맞게 구현하도록 하는 방법을 템플릿 메소드 패턴이라고 한다.

팩토리 메소드 패턴

위와 같이 객체 생성을 서브 클래스에 위임하는 패턴팩토리 메소드 패턴이라고 한다.
이 경우, 템플릿 메소드 패턴 내에서 팩토리 메소드 패턴이 사용된 것이다.

자바에서는 종종 오브젝트를 생성하는 메소드 자체를 팩토리 메소드라고 부른다고도 한다.
하지만 이 팩토리 메소드와, 팩토리 메소드 패턴은 다른 것이라고 하니, 유의하자.


📌 1.3 DAO의 확장

전략 패턴

위와 같이 DAO가 DB에 따라 다른 서브 클래스를 가진다면,
모든 DAO들이 서브 클래스들을 가져야 하며,
DB 접근 방식이 변할 때마다 모든 DAO들의 서브 클래스들에 수정이 필요해진다.

이를 해결하기 위해 Connection을 만드는 인터페이스를 분리하여 사용한다.

이 때, 불필요한 의존관계를 없애기 위해 DAO는 필요한 ConnectionMaker주입받는다.
이러한 형태는 곧 전략 패턴이다.

해당 소단원에는 SOLID 원칙 중 개방 폐쇄 원칙에 대한 내용 또한 다룬다.
전략 패턴'개발 폐쇄 원칙을 실현하기에도 가장 맞는 패턴이라고 한다.


📌 1.4 제어의 역전 (IoC)

클래스가 본인이 어떻게 생성될지를 정하지 않고,
해당 클래스를 사용하는 객체가 해당 클래스를 어떻게 생성할지를 능동적으로 결정하도록 하는 것이
제어의 역전이다.
즉, 사용자가 생성마다 직접 작성하는 것이 아닌, 프레임워크에 위임해놓는 것이다.

ConnectionMaker의존성을 주입받는 DAO와, DAO들을 생성하는 팩토리 클래스,
이러한 형태가 제어의 역전이 적용된 상태로,

프레임워크는제어의 역전이 적용된 대표적인 기술이라고 한다.


📌 1.5 스프링의 IoC

어플리케이션 컨텍스트

  • 빈(bean) : 스프링이 IoC 방식으로 관리하는 객체

  • 빈 팩토리 : 빈을 관리하는 스프링 컨테이너의 최상위 인터페이스

  • 어플리케이션 컨텍스트 : 빈 팩토리의 확장
    어플리케이션 컨텍스트 인터페이스는 빈 팩토리를 상속
    어플리케이션 컨텍스트를 그냥 스프링 자체로 부르는 개발자들도 있다고 한다.

@Configuration	// 어플리케이션 컨텍스트가 사용할 정보라는 명시
public class DaoFactory {

	@Bean		// 오브젝트 생성 메소드
    public UserDao userDao() {
    	return new UserDao(connectionMaker());
    }
    
	@Bean		// ConnectionMaker 구현체 생성 메소드
    public ConnectionMaker connectionMaker() {
    	return new DConnectionMaker();
    }
}

위는 빈 팩토리가 사용할 설정 정보를 담은 클래스이다.

이전에 우아한테크코스 프리코스의 코드 리뷰 활동 중 이와 같은 형태로 생성자를 관리하는 코드를 본 적이 있었는데, 그것이 바로 빈 팩토리를 응용한 방식이었음을 이번에 깨달았다.

	ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
    UserDao userDao = context.getBean("userDao", UserDao.class);

위는 테스트 클래스에서 어플리케이션 컨텍스트로부터 Dao를 생성하는 방법의 예시이다.

  • AnnotiationConfigApplicationContext : 매개변수인 클래스를 기반으로 ApplicationContext를 생성한다.

  • getBean() : 설정된 어플리케이션 컨텍스트로부터 객체를 생성한다.
    1번째 매개변수는 객체 생성 메소드의 이름이다.
    2번째 매개변수는 받고자 하는 객체의 타입이다.
    기본적으로 Object 타입을 반환하기에 제너릭 방식을 이용하여 반환타입을 설정하는 것이다.

아직 책의 초반부라 요즘도 실제로 이런 식으로 테스트를 하는지는 아직 잘 모르지만,
개발에는 테스트가 무척이나 중요하기에 이러한 테스트 코드 방식도 유심히 읽어야 한다고 생각한다.


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

싱글톤 패턴

멀티 스레드 환경에서 수백개의 요청이 들어올 때마다, Dao를 생성하게 된다면 큰 부담이 된다.
이를 해결하기 위한 방법이 싱글톤 패턴이다.
하지만 기본적인 형태싱글톤 패턴은 제약이 많으며 (생성자 private, 상속 불가, 어려운 테스트), 전역 상태가 되어버리는 단점이 존재한다. 그렇기에 안티 패턴이라고도 한다.

싱글톤 레지스트리

스프링에서는 기본적인 싱글톤 패턴의 단점을 극복한,
평범한 자바 클래스를 싱글톤으로 사용할 수 있는 기능을 제공한다고 한다.

스프링은 기본적으로 모든 을 싱글톤으로 처리한다.
해당 기능을 간단하게 사용할 수 있도록 구현하였다는 것이 무척이나 놀라웠다.

상태 관리

싱글톤 패턴의 객체는 상태 관리가 중요하다.

빈의 스코프

빈이 생성되고 관리되는 범위
스프링의 경우, 대부분이 싱글톤 스코프이다.


📌 1.7 의존관계 주입(DI)

1.3 장에서 이미 나왔던 내용이다.
이번에는 의존관계 주입에 대해 더 자세히 다루어졌다.

인터페이스 분리는 곧 느슨한 결합을 갖게 한다.

주입을 받게 되면, Dao는 어떠한 ConnectionMaker 구현체를 사용하는지 모른다.
그저 ConnectionMaker의 추상적인 메소드들을 사용하게 되는 것이다.

부가 기능 추가

책에는 커넥션을 가지는 횟수를 세는 클래스에 대한 예시가 나온다.
분석 작업을 위한 클래스인데, ConnectionMaker 인터페이스를 구현하되, 실제로 사용될 ConnectionMaker 구현체를 주입받고, CountingConnectionMaker는 중간에서 횟수만 센다.

이러한 방식으로 같은 인터페이스의 구현체를 주입받는 방식은 처음 보는 것이었으며, 무척 인상깊었다.

수정자 메소드 의존관계 주입

setter를 통해 의존관계를 주입하는 방식이 소개되었다.
setter는 객체지향적으로 굉장히 좋지 않다고 생각하고 있었는데,
스프링에서는 전통적으로 메소드로 DI를 하기 위해선, 수정자 메소드를 가장 많이 사용하였다고 한다.

setter로 의존관계를 주입하는 것은 객체의 불변성을 해치며, 의존성을 일부 주입하지 않는 실수도 발생시킬 수 있다고 생각한다.

하지만 생성자의 매개변수로 의존관계를 주입할 경우,
주입해야 할 인터페이스 구현체가 많아질수록, 매개변수가 많아지고, 실수하기도 쉽다.
또한 테스트 시, 사용하지 않는 의존 오브젝트들 또한 일일히 생성하여 주입해야 했다.

처음에는 당연히 왜 setter를 쓰지? 라고 생각하였는데,
트레이드 오프를 생각해보니, setter를 쓰더라도 가져갈 수 있는 장점이 생각보다 크다는 것을 느꼈다.
(특히 테스트에서)

또한 이후 등장하는 XML을 사용 시 수정자 메소드가 사용하기 가장 편리하다고 하다.


📌 1.8 XML을 이용한 설정

XML를 활용한 의존관계

자바 코드에 비해 의존 관계 정보 명시 및 변경이 편리

<beans>
  <bean id="localDBConnectionMaker" class="springbook.user.dao.LocalDBConnectionMaker" />
  <bean id="testDBConnectionMaker" class="springbook.user.dao.TestDBConnectionMaker" />
  <bean id="productionDBConnectionMaker" class="springbook.user.dao.ProductionDBConnectionMaker" />
  
  <bean id="userDao" class="springbook.user.dao.userDao">
    <property name="connectionMaker" ref="localDBConnectionMaker" />
  </bean>
</beans>

XML을 이용한 애플리케이션 컨텍스트

<?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-3.0.xsd">
  <bean.../>
  <bean.../>
</beans>

xmlns : XML 네임스페이스 선언
xsi : 특정 XML 스키마에 따라 작성되었는지 명시
xsi:schemaLocation : 어떤 스키마를 사용하였는지 명시

xsi:schemaLocation에 url이 비슷한 2개길래 오타인가 싶어 찾아보았는데, 네임스페이스 식별자와 해당 스키마 파일의 위치를 공백을 기준으로 쌍으로 지정한다고 한다.

GenericXmlApplicationContext

XML 파일을 읽어 스프링 컨테이너을 등록하고 관리하는 클래스

ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");

DataSource 인터페이스

DB 커넥션을 만들어주는 ConnectionMaker의 역할을 하는 Java의 표준 인터페이스
import javax.sql.DataSource;

SimpleDriverDataSource

DataSource 구현 클래스
DB 연결에 필수적인 정보에 대한 수정 메소드를 제공

@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;
}

위 클래스 또한 아래와 같이 XML로 변환할 수 있다.

  <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/springbook" />
    <property name="username" value="spring" />
    <property name="password" value="book" />
  <bean/>

📌 1.9 정리

지금까지 1장을 읽었다.

초반에는 스프링에 관련된 내용보다는,
객체지향에 대한 이야기가 상당히 많이 나왔다.
지금 생각해보니, 객체지향 프로그래밍 원칙을 지키는 것 또한 프레임워크의 임무 중 하나라고 생각한다.

스프링이라는 프레임워크의 구조에 대해 알기 위해 기반을 다지는 느낌이다.
그 과정에서 간접적으로 일부 디자인 패턴에 대한 지식 또한 보충할 수 있었다.

평범한 클래스를 싱글톤으로 사용하도록 기능을 제공한다는 것과,
Java 클래스 대신 XML을 활용하여 클래스를 만들어 의존관계를 주입하는 것이 특히 인상깊었다.

프로젝트를 편리하게 다루기 위한 개발자들의 노력이 보였다.

profile
입니다.

0개의 댓글

관련 채용 정보