티스토리 뷰

728x90
반응형

 

1. Servlet, Controller

 

 

Servlet(서블릿)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말한다.

예전에는 서블릿으로 모두 자바코드를 작성하였지만 스프링이 나오게 되고 Client와 Server간에 통신에 필요한 JSON코드로 스프링이 알아서 변경을 해주게 되어서 서블릿 코드를 직접 작성할 필요가 없게 되었다.

 

위 사진 처럼 Request가 들어오면 Controller전에 Servlet이 먼저 받게되는데 스프링에서 알아서 Servlet으로 받고 Controller로 보내주기 때문에 따로 코드를 작성안해도 된다.

 

 

2. Controller vs RestController

 

Controller

@Controller
class ExamController {
	@GetMapping("/home")
	public String home() {
		return "/home.html";
	}
}

Controller는 리턴으로 View를 리턴하게 되기 때문에 데이터를 리턴하고 싶을 때는 @ResponseBody를 붙여야한다.

 

@Controller
class ExamController {
	@Autowird
	private ProductRepository productRepository

	@GetMapping("/product/list")
	public @ResponseBody List<Product> getProducts() {
		return productRepository.findAll();
	}
}

 

RestController

 

그래서 데이터를 리턴하고 싶을때는 @Controller와 @ResponseBody 두개다 필요 하기 때문에 둘을 합친것이 @RestController이다.

@RestController
class ExamController {
	@Autowird
	private ProductRepository productRepository

	@GetMapping("/product/list")
	public List<Product> getProducts() {
		return productRepository.findAll();
	}
}

 

 

3. 객체지향 프로그래밍

 

초기 프로그래밍 방식은 절차적 프로그래밍 이였다. 하지만 대부분의 사람들이 한 번에 여러가지 다른 생각을 하는데 취약하기 때문에 라나의 사물(객체)에 하나의 의미를 부여하는 것처럼 프로그래밍 하는것이 객체지향 프로그래밍 이다.

그래서 스프링에서도 역할별로 분리를 하게 되어서 Controller, Service, Repository로 분리하여 정리하게 되었다.

 

Controller

  • 클라이언트의 요청을 받음
  • 요청에 대한 처리는 서비스에게 전달
  • 클라이언트에게 응답

 

Service

  • 비즈니스 로직을 처리 (비즈니스 로직이란 서버에서 사용자의 요구사항을 처리하는 것을 말한다.)
  • DB정보가 필요할 때는 Repository에게 전달

 

Repository

  • DB관리 (연결, 해제, 자원관리)
  • DB CRUD 작업처리

 

전체 흐름

 

 

 

3. DI (Dependency Injection)

 

  • 객체를 필요할 때 마다 생성하는 방법
public class ProductService {

    public List<Product> getProducts() throws SQLException {
        ProductRepository productRepository = new ProductRepository();
				// ...
    }

    public Product createProduct(ProductRequestDto requestDto) throws SQLException {
        ProductRepository productRepository = new ProductRepository();
        // ...
    }

    public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) throws SQLException {
        ProductRepository productRepository = new ProductRepository();
				// ...
    }
}		

위에 Service에서 보시면 객체 생성하는 new ProductRepository() 코드가 중복되는 것을 볼 수 있다. 이것은 마치 가위가 필요할 때마다 가위를 만들어서 사용하는 것과 같으므로 딱 한번 생성하여 계속 사용하는 것이 효율적이라고 볼 수 있다.

 

  • 생성자를 통해 한번 생성하고 계속 사용하는 방법
public class ProductService {
    // 멤버 변수 선언
    private final ProductRepository productRepository;

    // 생성자: ProductService() 가 생성될 때 호출됨
    public ProductService() {
        // 멤버 변수 생성 (this 지시자 생략 가능!)
        this.productRepository = new ProductRepository();
    }

    public List<Product> getProducts() throws SQLException {
        // 멤버 변수 사용 (this 지시자 생략 가능!)
        return this.productRepository.getProducts();
    }
		// ...
}

그런데 이렇게 모든 Service 또는 Cotroller마다 계속 객체를 생성해서 사용하게 되면 모두 다른 객체의 service와 repository를 만들게 되고 생성할때 필요한 생성자에 파라미터까지 모두 알고 있어야 하기 때문에 생성자가 조금만 변경되어도 계속 바꿔줘야 하는 불편함이 존재한다.

 

 

 

  • 미리 생성되어 있는 ProductRepository 객체를 가져다 사용
public class ProductService {
    // 멤버 변수 선언
    private final ProductRepository productRepository;

    // 생성자: ProductService() 가 생성될 때 호출됨
    public ProductService(ProductRepository productRepository) {
        // 멤버 변수 생성
        this.productRepository = productRepository;
    }
}

ProductService productService = new ProductService(productRepository);

이렇게 하나의 객체에서 다른 객체가 필요할 때, 객체를 직접 생성하지 않고, 이미 생성되어 있는 객체를 가져오는 작업을 DI(Dependency Injection) 의존성 주입 이라고 한다.

 

또한, 프로그램의 제어 흐름이 뒤바뀐다고 해서 제어의 역전(IoC: Inversion of Control) 이라고 부른다.

사용자가 자신이 필요한 객체를 생성해서 사용하는 것이 일반적인데, 반대로 필요한 객체를 요청하면 어디서 어떻게 만들어 졌는지 알 필요 없이 사용 가능하기 때문이다. 

 

만들때 객체를 주입받아 사용하기 때문에 생성자에 파라미터가 달라져도 수정할 필요가 없고 어떤 파라미터가 필요한지도 몰라도 된다. 그리고 객체는 1개만 필요하니 객체 생성은 1번만 하게 된다.

 

 

  • @RequiredArgsConstructor을 활용한 방법
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@Service // 서비스임을 선언합니다.
public class ProductService {

    private final ProductRepository productRepository;

    @Transactional // 메소드 동작이 SQL 쿼리문임을 선언합니다.
    public Long update(Long id, ProductMypriceRequestDto requestDto) {
        Product product = productRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
        );
        product.update(requestDto);
        return id;
    }

    @Transactional
    public Long updateBySearch(Long id, ItemDto itemDto){
        Product product = productRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("아이디가 존재하지 않습니다.")
        );
        product.updateByItemDto(itemDto);
        return id;
    }
}

lombok 어노테이션 기능으로 @RequiredArgsConstructor를 사용하면 초기화 되지않은 final필드나, @NotNull이 붙은 필드에 생성자를 생성해 준다.

주로 의존성 주입(Denpendency Injection)편의성을 위해서 사용되곤 한다.

 

어떠한 빈(Bean)에 생성자가 오직 하나만 있고, 생성자의 파라미터 타입이 빈으로 등록 가능한 존재라면 이 빈은 @Autowired 어노테이션 없이도 의존성 주입이 가능하다.

 

 

@Autowired 추가설명

@Autowired란 각 상황의 맞는 IoC컨테이너 안에 존재하는 Bean을 자동으로 주입받게 해준다.

@Autowired autowired 
public TestDataRunner(NaverShopSearch naverShopSearch){
   this.naverShopSearch = naverShopSearch;
}

위 방식으로 주입 받는것이 스프링에서 권장하는 방법이다. 하지만 좀 더 편리하게 DI주입을 받을 수 있다.

 

@Autowired
NaverShopSearch naverShopSearch;

그냥 이런식으로 써줘도 DI 주입을 받을 수 있다.

 

 

4. IoC (Inversion of Control)

 

스프링에서 DI를 사용하기 위해서 필요한 객체를 생성하여 관리하는 역할을 해준다.

빈(Bean) : 스프링이 생성해주는 객체

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

 

package com.sparta.springcore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {
    @Bean
    public ProductRepository productRepository() {
        String dbId = "sa";
        String dbPassword = "";
        String dbUrl = "jdbc:h2:mem:springcoredb";
        return new ProductRepository(dbId, dbPassword, dbUrl);
    }

    @Bean
    @Autowired
    public ProductService productService(ProductRepository productRepository) {
        return new ProductService(productRepository);
    }
}

 

@Controller, @RestController, @Service, @Repositoy와 같이 앞에 @를 붙이는 것을 어노테이션 이라고 부르고, 모두 스프링의 빈으로 등록되지만, 좀 더 특별한 역할을 맡은 빈이라고 보면 된다.

@Controller, @RestController, @Service, @Repositoy와 같은 특별한 기능은 위 방식처럼 빈을 등록하지 않고 어노테이션만 추가한다면 쉽게 빈으로 등록이 되어 사용가능하다.

 

Repository같은 경우는 어노테이션을 안붙이는데 extends Jpa를 하면 저절로 빈으로 인식하기 때문에 안붙여도 된다.

 

 

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28