[Spring] Spring Framework 특징

SEB_BE_43_yeori316·2023년 2월 2일
0

Spring

목록 보기
3/22

Spring Framework 특징

1. POJO(Plain Old Java Object - Java로 생성하는 순수한 객체)

  • Java나 Java의 스펙(사양)에 정의된 것 이외에는 다른 기술이나 규약에 얽매이지 않아야 함
    : 특정 프레임 워크에서 지원하는 클래스 사용 시 규약에 얽매이게 됨.
    : 다른 기술을 사용하는 상태에서 요구사항이 변경 될 경우에는 다시 전부 일일이 제거하거나 수정해야하는 일이 발생

  • 특정 환경에 종속적이지 않아야 함

  • POJO 프로그래밍이 필요한 이유
    • 특정 환경이나 기술에 종속적이지 않으면서 재사용이 가능하고, 확장 가능한 유연한 코드를 작성하기 위해
    • 저수준 레벨의 기술과 환경에 종속적인 코드를 애플리케이션 코드에서 제거 함으로써 코드가 간결해짐
    • 코드가 간결해지기 때문에 디버깅하기도 상대적으로 쉬움
    • 특정 기술이나 환경에 종속적이지 않기 때문에 테스트 역시 단순해짐
    • 객체지향적인 설계를 제한없이 적용 가능
  • Spring은 POJO 프로그래밍을 지향하는 Framework
  • Spring에서 POJO 프로그래밍 코드를 작성하기 위해 IoC/DI, AOP, PSA 3가지 기술을 지원

애플리케이션 프로그래밍 코드를 작성할 때 항상 작성한 코드가 객체지향스러운가에 대해 고민 하는 습관을 가지는 것


2-1. IoC(Inversion of Control - 제어의 역전)

  • 애플리케이션 흐름(제어)의 주도권이 뒤바뀐 것
    ex) 애플리케이션 흐름의 주도권이 사용자에게 있지 않고, Framework이나 서블릿 컨테이너 등 외부에 있는 것 즉, 흐름의 주도권이 뒤바뀐 것

※ main() 메서드처럼 애플리케이션이 시작되는 지점을 엔트리 포인트(Entry point)


2-2 DI(Dependency Injection - 의존성 주입)

  • DI(Dependency Injection)는 IoC 개념을 조금 구체화 시킨 것으로 객체 간의 관계를 느슨하게 해줌
  • 의존성 주입(DI)은 클래스들 간의 강한 결합을 느슨한 결합으로 만들어줌

1. 의존성 주입이 아닌 일반 의존성 관계

  • 클래스 끼리 사용하고자 하는 클래스의 객체를 생성해서 참조하게 되면 의존 관계가 성립
    (의존성 주입은 아님)
public class MenuController {
	public static void main(String[] args) {
    	MenuService menuService = new MenuServie();			// MenuService 클래스의 객체를 생성
        List<Menu> menuList = menuService.getMenuList();	// MenuService 기능 사용
  	}
}

public class MenuService {
  	public List<Menu> getMenuList() {
  		return null;
  	}
}

2. 의존성 주입

  • 생성자를 통해서 어떤 클래스의 객체를 전달 받는 것을 ‘의존성 주입’
  • 생성자의 파라미터로 객체를 전달하는 것을 '외부에서 객체를 주입한다'
public class CafeClient {
 	public static void main(String[] args) {
  		// menuService 인스턴스를 생성
  		MenuService menuService = new MenuService();
  		// menuController 인스턴스 생성 시에 앞서 생성한 munuService 인스턴스를 인자값으로 전달함.
  		// 외부에서 객체를 주입한다, 즉 의존성 주입에 해당됨
  		MenuController controller = new MenuController(menuService);	
  		List<Menu> menulist = controller.getMenus();
  	}
}

public class MenuController {
	private MenuService menuService;

  	public MenuController(MenuService menuService) {
  		this.menuService = menuService;
  	}
 		
  	public List<Menu> getMenus() {
  		return menuService.getMenuList();
  	}     
}

public class MenuService {
  	public List<Menu> getMenuList() {
  		return null;
  	}
}  

3. 의존성 주입은 왜 필요할까요? Why

  • 의존성 주입을 사용할 때, 항상 염두에 두어야 하는 부분
    • 현재의 클래스 내부에서 외부 클래스의 객체를 생성하기 위한 new 키워드를 쓸지 말지 여부를 결정하는 것
    • 애플리케이션 코드 내부에서 직접적으로 new 키워드를 사용할 경우 객체지향 설계의 관점에서 중요한 문제가 발생

3-1. new 키워드를 사용할 경우 문제 발생 예시

  • 인터페이스 테스트를 위한 Stub(스텁)을 사용해야 할 경우
  • MenuService 변경 시(MenuService -> MenuServiceStub)
public class CafeClient {
 	public static void main(String[] args) {
  		MenuServiceStub menuService = new MenuServiceStub();		// 변경 필요
  		MenuController controller = new MenuController(menuService);	
  		List<Menu> menulist = controller.getMenus();
  	}
}

public class MenuController {
	private MenuServiceStub menuService;							// 변경 필요

  	public MenuController(MenuServiceSutb menuService) {			// 변경 필요
  		this.menuService = menuService;
  	}
 		
  	public List<Menu> getMenus() {
  		return menuService.getMenuList();
  	}     
}

public class MenuServiceStub {									// 변경 될 경우
  	public List<Menu> getMenuList() {
  		return List.of( 
  				new Menu(1, "아메리카노", 2500),
  				new Menu(2, "바닐라 라떼", 3500)
  		);
  	}
}  
  • 이처럼 new 키워드를 사용해서 의존 객체를 생성할 경우, 클래스들 간에 강하게 결합(Tight Coupling)되어 있음
  • 결론적으로 의존성 주입을 하더라도 의존성 주입의 혜택을 보기 위해 클래스들 간의 강한 결합은 피하고 느슨한 결합(Loose Coupling)이 필요

3-2 느슨한 의존성 주입은 어떻게 할까요? How

public class CafeClient {
 	public static void main(String[] args) {
  		MenuService menuService = new MenuServiceStub(); 			// 변경 필요 (업 캐스팅)
  		MenuController controller = new MenuController(menuService);	
  		List<Menu> menulist = controller.getMenus();
  	}
}

public class MenuController {
	private MenuService menuService;								// 기존 MenuService

  	public MenuController(MenuService menuService) {				// 기존 MenuService
  		this.menuService = menuService;
  	}
 		
  	public List<Menu> getMenus() {
  		return menuService.getMenuList();
  	}     
}

public Interface MenuService {								// MenuService 를 인터페이스로 설정
  	List<Menu> getMenuList();
}  

public class MenuServiceStub implements MenuService {	// Stub을 MenuServce 인터페이스로 구현
  	public List<Menu> getMenuList() {
  		return List.of( 
  				new Menu(1, "아메리카노", 2500),
  				new Menu(2, "바닐라 라떼", 3500)
  		);
  	}
}  
  • Spring에서는 위와 같은 애플리케이션 코드에서 이루어지는 의존성 주입(DI)을 Spring에서 대신 해줌

3. AOP(Aspect Oriented Programming - 관심 지향 프로그래밍)

  • 애플리케이션의 핵심 업무 로직에서 로깅이나 보안, 트랜잭션 같은 공통 기능 로직들을 분리하는 것

  • 핵심 관심 사항(Core concern)
    : 비즈니스 로직 즉, 애플리케이션의 주목적을 달성하기 위한 핵심 로직에 대한 관심사

  • 공통 관심 사항(Cross-cutting concern)
    : 애플리케이션 전반에 걸쳐 공통적으로 사용되는 기능들에 대한 관심사

  • 핵심 로직에서 공통 기능을 분리하는 이유
    • 코드의 간결성 유지
    • 객체 지향 설계 원칙에 맞는 코드 구현
    • 코드의 재사용

애플리케이션을 제작하면서 항상 기본적으로 가져야 하는 사고

  • 어떻게 하면 이 코드를 깔끔하게 유지할 수 있을까?
  • 어떻게하면 여기 저기 중복되는 코드들을 재사용할 수 있을까?
  • AOP를 활용하면 애플리케이션에 전반에 걸쳐 적용되는 공통 기능(트랜잭션, 로깅, 보안, 트레이싱, 모니터링) 등을 비즈니스 로직에서 깔끔하게 분리하여 재사용 가능한 모듈로 사용할 수 있음.

4. PSA(Portable Service Abstraction - 일관된 서비스 추상화)

  • 서비스의 기능을 접근하는 방식 자체를 일관되게 유지하면서 기술 자체를 유연하게 사용할 수 있도록 하는 것

추상화(Abstraction) : 객체지향 프로그래밍 세계에서 어떤 클래스의 본질적인 특성만을 추출해서 일반화 하는 것

  • 추상화 된 상위 클래스를 일관되게 바라보며 하위 클래스의 기능을 사용하는 것이 바로 일관된 서비스 추상화(PSA)의 기본 개념
  • PSA가 필요한 이유

    • 어떤 서비스를 이용하기 위한 접근 방식을 일관된 방식으로 유지함으로써 애플리케이션에서 사용하는 기술이 변경되더라도 최소한의 변경만으로 변경된 요구 사항을 반영하기 위함

    • 애플리케이션의 요구 사항 변경에 유연하게 대처

  • Spring은 상황에 따라 기술이 바뀌더라도 변경된 기술에 일관된 방식으로 접근할 수 있는 PSA를 적극적으로 지원함




이미지 출처
: https://velog.velcdn.com/images/bimilless/post/8d4bd476-1c5c-43e2-9152-b5d053fae2b0/image.png

0개의 댓글