Spring Framework

토비의 스프링 1.4

coga 2024. 4. 9. 09:22

제어의 역전

제어의 역전(Inversion of Control, IoC)은 객체지향 프로그래밍에서 중요한 개념이며, 스프링 프레임워크를 통해 널리 알려진 용어입니다. 이 개념은 프로그래밍에서 꽤 오래전부터 존재해왔으며, IoC의 핵심은 프로그램의 흐름 제어를 개발자가 아닌 프레임워크에 위임하는 것입니다. 이를 통해 개발자는 비즈니스 로직에 집중할 수 있으며, 코드의 유연성과 재사용성이 향상됩니다. UserDao 코드를 더욱 개선하여 IoC의 개념을 적용해 보겠습니다.

오브젝트 팩토리의 도입
UserDaoTest는 원래 테스트를 위한 클래스였지만, 특정 구현 클래스의 사용에 대한 책임을 가지게 되었습니다. 이를 해결하기 위해, 객체 생성 책임을 분리하는 팩토리(Facotry)의 개념을 도입할 수 있습니다. 이러한 팩토리는 객체의 생성 과정을 캡슐화하여, 객체 생성의 책임을 전담합니다. 

팩토리의 등장
팩토리의 주요 역할은 객체의 생성 방법을 결정하고, 생성된 객체를 반환하는 것입니다. 이는 추상 팩토리 패턴이나 팩토리 메소드 패턴과는 구별되는 개념입니다. 여기서의 목적은 단순히 객체 생성의 로직을 분리하여, 생성과 사용의 관심사를 명확하게 분리하는 것입니다. 이를 통해, UserDaoTest는 순수하게 테스트의 책임만을 진행하고, 어떤 ConnectionMaker 구현체를 사용할지에 대한 결정은 팩토리에 위임합니다.

IoC의 실현
이러한 팩토리의 도입은 IoC의 개념을 실현하는 한 예입니다. 여기서 제어의 역전은 프로그램의 흐름을 개발자가 직접 제어하는 것에서 벗어나, 이러한 제어를 외부의 팩토리나 프레임워크에 위임하는 것을 의미합니다. 이를 통해 UserDao는 어떻게 Connection 객체가 생성되는지에 대해 전혀 알 필요가 없으며, 단지 필요한 인터페이스에 대한 의존성만을 가지게 됩니다. 이렇게 IoC를 적용함으로써, 프로그램은 더 모듈화되고 테스트하기 쉽고, 확장성이 높아지며, 유지보수가 용이해집니다.

 

아래 클래스는 팩토리의 역할을 맡을 클래스 DaoFactory

public class DaoFactory {
    public UserDao userDao() {
        return new UserDao(new DSimpleConnectionMaker());
    }
}

 

DaoFactory 팩토리 클래스에 userDao()라는 메소드를 만들어 UserDao 객체를 리턴하도록 해주었다. 그렇다면, 이제 UserDaoTest에 있는 내용도 변경한다.

public class UserDaoTest {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        UserDao dao = new DaoFactory().userDao();

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

        dao.add(user);

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

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

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

UserDaoTest는 이제 UserDao의 생성 과정에 대한 책임을 전혀 지지 않습니다. 그 대신, UserDaoTest의 역할은 오로지 UserDao에 정의된 메소드들의 기능을 검증하는 데에만 집중됩니다.

 

설계도로서의 팩토리
현재까지의 책임과 관계 분석을 해보겠습니다.

여기서 UserDao, ConnectionMaker는 실질적인 로직을 담당하는 컴포넌트라면, DaoFactory는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있습니다.

DaoFactory를 분리함으로써 얻는 여러 장점 중에서 가장 중요한 것은, 애플리케이션의 핵심 기능을 담당하는 컴포넌트와, 애플리케이션의 전체 구조와 흐름을 결정하는 컴포넌트를 명확히 구분하였다는 점이다. 이러한 분리는 애플리케이션의 설계와 유지보수성을 크게 향상시킨다.

 

오브젝트 팩토리의 활용

다른 DAO들이 필요한 상황이 되었다고 가정해 보겠습니다. AccountDao, MessageDao 등을 만들어야 합니다. DaoFactory에 해당 DAO들을 생성해주기 위해 메소드를 아래와 같이 추가했습니다.

public class DaoFactory {
    public UserDao userDao() {
        return new UserDao(new DSimpleConnectionMaker());
    }

    public MessageDao messageDao() {
        return new MessageDao(new DSimpleConnectionMaker());
    }

    public AccountDao accountDao() {
        return new AccountDao(new DSimpleConnectionMaker());
    }
}

 

DaoFactory 내에서 이전에 직면했던 중복 코드 문제가 재발하였습니다. 이전에 성공적으로 적용했던 메소드 추출(refactoring technique) 기법을 다시 활용하여 중복 코드를 제거함으로써, 코드의 효율성과 유지보수성을 향상시키고자 합니다.
public class DaoFactory {
    public UserDao userDao() {
        return new UserDao(getConnectionMaker());
    }

    public MessageDao messageDao() {
        return new MessageDao(getConnectionMaker());
    }

    public AccountDao accountDao() {
        return new AccountDao(getConnectionMaker());
    }

    private DSimpleConnectionMaker getConnectionMaker() {
        return new DSimpleConnectionMaker();
    }
}

 

팩토리는 여전히 설계도(Blueprint) 역할을 수행하고 있어, 애플리케이션의 구조적 설계에 중요한 기능을 지속적으로 제공하고 있습니다.

 

제어권의 이전을 통한 제어관계 역전
제어의 역전이란?
제어의 역전(Inversion of Control, IoC)은 프로그램의 제어 흐름이 전통적인 접근에서 벗어나 역전된다는 개념을 의미합니다. 기존의 프로그래밍 방식에서는 main 메소드와 같은 진입점에서 시작하여, 필요한 객체를 생성하고, 객체가 사용할 구현체를 직접 결정하며, 그 객체의 행동을 관리하는 방식으로 진행됩니다.

예를 들어, 초기 버전의 UserDao는 DConnectionMaker와 같은 구체적인 구현체를 직접 사용하여 데이터베이스 연결을 관리하는 방식을 채택하고 있었습니다. 이는 모든 객체가 자신의 동작과 관련된 클래스를 직접 결정하고, 객체의 생성과 생명주기를 직접 관리하는 자기주도적인 형태의 프로그래밍이었습니다.

그러나 제어의 역전 개념을 적용한 현재 상태에서는, 이러한 제어 흐름이 완전히 바뀌었습니다. DaoFactory와 같은 외부 컨트롤러가 UserDao에 사용될 ConnectionMaker의 구현체를 결정하고 제공합니다. 이 경우, UserDao는 더 이상 자신이 사용할 구체적인 ConnectionMaker 클래스를 직접 결정하지 않습니다. 대신, 이 결정은 외부에서 주입되며, UserDao는 주어진 구현체를 사용하기만 합니다. 이렇게 제어의 주도권이 사용하는 객체에서 객체를 생성하고 관리하는 외부 시스템 또는 프레임워크로 이동하는 것이 바로 제어의 역전입니다.

제어의 역전은 객체의 생성과 사용에 대한 관리를 프로그램의 다른 부분으로 이동시키므로, 코드의 유연성과 재사용성을 크게 향상시킵니다. 또한, 의존성 관리와 테스트 용이성 면에서도 큰 장점을 가지고 있습니다. IoC를 통해 객체는 더 이상 자신의 행동을 결정하는 데 필요한 의존성을 직접 관리하지 않으므로, 프로그램 전체의 결합도가 낮아지고, 각 컴포넌트의 응집도가 높아집니다.
 
제어의 역전의 특성
제어의 역전(Inversion of Control, IoC)은 객체가 스스로의 행동과 객체의 생성을 결정하지 않고, 이러한 책임을 외부에 위임하는 프로그래밍 패러다임을 말합니다. 이 패러다임에서 객체는 자신이 어떻게 생성되거나 사용되는지에 대해 알지 못하며, 모든 제어 권한은 객체가 아닌 다른 외부 요소에 의해 행사됩니다.

IoC에서 객체는 더 이상 자신을 사용할 다른 객체를 선택하거나 생성하지 않습니다. 대신, 객체의 생성과 관리는 외부에서 결정되고 실행됩니다. 예를 들어, 애플리케이션에서 DaoFactory와 같은 클래스는 이러한 제어의 역전 개념을 구현하는 데 중요한 역할을 합니다. DaoFactory는 UserDao와 같은 객체의 생성 및 해당 객체가 사용할 의존성을 결정하고 제공하는 책임을 가집니다.

이러한 접근 방식은 프로그램의 모듈성을 높이고, 코드의 재사용성과 테스트 용이성을 개선합니다. 또한, 애플리케이션의 구성 요소 간 결합도를 낮추고, 응집도를 높이는 데에도 기여합니다. IoC를 통해 객체들은 자신의 생명주기와 의존성에 대해 관심을 가질 필요가 없어지며, 이는 설계의 단순화와 유연한 시스템 구축을 가능하게 합니다.
 
 
템플릿 메소드 패턴의 제어의 역전
템플릿 메소드 패턴은 제어의 역전(Inversion of Control, IoC) 개념을 활용하는 일종의 디자인 패턴으로 해석될 수 있습니다. 이 패턴에서 제어의 역전은 상위 클래스에 있는 템플릿 메소드가 전체적인 처리 흐름을 제어하고, 하위 클래스에서 정의된 메소드들이 필요에 따라 템플릿 메소드에 의해 호출되는 구조에서 나타납니다. 즉, 프로그램의 실행 흐름은 상위 클래스에 정의된 템플릿 메소드에 의해 관리되며, 하위 클래스는 이러한 흐름 내에서 특정 단계에서만 개입하여 필요한 기능을 제공합니다.

이러한 구조는 제어의 역전의 핵심 원리를 반영합니다. 보통의 절차적 프로그래밍에서는 프로그램의 흐름이 명시적으로 프로그래머에 의해 제어되지만, 템플릿 메소드 패턴에서는 이 흐름이 '템플릿' 역할을 하는 상위 클래스에 의해 정의되고 제어됩니다. 하위 클래스는 전체적인 흐름에 개입하지 않고, 오직 상위 클래스가 정의한 특정 지점에서만 그들의 특정 로직을 실행합니다.

템플릿 메소드 패턴은 이러한 방식으로 프로그램의 전체적인 흐름을 간결하게 유지하면서도, 특정 부분의 구현을 하위 클래스에 위임하여 확장성과 유연성을 제공합니다. 이는 코드의 중복을 줄이고, 확장에 용이한 구조를 만들어 소프트웨어의 유지보수성을 개선하는 데 도움이 됩니다. 이처럼 템플릿 메소드 패턴은 제어의 역전 개념을 적절히 활용하여 복잡한 문제를 해결하는 데 효과적인 디자인 패턴입니다.
 
프레임워크의 제어의 역전
 
프레임워크와 라이브러리 간의 핵심적인 차이점 중 하나는 제어의 역전(Inversion of Control, IoC)의 존재 여부입니다. 라이브러리를 사용하는 애플리케이션 코드는 프로그램의 제어 흐름을 직접 관리합니다. 즉, 개발자가 작성한 애플리케이션 코드는 필요에 따라 라이브러리의 기능을 호출하며, 전체적인 프로그램의 흐름을 주도합니다. 이는 라이브러리가 단지 유용한 도구 모음이라는 것을 의미하며, 개발자는 이러한 도구를 자신의 코드 내에서 적절한 시점에 사용합니다.

반면, 프레임워크는 제어의 흐름이 역전된 구조를 가집니다. 여기서 애플리케이션 코드는 프레임워크에 의해 호출되고 사용됩니다. 개발자는 프레임워크가 정의한 규약에 따라 클래스나 메소드를 작성하고, 이들을 프레임워크에 등록합니다. 그 후, 프레임워크는 자체적인 로직에 따라 이러한 애플리케이션 코드를 적절한 시점에 호출하고 실행합니다. 이는 프레임워크가 애플리케이션의 실행 흐름을 관리하며, 개발자가 작성한 코드는 프레임워크에 의해 필요에 따라 사용되는 구조입니다.

이러한 제어의 역전은 프레임워크를 사용할 때의 핵심적인 이점 중 하나입니다. IoC를 통해 프레임워크는 표준화된 흐름을 제공하며, 개발자는 특정 규약에 따라 필요한 부분만을 구현함으로써, 보다 효율적이고 일관된 방식으로 애플리케이션을 개발할 수 있습니다. 또한, 이러한 접근 방식은 코드의 재사용성과 테스트 용이성을 향상시키며, 애플리케이션의 유지보수를 용이하게 합니다. 프레임워크에 의해 정의된 흐름과 규약을 따르는 것은, 복잡한 애플리케이션을 구축하는 과정을 단순화하고 표준화하는 데 크게 기여합니다.
 
UserDao, DaoFactory의 제어의 역전
 
UserDao는 이제 DaoFactory에 의해 정해진 구현체를 사용하여 특정 행위를 수행하는, 보다 수동적인 역할을 수행하게 되었습니다. 마찬가지로, UserDaoTest 역시 DaoFactory가 제공하는 DAO 인스턴스를 사용할 수밖에 없습니다. 이는 DAO와 ConnectionMaker의 구현체 생성 책임이 모두 DaoFactory에 집중되어 있음을 의미합니다. 이러한 상황은 제어의 역전(Inversion of Control, IoC)이 구현된 사례로 볼 수 있습니다.

DaoFactory의 도입은 단순히 객체 생성 메커니즘을 중앙화하는 것 이상의 의미를 가지고 있습니다. 이는 관심사의 분리와 책임의 명확한 할당을 통해 애플리케이션 구조를 더욱 유연하고 확장 가능하게 만들려는 목적에서 출발했습니다. DaoFactory를 통해, 객체 생성과 관련된 결정은 중앙 집중적으로 관리되고, 이는 애플리케이션 전반에 걸쳐 일관된 접근 방식을 제공합니다. 또한, 이 방식은 애플리케이션의 다른 부분들이 구체적인 구현 클래스에 대한 의존성을 최소화하게 하며, 결과적으로 코드의 재사용성과 테스트 용이성을 향상시킵니다.

결국, DaoFactory를 통한 이러한 접근 방식은 IoC의 핵심 원칙을 실현하는 과정이라고 할 수 있습니다. 제어의 역전을 통해, 애플리케이션의 각 컴포넌트는 각자의 책임에 더욱 집중할 수 있으며, 이는 전체 시스템의 유연성과 확장성을 증가시키는 동시에, 유지보수를 용이하게 만듭니다.
 
제어의 역전(IoC)과 스프링 프레임워크
 
제어의 역전(Inversion of Control, IoC)은 스프링 프레임워크에만 국한된 기술이 아니라, 널리 적용되는 프로그래밍 모델입니다. IoC는 프레임워크나 특정 기술에 제한되지 않으며, 다양한 디자인 패턴과 소프트웨어 아키텍처에서 찾아볼 수 있는 개념입니다. IoC를 적용함으로써 설계의 깔끔함, 유연성, 확장성이 개선될 수 있으므로, 이러한 스타일의 설계와 코드 작성이 바람직합니다.

제어의 역전에서 핵심적인 부분은 애플리케이션의 컴포넌트 생성, 관계 설정, 사용 및 생명주기 관리를 담당하는 외부 엔티티의 필요성입니다. 이 역할을 수행하는 구조는 IoC 컨테이너 또는 IoC 프레임워크로 불립니다. DaoFactory와 같은 단순한 구조는 오브젝트 수준에서 IoC의 개념을 구현하는 가장 기본적인 형태의 IoC 컨테이너로 볼 수 있습니다.

IoC를 보다 광범위하게 애플리케이션에 적용하기 위해서는 스프링과 같은 IoC 프레임워크의 사용이 매우 유리합니다. 스프링은 IoC를 핵심 기술로 채택하고 있으며, 이를 통해 애플리케이션의 다양한 측면에서 유연하고 확장 가능한 아키텍처를 제공합니다. 스프링 프레임워크는 IoC 원칙을 극대화하여 적용하고 있으며, 이를 통해 개발자는 객체 생성과 의존성 관리에서 발생할 수 있는 복잡성으로부터 자유로워지고, 보다 집중적으로 비즈니스 로직 개발에 몰두할 수 있습니다.