Helmi

스프링 특징, 의존성 주입 본문

Spring

스프링 특징, 의존성 주입

Helmi 2023. 4. 12. 19:25

   What to learn?

- Java 관련 프레임워크들이 추구했던 목표

- 스프링 프레임워크가 살아남은 이유

- 스프링의 가장 중요한 특징, 의존성 주입


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

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

프로그램의 기본 흐름이나 구조를 정하고 모든 팀원이 이 구조에 자신의 코드를 추가하는 방식으로 개발

 

- 장점 : 개발에 필요한 구조를 이미 코드로 만들어 놓았으므로 능력 부족한 개발자도 반쯤 완성한 상태에서 필요 부분 조립하는 형태의 개발 가능. 즉, 회사에선 일정한 품질 보장되는 결과물 얻을 수 있고 개발자는 완성된 구조에 자신이 맡은 코드 개발해서 넣어주므로 개발 시간이 단축됨

 

스프링 프레임 워크 특징

- 복잡함에 반기를 들어 만들어진 프레임워크 : 경량화된 프레임워크. Java 클래스와 인터페이스 이용하는 구조 - 진입 장벽 높지 않고 가벼움

- 프로젝트의 전체 구조를 설계할 때 유용한 프레임 워크 : 전체를 설계하는 용도로 사용. 대부분 Web이라는 제한된 영역에서 많이 사용되기는 하지만, 근본 사상 자체는 OOP 구조 뒷받침 하고 구조 설계하는 사상. 의존성 주입은 이에 대한 설명.

- 다른 프레임워크들의 포용 : 스프링은 전체 구조에 집중 - 특정 영역 프레임워크와 공존하는 방식으로 사용 가능. 기본 뼈대 흔들지 않고 여러 종류 프레임워크 혼용해 사용 가능

- 개발 생산성과 개발 도구 지원

 

스프링 버전의 변화

- spring 2.5 : 어노테이션 활용하는 설정 도입 - 편리한 설정 및 개발 가능하도록 지원

- spring 3.0 : 별도 설정 없이도 Java 클래스 만으로 설정파일 대신 할 수 있도록 지원

- spring 4.0 : 모바일 환경과 웹 환경에서 많이 사용되는 REST 방식 컨트롤러 지원

- spring 5.0 : Reactor 이용한 Reactive 스타일의 개발환경 지원

 

★스프링의 주요 특징

- POJO 기반 구성 : 관계 구성 시 별도의 API등 사용하지 않는 POJO (Plain Old Java Object) 구성만으로 가능.

즉, 일반적인 Java 코드를 이용해 객체를 구성하는 방식을 그대로 스프링에서 사용하는 것. 이는 코드 개발시 개발자가 특정 라이브러리나 컨테이너의 기술에 종속적이지 않음. 생산성에서 유리, 코드에 대한 테스트 작업이 좀 더 유연해짐

 

- 의존성 주입(DI) 통한 객체간의 관계 구성 : 의존성이란, 하나의 객체가 다른 객체 없이 제대로 된 역할 할 수 없다는 것. 하나의 객체가 다른 객체의 상태에 따라 영향을 받는 것. 주입이란, 외부에서 '밀어 넣는 것'

즉, 의존성 주입이란 "어떤 객체가 필요한 객체를 외부에서 밀어 넣는다"는 의미. 어떤 객체에 의존하던 자신의 역할은 변하지 않음. 

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

따라서, 스프링 이용하면 개발자들은 기존 프로그래밍과 달리 객체와 객체 분리해 생성하고, 이 객체들을 엮는(wiring) 작업을 하는 형태의 개발을 하게 됨.

ApplicationContext가 관리하는 객체들을 '빈(Bean)'이라고 부름. 빈과 빈 사이 의존 관계를 처리하는 방식으로 XML 설정, 어노테이션 설정, Java 설정 방식을 이용 가능

 

- AOP (Aspect-Oriented-Programming) 지원 : 횡단 관심사란, 대부분 시스템이 공통으로 가지고 있는 보안이나 로그, 트랜잭션 같이 비즈니스 로직은 아니지만 반드시 처리가  필요한 부분.  스프링은 횡단 관심사를 분리해 제작하는 것이 가능.

AOP는 이러한 횡단 관심사를 모듈로 분리하는 프로그래밍의 패러다임.

AOP는 AspectJ 문법을 통해 작성 가능. 이를 통해 개발자는

①핵심 비즈니스 로직에만 집중해 코드 개발 가능

②각 프로젝트마다 다른 관심사 적용 시 코드 수정 최소화 가능

③원하는 관심사의 유지 보수가 수월한 코드 구성 가능

 

- 편리한 MVC 구조

 

- WAS의 종속적이지 않은 개발 환경

 

- 트랜젝션 지원 : 하나의 업무가 여러 작업으로 이루어지는 경우 트랜젝션 처리를 반드시 신경써줘야 함. 스프링은 이런 트랜젝션 관리를 어노테이션이나 XML로 설정 가능 - 개발자가 매번 상황 맞는 코드 작성할 필요가 없음.


2. 의존성 주입 테스트

생성자 이용한 주입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.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

pom.xml에 변경되는 라이브러리

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

 

1) 예제 클래스 작성

ex230412 프로젝트에 'com.helmi.sample' 패키지 생성 후 Restaurant 클래스와 Chef 클래스 생성함

일반적으로 스프링에서 의존성 주입은 Chef를 클래스 아닌 인터페이스로 설계하는 것이 좋으나 최소한 코드 이용해 의존성 주입 테스트 해보기 위한 것이므로 클래스로 설계

 

com.helmi.sample.Chef.java

package com.helmi.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@Data
public class Chef {

}

Restaurant 클래스는 Chef 주입 받도록 설계. 이때 Lombok의 setter 생성하는 기능과 생성자, toString() 등을 자동으로 생성하도록 @Data 어노테이션 이용

 

com.helmi.sample.Restaurant.java

package com.helmi.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 : 자동으로 setChef()를 컴파일시 생성
	//onMethod : 생성되는 setChef()에 @Autowired 어노테이션 추가하도록 함
	@Setter(onMethod_ = @Autowired) 
	private Chef chef;
}

Restautant 객체는 Chef 타입의 객체를 필요로 하는 상황


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

스프링은 클래스에서 객체 생성, 객체들의 의존성 대한 처리 작업까지 내부에서 모든 것이 처리 됨.

스프링에서 관리되는 객체를 흔히 빈(Bean)이라 하고 이에 대한 설정은 XML과 Java 이용해 처리 가능

 

src 폴더 내에 'root-context.xml'은 스프링 프레임워크에서 관리해야하는 객체(빈)를 설정하는 설정파일

root-context.xml - Namespaces - context 체크

아래 코드 추가

 

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="com.helmi.sample">
</context:component-scan>
		
</beans>

 

+) Java 설정 이용하는 의존성 주입

root-context.xml 대신 하는 것은 RootConfig 클래스

XML로 설정된 내용은 RootConfig에서 @ComponentScan 어노테이션 이용해 처리 가능

 

jex230412의 RootConfig.java

package com.helmi.config;

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

@Configuration
@ComponentScan(basePackages={"com.helmi.sample"})
public class RootConfig {
}

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

테스트 코드를 통한 확인

spring-test 모듈 이용해 간단히 스프링 가동 시키고, 동작들 일어나게 함. 이때 Junit은 반드시 4.10이상 버전 사용!

src/test/java 폴더 내 com.helmi.sample.SampleTests.java

package com.helmi.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지정된 클래스나 문자열 이용해 필요한 객체들을 스프링 내에 객체로 등록(빈으로 등록)

'classpath:'나 'file:' 문자열 이용, 이클립스에서 자동으로 생성된 root-context.xml 경로 지정 가능

 

@Log4j : Lombok 이용해 로그 기록하는 Logger를 변수로 생성. 별도 Logger 객체 선언 없이도 Log4j 라이브러리 설정 존재하면 바로 사용 가능. Spring Legacy Project로 생성하는 경우 기본으로 Log4j와 해당 설정 완료되는 상태이므로 별도 처리 없어도 사용 가능.

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

 

@Autowired : 해당 인스턴스 변수가 스프링으로부터 자동으로 주입해달라는 표시. testExist()에 선언되어있는 @Test는 Junit에서 테스트 대상을 표시하는 어노테이션.

 

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

 

테스트 결과가 의미하는 바

① 테스트 코드 실행 위해 스프링 프레임워크가 동작함

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

③ 의존성 주입이 필요한 객체는 자동으로 주입 이루어짐

 

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

Lombok 관련 Spring 관련 테스트 관련
@Setter
@Data
@Log4j
@Autowired
@Component
@RunWith
@ContextConfiguration
@Test

 

▶ Lombok 관련

컴파일시 흔하게 코드 작성하는 기능들 완성해주는 라이브러리

@Setter - value, onMethod, onParam 3가지 속성 부여 가능

속성명 의미
value 접근 제한 속성
기본값은 lombokAccessLevel.PUBLIC
onMethod Setter 메서드 생성시 메서드에 추가할 어노테이션 지정
'_' 표기는 JDK 버전따라 차이 있음
up to JDK7:
@Setter(onMethod=@_({@AnnotationsGoHere}))
from JDK8 : 
@Setter(onMethod_={@AnnotationsGoHere})
onParam setter 메서드의 파라미터에 어노테이션 사용하는 경우

 

@Data : Lombok에서 가장 자주 사용되는 어노테이션

@ToString, @EqualAndHashCode, @Getter/@Setter, @RequiredArgsConstructor 모두 결합한 형태. 한 번에 자주 사용되는 모든 메서드 생성 가능. 세부적인 설정 필요 없는 경우라면 @Data 주로 이용

 

@Log4j : 로그 객체 생성. Log4j 설정 이용. Log4j 존재하지 않을 경우 @Log 이용 가능. 

 Spring Legacy Project로 생성한 경우 기본적으로 Log4j 설정이 있으므로 추가적인 설정 없이 @Log4j만으로 로그 객체 준비 가능

 

Spring 관련

@Component : 해당 클래스가 스프링에서 객체로 만들어서 관리하는 대상임을 명시. @ComponentScan 통해 지정되어 있으므로 해당 패키지에 있는 클래스를 조사하며 @Component가 존재하는 클래스들을 객체로 생성해 빈으로 관리

 

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

 

테스트 관련

★★@ContextConfiguration : 스프링이 실행되면서 어떤 설정 정보를 읽어들여야 하는지 명시

속성으로는 locations 이용해 문자열의 배열로 XML 설정 파일 명시 가능, classes 속성으로 @Configuration 적용된 클래스 지정 가능. 

 

@Runwith : 테스트 시 필요한 클래스 지정. SpringJUnit4ClassRunner 클래스가 대상

 

@Test : junit에서 해당 메서드가 jUnit 상에서 단위 테스트의 대상인지 알려줌


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

스프링 의존성 주입 방법

① 생성자 주입 : 객체 생성시 의존성 주입 필요하므로 좀 더 엄격히 의존성 주입 체크하는 장점 있음

② Setter 주입 : setXXX() 같은 메서드 작성 후 (or Lombok으로 생성) @Autowired 같은 어노테이션 통해 필요한 객체 주입

 

@AllArgsConstructor : 인스턴스 변수로 선언된 모든 것을 파라미터로 받는 생성자 작성

 

여러 개 인스턴스 변수들 중 특정 변수에 대해서만 생성자 작성하고 싶을 시

: @NonNull, @RequiredArgsConstructor 이용 가능

(@RequiredArgsConstructor 는 @NonNull 이나 final 붙은 인스턴스 변수에 대한 생성자 만들어 냄)

'Spring' 카테고리의 다른 글

MyBatis + Spring 연동  (0) 2023.04.13
커넥션 풀 설정  (0) 2023.04.12
스프링 설명 조금 + 설정 시 Java Configuration 하는 경우  (0) 2023.04.11
구) 스프링 + Mybatis 실전  (0) 2023.04.07
구) 스프링 + MyBatis 소개  (0) 2023.04.06