본문 바로가기

개발관련/코드로 배우는 스프링 웹 프로젝트(개정판)

Part1 - 스프링 개발 환경 구축 Chapter02

코드로 배우는 스프링 웹 프로잭트 Part1

Chapter02 스프링의 특징과 의존성 주입

1장에서 예제를 위한 이클립스와 스프링의 기본 설정을 완료 하였습니다.

2장에서는 스프링 프레임워크에 대한 이론적인 부분을 살펴보겠습니다.

  • 스프링 프렝미워크를 이용해서 의존성 주입에 대한 이해와 테스트
  • 스프링에서 XML을 이용하는 객체 관리 방법
  • 스프링의 테스트 환경 구축

 

2.1 스프링 프레임워크의 간략한 역사

프레임워크 : 뼈대나 근간을 이루는 코드들의 묶음이다.

프레임워크를 이용한다는 의미는 프로그램의 기본 흐름이나 구조를 정하고 모든 팀원이 이 구조에 자신의 코드를 추가하는 방식으로 개발하게 됩니다.

최대 장점은 개발에 필요한 구조를 이미 코드로 만들어 놓았기 떄문에 실력이 부족한 개발자라 하더라도 반쯤 완성한 상태에서 필요 부분을 조립하는 형태로 개발이 가능합니다.

 

회사 입장에서는 프레임 워크를 사용하면 일정한 품질이 보장되는 결과물을 얻을 수 있고,

개발자 입장에서는 완성된 구조에 자신이 맡은 코드를 개발해서 넣어 주는 형태이므로 개발 시간을 단축할 수 있습니다.

 

2000년대 초반부터 시작된 엔터프라이즈급의 개발은 안정된 품질의 개발이 절실했고, 그 결과 많은 프레임워크의 전성시대라고 할 수 있었습니다.

스프링은 비교적 그 시작이 조금 늦은 프로잭트였지만, 가장 성공한 경랑(light-weight) 프레임워크 입니다.

 

  • 복잡함에 반기를 들어서 만든 프레임워크
  • 프로젝트의 전체 구조를 설계할 때 유용한 프레임워크
  • 다른 프레임워크들의 포용
  • 개발 생산성과 개발도구의 지원

 

2.1.1 스프링의 주요 특징

  • POJO 기반의 구성
  • 의존성 주입(DI)을 통한 객체간의 관계 구성
  • AOP(Aspect-Oriented-Programming)지원
  • 편리한 MVC 구조
  • WAS의 종속적이지 않은 개발 환경

 

POJO 기반의 구성

스프링의 성격 자체가 가벼운 프레임워크지만, 그 내부에는 개겣 간의 관계를 구성할 수 있는 특징을 가지고 있습니다.

스프링은 다른 프레임워크들과 달리 이 관계를 구성할 때 별도의 API 등을 사용하지 않는 POJO(Plain Old Java Object)의 구성만으로 가능하도록 제작되어 있습니다.

 

쉽게 말해서 일반적인 JAVA 코드를 이용해서 객체를 구성하는 것이 가능하다는 이야기 입니다.

 

이것이 중요한 이유는 코드를 개발 할 때 개발자가 특정 라이브러리나, 컨테이너의 기술에 종속적이지 않다는 것을 의미하기 때문입니다.

개발자는 가장 일반적인 형태로 코드를 작성하고 실행할 수 있기 때문에 생산성에서도 유리하고, 코드에 대한 테스트 작업 역시 좀 더 유연하게 할 수 있다는 장점이 있습니다.

 

의존성 주입(DI)과 스프링

의존성(Dependency)이라는 것은 하나의 객체가 다른 객체 없이 제대로 된 역할을 할 수 없다는 것을 의미합니다.

 

예를 들어 음식점이라면 서빙을 담당하는 직원이 나오지 못하는 상황에서도 장사를 할 수 있지만 주방장이 나오지 않으면 장사를 못 하는 상황이 발생하게 됩니다.

 

흔히 A 객체가 B 객체 없이 동작이 불가능한 상황을 A가 B에 의존적이다 라고 표현 합니다.

 

주입(Injection)은 말 그대로 외부에서 밀어 넣는 것을 의미합니다.

 

어떤 음식점같은 경우에는 매일 가게를 열기 전 직접 식재료를 구하기 위해 시장에 가지만,

프랜차이즈 식당들은 본사가 트럭 등을 이용해 식재료를 공급 합니다.

이 두가지 방식의 차이는 필요한 객체를 얻기 위해 주체가 능동적인지 수동적인지에 대한 문제입니다.

 

의존성과 주입을 결합해서 생각해보면 "어떤 객체가 필요한 객체를 외부에서 밀어넣는다" 는 의미가 됩니다.

 

그렇다면 왜 외부에서 객체를 주입하는 방식을 사용하는지 에 대한 문제를 알아 볼 필요가 있습니다.

 

음식점의 예에서 직접 식재료를 사지 않고, 대행업체에서 배송을 해주는 것을 사용하는 경우에 얻는 장점이 무엇인가를 생각해보면 역시 "편리하다", "장사에만 집중할 수 있다"와 같은 장점들이 있습니다.

 

이를 코드이 대입해 살펴보면 "주입을 받는 입장에서는 어떤 객체인지 신경 쓸 필요가 없다", "어떤 객체에 의존하든 자신의 역할은 변하지 않는다" 와 같은 의미로 볼 수 있습니다.

 

"의존성 주입"방식을 사용하려면 오른쪽 그림의 바깥쪽 도형처럼 추가적인 하나의 존재가 필요하게 됩니다.

이 존재는 의존성이 필요한 객체에 필요한 객체를 찾아서 "주입"하는 역할을 하게 됩니다.

 

스프링은 이러한 구조를 만드는데 적합한 구조로 설계 되어 있습니다.

스프링에서는 ApplicationContext라는 존재가 필요한 객체들을 생성하고, 필요한 객체들을 주입하는 역할을 해 주는 구조입니다.

 

따라서 스프링을 이용하면 개발자들은 기존의 프로그래밍과 달리 객체를 분리해서 생성하고, 이러한 객체들을 엮는 작업을 하는 형태의 개발을 하게 됩니다.

 

스프링에서는 ApplicationConext가 관리하는 객체들을 빈(Bean)이라는 용어로 부르고, 빈과 빈 사이의 의존 관계를 처리하는 방식으로 XML 설정, 어노테이션 설정, JAVA 설정 방식을 이용할 수 있습니다.

 

 

AOP의 지원(Aspect Oriented Programming)

좋은 개발환경의 중요 원칙은 개발자가 비즈니스 로직에만 집중할 수 있게 한다 입니다.

이 목표를 이루기 위해서는 반복적인 코드 제거 가 되어야 합니다.

 

대부분의 시스템이 공통으로 가지고 있는 보안이나 로그,트랜잭션과 같은 비즈니스 로직은 아니지만, 반드시 처리가 필요한 부분을 스프링에서는 횡단 관심사(cross-concern)라고 합니다. 

스프링은 이러한 횡단 관심사를 분리해서 제작하는 것이 가능합니다.

AOP(Aspect Oriented Programming)는 이러한 횡단 관심사를 모듈로 분리하는 프로그래밍의 패러다임 입니다.

 

스프링은 AOP를 AspectJ의 문법을 통해 작성할 수 있고, 이를 통해 개발자는 아래 3가지를 수행 할 수 있습니다.

  1. 핵심 비즈니스 로직에만 집중할 수 있다.
  2. 각 프로젝트마다 다른 관심사를 적용할 때 코드의 수정을 최소화 시킬 수 있다.
  3. 원하는 관심사의 유지보수가 수월한 코드를 구성할 수 있다.

트랜잭션의 지원

데이터베이스를 이용할 때 반드시 신경써야 하는 부분은 하나의 업무가 여러 작업으로 이뤄지는 경우의 트랜잭션 입니다.

이 트랜잭션 처리는 상황에 따라서 복잡하게 구성될 수 있고, 아닐수도 있는데 그때마다 코드를 이용해서 처리하는 작업은 개발자에게 상당히 피곤한 일입니다.

 

스프링은 이런 트랜잭션의 관리를 어노테이션이나 XML로 설정할 수 있기 때문에 개발자가 매번 상황에 맞는 코드를 작성할 필요가 없도록 설계되어 있습니다.

 

 

2.2 의존성 주입 테스트

예제로 구성할 내용은 레스토랑(Restaurant) 객체를 만들고 레스토랑에서 일하는 셰프(Chef) 객체를 주입하는 예제를 작성하려고 합니다.

 

스프링에서는 생성자를 통한 주입과 setter 메서드를 이용한 주입으로 의존성 주입을 구현합니다.

설정 방식은 주로 XML이나 어노테이션을 이용해서 처리합니다.

 

예제는 Lombok을 이용해서 setter 메서드를 자동으로 구현되도록 할 것 이고, 스프링의 동작을 테스트 할 것이므로 pom.xml에서 Lombok 라이브러리를 추가하고, spring-test 라이브러리를 사용 합니다.

예제를 작성하기 위해 pom.xml에 추가하거나 혹은 변경할 라이브러리들이 존재합니다.

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>${org.springframework-version}</version>
</dependency>  
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
    <scope>provided</scope>
</dependency>

POM.XML에 위 코드들을 추가해줍니다.

 

생성된 프로잭트의 log4j라이브러리는 1.2.15로 설정 되어 있으므로 위와 같이 log4j 1.2.17 버전으로 수정합니다.

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
        <exclusions>

 

마지막으로 junit을 아래와 같이 버전을 수정해줍니다.

<!-- Test -->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.12</version>
	<scope>test</scope>
</dependency>

 

2.2.1 예제 클래스 생성

ex00 프로잭트에 org.zerock.sample 패키지를 생성하고, Resturant 클래스와 Chef 클래스를 생성합니다.

패키지 생성

 

클래스 생성
생성 결과

 

 

일반적으로 스프링에서 의존성 주입은 Chef 클래스가 아닌 인터페이스로 설계하는것이 좋치만, 지금은 최소한의 코드만을 이용해서 의존성 주입을 테스트 해보기 위한 것이므로 클래스로 설계 합니다.

 

Chef 클래스를 다음과 같이 작성합니다.

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@Data
public class Chef {

}

 

Restaurant 클래스는 Chef를 주입받도록 설계합니다.

이때 Lombok의 setter를 생성하는 기능과 생성자, toString() 등을 자동으로 생성하도록 @Data 어노테이션을 이용합니다.

package org.zerock.sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.Data;
import lombok.Setter;

@Component
@Data


public class Restaurant {
	@Setter(onMethod_ =@Autowired)
	private Chef chef;
}

작성된 코드가 의미하는 것은 Restaurant 객체는Chef 타입의 객체를 필요로 한다는 것입니다.

 

  • @Component : 스프링에게 해당 클래스가 스프링에서 관리해야 하는 대상임을 표시
  • @Setter : 자동으로 setChef()를 컴파일시 생성
  • onMethod : 생성되는 setChef()에 @Autowired 어노테이션을 추가

Lombok으로 작성된 클래스에 대한 정보는 이클립스를 토해 확인 할 수 있습니다.

 

2.2.2 XML을 이용하는 의존성 주입 설정

스프링은 클래스에서 객체를 생성하고 객체들의 의존성에 대한 처리 작업까지 내부에서 모든것이 처리 됩니다.

스프링에서 관리되는 객체를 흔히(Bean)이라고 하고, 이에 대한 설정은 XML과 JAVA를 이용해 할 수 있습니다.

 

기존의 시스템은 XML을 선호하지만 최근은 JAVA 를 이용한 설정이 많이 사용되고 있습니다.

 

Spring Legacy Project 는 기본적으로 XML을 이용해서 스프링에서 관리해야 하는 객체들을 처리합니다.

 

프로잭트의 src 폴더 내의 root-context.xml은 스프링 프레임워크에서 관리하는 객체(Bean)를 설정하는 파일입니다.

root-context.xml

 

root-context.xml을 클릭하면 아래쪽에 NameSpace라는 탭이 보이는데 이때 context라는 항목을 체크 합니다.

Source 탭을 선택해서 아래 코드를 추가 합니다.

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

 

2.3 스프링이 동작하면서 생기는 일

작성한 2개의 클래스와 root-context.xml이 어떻게 동작하는지 이해하기 위해서는 스프링과 함께 시간의 순서대로 고민을 해봐야 합니다.

 

스프링이 동작한다는 가정하에 스프링 안에서 어떤 일들이 벌어지는지 다음 그림으로 표현할 수 있습니다.

  • 스프링 프레임워크가 시작되면 먼저 스프링이 사용하는 메모리 영역을 만들게 되는데 이를 컨텍스트(Context)라고 합니다. 스프링에서는 ApplicationContext라는 이름의 객체가 만들어집니다.
  • 스프링은 객체를 생성하고 관리해야 하는 객체들에 대한 설정이 필요합니다. root-context.xml 파일 입니다.
  • root-context.xml에 설정되어 있는 <context:component-scan> 태그의 내용을 통해서 "org.zerock.sample"패키지를 스캔(scan)하기 시작 합니다.
  • 해당 패키지에 있는 클래스들 중에서 스프링이 사용한느 @Component라는 어노테이션이 존재하는 클래스의 인스턴스를 생성합니다.
  • Restaurant 객체는 Chef 객체가 필요하다는 어노테이션(@Autowired) 설정이 있으므로, 스프링은 Chef 객체의 래퍼런스를 Restaurant 객체에 주입합니다.

2.3.1 테스트 코드를 통한 확인

프로젝트 내 src/test/java 폴더 내에 org.zerock.sample.SampleTests 클래스를 추가 합니다.

 

SampleTests 클래스는 spring-test 모듈을 이용해서 간단하게 스프링을 가동시키고 위에서 설명 된 동작들을 일어나게 합니다.

이때 Junit은 반드시 4.10 이상의 버전을 사용해야 합니다.

package org.zerock.sample;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class SampleTests {
  
  @Setter(onMethod_ = { @Autowired })
  private Restaurant restaurant;  

  @Test
  public void testExist() {
    
    assertNotNull(restaurant);
    
    log.info(restaurant);
    log.info("----------------------------------");
    log.info(restaurant.getChef());
    
  }
  
  
}

위 테스트 코드는 우선 현재 테스트 코드가 스프링을 실행하는 역할을 할 것이라는 것을 @Runwith 어노테이션으로 표시합니다.

 

다음으로 가장 중요한 설정은 @ContextConfiguration 어노테이션과 속성값인 문자열 설정입니다.

@ContextConfiguration은 지정된 클래스나 문자열을 이용해서 필요한 객체들을 스프링 내에 객체로 등록하게 됩니다.

(흔히 스프링 빈으로 등록된다고 표현 합니다.)

 

@ContextConfiguration에 사용하는 문자열은 "classpath:" 나 "file.:" 을 이용할 수 있으므로, 이클립스에서 자동으로 생성된 root-context.xml의 경로를 지정 할 수 있습니다.

 

@Log4j는 Lombok을 이용해서 로그를 기록하는 Logger를 변수로 생성합니다.

별도의 Logger 객체의 선언이 없이도 Log4j 라이브러리와 설정이 존재한다면 바로 사용 할 수 있습니다.

 

Spring Legacy Project로 생성한느 경우 기본으로 Log4j와 해당 설정이 완료되는 상태이기 때문에 별도의 처리 없이 사용이 가능 합니다.

 

로그에 대한 설정은 src/main/resources 와 src/test/resources에 별도로 존재 합니다.

 

 

@Autowired는 해당 인스턴스 변수가 스프링으로 부터 자동으로 주입해 달라는 표시이고, 스프링은 정상적으로 주입이 가능 하다면 obj변수에 Restaurant 타입의 객체를 주입하게 됩니다.

testExist()에 선언되어 있는 @Test는 JUnit에서 테스트 대상을 표시하는 어노테이션 입니다.

 

해당 메서드를 선택하고 JUnit Test 기능을 실행합니다.

assertNotNull()은 restaurant변수가 Null이 아니어야만 테스트가 성공 한다는 것을 의미합니다.

 

이제 실행을 하기 위해

Run As > Junit Test를 실행해서 테스트 결과를 확인 합니다.

 

코드 실행 결과를 보면 정상적으로 객체가 생성된 것을 확인할 수 있습니다.

만일 테스트 자체가 실행되지 않는다면 junit의 버전을 다시 한번 확인하고, spring-test라이브러리가 포함 되어 있는지 확인 해야 합니다.

 

실행된 결과에서 주목할 부분은 다음과 같습니다. 

  • new Restaurant()와 같이 Restaurant 클래스에서 객체를 생성한 적이 없는데도 객체가 만들어졌다는 점 입니다.
  • 스프링은 관리가 필요한 객체(bean)를 어노테이션 등을 잉요해서 객체를 생성하고 관리하는 일종의 컨테이너나 팩토리의 기능을 가지고 있습니다.

 

  • Restaurant 클래스의 @Data 어노테이션으로 Lombok을 이용해서 여러 메서드가 만들어진 점
  • Lombok은 자동으로 getter/setter 등을 만들어 주는데 스프링은 생성자 주입 혹은 setter 주입을 이용해서 동작 합니다.
  • Lombok을 통해서 getter/setter 등을 자동으로 생성하고 onMethod 속성을 이용해 작성된 setter에 @Autowired 어노테이션을 추가 합니다.

 

  • Restaurant 객체의 Chef 인스턴스 변수(멤버 변수)에 Chef 타입의 객체가 주입되어 있다는 점
  • 스프링은 @Autowired와 같은 어노테이션을 이용해서 개발자가 직접 객체들과의 관계를 관리하지 않고, 자동으로 관리되도록 합니다.

 

2.3.2 코드에 사용된 어노테이션들

 

Lombok 관련

Lombok을 간단히 설명하면 컴파일 시 흔하게 코드를 작성하는 기능들을 완성해 주는 라이브러리 입니다.

@Setter 어노에티션은 setter 메서드를 만들어 주는 역할을 합니다.

Restaurant 클래스를 조사해 보면 아래 와 같이 @Setter 메서드가 작성된 것을 볼 수 있습니다.

 

@Setter 는 말 그래도 setter 메서드를 생성해주는 역할을 합니다.

@Setter에는 3가지의 속성을 부여해 줄 수 있습니다.

@Data는 Lombok에서 가장 자주 사용되는 어노테이션입니다.

@Data는 @ToString,@EqualsAndHashCode, @Getter/@Setter, @RequiredArgsConstructor를 모두 결합한 형태로 한번에 자주 사용되는 모든 메스드들을 생성할 수있다는 장점이 있습니다.

세부적인 설정이 필요하지 않은 상황이라면 @Data를 주로 이용 합니다.

 

@Log4j 어노테이션은 로그 객체를 생성하게 됩니다.

@Log4j는 log4j 설정을 이용하고, Log4j가 존재하지 않을 경우에는 @Log를 이용할 수 있습니다.

 

@Log
public class LogExample{
}

//클래스로 변환된 후

public classLogExample{
	private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
}

@Log를 붙여주면 내부적으로 static final Logger 객체가 생성되므로 개발시 별도의 로그를 설정할 필요가 없습니다.

 

STS를 이용해서 Spring Legacy Project롤 생성한 경우에는 Log4j가 기본적으로 설정 되어 있기 때문에 추가적인 설정 없이 @logj4만으로 객체를 준비 할 수 있습니다.

 

 

Spring 관련

@Component는 해당 클래스가 스프링에서 객체로 만들어서 관리하는 대상임을 명시하는 어노테이션입니다.

@Component가 있는 클래스를 스프링이 읽어 주도록 @ComponentScan을 통해 지정되어 있으므로 해당 패키지에 있는 클래스들을 조사하면서 @Component 가 존재하는 클래스들을 객체로 생성해서 빈으로 관리하게 됩니다.

 

@Autowired는 스프링 내부에서 자신이 특정한 객체에 의존적이므로 자신에게 해당 타입의 빈을 주입하라는 뜻입니다.

예제에서 Restaurant 객체는 Chef 타입의 객체가 필요하다는 것을 명시합니다.

스프링은 @Autowired 어노테이션을 보고 스프링 내부에 관리되는 객체들 중에 적당한 것이 있는지를 확인하고 자동으로 주입해 줍니다.

 

만약 필요한 객체가 존재하지 않는다면 스프링은 제대로 객체를 구성할 수 없기 떄문에 에러를 발생 시킵니다.

 

예를들어 Chef 클래스에 @Component가 없다면 스프링은 Chef 객체를 스프링에서 관리하지 않게 됩니다.

따라서 실행시 Chef 타입의 객체가 없어서 제대로 실행 할 수 없게 됩니다.

 

테스트 관련 어노테이션

테스트 관련해서 가장 중요한 어노테이션은 @ContextConfiguration 입니다.

@Context Configuration은 스프링이 실행되면서 어떤 설정 정보를 읽어 들어야 하는 지를 명시합니다.

 

속성으로는 locations를 이요해서 문자열의 배열로 xml 설정파일을 명시할 수 있고, classes 속성으로 @Configuration이 적용된 클래스를 지정해 줄 수도 있습니다.

 

@Runwith는 테스트 시 필요한 클래스를 지정합니다.

스프링은 SpringJUnit4ClassRunner 클래스가 대상이 됩니다.

 

@Test는 junit에서 해당 메서드가 jUnit 상에서 단위 테스트의 대상인지를 알려 줍니다.

 

2.4 스프링 4.3 이후 단일 생성자의 묵시적 자동 주입

스프링의 의존성은 크게 

  1. 생성자 주입
  2. Setter 주입

을 사용 합니다.

 

Setter 주입은 앞의 에제와같이 setXXX()와 같은 메서드를 작성하고(혹은 Lombok으로 생성) 

@Autowired와 같은 어노테이션을 이용해서 스프링으로부터 자신이 필요한 객체를 주입해 주도록 합니다.

 

생성자 주입은 이러한 주입 생상자를 통해 처리합니다.

 

기존 스프링에서는 생성자 주입을 하기 위해서 생성자를 정의하고, @Autowired 와 같은 어노테이션을 추가해야만 생성자 주입이 이뤄 졌지만,

스프링4.3 이후에는 묵시적으로 생성자 주입이 가능 합니다.

 

예를 들어 Chef라는 존재는 호텔에서도 필요하다고 가정하고 코드를 만들어 본다면 아래와 같이 구성이 가능 합니다.

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.ToString;

@Component
@ToString
@Getter
public class SampleHotel {
	private Chef chef;
	
	public SampleHotel(Chef chef) {
		this.chef = chef;
	}
	
}

 

 

코드를 보면 기존과 달리 생성자를 선언하고 Chef를 주입하도록 작성되었습니다.

기존과 다른 점은 @Autowired 어노테이션 없이 처리 되고 있다는 점입니다.

 

SampleHotel에 대한 테스트 코드는 다음과 같이 만들어 볼 수 있습니다.

package org.zerock.sample;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class HotelTests {

	  @Setter(onMethod_ = { @Autowired })
	  private SampleHotel hotel;  

	  @Test
	  public void testExist() {
	    
	    assertNotNull(hotel);
	    
	    log.info(hotel);
	    log.info("----------------------------------");
	    log.info(hotel.getChef());
	    
	  }
}

테스트 코드의 내용은 기존과 거의 동일하고 아래와 같이 의존성 주입이 이뤄진 것을 확인 할 수 있습니다.

생성자의 자동 주입과 Lombok을 결합하면 SampleHotel은 아래와 같이 변경 될 수 있습니다.

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.ToString;

@Component
@ToString
@Getter
@AllArgsConstructor
public class SampleHotel {
	private Chef chef;
}

@AllArgsConstructor는 인트선트 변수로 선언된 모든 것을 파라미터로 받는 생성자를 작성하게 됩니다.

컴파일 된 결과를 살펴보면 생성자로 Chef를 받도록 만들어 진 것을 확인할 수 있습니다.

 

만일 여러 개의 인스턴스 변수들 중에서 특정한 변수에 대해서면 생성자를 작성하고 싶다면 아래와 같이 @NonNull과 @RequiredArgsConstructor 어노테이션을 이용할 수 있습니다.

@Component
@ToString
@Getter
@RequiredArgsConstructor
public class SampleHotel {
	@NonNull
	private Chef chef;
}

@RequiredArgsConstructor는 @NonNull이나 final이 붙은 인스턴스 변수에 대한 생성자를 만들어 냅니다.