Burninghering's Blog
article thumbnail

이 글은 스파르타 코딩클럽 웹개발 심화반 강의를 듣고 작성하였습니다. 

"강한 결합" 이해를 위한 예제

Contoller1 Service1 객체를 생성하여 사용

public class Controller1 {
	private final Service1 service1;

	public Controller1() {
		this.service1 = new Service1();
	}
}

 

Service1 Repostiroy1 객체를 생성하여 사용

public class Service1 {
	private final Repository1 repository1;

	public Service1() {
		this.repository1 = new Repository1();
	}
}

 

Repostiroy1 객체 선언

public class Repository1 { ... }

 

만약, 다음과 같이 변경된다면?

-> Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용, 생성자에 DB 접속 id, pw 를 추가

public class Repository1 {

	public Repository1(String id, String pw) {
    // DB 연결
    Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
  }
}

"강한 결합"의 문제점 

Controller 5 개가 각각 Service1 을 생성하여 사용 중

Repository1 생성자 변경에 의해, 모든 Contoller모든 Service 의 코드 변경이 필요


"강한 결합" 해결 방법?


1. 각 객체에 대한 객체 생성은 딱 1번만 생성하기
2. 생성된 객체를 모든 곳에서 재사용

예제

Repository1 클래스 선언 및 객체 생성repository1

public class Repository1 { ... }

// 객체 생성
Repository1 repository1 = new Repository1();

 

Service1 클래스 선언 및 객체 생성 (repostiroy1 사용)service1

Class Service1 {
	private final Repository1 repitory1;

	// repository1 객체 사용
	public Service1(Repository1 repository1) {
		this.repository1 = new Repository1();
		this.repository1 = repository1;
	}
}

// 객체 생성
Service1 service1 = new Service1(repository1);

 

Contoller1 선언 ( service1 사용)

Class Controller1 {
	private final Service1 service1;

	// service1 객체 사용
	public Controller1(Service1 service1) {
		this.service1 = new Service1();
		this.service1 = service1;
	}
}

 

만약, 다음과 같이 변경된다면 :

Repository1 객체 생성 시 DB 접속 id, pw 를 받아서 DB 접속 시 사용 -> 생성자에 DB 접속 id, pw 를 추가

public class Repository1 {

	public Repository1(String id, String pw) {
    // DB 연결
    Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
  }
}

// 객체 생성
String id = "sa";
String pw = "";
Repository1 repository1 = new Repository1(id, pw);

"강한 결합" 개선 결과 ⇒ "느슨한 결합"

Repository1 생성자 변경은 이제 누구에게도 피해를 주지 않음

Service1 생성자가 변경되면? 모든 Contoller → Controller 변경 필요 X

결론적으로, 강한 결합 ⇒ 느슨한 결합

 


DI (의존성 주입)의 이해

"제어의 역전 (IoC: Inversion of Control)"
프로그램의 제어 흐름이 뒤바뀜

일반적: 사용자가 자신이 필요한 객체를 생성해서 사용

IoC (제어의 역전)

  • 용도에 맞게 필요한 객체를 그냥 가져다 사용
  • "DI (Dependency Injection)" 혹은 한국말로 "의존성 주입"이라고 부릅니다.
  • 사용할 객체가 어떻게 만들어졌는지는 알 필요 없음

DI를 사용하기 위해 IoC 컨테이너 사용하기 

DI 를 사용하기 위해서는 객체 생성이 우선 되어야 하는데,
어디서 객체 생성을 해야 할까?
-> "스프링 프레임워크"가 필요한 객체를 생성하여 관리하는 역할을 대신해준다. 
  • 빈 (Bean): 스프링이 관리하는 객체
  • 스프링 IoC 컨테이너: "빈" 을 모아둔 통

스프링 "Bean" 등록 방법

@Component

클래스 선언 위에 설정

@Component
public class ProductService { ... }

 

스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장 -> @Component 클래스에 대해서 스프링이 해 주는 일

// 1. ProductService 객체 생성
ProductService productService = new ProductService();

// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너

 

'빈' 아이콘 확인 → 스프링 IoC 에서 관리할 '빈' 클래스라는 표시

 

@Component 적용 조건

@ComponentScan 에 설정해 준 packages 위치와 하위 packages 들

@Configuration
@ComponentScan(basePackages = "com.sparta.springcore")
class BeanConfig { ... }

@SpringBootApplication 에 의해 default 설정이 되어 있음


@Bean

직접 객체를 생성하여 빈으로 등록 요청

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

@Configuration
public class BeanConfiguration {

    @Bean
    public ProductRepository productRepository() {
        String dbUrl = "jdbc:h2:mem:springcoredb";
        String dbId = "sa";
        String dbPassword = "";

        return new ProductRepository(dbUrl, dbId, dbPassword);
    }
}

 

스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장

// 1. @Bean 설정된 함수 호출
ProductRepository productRepository = beanConfiguration.productRepository();

// 2. 스프링 IoC 컨테이너에 빈 (productRepository) 저장
// productRepository -> 스프링 IoC 컨테이너

스프링 "Bean" 사용 방법

@Autowired

멤버변수 선언 위에 @Autowired → 스프링에 의해 DI (의존성 주입) 됨

@Component
public class ProductService {
		
    @Autowired
    private ProductRepository productRepository;
		
		// ...
}

 

'빈' 을 사용할 함수 위에 @Autowired → 스프링에 의해 DI 됨

@Component
public class ProductService {

    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
		
		// ...
}

 

@Autowired 적용 조건

스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능

 

@Autowired 생략 조건

  • Spring 4.3 버젼 부터 @Autowired 생략가능
  • 생성자 선언이 1개 일 때만 생략 가능

 

파라미터가 다른 생성자들

public class A {
	@Autowired // 생략 불가
	public A(B b) { ... }

	@Autowired // 생략 불가
	public A(B b, C c) { ... }
}

 

Lombok 의 @RequiredArgsConstructor 를 사용하면 다음과 같이 코딩 가능

 

@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {

    private final ProductService productService;
    
    // 생략 가능
		// @Autowired
		// public ProductController(ProductService productService) {
		//     this.productService = productService;
		// }
}
profile

Burninghering's Blog

@개발자 김혜린

안녕하세요! 반갑습니다.