스프링 프레임워크

2023. 10. 23. 16:57웹/백엔드-spring

728x90
반응형

프레임워크는 말 그대로 "뼈대나 근간을 이루는 코드들의 묶음"이라고 할 수 있습니다. 

 

개발자는 각 개인의 능력 차이가 큰 직종이고, 개발자 구성에 따라 프로젝트의 결과 역시 큰 차이가 납니다. 프레임워크는 이런 상황을 극복하기 위한 코드의 결과물입니다. 프레임워크를 이용한다는 의미는 프로그램의 기본 흐름이나 구조를 정하고, 모든 사람들이 이 구조에서 자신의 코드를 추가하는 방식으로 개발하게 됩니다.

 

프레임워크는 개발에 필요한 구조를 이미 코드로 만들어 놓았기 때문에, 반쯤 완성된 상태에서 필요한 부분을 조립하는 형태의 개발이 가능합니다. 프레임워크를 사용하면 일정한 품질이 보장되는 결과물을 얻을 수 있습니다. 

 

경량 프레임워크(light-weight Framework)

경량 프레임워크 용어는 복잡한 구동 환경, 하드웨어적인 구성이 필요한 프레임워크의 반대되는 개념입니다.
J2EE 기술은 너무나 복잡하고 방대했기 때문에, 그 전체를 이해하고 개발하기에는 어려운 점이 많아서, 특정 기능을 위주로 간단한 jar 파일 등을 이용해서 모든 개발이 가능하도록 구성된 프레임워크입니다.

스프링의 주요 특징

POJO 기반의 구성

객체 간의 관계를 구성할 때 별도의 API 없이 POJO의 구성만으로 가능하도록 제작되어 있습니다. Java 코드를 이용해서 객체를 구성하는 방식을 그대로 스프링에서 사용할 수 있다는 이야기입니다.

 

이것이 중요한 이유는 특정한 라이브러리, 컨테이너의 기술에 종속적이지 않다는 것을 의미합니다. 일반적인 형태로 코드를 작성하고 실행할 수 있기 때문에 생산성에서도 유리하며, 테스트 작업 역시 좀 더 유연하게 할수 있습니다.

의존성 주입과 스프링

의존성은 하나의 객체가 다른 객체 없이 제대로 된 역할을 할 수 없다는 것을 의미합니다. 예를 들어 식당에서 서빙 직원이 하루 안나와도 크게 문제가 되지않지만 주방장이 못나오면 장사를 할 수 없습니다. 의존성은 이처럼 하나의 객체가 다른 객체의 상태에 따라 영향을 받는 것을 의미합니다. 즉 식당이 요리사 없이 장사를 못하는 상황이면 식당이 요리사에 의존적이다라고 말할 수 있습니다.

 

주입(Injection)은 외부에서 '밀어 넣는 것'을 의미합니다. 외부에서 주입하는 것과 그렇지 않은 것을 이해하기 위해서 음식점의 식재료를 생각해 보면 이해가 쉽습니다. 음식점이 매일 가게을 열기 전 직접 식재료를 구하기위해 시장을 가지만, 프렌차이즈 식당들은 본사가 식재료를 공급합니다. 이 처럼 능동적으로 구하거나, 수동적으로 공급받던지 필요한 것을 외부에서 얻어야 합니다.

 

의존성과 주입을 결합해서 생각해 보면 '어떤 객체가 필요한 객체를 외부에서 밀어 넣는다'의미가 됩니다. 외부에서 주입을 받으면 직접 생성하는 것보다 장점이 정말 중요한 점에 집중할 수 있습니다. 식재료를 본사에서 재공을 해주면 가게 주인은 장사에만 집중하면 됩니다.

 

이처럼, 의존성 주입 방식을 사용하려면 '주입 해주는 객체'가 필요합니다. 스프링은 ApplicationContext라는 존재가 필요한 객체들을 생성하고, 주입하는 역할을 해 주는 구조입니다. 이런 의존관계를 처리하는 방식으로는 xml, 어노테이션, Java 설정 방식 3가지가 있습니다. 

AOP의 지원

'반복적인 코드의 제거'를 위한 방법입니다. 대부분 시스템은 공통으로 보안, 로그, 트랜잭션 등과 같은 비즈니스 로직에 상관없는 부분이 있습니다. 스프링은 이러한 관심사를 분리해서 처리해줍니다. 이를 횡단 관심사를 모듈로 분리하는 프로그래밍의 기법 AOP라고 합니다.

트랜잭션의 지원

데이터베이스 업무 시 하나의 업무가 여러 작업으로 이루어지는 경우의 트랜잭션 처리입니다. 스프링에서는 xml, 어노테이션 설정으로 트랜잭션의 관리를 도와줍니다.


의존성 주입 테스트

레스토랑 객체에 셰프 객체를 주입하는 예제

 

Chef

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@Data
public class Chef {

}

Restaurant

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;

}

root-context.xml에 관리해야하는 객체를 설정해줍니다. root-context.xml 파일을 열고 아래쪽 Namespaces 클릭 -> context를 체크해줍니다.

xml 파일에 아래와 같이 설정해 줍니다.

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

설정을 저장 후 "Bean Graph"탭을 선택해 봅니다. restaurant, chef 객체가 설정된게 확인됩니다.


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

root-context.xml 대신 RootConfig 클래스를 이용합니다. @ComponentScan 어노테이션을 이용해서 처리합니다.

RootConfigure 클래스

package org.zerock.config;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;

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

}

스프링이 동작

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

 

테스트 코드를 통한 확인

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

}
INFO : org.zerock.sample.SampleTests - Restaurant(chef=Chef())
INFO : org.zerock.sample.SampleTests - ------------------------------
INFO : org.zerock.sample.SampleTests - Chef()

자바 이용한 테스트 설정

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;
	
	@Test
	public void testExist() {
		assertNotNull(restaurant);
		
		log.info(restaurant);
		log.info("------------java 설정------------------");
		log.info(restaurant.getChef());
	}

}
INFO : org.zerock.sample.SampleTests - Restaurant(chef=Chef())
INFO : org.zerock.sample.SampleTests - ------------java 설정------------------
INFO : org.zerock.sample.SampleTests - Chef()

스프링 4.3 이후의 묵시적 주입

SampleHotel.java 코드

package org.zerock.sample;

import org.springframework.stereotype.Component;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

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

}
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;
	
	@Setter(onMethod_ = {@Autowired})
	private SampleHotel sampleHotel;
	
	@Test
	public void testExist() {
		assertNotNull(restaurant);
		
		log.info(restaurant);
		log.info("------------------------------");
		log.info(restaurant.getChef());
	}

	@Test
	public void sampleHotel() {
		assertNotNull(sampleHotel);
		
		log.info(sampleHotel);
		log.info("------------------------------");
		log.info(sampleHotel.getChef());
	}
}
INFO : org.zerock.sample.SampleTests - SampleHotel(chef=Chef())
INFO : org.zerock.sample.SampleTests - ------------------------------
INFO : org.zerock.sample.SampleTests - Chef()
반응형

' > 백엔드-spring' 카테고리의 다른 글

스프링 개발 환경  (0) 2023.10.23