개발/[Spring] 블로그 만들기

[코드로 배우는 스프링 웹 프로젝트] 2강. 스프링 특징과 의존성 주입 (의존성 주입(DI) 코드로 테스트 하기)

ee2ee2 2021. 11. 25. 17:43
728x90
반응형

해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.


- 스프링 프레임 워크를 이용해서 '의존성 주입(DI)에 대한 이해와 테스트

- 스프링에서 XML을 이용하는 객체 관리 방법

- 스프링의 테스트 환경 구축


프레임워크란?

: 뼈대나 근간을 이루는 코드들의 묶음. 즉, 개발을 시작하는데 있어 기본 틀이라 생각하면 쉽다.


스프링의 주요 특징 

1) POJO (Plain Old Java Object) 기반의 구성

 -. 객체간의 관계를 구성할 때, 별도의 API 등을 사용하지 않는 POJO의 구성만으로 가능

 -. 코드를 개발할 때 개발자가 특정한 라이브러리나 컨테이너의 기술에 종속적이지 않는 것을 뜻함


2) 의존성 주입(DI : Dependency Injection)과 스프링

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

 -. 주입(Injection)은 외부에서 밀어 넣는 것

-> 즉, 의존성 주입이란 '어떤 객체가 필요한 객체를 외부에서 밀어 넣는 것'을 의미함

(주입을 받는 입장에서는 어떤 객체인지 신경쓸 필요 없으며, 어떤 객체에 의존하든 자신의 역할은 변하지 않는다.)

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

ApplicationContext 가 관리하는 객체들을 '빈(Bean)'이라 부르며, 빈과 빈사이의 의존 관계를 처리하는 방식은 아래 3가지가 있다.

1) XML 설정
2) Annotation 설정
3) Java 설정

3) AOP(Aspect Oriented Programming)의 지원

: 횡단 관심사(cross-concern)를 모듈로 분리하는 프로그래밍의 패러다임

횡단 관심사(cross-concern)란?
 : 공통으로 가지고 있는 보안이나 로그, 트랜잭션과 같이 비즈니스 로직은 아니지만, 반드시 처리가 필요한 부분

<AOP의 장점>

1) 핵심 비즈니스 로직에만 집중해서 코드를 개발할 수 있음.

2) 각 프로젝트마다 다른 관시마를 적용할 때 코드의 수정을 최소화할 수 있음

3) 원하는 관심사의 유지보수가 수월한 코드를 구성할 수 있음

 


의존성 주입 테스트

Ex) 레스토랑(Reataurant) 객체를 만들고, 레스토랑에서 일하는 셰프(Chef) 객체를 주입하는 예제

스프링 의존성 주입 방법 2가지

1) 생성자를 이용한 주입
2) setter 메소드를 이용한 주입

pom.xml (추가 및 수정)

 : <dependencies> </dependencies> 안에 추가 및 수정

<!-- lombok 추가 -->
		<dependency>
      		<groupId>org.projectlombok</groupId>
      		<artifactId>lombok</artifactId>
      		<version>1.18.22</version>
     	 	<scope>provided</scope>
		</dependency>

<!-- log4j 버전 수정 -->

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

예제 클래스 생성하기

 : org.zerock.sample 패키지 생성 후, Chef, Restarant 클래스 생성

Chef.java

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@Data
public class Chef {

}

Restaurant.java

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 {

	//Restaurant 객체는 Chef 객체를 필요함을 의미함.
	@Setter(onMethod_ = @Autowired)	
	private Chef chef;
	
	/*
	 * @Setter : 자동으로 setChef()를 컴파일시 생성하도록 하는 어노테이션
	 * onMethod 속성은 생성되는 setChef()에 @Autowired 어노테이션을 추가하도록 함.
	 */
}

@Data 어노테이션을 사용함으로써 아래와 같이 기본 함수들을 사용할 수 있음.

lombok - @Data 사용

 


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

 : 프로젝트 src 폴더 내에 'root-context.xml'은 스프링 프레임워크에서 관리해야하는 객체(이러한 객체를 빈(Bean)이라고 표현)를 설정하는 설정 파일

root-context.xml파일 선택 후, Namespaces 클릭 > context 체크 설정

Source 선택 후, 아래 코드 추가

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	
	<context:component-scan base-package="org.zerock.sample"></context:component-scan>
</beans>

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

: java 설정을 이용하는 경우에 'root-context.xml'을 대신하는 것은 RootConfig 클래스

RootConfig.java 

jex00 프로젝트의 RootConfig.java (1강에서 생성함) 

@ComponentScan 어노테이션을 이용하여 처리

package org.zerock.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {"org.zerock.sample"})
public class RootConfig {


}

스프링의 동작 방식

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

테스트 코드를 통한 확인해보기

: 'src/test/java' 폴더 내에 'org.zerock.sample.SampleTests' 클래스 생성

SampleTests.java

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());
	}
}

Run As > Junit Test 실행

실행 결과

-. 테스트 코드가 실행되기 위해서 스프링 프레임워크가 동작함.

-. 동작하는 과정에서 필요한 객체들이 스프링에 등록됨

-. 의존성 주입이 필요한 객체는 자동으로 주입됩


Java를 이용하는 경우의 테스트 설정

SampleTests.java

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 org.zerock.config.RootConfig;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {RootConfig.class})
@Log4j
public class SampleTests {
	
	@Setter(onMethod_ = {@Autowired})
	private Restaurant restaurant;
    
    ..... 생략 ....
    
    }

테스트 수행시, 동일한 결과를 얻을 수 있다.

 


사용된 어노테이션 정리

 ---- 테스트 관련 어노테이션 ----
@RunWith : 현재 테스트 코드가 스프링을 실행하는 역할을 할 것 이라는 의미
@ContextConfiguration : 지정된 클래스나 문자열을 이용해서 필요한 객체들을 스프링 내에 객체로 등록하게 됨
@Test : JUnit에서 TEST 기능을 실행
 


---- Lombok 관련 어노테이션 ----
@Log4j : Lombok 을 이용해서 로그를 기록하는 Logger를 변수로 생성 (Log4j 라이브러링와 설정이 존재한다면 Logger 객체 선언 없이 바로 사용 가능)
@Setter : 자동으로 setChef()를 컴파일시 생성하도록 하는 어노테이션
@Data : @ToString, @Getter/@Setter, @EqualsAndHashCode 등등 모두를 결합한 형태. 모두를 사용 가능하게 함.


---- Spring 관련 어노테이션 ----
@Autowired : 해당 인스턴스 변수가 스프링으로부터 자동으로 주입해달라는 의미
@Component : 스프링에게 해당 클래스가 스프링에서 관리해야하는 대상임을 표시하는 어노테이션


assertNotNull(INSTANCE) : 변수가 null이 아니어야만 테스트가 가능함을 의미