토비의 스프링 정리 프로젝트 #1.8 XML을 이용한 설정

Jake Seo·2021년 7월 12일
0

토비의 스프링

목록 보기
9/29

XML의 도입

오브젝트 사이의 의존 정보는 틀에 박힌 구조를 갖고 있으며, 일일이 자바 코드로 만들어주기 번거롭다. 또, DI 구성이 바뀔 때마다 자바 코드를 수정하고 클래스를 다시 컴파일하기도 귀찮다.

사실 현재에 와서는 스프링 부트 이후 레거시 프로젝트 외에는 XML로 의존관계 설정을 하는 것을 본적은 없다. 지금은 사장된 것 같지만 일단 배워두자. (2021-07-12 작성)

XML의 특징

  • 단순한 텍스트 파일이다.
  • 별도의 빌드 작업이 필요 없다.
  • 사람의 눈으로 이해하기 쉽다.
  • 환경이 바뀌어도 XML의 내용은 바뀔 필요가 없다.
  • 스키마나 DTD를 이용해 정해진 포맷을 따라 작성했는지 쉽게 체크 가능하다.

XML 설정

<beans>를 루트 엘리먼트로 사용한다. <beans> 내부에는 여러개의 <bean>을 정의할 수 있다.

자바 애노테이션과 XML의 차이

XML 설정은 @Configuration, @Bean 애노테이션이 붙은 자바 클래스로 만든 설정 내용과 결국 동일하다.

  • @Configuration<beans>에 대응된다.
  • @Bean<bean>에 대응된다.

빈의 대표적 DI 정보

  • 빈의 이름: 애노테이션에서는 @Bean 메소드 이름이 빈의 이름이 되었다.
  • 빈의 클래스: 빈 오브젝트를 어떤 클래스를 이용해 만들지를 정의한다.
    • 이전 @Bean 애노테이션이 붙은 메소드의 new 클래스명클래스명이 될 수 있다.
  • 빈의 의존 오브젝트: 빈의 생성자나 수정자 메소드를 통해 의존 오브젝트를 넣어준다. 의존 오브젝트도 하나의 빈이므로 이름이 있고, 그 이름에 해당하는 메소드를 호출하여 의존 오브젝트를 가져온다. 의존 오브젝트는 하나 이상일 수도 있다.
    • 이전 @Bean 메소드에서도 다른 @Bean 메소드를 호출하여 의존 오브젝트를 설정했었다.

connectionMaker() 전환

자바와 XML 비교

  • 빈 설정파일
    • 자바: @Configuration
    • XML: <beans>
  • 빈 이름
    • 자바: @Bean methodName()
    • XML: <bean id="methodName">
  • 빈 클래스
    • 자바: return new BeanClass();
    • XML: class="a.b.c...BeanClass">
      • 반드시 리턴 타입 인터페이스가 아닌 구현체 클래스를 가리켜야 한다. XML에서는 리턴하는 타입을 지정하지 않아도 된다.

IDE는 클래스 이름만으로 빈 클래스를 쉽게 import할 수 있었는데, XML은 상대적으로 번거롭다.

@Bean // 오브젝트 생성을 담당하는 IoC용 메소드라는 표시이다.
public ConnectionMaker connectionMaker() {
    return new DConnectionMaker();
}
<bean
id="connectionMaker"
class="toby_spring.chapter1.user.connection_maker.DConnectionMaker" />

userDao() 전환

XML로 의존관계 정보를 만들 때는 자바빈의 관례를 따라 수정자 메소드를 프로퍼티로 사용한다. 프로퍼티 이름은 set을 제외한 나머지 부분을 사용한다.

위에서 정의한 @Bean connectionMaker() 메소드를 이용한 의존성 주입자바코드는 아래와 같다.

userDao.setConnectionMaker(connectionMaker());

XML에서는 의존관계 정보를 줄 때, <bean id="userDao"> 내부에 아래와 같이 치환될 것이다.

<property name="connectionMaker" ref="connectionMaker" />
<beans>
  <bean id="connectionMaker" class="toby_spring.chapter1.user.connection_maker.DConnectionMaker" />
  <bean id="userDao" class="toby_spring.chapter1.user.dao.UserDao">
    <property name="connectionMaker" ref="connectionMaker" />
  </bean>
</beans>

빈의 이름을 바꾸는 경우 주의사항

properties 내부에 nameref 속성은 문자열 자체는 같지만, 의미가 다르다. name은 수정자 프로퍼티를 가리키는 것이고, ref는 주입할 오브젝트를 정의한 빈의 ID이다.

보통 빈의 이름은 바뀔 수 있는 클래스의 이름보다는 인터페이스의 이름을 많이 사용한다. 이로 인해 프로퍼티 이름과 빈의 이름이 같은 경우가 흔하다. 물론 빈의 이름(id)과 프로퍼티 이름이 달라도 상관없다. 다만, 빈의 이름을 바꿀 때는 해당 빈을 프로퍼티로 쓰는 ref의 이름도 잘 바꿔주자.

XML은 자바코드에 비해 상대적으로 리팩토링에 주의가 많이 필요하다.

<beans>
  <bean id="myConnectionMaker" class="toby_spring.chapter1.user.connection_maker.DConnectionMaker" />
  <bean id="userDao" class="toby_spring.chapter1.user.dao.UserDao">
    <property name="connectionMaker" ref="myConnectionMaker" />
  </bean>
</beans>

connectionMakermyConnectionMaker로 바꾸어보았다. 빈의 id와 프로퍼티의 ref를 둘 다 변경했다.

같은 인터페이스 타입의 빈을 여러개 정의해놓고 쓰는 경우

<beans>
  <bean id="dConnectionMaker" class="toby_spring.chapter1.user.connection_maker.DConnectionMaker" />
  <bean id="nConnectionMaker" class="toby_spring.chapter1.user.connection_maker.NConnectionMaker" />
  
  
  <bean id="userDao" class="toby_spring.chapter1.user.dao.UserDao">
    <property name="connectionMaker" ref="nConnectionMaker" />
  </bean>
</beans>

위와 같이 dConnectionMaker, nConnectionMaker 등 여러개의 빈을 정의해놓고 필요한 것을 골라쓸 수도 있다. local, test, production 등의 빈을 만들어놓고 테스트하면 용이하다.

DTD와 스키마

DTD스키마를 활용하면 XML이 미리 정해진 구조를 따라 작성했는지 알 수 있다. 스프링의 XML 설정파일은 이 두가지 방식을 모두 지원한다.

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">

위는 DTD를 이용한 방식이다.

<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">

위는 스키마를 이용한 방식이다.

스프링은 DI를 위한 기본 태그인 <beans>, <bean>이외에도 특별한 목적을 위해 별도의 태그를 사용할 수 있는 방법을 제공한다. 이런 태그를 이용하려면 별개의 스키마 파일을 이용해 독립적인 네임스페이스를 제공해야 해서 DTD 대신 스키마를 이용하는 것이 바람직하다.

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

GenericXmlApplicationContext를 이용하여 애플리케이션 컨텍스트를 만들어보자.

Intellij의 위 메뉴를 이용하면 손쉽게 Spring Config XML 파일을 생성할 수 있다.

애플리케이션 컨텍스트의 XML 파일 이름은 관례를 따라 applicationContext.xml으로 지정했다.

java 내부에 만드는 것이 아니라 resources 내부에 만들어야 한다.

기본적인 네임스페이스들은 전부 설정되어 있다.

빈을 추가할 때도 class 애트리뷰트는 앞글자만 입력해도 자동완성된다.

수정자가 없으면 위와 같이 수정자를 만들라는 안내 메세지가 나온다.

정상적으로 수정자가 생성되면 위와 같이 IDE에서 연결된 프로퍼티를 표기해준다.

public class XmlUserDaoTest {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        ApplicationContext applicationContext = new GenericXmlApplicationContext("spring/applicationContext.xml");
        UserDao userDao = applicationContext.getBean(UserDao.class);

        User user = new User();
        user.setId("22522");
        user.setName("제이크22522");
        user.setPassword("jakejake");

        userDao.add(user);

        System.out.println(user.getId() + " register succeeded");

        User user2 = userDao.get(user.getId());
        System.out.println(user2.getName());
        System.out.println(user2.getPassword());

        System.out.println(user2.getId() + " query succeeded");
    }
}

위와 같이 소스코드를 작성해주고 테스트한 결과 정상적으로 동작한다.

DataSource 인터페이스로 변환하기

DataSource 인터페이스 적용

사실 자바에서는 ConnectionMaker의 기능을 지원하는 인터페이스가 이미 있다. DataSource는 DB 커넥션을 가져오는 기능 외에도 여러 개의 메소드를 추가적으로 갖고 있다. 또한 스프링에는 해당 DataSource를 구현하여 DB 연결, 풀링 등 많은 기능을 갖춘 클래스를 제공한다.

당연히 DB의 종류, 아이디, 비밀번호는 얼마든지 바꾸어도 구현 클래스를 다시 만들지 않아도 되는 정도의 유연성은 제공한다.

스프링에서 제공하는 구현체를 사용하기 위해 implementation group: 'org.springframework', name: 'spring-jdbc', version: '5.3.8' 의존성을 추가해주자.

SimpleDriverDataSoruce 적용

SimpleDriverDataSource는 스프링에서 제공하는 구현 클래스 중 테스트 환경에서 간단히 사용할 수 있는 DataSource이다. 이 클래스를 사용하도록 DI를 재구성하자.

자바 코드로 적용

UserDao.java

public class UserDao {
    DataSource dataSource;

    public UserDao() {
    }
    
    public UserDao(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

DaoFactory.java

@Configuration
public class DaoFactory {
    @Bean
    public UserDao userDao() throws ClassNotFoundException {
        return new UserDao(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        dataSource.setDriverClass(org.postgresql.Driver.class);
        dataSource.setUrl("jdbc:postgresql://localhost/toby_spring");
        dataSource.setUsername("postgres");
        dataSource.setPassword("iwaz123!@#");
        return dataSource;
    }
}

위와 같이 UserDao가 의존하는 객체를 DataSource 인터페이스로 바꾼 뒤에 DaoFactory에서 새로 생성한 SimpleDriverDataSource 오브젝트를 주입하면 된다. SimpleDriverDataSourcesetterDriverClass, URL, Username, Password를 삽입할 수 있다.

XML로 적용

applicationContext.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.xsd">
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="username" value="postgres" />
        <property name="password" value="iwaz123!@#" />
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost/toby_spring" />
    </bean>

    <bean id="userDao" class="toby_spring.chapter1.user.dao.UserDao">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

위와 같이 작성해주었다. 자바 코드와 같은 내용을 xml로만 바꾼 것이다.

bean으로 정의한 것을 프로퍼티로 주입할 때는 ref를 사용하고, 일반 값을 프로퍼티로 주입할 때는 value를 사용한다.

xml 프로퍼티 value 값의 자동 변환

위에 driverClass를 자바 소스로 적용할 때와는 다르게 ...Driver.class까지 적어주지 않았는데, 그 이유는 스프링이 프로퍼티의 값을 수정자 메소드 파라미터의 타입을 참조하여 적절하게 변환해주기 때문이다. 스프링은 수정자 메소드의 파라미터 타입이 Class임을 확인하고 org.postgresql.Driver.class 오브젝트로 자동 변경해준다.

스프링은 value에 지정한 텍스트 값을 적절한 자바 타입으로 변환해주는데, Integer, Double, String, Boolean과 같은 기본 타입은 물론이고, Class, URL, File, Charset 같은 오브젝트로 변환이 가능하다. 값이 여러개라면 List, Map, Set, Properties나 배열로도 주입이 가능하다.

변경 후에 테스트를 돌려본 결과 정상적으로 동작한다. 항상 테스트는 돌려보자.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글