<TIL> 122. Study / DI, Spring MVC 예외처리

YUJIN LEE·2023년 5월 31일
0

개발log

목록 보기
112/149

백엔드(스프링)

DI(Dependency Injection)에 대한 설명과 해당 기술의 장점에 대해 설명

스프링에서 의존성 주입(DI)란,
객체간 의존성을 개발자가 객체 내부에서 직접 호출(new 연산자)하는 대신, 외부(스프링 컨테이너)에서 객체를 생성해서 넣어주는 방식.

외부에서 두 객체 간의 관계 설정을 해주는 디자인 패턴,
인터페이스를 사이에 두어 클래스 레벨에서는 의존관계가 고정되지 않도록 하고,
런타임 시 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게함

DI를 사용 시 장점?

  • 클래스들 간 의존 관계를 최소화할 수 있다.
  • 프로젝트 유지보수 용이
  • 기존에는 개발자가 직접 객체의 생성과 소멸을 제어했지만, DI로 인해 객체의 생성과 소멸 등 클래스간 의존 관계를 스프링 컨테이너가 제어해줌.

요약

  • DI는 클래스들 간 의존관계를 낮추기 위해 사용
  • 스프링에서는 의존관계를 스프링 자체에서 관리해주는데 이러한 특징을 IoC라고 부름.
  • 소스 코드 자체에 객체를 생성시켜주지 않고 Bean을 이용해 의존 객체를 넣어줌으로써 클래스들간 의존관계를 낮춤
public class Controller {
	private Service service;
    
   	service.test();
    }
}

다음과 같이 Controller 객체가 Service 객체를 사용하고 있는 경우, Controller는 Service에 의존성이 있다고 할 수있다.

의존한다는 것 -> 의존 대상, 여기서는 Service가 변하면 그것이 Controller에게로 전달된다는 뜻.

만약, Servcie의 test()메소드가 변경되면 Controller에서도 그에 따른 수정이 필요하게 되거나 형식은 그대로지만 로직이 변경되면 결과적으로 Controller의 기능에도 영향을 미친다.

이와같이 두 객체간의 관계를 맺어주는 것을 의존성 주입이라고 하며,
스프링에는 3가지 방법의 의존성 주입방법이 있다.

  1. 생성자 주입
@Controller
public class Controller{
	private Service service;
    
    @Autowired
    public Controller(Service service){
    	this.service = service;
    }
}

생성자에 @Autowired를 붙여 의존성을 주입받을 수 있다.
클래스 내 생성자가 하나이고, 그 생성자로 주입받을 객체가 빈으로 등록되어 있다면 @Autowired 생략 가능하다.

생성자 주입은 인스턴스 생성시 1회 호출되는 것이 보장되기 때문에, 주입받은 객체가 변하지 않거나, 반드시 객체주입이 필요한 경우 강제하기 위해 사용된다.

  1. 필드 주입
@Controller 
public class Controller{
	@Autowired
    private Service service;
}

코드가 간결하고 편하지만 의존 관계를 정확히 파악하기 힘들다.
필드 주입시 final 키워드를 선언할 수 없어 객체가 변할 수 있다.
주입이 동시에 일어나 겹치는 경우 순환참조 에러가 난다.

  1. 수정자(setter) 주입
@Controller 
public class Controller{
	private Service service;
    
    @Autowired
    public setService(Service service){
    	this.service = service;
    }
}

setter 혹은 사용자정의 메서드를 통해 의존관계 주입.
setter의 경우 객체가 변경될 필요성이 있을 때만 사용된다.(주입하는 객체를 변경하는 경우는 드물다.)

스프링에서는 생성자 주입을 권장
인텔리제이에서 필드주입 사용시 경고창 뜸.

생성자 주입을 권장하는 이유

  1. 객체의 불변성 확보
    객체 생성자는 객체 생성시 최초 1회만 호출.
    때문에 주입받은 객체가 불변 객체여야 하거나 반드시 해당 객체의 주입이 필요한 경우에 사용.

의존성 주입(Dependency Injection)의 개념과 필요성

의존성 주입(Dependency Injection)이란?

Spring 프레임워크는 3가지 핵심 프로그래밍 모델 지원.
그 중 하나 -> DI

DI?
외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴,
인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계까 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해줌.

의존성 주입이 필요한 이유?

public class Store {

    private Pencil pencil;

    public Store() {
        this.pencil = new Pencil();
    }

}

연필이라는 상품과 1개의 연필을 판매하는 Store 클래스가 있다고 치면,
위와 같은 예시 클래스는 다음과 같은 문제점을 가진다.

  1. 두 클래스가 강하게 결합되어 있다.
  • Store클래스에서 pencil 이 아닌 다른 상품을 판매하고자 한다면 Store 클래스의 생성자 변경이 필요하다. -> 유연성이 떨어짐.
    각각의 다른 상품들을 판매하기 위해 생성자만 다르고 나머지는 중복되는 Store클래스들이 파생되는 것은 좋지 못함. -> 이에대한 해결책으로 상속? -> 상속은 제약이 많고 확장성이 떨어져서 피해야함.
  1. 객체들 간의 관계가 아닌 클래스 간의 관계가 맺어짐
    Store과 Pencil는 객체들간의 관계까 아닌 클래스들 간의 관계가 맺어져 있다는 문제가 있다.
    올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 한다.
    객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스(Pencil인지 Food인지 등)을 전혀 알지 못하더라도, (해당 클래스가 인터페이스를 구현했다면) 인터페이스 타입(Product)으로 사용할 수 있다.

결국 위와 같은 문제점이 발생하는 근본적인 이유는 Store에서 불필요하게 어떤 제품을 판매할지에 대한 관심이 분리되지 않았기 때문.
Spring에서는 DI를 적용해 이러한 문제를 해결.

Spring MVC에서의 예외 처리 기법에 대해서 설명

스프링의 처리과정을 보면 예외가 발생하는 부분은 크게 두가지.

예외(Exception)처리는 어떻게?

  1. Controller 레벨에서의 처리
  2. Global 레벨에서 처리
  3. HandlerExceptionResolver를 이용한 처리

Dispatcher Servlet 내에서 발생하는 예외(Controller, Service, Repository 등)

DispatcherServlet 예외

스프링의 MVC 에러 중 99프로는 DispatcherServlet에서 발생.
DispatcherServlet 내에서 발생하는 예외를 내부에서 자체적으로 해결할 수 있음.
-> HandlerExceptionResolver를 사용한 예외 전략

HandlerExceptionResolver?

컨트롤러 작업 중 발생한 예외를 어떻게 결정할 지 결정하는 전략

Dispatcher Servlet 전의 서블릿(Filter)에서 발생하는 예외


Spring MVC에서의 예외 처리는 애플리케이션에서 발생하는 예외를 적절하게 처리하고, 사용자에게 적절한 응답을 제공하는 기법입니다. Spring MVC는 다양한 방법을 제공하여 예외 처리를 수행할 수 있습니다.

@ExceptionHandler 어노테이션

컨트롤러 클래스 내부에 @ExceptionHandler 어노테이션을 사용하여 특정 예외를 처리하는 메소드를 정의할 수 있습니다.
이 메소드는 예외가 발생했을 때 해당 예외를 처리하는 역할을 수행합니다.
예외 처리 메소드는 컨트롤러 내에서 예외가 발생하면 실행되며, 예외에 대한 적절한 응답을 반환할 수 있습니다.

@ControllerAdvice 어노테이션

@ControllerAdvice 어노테이션을 사용하여 전역 예외 처리 클래스를 정의할 수 있습니다.
이 클래스는 여러 컨트롤러에서 발생하는 예외를 처리하는 역할을 수행합니다.
@ExceptionHandler 어노테이션과 함께 사용하여 특정 예외에 대한 처리를 수행할 수 있습니다.
또한, @InitBinder 및 @ModelAttribute 어노테이션을 사용하여 컨트롤러에 대한 전역 설정을 수행할 수도 있습니다.

ResponseEntity를 활용한 예외 처리

ResponseEntity를 사용하여 예외 발생 시 HTTP 응답 상태 코드와 함께 사용자에게 적절한 응답을 제공할 수 있습니다.
예를 들어, ResponseEntity의 status 메소드를 사용하여 응답 상태 코드를 설정하고, body 메소드를 사용하여 응답 본문을 설정할 수 있습니다.

@ControllerAdvice와 함께 사용되는 @ExceptionHandler 메소드의 다양한 기능

@ExceptionHandler 메소드는 예외를 처리하는 기본적인 기능 외에도 다양한 기능을 수행할 수 있습니다.
예를 들어, 특정 예외에 대한 로깅, 예외 정보를 기반으로한 통계 수집, 예외 발생 시 이메일 알림 등의 작업을 수행할 수 있습니다.

profile
인정받는 개발자가 되고싶습니다.

0개의 댓글