Spring 심화반 - 1주차 - 2

귀찮Lee·2022년 4월 12일
0
post-custom-banner

2022년 4월 12일(화)
[스파르타코딩클럽] Spring 심화반 - 1주차

◎ Controller 의 역할분리

  • Controller
    • 클라이언트의 요청을 받음
    • 요청에 대한 처리는 서비스에게 전담
    • 클라이언트에게 응답
  • Service
    • 사용자의 요구사항을 처리 ('비즈니스 로직') 하는 실세 중에 실세!!!
      • 현업에서는 서비스 코드가 계속 비대해짐
    • DB 정보가 필요할 때는 Repository 에게 요청
  • Repository
    • DB 관리 (연결, 해제, 자원 관리)
    • DB CRUD 작업 처리
  • 장점: 특정 수정할 곳을 찾을 때, 해당 부분을 쉽게 찾을 수 있다.

◎ 객체 중복 생성 문제

  • 예를 들어, Service를 사용할 때는 Service에 있는 대부분의 함수가 Repository를 생성해서 사용함
    -> 멤버 변수 선언하여 모두가 사용할 수 있게 하자.
public class ProductService {
    
    // 멤버 변수 선언
    private final ProductRepository productRepository;

    // 생성자: ProductService() 가 생성될 때 호출됨
    public ProductService() {
        // 멤버 변수 생성
        this.productRepository = new ProductRepository();
    }
    
    public Product createProduct(ProductRequestDto requestDto) throws SQLException {
        // 요청받은 DTO 로 DB에 저장할 객체 만들기
        Product product = new Product(requestDto);

        // 멤버 변수 사용
        this.productRepository.createProduct(product);

        return product;
    }

◎ DI (의존성 주입)

  • 강한 결합의 문제점

    • 만약, repository의 특정 Data를 변수로 받기로 했다면
      -> 이와 연결된 모든 Service는 특정 Data를 변수로 받기 위해 코드 변경
      -> 이와 연결된 모든 Controller는 특정 Data를 변수로 받기 위해 코드 변경
  • 해결 방법

    1. 각 객체에 대한 객체 생성은 딱 1번만
    2. 생성된 객체를 모든 곳에서 재사용
  • 해결 방법 구체화 (느슨한 결합)

    1. Repository1 클래스 선언 및 객체 생성
    2. Service1 클래스 선언 및 객체 생성 (repostiroy1 사용) → service1
    3. Contoller1 선언 (service1 사용)
public class Repository1 {

    private final String dbUrl;
    private final String dbId;
    private final String dbPassword;

    public Repository1(String dbUrl, String dbId, String dbPassword) {
        this.dbUrl = dbUrl;
        this.dbId = dbId;
        this.dbPassword = dbPassword;
    }
    ...
 }
Class Service1 {
	private final Repository1 repitory1;

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

// 객체 생성
Service1 service1 = new Service1(repository1);
Class Controller1 {
	private final Service1 service1;

	// service1 객체 사용
	public Controller1(Service1 service1) {
		// this.service1 = new Service1(); // 기존 코드
		this.service1 = service1;
	}
}
  • DI (의존성 주입)의 이해

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

    • 일반적: 사용자가 자신이 필요한 객체를 생성해서 사용
    • IoC (제어의 역전)
      • 용도에 맞게 필요한 객체를 그냥 가져다 사용
        -> "DI (Dependency Injection)" 혹은 한국말로 "의존성 주입"이라고 부른다.
      • 사용할 객체가 어떻게 만들어졌는지는 알 필요 없음

◎ 스프링 IoC 컨테이너 사용하기

  • 현 상황 문제점: repository 선언시, 변수가 지정이 안됨.
    -> DI 를 사용하기 위해서는 객체 생성이 우선
    -> 스프링 프레임워크가 필요한 객체를 생성하여 관리하는 역할을 대신해 준다.

  • 빈 (Bean): 스프링이 관리하는 객체

  • 스프링 IoC 컨테이너: '빈'을 모아둔 통

  • Spring "Bean" 등록방법

  1. @Component

    • 클래스 선언 위에 설정
      @Component
      public class ProductService { ... }
    • 스프링 서버가 뜰 때 스프링 IoC 에 '빈' 저장
      // 스프링 '빈' 이름: 클래스의 앞글자만 소문자로 변경
    • 빈' 아이콘 확인 → 스프링 IoC 에서 관리할 '빈' 클래스라는 표시
    • 적용 조건: @ComponentScan 에 설정해 준 packages 위치와 하위 packages 들 (@SpringBootApplication 에 의해 default 설정이 되어 있음)
  2. @Configuration, @Bean : 직접 객체를 생성하여 빈으로 등록 요청

      // BeanConfiguration.js
    
      @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);
        }
    }
    • 스프링 '빈' 이름: @Bean 이 설정된 함수명
    • '빈' 아이콘 확인 → 스프링 IoC 에 '빈' 에 등록될 것이라는 표시
  • 스프링 'Bean'에 등록한 객체 사용 방법
  1. @Autowired

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

      @Component
      public class ProductService {
      		
        @Autowired
        private ProductRepository productRepository;
      		
      		// ...
      }
    • @Autowired 적용 조건: 스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능

    • @Autowired 생략 조건

      • Spring 4.3 버젼 이후
      • 생성자 선언이 1개 일 때만
    • Lombok 의 @RequiredArgsConstructor 를 사용하면 생략가능

      @RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
      @RestController // JSON으로 데이터를 주고받음을 선언합니다.
      public class ProductController {
      
        private final ProductService productService;
        
        // 생략 가능
      		// @Autowired
      		// public ProductController(ProductService productService) {
      		//     this.productService = productService;
      		// }
      }
  2. ApplicationContext: 스프링 IoC 컨테이너에서 빈을 수동으로 가져오는 방법

    @Component
    public class ProductService {
        private final ProductRepository productRepository;
    
        @Autowired
        public ProductService(ApplicationContext context) {
            // 1.'빈' 이름으로 가져오기
            ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
            // 2.'빈' 클래스 형식으로 가져오기
            // ProductRepository productRepository = context.getBean(ProductRepository.class);
            this.productRepository = productRepository;
        }
    
            // ...		
    }

◎ 스프링 3계층 Annotation 적용하기

  • 스프링 3계층 Annotation 은 모두 @Component

    1. @Controller, @RestController
    2. @Service
    3. @Repository
  • Repository 간단 설명

    • JpaRepository<"@Entity 클래스", "@Id 의 데이터 타입">를 상속받는 interface 로 선언
    • 스프링 Data JPA 에 의해 자동으로 @Repository 가 추가됨
profile
배운 것은 기록하자! / 오류 지적은 언제나 환영!
post-custom-banner

0개의 댓글