Spring

토비의 Spring_Chapter01_오브젝트와 의존관계

강용민 2022. 10. 11. 17:33

개요

스프링은 자바 기반의 프레임워크다. 그리고 자바의 가장 큰 특징이라면 객체지향 프로그래밍이 가능한 언어라는 것이다.

자바 엔터프라이즈 기술(Java EE)의 혼란속에서 잃어버렸던 객체지향 기술의 진정한 가치를 회복시키고, 그로부터 객체지향 프로그래밍이 제공하는 폭넓은 혜택을 누릴 수 있도록 기본으로 돌아가자는 것이 스프링의 핵심 철학이다.

 

javaEE에 대해 궁금할 수 있지만 이번 챕터는 오브젝트에 초점을 맞췄기에 간단히 알아보면 다음과 같다.

더보기

JAVA EE(Java Enterprise Edition)

 

Java EE는 자바 기술로 기업환경의 어플리케이션을 만드는데 필요한 스펙들을 모아둔 스펙 집합니다.

자바로 구현된 웹프로그래밍에서 가장 많이 사용되는 JSP, Servlet을 비롯하여 데이터베이스에 연동하는 JDBC, 그 외에도 EJB, JNDI 등의 많은 기술들이 포함되어 있다.

정말 넓고 유용하지만 오히려 너무 넓어 복잡성을 키웠다는 단점이 있다.

이 단점을 타파하기 위해 Spring은 Pojo라는 개념을 도입하였다.

그래서 스프링이 가장 관심을 많이 두는 대상은 오브젝트다. 오브젝트가 생명주기를 시작으로 오브젝트의 설계로 관심을 발전하게 된다

 

1.1 초난감 DAO

User.java

package springbook.user.domain;

public class User {
    String id;
    String name;
    String password;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

UserDao.java

package springbook.user.dao;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
import springbook.user.domain.User;
 
public class UserDao {
	public void add(User user) throws ClassNotFoundException, SQLException{
    		//DB연결을 위한 커넥션 가져오기
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook","spring","book");
		
        	//SQL을 담을 PreparedStatement 만들기
		PreparedStatement ps = c.prepareStatement("insert into users(id,name,password) value(?,?,?)");
		ps.setString(1, user.getId());
		ps.setString(2, user.getName());
		ps.setString(3, user.getPassword());
		
        	//Statement 실행
		ps.executeUpdate();
		
        	//리소스 닫아주기
		ps.close();
		c.close();
	}
	
	public User get(String id) throws ClassNotFoundException, SQLException{
    		//DB연결을 위한 커넥션 가져오기
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook","spring","book");
		
        	//SQL을 담을 PreparedStatement 만들기
		PreparedStatement ps = c.prepareStatement("select * from users where id = ?");
		ps.setString(1, id);
 
			//실행결과를 ResultSet에 담기
		ResultSet rs = ps.executeQuery();
		rs.next();
        
        	//정보를 User객체에 담기
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
       		//리소스 닫아주기
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

초난감 DAO의 내용은 위의 코드로 설명된다.

  • 각 메서드 내부에 핵심 로직이 아닌 부가 로직을 적용시켜 중복된 코드가 가득하다.
    • 연결될 DB의 주소 혹은 드라이버가 바뀌어야 한다면?
      • 모든 메서드를 찾아가며, DB 연결에 해당하는 모든 로직을 변경해야 한다.
  • main 메서드에서 테스트를 하고 있다.
    • 많은 테스트를 진행할 때는 어떻게 해야 할까?
      • 매번 썼다 지웠다 할 수는 없는 노릇이다.

1.2 DAO의 분리

관심사의 분리

객체지향 언어는 변화에 대응할 준비가 되어있어야 한다. 이를 위해 Java는 상속, 다형성 등의 그러한 특성을 가지고 있다.

문제는 어떠한 요청이 들어왔을때 그 변화에 어떻게 대비할 것인가가 중요한 과제이다. 가장 좋은 대책은 변화의 폭을 최소한으로 줄여주는 것이다.

그러면 어떻게 변경이 이어날 때 필요한 작업을 최소화하고, 그 변경이 다른 곳에 문제를 일으키지 않게 할 수 있었을까?

그것은 분리와 확장을 고려한 설계가 있었기 때문이다.

프로그래밍의 기초 개념 중에 관심사의 분리(Separation of Concerns)라는 게 있다. 이를 객체지향에 적용해보면, 관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것이다.

 

커넥션 만들기의 추출

그럼 분리 및 확장하는 방법은 무엇들이 있을까?

  • 중복 코드의 메소드 추출에 의한 분리
  • 상속을 통한 확장
    • 템플릿 메소드 패턴
    • 팩토리 메서드 패턴

 

중복 코드의 메소드 추출에 의한 분리

말그대로 중복된 코드를 분리하는 것이다. 다음 코드는 DAO에서 connection부분이 있다 가정해보자.

public void add(User user) throws ClassNotFoundException, SQLException{
    Class.forName("com.mysql.jdbc.Driver");
    Connection c = DriveManager.getConnection(
        "jdbc:mysql://localhost/springbook","spring","book");
    //테이블에 값 삽입
}

public void get(String id) throws ClassNotFoundException, SQLException{
    Class.forName("com.mysql.jdbc.Driver");
    Connection c = DriveManager.getConnection(
        "jdbc:mysql://localhost/springbook","spring","book");
	//테이블의 값 추출
}

만약 후에 mysql이 아닌 oracle로 바꾼다던가 jdbc대신 다른것을 사용한다고 한다면 connection을 포함하고 있는 모든 메서드를 수정해야 한다.

//2
public void add(User user) throws ClassNotFoundException, SQLException{
    connection c = getConnection();
    //테이블에 값 삽입
}

public void get(String id) throws ClassNotFoundException, SQLException{
	connectio c = getConnection();
    //테이블의 값 추출
}

private Connection getConnection() throws ClassNotFoundException, SQLException{
	Class.forName("com.mysql.jdbc.Driver");
    Connection c = DriveManager.getConnection(
        "jdbc:mysql://localhost/springbook","spring","book");
    return c;
}

이런일을 방지하기 위해 중복되는 코드는 따로 메서드로 만들어 해당 일만 담당하게 하여 나중에 수정할 때 해당 부분만 변경시킨다면 추가적인 일을 하지 않아도 된다.

 

DB 커넥션 만들기의 독립

상속을 통한 확장

만약 connection을 할 때 다른 커넥션이 필요하다면 어떨까? N사와 D사의 연결이 다르다면?

UserDao.java

public abstract class UserDao {
    public void add(User user) throws SQLException, ClassNotFoundException {
        Connection c = getConnection();
        //생략
    }

    public User get(String id) throws SQLException, ClassNotFoundException {
        Connection c = getConnection();
        //생략
    }

    public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
}

public class NDAO extends UserDao{
    public Connection getConnection() throws ClassNotFoundException, SQLException{
    	//N사와 연결
    }
}

public class DDAO extends UserDAO{
    public Connection getConnection() throws CLassNotFoundException, SQLException{
        //D사와 연결
    }
}

이때는 getConnection 부분을 추상 메소드로 만들어 각각에 맞는 커넥션에 맞게 확장시키면 getConnection 하나로 상황에 맞는 커넥션을 할 수 있다. 

 

템플릿 메소드 패턴(Template Method Pattern)

디자인 패턴의 한 종류로 위 코드같이 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하는 방법을 의미한다.

 

팩토리 메소드 패턴(Factory Method Pattern)

서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 방법을 의미한다.

위 코드에서는 UserDAO의 서브클래스의 getConnection() 메소드는 어떤 Connection 클래스의 오브젝트를 어떻 게 생성할 것인지 결정한다. 

 

상속의 한계

상속은 간단해 보이고 사용성도 좋지만 많은 한계가 있다. 

  • 자바는 다중 상속을 지원하지 않는다.
  • 상속을 통한 상하위 클래스의 관계는 밀접하다.
    • 슈퍼클래스 내부의 변경이 있을 때 모든 서브 클래스를 함께 수정해야 할 수 있다.

 

1.3 DAO의 확장

추상 클래스를 만들고 이를 상속한 서브클래스에서 변화가 필요한 부분을 바꿔서 쓸 수 있게 만든 이유는 변화의 성격이 다른 것을 분리해서, 서로 영향을 주지 않은 채로 각각 필요한 시점에 독립적으로 변경할 수 있게 하기 위해서다.

그러나 여러가지 단점이 많은, 상속이라는 방법을 사용했다는 사실이 불편하게 느껴진다.

 

클래스의 분리

두 개의 관심사를 본격적으로 독립시키면서 동시에 손쉽게 확장할 수 있는 방법을 알아보자.

이번에는 아예 상속관계도 아닌 DB커넥션 관심을 갖는 부분을 완전히 독립적인 클래스로 만들어 보자.

UserDao.java

public class UserDao{
	private SimpleConnectionMaker simpleConnectionMaker;
    
    public UserDao(){
        simpleConnectionMaker = new SimpleConnectionMaker();
    }
    
    public void add(User user) throws ClassNotFoundException, SQLException{
        Connection c = simpleConnectionMaker.makeNewConnection();
    }
    
    public User get(String id) throws ClassNotFoundException, SQLException{
        Connection c = simpleConnectionMaker.makeNewConnection();
    }
}

public class SimpleConnectionMaker {
    public Connection MakeConnection() throws ClassNotFoundException, SQLException{
        Class.forNma("com.mysql.jdbc.Driver");
        Connection c = DriverManager.getConnection(
        		"jdbc:mysql://localhost/springbook","spring","book");
        return c;
    }
}

하지만 이번에는 다른 문제가 생겼다.

UserDao가 SimpleConnectioinMaker라는 특정 클래스에 종속되어 있기에 상속을 사용했을 때 처럼 UserDao 코드의 수정 없이 DB 커넥션 생성 기능을 변경할 방법이 없다.

 

인터페이스의 도입

UserDao는 생성자에서 해당 인터페이스를 주입 받는 방식으로 변경해보자.

결국 Connection 오브젝트를 만들려면 구체적인 클래스 하나를 선택해야겠지만 인터페이스로 추상화해놓은 최소한의 통로를 통해 접근하는 쪽(UserDao)에서는 오브젝트를 만들 때 사용할 클래스가 무엇인지 몰라도 된다.

 

ConnectionMaker.java

package springbook.user.dao;
 
import java.sql.Connection;
import java.sql.SQLException;
 
public interface ConnectionMaker {
	public Connection makeNewConnection() throws ClassNotFoundException, SQLException;
}

DConnectionMaker.java

package springbook.user.dao;
 
import java.sql.Connection;
import java.sql.SQLException;
 
public class DConnectionMaker implements ConnectionMaker {
	@Override
	public Connection makeNewConnection() throws ClassNotFoundException, SQLException {
		//D사 전용 DB 커넥션 코드
		return null;
	}
}

UserDao.java

package springbook.user.dao;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
import springbook.user.domain.User;
 
public class UserDao {
    //DB 커넥션 관심은 인터페이스로 추상화하여 사용
    private ConnectionMaker connectionMaker;	
	
    public UserDao() {
        //DB 커넥션 관심을 인터페이스로 추상화했지만 초기화 때 클래스 종속 문제 발생
        connectionMaker = new NConnectionMaker();	
    }
	
    public void add(User user) throws ClassNotFoundException, SQLException{
        //DB 커넥션 관심은 인터페이스로 추상화하여 사용
         Connection c = connectionMaker.makeNewConnection();	
        //생략
     }
	
	public User get(String id) throws ClassNotFoundException, SQLException{
        //DB 커넥션 관심을 아예 클래스로 분리하여 사용
        Connection c = connectionMaker.makeNewConnection();	
        //생략
    }
}

UserDao 코드를 자세히 보면 DConnection 클래스의 생성자를 호출해서 오브젝트를 생성하는 코드가 남아있는 것을 알 수 있다.

UserDao의 다른 모든 곳에서는 인터페이스를 이용하게 만들어서 DB 커넷견을 제공하는 클래스에 대한 구체적인 정보는 모두 제거가 가능했지만, 초기에 한 번 어떤 클래스의 오브젝트를 사용할지를 결정하는 생성자의 코드는 제거되지 않고 남아 있다.

 

관계설정 책임의 분리

DConnectionMaker()라는 코드는 짧고 간단하지만 그 자체로 충분히 독립적인 관심사를 담고 있다.

바로 UserDao가 어떤 ConnectionMaker 구현 클래스의 오브젝트를 이용하게 할지를 결정하는 것이다.

간단히 말하자면 UserDao와 UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 관한 관심이다.

이 관심을 UserDao가 아닌 UserDao를 사용하는 클라이언트에게 넘기자.

클라이언트와 같은 제3의 오브젝트가 다형성을 활용하여 UserDao 오브젝트가 사용할 ConnectionMaker 오브젝트를 파라미터를 이용해 전달해주도록 만든다.

 

UserDao.java 일부

public UserDao(ConnectionMaker connectionMaker){
    this.connectionMaker = connectionMaker;
}

UserDaoTest.java

package springbook.user.dao;

public class UserDaoTest {
    public static void main(String[] args) {
        ConnectionMaker connectionMaker = new DConnectionMaker();
        UserDao user = new UserDao(connectionMaker);
    }
}

클라이언트인 UserDaoTest가 어느 ConnectionMaker를 사용할지에 대한 책임을 받은 덕분에 UserDao는 자신의 관심사이자 책임인 사용자 데이터 엑세스 작업을 위해 SQL을 생성하고, 이를 실행하는 데만 집중할 수 있게 됐다.

아래는 인터페이스를 사용한 방식으로 변경한 관계 및 구조이다.

앞에서 사용했던 상속을 통한 확장 방법보다 더 깔끔하고 유연한 방법으로 UserDao와 ConnectionMaker 클래스들을 분리하고, 서로 영향을 주지 않으면서도 필요에 따라 자유롭게 확장할 수 있는 구조가 됐다.

 

원칙과 패턴

앞서 설명한 구현은 사실 잘 알려진 원칙을 따른 것이다.

 

개방 폐쇄 원칙(Open-Close Principle)

이 원칙을 간단히 정의하자면 '클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀있어야 한다'라고 할 수 있다.

UserDao는 DB 연결 방법이라는 기능을 확장하는 데는 열려 있다.

동시에 UserDao 자신의 핵심 기능을 구현한 코드는 그런 변화에 영향을 받지 않고 유지할 수 있으므로 변경에는 닫혀 있다고 말할 수 있다.

 

높은 응집도와 낮은 결합도

응집도가 높다는 것은 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다는 것이다.

작업은 항상 전체적으로 일어나고 무엇을 변경할지 명확하며, 그것이 UserDao 등 다른 클래스의 수정을 요구하지 않을뿐더러, 그렇게 변경한 것이 혹시 DAO의 다른 기능에 영향을 줘서 오류를 발생시키지는 않는지 확인하지 않게 된다.

DB 연결 방식 테스트를 하려명 DB 커넥션 관심사가 응집되어 있는 DConnectionMaker를 직접 테스트해도 충분하다.

 

결합도란 '하나의 오브젝트가 변경이 일어날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도'라 한다.

낮은 결합도란 결국, 하나이 변경이 발생할 때 여타 모듈과 객체로 변경에 대한 요구가 전파되지 않는 상태를 말한다.

느슨한 연결은 관계를 유지하는데 꼭 필요한 최소한의 방법만 간접적인 형태로 제공하고, 나머지는 서로 독립적이고 알 필요도 없게 만들어주는 것이다.

결합도가 낮아지면 변화에 대응하는 속도가 높아지고, 구성이 깔끔해진다. 또한 확장하기에도 매우 편리하다.

 

UserDao는 그 자체로 자신의 책임에 대한 응집도가 높다. 사용자의 데이터를 처리하는 기능이 여기저기 흩어져 있지 ㅇ낳고 DAO 안에 모여있다. ConnectionMaker 또한 마찬가지다.

동시에 UserDao와 ConnectionMaker의 관계는 인터페이스를 통해 매우 느슨하게 연결되어 있다. 꼭 필요한 관계만 ConnectionMaker라는 인터페이스를 통해 낮은 결합도로 최소한으로 연결되어 있다.

 

전략 패턴

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

UserDao는 전략 패턴의 컨텍스트에 해당한다. 컨텍스트는 자신의 기능을 수행하는 데 필요한 기능 중에서 변경 가능한, DB 연결 방식이라는 알고리즘을 ConnectionMaker라는 인터페이스로 정의하고, 이를 구현한 클래스, 즉 전략을 바꿔가면서 사용할 수 있게 분리했다.

전략 패턴의 적용 방법을 보면 크라이언트의 역할이 잘 설명되어 있다.

컨텍스트(UserDao)를 사용하는 클라이언트(UserDaoTest)는 컨텍스트가 사용할 전략(ConnectionMaker를 구현한 클래스, DConnectionMaker)을 컨텍스트의 생성자 등을 통해 제공해줬다.

 

1.4 제어의 역전(Inversion of Control)

오브젝트 팩토리

원래 UserDaoTest는 UserDao의 기능이 잘 동작하는지를 테스트하려고 만든 것이다.

그런데 지금은 어떤 ConnectionMaker 구현 클래스를 사용할지까지 떠맡고 있으니 문제가 있다.

UserDao와 ConnectionMaker 구현 클래스의 오브젝트를 만드는 것과, 그렇게 만들어진 두 개의 오브젝트가 연결돼서 사용될 수 있도록 관계를 맺어주는 기능을 따로 분리하자.

 

팩토리

오브젝트를 생성하는 쪽과 생성된 오브젝트를 사용하는 쪽의 역할과 책임을 분리하려는 목적으로 만든 오브젝트다.

객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 역할을 한다.

DaoFactory클래스를 만들고, 그에 맞게 UserDaoTest 클래스를 수정하자.

 

DaoFactory.java

package springbook.user.dao;

public class DaoFactory {
    public UserDao userDao(){
        ConnectionMaker connectionMaker = new DConnectionMaker();
        UserDao userDao = new UserDao(connectionMaker);
        return userDao;
    }
}

UserDaoTest.java

package springbook.user.dao;

public class UserDaoTest {
    public static void main(String[] args) {
        UserDao dao = new DaoFactory().userDao();
    }
}

 

설계도로서의 팩토리

이렇게 분리된 오브젝트들의 역할과 관계를 분석해보자.

UserDao와 ConnectionMaker는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있음을 알 수 있다.

전자가 실질적인 로직을 담당하는 컴포넌트라면, 후자는 애플리케이션을 구성하는 컴포넌트 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있다.

DaoFactory를 분리했을 때 얻을 수 있는 장점은 매우 다양하다.

그중에서도 애플리케이션의 컴포넌트 역할을 하는 오브젝트와 애플리케이션의 구조를 결정하는 오브젝트를 분리했다는 데 가장 의미가 있다.

 

오브젝트 팩토리의 활용

Dfacotry를 좀 더 살펴보자. DaoFacotry에 UserDao가 아닌 다른 DAO의 생성 기능을 넣으면 어떻게 될까?

 

DaoFactory.java

package springbook.user.dao;

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

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

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

    public ConnectionMaker connectionMaker(){
        return new DConnectionMaker();
    }
}

 

 

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

일반적 프로그램 흐름은 사용하는 쪽에서 오브젝트를 생성, 호출하며 제어한다. main() 메소드 같이 프로그램 시작점에서 사용할 오브젝트를 생성하고, 그 오브젝트에서 사용할 오브젝트를 또 생성하여 사용하는 등 사용하는 쪽에서 제어한다.

제어의 역전은 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.

제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하지도 않는다.

또 자신도 어떻게 만들어지고 어디서 사용되는지를 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다.

 

이늘 서블릿에서도 탬플릿 메소드에서도 볼 수 있다.

서블릿을 개발해서 서버에 배포할 수는 있지만, 그 실행을 개발자가 직접 제어할 수 있는 방법은 없다.

대신 서블릿에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출한다.

또 탬플릿 메소드를 보면 제어권을 상위 템플릿 메소드에 넘기고 자신은 필요할 때 호출되어 사용되도록 한다는 개념을 볼 수 있다.

 

프레임워크 관점에서의 제어의 역전

프레임워크도 제어의 역전 개념이 적용된 대표적인 기술이다.

프레임워크가 어떤 것인지 이해하려면 라이브러리와 프레임워크가 어떻게 다른지 알아야 한다.

라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다. 단지 동작하는 중에 필요한 기능이 있을 때 능동적으로 라이브러리를 사용할 뿐이다.

반면에 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발한 클래스를 등록해두고,  프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다.

 

UserDao와 DaoFactory에서의 제어의 역전

원래 ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 제어권은 UserDao 에게 있었다. 그런데 지금은 DaoFactory에게 있다. 자신이 어떤 ConnectionMaker 구현 클래스를 만들고 사용할지를 결정할 권한을 DaoFactory에 넘겼으니 UserDao는 이제 능동적이 아니라 수동적인 존재가 됐다.

 

1.5 스프링의 IoC(Inversion of Control)

스프링은 애플리케이션 개발의 다양한 영역과 기술에 관여한다. 하지만 스프링의 핵심을 담당하는 건, 바로 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것이다.

 

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

애플리케이션 컨텍스트와 설정정보

스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(Bean)이라 한다.

빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리(Bean Factory)라 한다.

또 빈 팩토리에서 애플리케이션 전반에 걸쳐 모든 구성요소의 제어 작업을 담당하는 IoC엔진을 애플리케이션이라 한다.

 

DaoFactory를 사용하는 애플리케이션 컨텍스트

먼저 스프링 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스라고 인식할 수 있도록 @Configuration이라는 애노테이션을 추가한다.

그리고 오브젝트를 만들어주는 메소드에 @Bean이라는 애노테이션을 붙여준다.

 

DaoFactory.java

package springbook.user.dao;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DaoFactory {
    @Bean
    public UserDao userDao(){
        return new UserDao(connectionMaker());
    }

    @Bean
    public ConnectionMaker connectionMaker(){
        return new DConnectionMaker();
    }
}

애플리케이션 컨텍스트는 ApplicationContext 타입의 오브젝트다.

AnnotationConfigApplicationContext를 이용해 애플리케이션 컨텍스트를 만들고 생성자 파라미터로 DaoFactory 클래스를 넣어준다.

UserDaoTest.java

package springbook.user.dao;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.sql.SQLException;

public class UserDaoTest {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
        UserDao dao = context.getBean("userDao",UserDao.class);
    }
}

getBean() 메소드는 AppicationContext가 관리하는 오브젝트를 요청하는 메소드다. getBean()의 파라미터인 "userDao"는 applicationContext에 등록된 빈의 이름이다.

 

애플리케이션 컨텍스트의 동작방식

@Configuration이 붙은 DaoFacotyr는 이 애플리케이션 컴텍스트가 활용하는 IoC 설정정보다.

내부적으로는 애플리케이션 컨텍스트가 DaoFacotyr의 userDao() 메소드를 호출해서 오브젝트를 가져온 것을 클라이언트가 getBean()으로 요청할 때 전달해준다.

애플리케이션 컨텍스트는 DaoFacotyr 클래스를 설정정보로 등록해두고 @Bean이 붙은 메소드의 이름을 가져와 빈 목록을 만들어 준다.

클라이언트가 애플리케이션 컨텍스트의 getBean() 메소드를 호출하면 자신의 빈 목록에서 요청한 이름이 있는지 찾고, 있다면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려준다.

 

DaoFactory를 오브젝트 팩토리로 직접 사용했을 때와 비교해서 애플리케이션 컨텍스트를 사용했을 때 얻을 수 있는 장점은 다음과 같다.

  • 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
    • 일관된 방식으로 원하는 클래스를 가져올 수 있다.
    • 그저 상위 타입의 인터페이스를 가져온다고 명시만 하면 구현체에 상관없이 해당하는 타입을 가져올 것이다.
  • 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
    • 오브젝트가 만들어지는 방식, 시점과 전략을 다르게 가져가는 방법
    • 오브젝트 자동 생성
    • 오브젝트에 대한 후처리
    • 정보의 조합
    • 설정방식의 다변화
    • 인터셉팅
  • 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.
    • 빈의 이름으로 검색
    • 빈의 타입으로 검색
    • 특별한 애노테이션 설정이 되어있는 빈 검색

 

스프링 IoC의 용어 정리

  • 빈(Bean)
    • 스프링이 IoC 방식으로 관리하는 오브젝트
    • 스프링을 사용하는 애플리케이션에서 만들어지는 모든 오브젝트가 다 빈이 아닌 스프링이 직접 생성과 제어를 담당하는 오브젝트만 빈이라 한다.
  • 빈 팩토리(Bean Factory)
    • 스프링의 IoC를 담당하는 핵심 컨테이너로 빈을 등록하고, 생성하고, 조회하고 돌려주고, 그 외에 부가적인 빈을 관리하는 기능을 담당한다.
  • 애플리케이션 컨텍스트(Application Context)
    • 빈 팩토리를 확장한 IoC 컨테이너이다. 빈 팩토리기능 뿐 아니라 스프링이 제공하는 애플리케이션 지원 기능을 모두 포함해서 얘기한다.
  • 설정정보/설정 메타정보(Configuration metadata)
    • 애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보를 말한다.
    • 실제로 스프링의 설정저보는 컨테이너에 어떤 기능을 세팅하거나 조정하는 경우에도 사용하지만, 그보다는 IoC 컨테이너에 의해 관리되는 애플리케이션 오브젝트를 생성하고 구성할 때 사용된다.
  • 컨테이너(Container) 또는 IoC 컨테이너
    • IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고도 한다.
  • 스프링 프레임워크
    • IoC 컨테이너, 애플리케이션 컨텍스를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할 때 주로 사용한다.

 

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

DaoFactory를 직접 사용하는 것과 @Configuration 애노테이션을 추가해서 스프링의 애플리케이션 컨텍스트를 통해 사용하는 것은 무슨 차이가 있을까?

DaoFacotry의 userDao()를 여러 번 호출했을 때 new 연산자에 의해 새로운 오브젝트가 만들어진다.설령 값이 같더라도 말이다.

반면에 스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다.

 

싱글톤 레지스트리로서의 애플리케이션 컨텍스트

애플리케이션 컨텍스트는 IoC 컨테이너다. 그러면서 동시에 이 애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리(singleton registry)이기도 하다.

 

서버 애플리케이션과 싱글톤

스프링이 싱글톤으로 빈을 만드는 이유는 스프링이 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버환경이기 때문이다.

만약 매번 클라이언트서 요청이 올 때마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용한다고 생각해보자.

부하가 걸려 서버가 감당하기 힘들것이다.

그래서 엔터프라이즈 분야에서는 서비스 오브젝트라는 개념을 일찍부터 사용해왔다.

 

싱글톤 패턴의 한계

자바에서 싱글톤을 구현하는 방법은 보통 이렇다.

  • 클래스 밖에서는 오브젝트를 생성하지 못하도록 생성자를 private으로 만든다.
  • 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드를 정의한다.
  • 스태틱 팩토리 메서드인 getInstance()를 만들고 이 메소드가 최초로 호출되는 시점에서 한번만 오브젝트가 만들어지게 한다. 생성된 오브젝트는 스태틱 필드에 저장된다. 또는 스태틱 필드의 초기값으로 오브젝트를 미리 만들어둘 수도 있다.
  • 한번 오브젝트(싱글톤)가 만들어지고 난 후에는 getInstance() 메소드를 통해 이미 만들어져 스태틱 필드에 저장해둔 오브젝트를 넘겨준다.

하지만 일반적으로 싱글톤 패턴 구현 방식에는 다음과 같은 문제가 있다.

  • private 생성자를 갖고 있기 때문에 상속할 수 없다.
    • private 생성자를 가진 클래스는 다른 생성자가 없다면 상속이 불가능하다는 점이다. 객체지향의 장점인 상속과 이를 이용한 다형성을 적용할 수 없다.
  • 싱글톤은 테스트하기가 어렵다.
    • 싱글톤은 초기화 관정에서 생성자 등을 통해 사용할 오브젝트를 다이내믹하게 주입하기도 힘들기 때문에 필요한 오브젝트는 직접 오브젝트를 만들어 사용할 수밖에 없다.
  • 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
    • 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 클래스임에도 하나 이상의 오브젝트가 만들어질 수 있다.
  • 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
    • 아무 객체나 자유롭게 접근하고 수정하는 건 객체지향과 전혀 맞지 않는다.

 

싱글톤 레지스트리

자바의 기본적인 싱글톤 패턴의 구현 방식은 여러 가지 단점이 있기에 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다. 그것이 바로 싱글톤 레지스트리다.

스프링의 싱글톤 레지스트리의 장점은 다음과 같다.

  • 평범한 자바 클래스라도 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리되게 할 수 있다.
  • 테스트 환경에서 자유롭게 오브젝트를 만들 수 있고, 테스트를 위한 목(Mock) 오브젝트로 대체하는 것도 간단하다.
  • 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴 등을 적용하는 데 아무런 제약이 없다는 점이다.

 

싱글톤과 오브젝트의 상태

싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있다.

따라서 상태 관리에 주의를 기울여야 한다. 저장할 공간이 하나뿐이니 서로 값을 덮어쓰고 자신이 저장하지 않은 값을 읽어 올 수 있기 때문이다.

따라서 싱글톤은 기본적으로 인스턴스 필드의 값을 변경하고 유지하는 상태유지(stateful)방식으로 만들지 않는다.

대신 파라미터와 로컬 변수, 리턴 값 등을 이용해 매번 새로운 값을 저장할 독립적인 공간을 만들어지기 떄문에 싱글톤이라고 해도 여러 스레드가 변수의 값을 덮어쓸 일은 없다.

 

스프링 빈의 스코프

빈의 스코프(scope)는 스프링이 관리하는 오브젝트, 즉 빈이 생성되고, 존재하고, 적용되는 범위를 의미한다.

경우에 따라서는 싱글톤 외의 스코프를 가질 수 있다.

  • 프로토타입 스코프 : 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어준다.
  • 요청 스코프 : 웹을 통해 새로운 HTTP 요청이 생길떄마다 생성된다.
  • 세션 스코프 : 웹의 세션과 비슷한 역할을 한다.

 

1.7의존관계 주입(Dependency Injection)

IoC와 DI

IoC는 매우 폭 넓게 사용되던 용어라 스프링에 더욱 알맞은 용어가 새로 나왔고 그것이 DI다.

DI는 오브젝트 래퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.

 

런타임 의존관계 설정

의존관계

의존한다는 건 의존대상, B라는 클래스가 변하면 그것이 A라는 클래스에 영향을 미친다는 뜻이다.

대표적인 예는 A에서 B에 정의된 메소드를 호출해서 사용하는 경우다. B의 기능이 추가되거나 변경되거나, 형식이 바뀌거나 하면 그 영향이 A로 전달되는 것이다.

의존관계는 방향성이 있다. A가 B에 의존하고 있지만, 반대로 B는 A에 의존하지 않는다.

 

UserDao의 의존관계

UserDao가 ConnectionMaker에 의존하고 있는 형태다. 따라서 ConnectionMaker 인터페이스가 변한다면 그 영향을 UserDao가 직접적으로 받게 된다.

하지만 DConnectionMaker 등이 다른 것으로 바뀌거나 그 내부에서 사용하는 메소드에 변화가 생겨도 UserDao에 영향을 주지 않는다.

의존관계란 한쪽의 변화가 다른 쪽에 영향을 주는 것이라고 했으니, 인터페이스를 통해 의존관계를 제한해주면 그만큼 변경에서 자유로워진다.

 

프로그램이 시작되고 UserDao 오브젝트가 만들어지고 나서 런타임 시에 의존관계를 맺는 대상, 즉 실제 사용대상인 오브젝트를 의존 오브젝트(dependent object)라한다.

의존관계 주입은 이렇게 구체적인 의존 오브젝트와 그것을 사용할 주체, 보통 클라이언트라고 부르는 오브젝트를 런타임 시에 연결해주는 작업을 말한다.

  • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스에만 의존하고 있어야 한다.
  • 런타임 시점의 의존관계는 컨테이어난 팩토리 같은 제3의 존재가 결정한다.
  • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.

의존관계 주입의 핵심은 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제3의 존재가 있다는 것이다.

 

의존관계 검색과 주입

스프링이 제공하는 IoC방법에는 의존관계 검색도 있다.

의존관계 검색은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성 작업은 외부 컨테이너에게 IoC로 맡기지만, 이를 가져올 때는 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용한다.

 

UserDao.java 일부

public UserDao(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
    this.connectionMaker = context.getBean("connectionMaker",ConnectionMaker.class);
}

그럼 의존관계 검색 방식이 필요할 때는 언제일까?

의존관계 검색과 의존관계 주입을 적용할 때 발견할 수 있는 중요한 차이점이 하나 있다.

  • 의존관계 검색 방식 : 검색하는 오브젝트는 자신이 스프링의 빈일 필요가 없다. 그냥 어딘가에서 직접 new UserDao() 해서 만들어서 사용해도 된다. 이때는 ConnectionMaker만 스프링의 빈이기만 하면 된다.
  • 의존관계 주입 : UserDao와 ConnectionMaker 사이에 DI가 적용되려면 UserDao도 반드시 컨테이너가 만드는 빈 오브젝트여야 한다.

의존관계 주입의 응용

DI 기술은 코드에는 런타임 클래스에 대한 의존관계가 나타나지 않고, 인터페이스를 통해 결합도가 낮은 코드를 만듦으로, 다른 책임을 가진 사용 의존관계에 있는 대상이 바뀌거나 변경되더라도 자신은 영향을 받지 않으며, 변경을 통한 다양한 확장 방법에는 자유롭다는 게 지금까지 설명한 장점이였다.

 

기능 구현의 교환

로컬DB를 사용하다가 개발이 끝나고 운영DB로 DB 커넥션을 변경해야 하는 경우가 있을 수 있다. DI 적용 전인 경우, DAO가 100개면 100개의 LocalDBConnection() 코드를 ProdDBConnection() 코드로 바꿔야 한다. 유지보수의 문제가 발생한다. 반면 DI를 사용하면 아래와 같이 사용할 수 있다.

 

DaoFactory.java 일부

@Bean
public ConnectionMaker localConnectionMaker(){
	return new LocalDBConnectionMaker();	//로컬 사용시
    //return new ProductionDBConnectionMaker();	//운영 사용시(로컬을 주석처리 후 이 부분 주석 제거)
}

이를  서버에 배포할 때는 어떤 DAO 클래스와 코드도 수정할 필요가 없다.

 

부가기능 추가

이해못함.

 

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

의존관계 주입 시 반드시 생성자를 사용해야 하는 것은 아니다. 

생성자가 아닌 일반 메소드를 이용해 의존 오브젝트와의 관계를 주입해주는 데는 크게 두 가지 방법이 있다.

  • 수정자 메소드를 이용한 주입
    • 외부에서 오브젝트 내부의 애트리뷰트 값을 변경하려는 용도로 주로 사용된다.
    • 핵심기능은 파라미터로 전달된 값을 보통 내부의 인스턴스 변수에 저장하는 것이다. 부가적으로, 입력 값에 대한 검증이나 그 밖의 작업을 수행할 수 도 있다.
  • 일반 메소드를 이용한 주입
    • 생성자가 수정자 메소드보다 나은 점은 한 번에 여러 개의 파라미터를 받을 수 있다는 점이지만 실수하기 쉽다.
    • 임의의 초기화 메소드를 이용하는 DI는 적절한 파라미터를 가진 여러 개의 초기화 메소드를 만들 수도 있기 때문에 생성자보다 낫다.

스프링은 전통적으로 메소드를 이용한 DI 방법중 수정자 메소드를 가장 많이 사용해왔다.

 

UserDao.java 일부

public class UserDao {
    private ConnectionMaker connectionMaker;

    public UserDao(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
        this.connectionMaker = context.getBean("connectionMaker",ConnectionMaker.class);
    }

    public void setConnectionMaker(ConnectionMaker connectionMaker){
        this.connectionMaker = connectionMaker;
    }
}

DaoFactory.java

package springbook.user.dao;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DaoFactory {
    @Bean
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        userDao.setConnectionMaker(connectionMaker());
        return userDao;
    }

    @Bean
    public ConnectionMaker connectionMaker(){
        return new DConnectionMaker();
    }
}

 

1.8 XML을 이용한 설정

스프링은 자바 클래스를 이용ㅇ하는 것 외에도, 다양한 방법을 통해 DI 의존관계 설정정보를 만들 수 있다. 가장 대표적인 것이 XML이다.

자바 코드로 만든 설정정보와 그에 대응하는 XML의 설정정보다.

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

 

DaoFactory부분을 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-3.0.xsd">
	
	<bean id="connectionMaker" class="springbook.user.dao.DConnectionMaker" />
 
	<bean id="userDao" class="springbook.user.dao.UserDao">
		<property name="connectionMaker" ref="connectionMaker"/>
	</bean>
</beans>

<property>는 수정자 메소드에 매칭된다.

그리고 애플리케이션 컨텍스트도 GenericXmlApplicationContext로 변경해주어야 한다.

 

UserDaoTest.java

package springbook.user.dao;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import java.sql.SQLException;

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

 

DataSource 인터페이스로 변환

DataSource 인터페이스 적용

우리가 만들었던 ConnectionMaker처럼 자바에서는 DB 커넥션을 가져오는 오브젝트의 기능을 추상화해서 비슷한 용도로 사용할 수 있게 만들어진 DataSource라는 인터페이스가 이미 존재한다.

DAO에서는 DataSource의 getConnection() 메소드를 사용해 DB 커넥션을 가져오면 된다.

DI받을 클래스는 DataSource 구현 클래스인 SimpleDriverDataSource를 사용한다. 이를 위해 jdbc 라이브러리를 추가한다.

 

UserDao.java 일부

package springbook.user.dao;

public class UserDao {
    private DataSource dataSource;
    
    public void setDataSoure(DataSource dataSource){
        this.dataSource = dataSource;
    }

    //생략
}

 

자바 코드 설정 방식

먼저 DaoFactory 설정 방식을 이용해보자.

 

DaoFacotry.java

package springbook.user.dao;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import javax.sql.DataSource;

@Configuration
public class DaoFactory {
    @Bean
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        userDao.setDataSoure(dataSource());
        return userDao;
    }

    @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 설정 방식

이번에는 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-3.0.xsd">
	
	<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>
 
	<bean id="userDao" class="springbook.user.dao.UserDao">
		<property name="dataSource" ref="dataSource"/>
	</bean>
</beans>

 

문제.

개발자는 관심사를 따로 분리시킨다. 그 이유는 무엇인가?

다양한 요청에 의해 변경을 해야할 때 변화의 폭을 최소화하기 위해서다.

 

OCP의 정의는 무엇인가?

클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀있어야 한다.

 

높은 응집도와 낮은 결합도는 무엇을 의미하는가?

응집도가 높다는 것은 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다는 것이다.

낮은 결합도란 결국, 하나이 변경이 발생할 때 여타 모듈과 객체로 변경에 대한 요구가 전파되지 않는 상태를 말한다

 

전략 패턴은 무엇인가?

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

 

결합도를 낮추는 법은 어떤것들이 있는가?

 

 

DI란 무엇인가?

설계 시점과 코드에서는 느슨한 의존관계만 만들어놓고, 런타임시에 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만든다.

 

IoC란 무엇인가?

메소드나 객체의 호출작업을 코드로 직접 설정하는 것이 아닌, 즉 개발자에 의해서 제어되는 것이 아 외부에서 결정하는 것이다.

 

싱글톤 레지스트리란 무엇인가?

전통적인 싱글톤의 장점(서비스 오브젝트)을 살리고 단점을 극복할 수 있도록 설계된 컨테이너

 

 

[참고]

토비의 스프링

https://jake-seo-dev.tistory.com/18

https://roadofdevelopment.tistory.com/39

'Spring' 카테고리의 다른 글

토비의 Spring_Chapter03_템플릿  (0) 2022.10.19
토비의 Spring_Chapter02_테스트  (0) 2022.10.14
SpringBoot_Chapter01_개요  (0) 2022.10.05
Spring_Chapter08_Transaction  (0) 2022.05.12
Spring_Chapter07_A.O.P  (0) 2022.05.02