의존성 주입과 스프링

강상은·2023년 12월 4일

의존성 주입하기

설정 파일 추가

Servlet 컨텍스트에 빈을 주입하기

@Log4j2
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")//스프링의 설정 정보 로딩
@ExtendWith(SpringExtension.class)//Junit 5버전에서 spring-test를 이용하기 위한 설정, jUnit4에서는 @Runwith
public class SampleTests {

    @Autowired
    private SampleService sampleService;

    @Test
    public void testService() {
        log.info(sampleService);
        Assertions.assertNotNull(sampleService);
    }

}
//@ContextConfiguration은 통합 테스트를 위해 ApplicationContext를 로드하고 구성하는 방법
//결정하는데 사용되는 클래스 수준 메타데이터를 정의합니다

Root-context.xml에 빈 주입

<bean class="org.zerock.springex.sample.SampleDAO"/>
    <bean class="org.zerock.springex.sample.SampleService"/>

위의 방식을 아래와 같이 바꿈

<context:component-scan base-package="org.zerock.springex.sample"></context:component-scan>

ApplicationContext에 빈이 주입된 것을 확인

빈(Bean)이라고 부르는 객체들을 관리하기 위해서 ApplicationContext라는 존재를 활용

ApplicationContext

빈(Bean)이라고 부르는 객체들을 관리하기 위해서 ApplicationContext라는 존재를 활용애플리케이션에 대한 구성을 제공하는 중앙 인터페이스. 이것은 애플리케이션이 실행되는 동안 읽기 전용이지만 구현이 이를 지원하는 경우 다시 로드될 수 있습니다.ApplicationContext는 다음을 제공합니다.애플리케이션 구성요소에 액세스하기 위한 Bean Factory 메소드. ListableBeanFactory에서 상속됨.일반적인 방식으로 파일 리소스를 로드하는 기능. ResourceLoader 인터페이스에서 상속됩니다.등록된 리스너에 이벤트를 게시하는 기능. ApplicationEventPublisher 인터페이스에서 상속됩니다.국제화를 지원하는 메시지를 해결하는 기능. MessageSource 인터페이스에서 상속됩니다.상위 컨텍스트에서 상속. 하위 컨텍스트의 정의가 항상 우선합니다. 이는 예를 들어 단일 상위 컨텍스트가 전체 웹 애플리케이션에서 사용될 수 있는 반면 각 서블릿은 다른 서블릿과 독립적인 자체 하위 컨텍스트를 가짐을 의미합니다.

필드 주입 방식

@ToString
public class SampleService {

    @Autowired
    private SampleDAO sampleDAO;

}

<context:component-scan> 태그는 스프링의 컴포넌트 스캔을 활용하여 빈을 자동으로 등록하는 설정입니다. 이 태그를 servlet.xml에 정의하는 이유는 주로 웹 애플리케이션에서 사용되는 컨트롤러(Controller)나 웹과 관련된 구성 요소들이 대부분 서블릿 컨텍스트에서 관리되기 때문입니다.

일반적으로 스프링에서는 여러 개의 컨텍스트를 가지며, 대표적으로는 Root 컨텍스트와 Servlet 컨텍스트가 있습니다.

root-context

: servlet-context.xml과는 반대로 view와 관련되지 않은 객체를 정의한다. 따라서 Service, Repository(DAO), DB등 비즈니스 로직과 관련된 설정을 해준다.백엔드 설정파일임

  1. Root 컨텍스트: 보통 비즈니스 로직과 데이터 액세스 로직과 같은 애플리케이션 전반에 걸친 빈들이 등록되는 곳입니다. 이 컨텍스트는 애플리케이션이 시작될 때 로드되고, 모든 서블릿이 공유하는 빈들이 여기에 등록됩니다.
  • 이곳에 등록되는 빈은 모든 컨텍스트에서 사용할 수 있다.
  • service나 dao를 포함한, 웹 환경에 독립적인 빈들을 담아둔다.
  • 서로 다른 servlet-context에서 공유해야하는 빈들을 등록해놓고 사용할 수 있다.
  • servlet-context 내의 빈들은 이용이 불가능하다

servlet-context

: 요청과 관련된 객체를 정의한다! url과 관련된 컨트롤러나 어노테이션, 뷰 리졸버 등의 설정을 해준다. 프론트엔드 설정파일

  • 이곳에 등록되는 빈은 해당 컨텍스트에서만 사용할 수 있다.
  • DispatcherServlet이 직접 사용하는 컨트롤러를 포함한 웹 관련 빈을 등록하는데 사용한다.
  • 독자적인 컨텍스트를 가지며, root-context내의 빈을 사용할수 있다.
  1. Servlet 컨텍스트: 각각의 서블릿이나 서블릿으로 구동되는 스프링 MVC의 DispatcherServlet이 사용하는 컨텍스트입니다. 이 컨텍스트는 보통 웹 계층과 관련된 빈들을 등록합니다. 따라서 컨트롤러나 뷰 리졸버와 같은 웹 계층의 빈들은 주로 Servlet 컨텍스트에서 관리됩니다.

만약 겹치는 빈이 생길 경우 servlet-context의 빈을 우선시 합니다.

<context:component-scan>servlet.xml에 정의하는 것은 주로 Servlet 컨텍스트에서 웹 계층과 관련된 빈들을 스캔하고 등록하기 위함입니다. 또한, servlet.xml은 해당 서블릿이 초기화될 때 로드되기 때문에 이 시점에 웹 계층과 관련된 빈들을 등록할 수 있습니다.

스프링 부트에서는 <context:component-scan> @ComponentScan 어노테이션으로 대체할 수 있는 속성이다

롬복(lombok)

@Builder(builderMethodName = "withAll") 해석은?

@Builder(builderMethodName = "withAll")은 Java 언어에서 Lombok 라이브러리를 사용할 때 사용되는 어노테이션입니다. Lombok은 자바 개발자들의 생산성을 향상시키기 위해 자주 사용되는 라이브러리 중 하나입니다.

@Builder 어노테이션은 불변(immutable) 객체를 생성하기 위해 빌더 패턴을 자동으로 생성해주는 기능을 제공합니다. 이를 통해 매우 간결하게 객체를 초기화하고, 가독성과 유지보수성을 향상시킬 수 있습니다.

해당 어노테이션을 사용하면, 클래스 내에 정적 이너 클래스 형태로 빌더 클래스가 생성되며, 이 빌더 클래스를 통해 객체의 인스턴스를 생성할 수 있습니다. 빌더 클래스 내에는 각 필드 값을 설정하는 메서드들이 자동으로 생성되며, 이러한 메서드의 이름은 주로 with{FieldName} 형태로 지정됩니다.

builderMethodName = "withAll"은 @Builder 어노테이션의 옵션 중 하나로, 빌더 클래스의 빌더 메서드 이름을 커스터마이징할 때 사용됩니다. 이 경우, withAll이라는 이름의 메서드가 빌더 클래스에 생성될 것입니다. 일반적으로 "with" 접두사를 사용하여 해당 필드의 값을 설정하는 메서드 이름을 지정합니다. 이렇게 하면 가독성이 높아지고, 객체를 초기화하는 코드가 깔끔해집니다.

@RequiredArgsConstructor

초기 스프링에서는 @Autowired를 멤버 변수에 할당하거나 ,Setter를 작성하는 방식을 많이 이용해 왔지만 스프링 3이후에는 생성자 주입방식을 더 많이 활용

규칙은 주입받아야 하는 객체의 변수는 final로 작성

생성자를 이용해서 해당 변수를 생성자의 매개변수(파라미터)로 지정, 생성자 주입 방식은 객체를 생성할 때 문제가 발생하는지를 미리 확인하므로 선호됨

롬복에서는 생성자 주입을 보다 간단히 작성할 수 있는데 @RequiredArgsConstructor를 이용해서 생성자 함수를 자동으로 작성하므로 builder() 메서드와 withAll() 메서드는 모두 빌더 패턴을 구현하는데 사용되는 메서드들이지만, 몇 가지 차이가 있습니다.

  1. 명명 규칙:
    • builder() 메서드는 주로 빌더 클래스에 기본적으로 제공되는 메서드로, 이 메서드를 호출하여 빌더 객체를 얻습니다.
    • withAll() 메서드는 빌더 클래스에서 정적으로 정의된, 명시적으로 모든 필수 필드를 초기화하기 위한 메서드입니다.
  2. 명확성:
    • withAll() 메서드는 이름 자체가 "모든 필드를 포함한"이라는 의미를 가지므로, 해당 메서드를 호출할 때 모든 필수 필드를 설정한다는 의도가 명확합니다.
    • builder() 메서드는 빌더 객체를 생성하기 위한 보편적인 명명 규칙일 뿐이므로, 이 메서드만 호출해도 빌더 객체를 얻을 수 있습니다. 그러나 어떤 필드가 필수이고 어떤 필드가 선택적인지 명시적으로 나타나지 않습니다.
  3. 사용법:
    • builder() 메서드는 주로 빌더 클래스에서 기본적으로 제공되며, 일반적으로 생성자 또는 정적 팩토리 메서드와 함께 사용됩니다.
    • withAll() 메서드는 명시적으로 필수 필드를 초기화하기 위해 호출되며, 해당 빌더 클래스에서 정적으로 정의된 메서드입니다.

둘 다 빌더 패턴을 사용하는 것이며, 선택은 주로 코드의 가독성과 개발자의 취향에 따라 달라집니다.

DTO 사용이유

DTO (Data Transfer Object)는 주로 데이터를 전송하거나 여러 레이어 간에 데이터를 전달하는 데 사용되는 객체입니다. 스프링 프로젝트에서 DTO를 사용할지 여부는 프로젝트의 구조, 규모, 유지 보수성 등 여러 요소에 의해 결정됩니다. 여기에는 몇 가지 고려해야 할 사항이 있습니다


데이터 전송 및 변환:

DTO는 주로 서비스 간에 데이터를 전송하거나 데이터베이스와 상호 작용할 때 사용됩니다. 만약 데이터베이스 엔터티가 도메인 모델과 다른 구조를 가지고 있거나, 클라이언트에게 전송하는 데이터가 도메인 모델과 다를 경우 DTO를 사용하는 것이 유용할 수 있습니다.


계층 간 결합도:

DTO를 사용하면 서비스나 레이어 간의 결합도를 줄일 수 있습니다. 예를 들어, 클라이언트에서 전송한 데이터가 도메인 모델과 일치하지 않는 경우에 DTO를 사용하여 변환하는 것이 도움이 될 수 있습니다.


클라이언트와의 통신:

웹 애플리케이션에서 클라이언트와의 통신이 필요한 경우 DTO를 사용하여 클라이언트에 전달할 데이터의 구조를 명확하게 정의할 수 있습니다.


도메인 모델의 순수성:

도메인 모델은 비즈니스 로직을 나타내는 데 중점을 둬야 합니다. 때로는 도메인 모델을 데이터 전송 목적으로 사용하기보다는 별도의 DTO를 정의하여 사용하는 것이 도메인 모델의 순수성을 보존하는 데 도움이 될 수 있습니다.


복잡성과 유지 보수성:

프로젝트의 규모와 복잡성이 증가할수록 DTO를 사용하는 것이 코드를 더 명확하게 만들 수 있습니다. 특히, 여러 레이어 간의 통신이나 다양한 데이터 전송 시나리오가 있는 경우 DTO를 사용하는 것이 좋습니다.


결론적으로, 스프링 프로젝트에서 DTO를 사용할지 여부는 프로젝트의 특성과 요구 사항에 따라 다를 수 있습니다. 프로젝트의 크기와 구조, 도메인 모델의 특성, 데이터 전송의 복잡성 등을 고려하여 적절하게 사용하면 됩니다.

https://velog.io/@house1021/DAOVODTO 참조

web.xml 설정

설정을 위한 설정파일이다. 즉 최초로 WAS가 구동될때 각종 설정을 정의해준다. 여러 XML파일을 인식하도록 각 파일을 가리킨다.

  • servlet-context.xml과 root-context.xml을 어디서 가져올건지 인식해준다.
  1. <context-param>을 이용하여 root-context를 설정해준다.
  2. <listner>태그의 ContextLoaderListener클래스를 이용하여 contextConfigLocation에 있는 root-context들을 불러온다.
  3. 클라이언트의 요청을 받으면 <servlet>태그 안에있는 설정들이 작동하면서 servlet-context(servlet-context.xml)을 불러옴과 동시에 root-context와 같이 불러온다. 이때 DispatcherServlet 클래스를 실행시킨다.

흐름을 보면 web.xml에서 ContextLoaderListener를 이용해 root-context를,
DispacherServlet을 이용하여 servlet-context를 생성한다

0개의 댓글