05AM

[Spring] Spring의 4가지 특징 본문

1 week 1 conquer/Spring

[Spring] Spring의 4가지 특징

_05AM 2023. 4. 21. 23:48

※ POJO와 IoC, DI 관련 내용은 만렙 개발자 키우기 (nowwatersblog.com)의 포스팅을 모아 정리하며 공부한 글입니다. 링크를 타고 들어가 본 글을 읽으시는 것을 추천드립니다!

 

1. POJO 프로그래밍 지향 : Plane Old Java Object

특별한 제한에 종속되지 않고, Class path를 필요로 하지 않는 일반적인 Java Object를 의미한다.

 

간단히 말해, 주요 Java object 모델, 컨벤션 또는 프레임워크를 따르지 않는 Java 오브젝트이다. 이는 특정 환경과 라이브러리와의 결합도가 낮다는 것을 의미한다.

POJO의 특징

하나의 오브젝트 안에 상태(state)와 행위(Behavior)를 모두 가지고 있다. 즉, 인스턴스 변수와 로직을 가진 메소드를 가지고 있는 것이다.

그렇게 만들기 위한 가장 간단한 방법은 오브젝트가 가지고 있어야할 행위를 오브젝트로 옮겨주는 것이다.

 

⇒ 풍성한 도메인 모델(Rich Domain Model) : 객체지향 원리에 충실하게 도메인 모델을 만드는 것

POJO의 조건

1. 특정 규약에 종속되지 않아야 한다.

그렇지 않으면 단일 상속 제한 때문에 객체 지향적인 설계 기법을 적용하기 어렵고, 다른 환경으로의 이전이 어려워진다.

 

2. 특정 환경에 종속되지 않아야 한다.

그렇지 않으면 다른 환경에서 사용하기 어려우며, 비즈니스 로직과 기술적인 내용을 담은 웹 정보 코드가 섞여 이해하기 어려워진다.

 

3. 단일 책임 원칙을 지키는 클래스여야 한다.

책임과 역할이 각기 다른 코드는 서로 다른 클래스로 나눠야 한다.

 

즉, POJO란 객체 지향적인 원리에 충실하면서, 특정 환경과 규약에 종속되지 않아 필요에 따라 재사용될 수 있는 방식으로 설계된 오브젝트이다.

POJO의 장점

위의 조건들이 곧 장점이 된다.

 

-  특정 규약에 종속되지 않아 객체지향 설계를 할 수 있다.

-  특정 환경에 종속되지 않아 테스트하기 좋다.

-  특정 규약에 종속되지 않아 로우레벨 코드와 비즈니스 코드가 분리되어 깔끔한 코드 작성이 가능하다.

 

POJO의 진정한 가치는 Java의 객체지향적인 특징을 살려 비즈니스 로직에 충실한 개발이 가능하도록 하는 것이다.

POJO를 잘 사용하면 군더더기 없는 최소한의 코드로 이전과 동일한 결과를 낼 수 있고, 그 이상으로 더 좋은 코드를 만들 수 있다.

그러한 코드를 만들기 위해서는 자동화된 테스트 코드를 개발해야 한다. 그리고 잘 만들어진 테스트 코드는 지속적인 변화에 유연하게 대응할 수 있도록 도와준다.

POJO 기반의 코드인지 확인하는 중요한 기준

1. 객체지향적인 설계원칙에 충실하도록 개발되어 있는가?

 

POJO의 Java object라는 것은 단순히 Java 문법을 지켜 만든 것이 아니라, 객체지향 언어로서의 Java object의 특징(SOLID 원칙)을 가지고 있는지가 중요하다.

반복적으로 등장하는 템플릿 코드, 테스트하기 힘든 구조, 확장이나 재활용의 어려움이 있다면 POJO 기반 코드가 아니다.

 

2. 테스트 코드 개발의 용이성

 

잘 만들어진 POJO 어플리케이션은 자동화된 테스트 코드 작성이 편리하다.

코드 작성이 편리할수록 더 자주 더 꼼꼼하게 테스트할 수 있어 코드 검증과 품질 향상에 유리해진다.

잘 만들어진 테스트 코드베이스가 있다면, 리팩토링할 여유가 생겨 POJO 코드를 좀 더 나은 설계 구조로 변경할 가능성도 높아진다.

Spring과 POJO

스프링 어플리케이션 = POJO를 이용해서 만든 애플리케이션 로직 + POJO가 어떻게 관계를 맺고 동작하는지 정의해놓은 설계 정보

스프링의 주요 기술인 IoC & DI, AOP, PSA 는 애플리케이션을 POJO 로 개발할 수 있게 해주는 기술들이다.

[출처]만렙 개발자 키우기 (nowwatersblog.com)

 

2. IoC / DI : Inversion of Control & Dependency Injection

Spring 프레임워크 사용 전에는 개발자가 프로그램의 흐름(어플리케이션 코드)를 제어하는 주체였다. 하지만 Spring에서는 프로그램의 흐름을 프레임워크가 주도한다.

객체의 생성부터 생명주기 관리를 컨테이너가 도맡아서 한다. 즉, 제어권이 컨테이너로 넘어가게 되고, 이것을 제어권의 흐름이 바뀌었다고 해서 제어의 역전(Inversion of Control)이라고 한다.

 

제어권이 컨테이너로 넘어옴으로써 의존성 주입(DI), 관점 지향 프로그래밍(AOP) 등이 가능해졌다.

 

의존성을 역전시켜 객체 간의 결합도를 줄이고, 유연한 코드를 작성할 수 있게 됨에 따라 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 해준다.

기존의 객체 생성 과정

1. 객체 생성

2. 의존성 객체 생성 -> 클래스 내부에서 생성

3. 의존성 객체 메소드 호출

Spring에서 객체 생성 과정

1. 객체 생성

2. 의존성 객체 주입 → 스스로 만드는 것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어놓은 객체를 주입한다.

3. 의존성 객체 메소드 호출

 

⇒ Spring이 실행될 때 모든 의존성 객체를 다 만들어주고, 필요한 곳에 주입시켜 준다.

 

이 의존성 객체를 Bean이라고 하는데, Bean들은 싱글톤 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤하는 것이 아니라 Spring에게 맡겨 작업을 처리하게 된다.

DI : Dependency Injection

Spring이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능이다.

객체를 직접 생성하는게 아니라 외부에서 생성한 후 주입시켜주는 방식이다.

이를 통해 모듈 간의 결합도가 낮아지고, 유연성이 높아진다.

DI 유형

1. Setter Injection
의존성을 입력 받는 setter 메서드를 만들고 이를 통해서 의존성을 주입한다.

@Component
public class SampleController {
	private SampleRepository sampleRepository;

	@Autowired // 있어도 되고, 없어도 됨
	public void setSampleRepository(SampleRepository sampleRepository) {
		this.sampleRepository = sampleRepository;
	}
}

2. Contructor Injection
필요한 의존성을 포함하는 클래스의 생성자를 만들고 이를 통해 의존성을 주입한다.

@Component
public class SampleController {
private SampleRepository sampleRepository;

	public SampleController(SampleRepository sampleRepository) {
		this.sampleRepository = sampleRepository;
	}
}

3. Field Injection
의존성을 주입할 변수에 @Autowired 어노테이션을 붙인다.

@Component
public class SampleController {
	@Autowired
	private SampleRepository sampleRepository;
}

이외에도 Lombok 라이브러리를 사용해 @AllArgsConstructor, @RequiredArgsConstructor 어노테이션을 통해 자동 생성자 메소드를 사용할 수 있다.

 

특히 @RequiredArgsConstructor의 경우, 의존성을 주입받을 객체가 private final로 선언되어 생성 시점에 딱 한번만 주입받을 수 있도록 하기 때문에 불필요한 변경점이 발생하지 않아 안전하다.

 

Spring Framework Reference에서 권장하는 방법은 생성자를 통한 주입이다. 이는 필수적으로 사용해야하는 의존성 없이는 인스턴스를 만들지 못하도록 강제할 수 있는 방법이다.

 

하지만 두 클래스 간에 순환 참조 관계가 생기면 생성자 주입으로는 어떤 인스턴스도 생성할 수 없기 때문에, 가장 좋은 방법은 순환 참조를 피하는 것이고, 어쩔 수 없다면 다른 의존성 주입 방법을 사용해서 해결할 수 있다.

Bean

Spring Bean Container에서 생성되는 객체를 의미한다. Bean container는 의존성 주입을 통해 Bean 객체를 사용할 수 있도록 해준다.

Bean은 우리가 컨테이너에 공급하는 설정 메타 데이터(XML 파일) 혹은 Java 설정파일에 의해 생성된다. 최근 추세는 자바 설정파일을 좀 더 많이 사용한다고 한다.

 

-  Bean Factory : Bean을 관리하는 컨테이너

-  Application Context : Bean Factory에 여러가지 컨테이너 기능을 추가한 컨테이너

 

Bean은 new 연산자로 생성된 객체가 아니고, ApplicationContext.getBean() 메소드로 얻는 객체를 말한다.

Spring Container에 Bean 생성 방법

1. Component Scan

@ComponentScan이 붙은 위치에서 자동으로 하위 클래스들을 스캔하면서 @Component, stereotype(@Service, @Controller, @Repository 등) 어노테이션이 부여된 class를 찾아 자동으로 Bean으로 등록해준다.

@SpringBootApplication 내부에 포함되어 있다.

@Service, @Controller, @Repository 내부에도 @Component 가 있다.

추가적으로 Bean으로 등록하고 싶은 class가 있으면 @Component 어노테이션을 붙여주면 된다.

 

2. @Configuration에서 @Bean으로 등록(Java 설정 파일)

일반적으로 xxxxConfiguration과 같은 네이밍으로 Java 클래스를 생성해 작성한다.

@Configuration 어노테이션을 사용해서 직접 @Bean으로 등록해준다.

@Bean 어노테이션을 사용하면 자동으로 Bean으로 등록된다.

@Configuration // 내부에 @Component 가 붙어 있다.
public class HttpConfig {
  @Bean
  public RestTemplate createRestTemplate(){
    return new RestTemplate(); // 리턴되는 객체가 스프링 IoC 컨테이너 안에 빈으로 등록
  }
}

 

3. AOP : Aspect Oriented Programming

관점 지향 프로그래밍
어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고, 그 관점을 기준으로 모듈화하겠다는 의미

 

예를 들어 핵심적인 관점을 비즈니스 로직이 될 수 있고, 부가적인 관점은 핵심 로직을 실행하기 위해 행해지는 데이터베이스 연결, 로깅, 파일 입출력 등이 될 수 있다.

 

AOP는 흩어진 관심사를 모듈화 할 수 있는 프로그래밍 기법이다.

[출처][Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란? (tistory.com)

 

소스 코드 상에서 다른 부분에 계속 반복해서 사용되는 코드들을 발견할 수 있는데 이것을 흩어진 관심사(Crosscutting Concerns)라고 부른다. 위 그림과 같이 여러 클래스에 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 취지이다.

AOP 관련 용어

용어 설명
Aspect  흩어진 관심사를 모듈화한 것
Target  Aspect를 적용하는 곳
Advice 실질적으로 어떤 일을 해야할지에 대한 것. 실질적인 부가 기능을 담은 구현체
Join Point Advice가 적용될 위치 혹은 끼어들 수 있는 시점. 메소드 진입 시점, 생성자 호출 시점, 필드에서 꺼내올 시점 등 끼어들 시점을 의미.

참고로 Spring에서 Join Point는 언제나 메소드 실행 시점을 의미한다.
Point Cut Join Point의 상세한 스펙을 정의한 것. "A란 메소드의 진입 시점에서 호출할 것"처럼 구체적으로 Advice가 실행될 시점을 정한다.

AOP 적용 방법

1. 컴파일 타임 적용

컴파일 시점에 바이트 코드를 조작하여 AOP가 적용된 바이트 코드를 생성하는 방법

 

2. 로드 타임 적용

순수하게 컴파일한 뒤, 클래스를 로딩하는 시점에서 클래스 정보를 변경하는 방법

 

3. 런타임 적용

스프링 AOP가 주로 사용하는 방법으로, A라는 클래스 타입의 Bean을 만들 때 A 타입의 Proxy Bean을 만들어 Proxy Bean이 Aspect 코드를 추가하여 동작하는 방법

Spring AOP

-  Spring에서 제공하는 Spring AOP는 Proxy 기반의 AOP 구현체이다.

-  Proxy 객체를 사용하는 것은 접근제어 및 부가 기능을 추가하기 위해서이다.

-  Spring AOP는 Spring Bean에만 적용할 수 있다.

-  모든 AOP 기능을 제공하는 것이 목적이 아닌, 중복 코드, 프록시 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위한 솔루션을 제공하는 것이 목적이다.

-  Spring AOP는 순수 Java로 구현되었기 때문에 특별한 컴파일 과정이 필요하지 않다.

 

[출처][Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란? (tistory.com) : Proxy 패턴

프록시 패턴에서는 interface가 존재하고 Client는 이 interface 타입으로 Proxy 객체를 사용한다. Proxy 객체는 기존의 타겟 객체(Real Subject)를 참조한다. Proxy 객체와 기존의 타겟 객체의 타입은 같고, Proxy는 원래 할 일을 가지고 있는 Real Subject를 감싸서 Client의 요청을 처리한다.

Spring AOP 구현

[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란? (tistory.com)

 

4. PSA : Portable Service Abstraction

환경의 변화와 관계 없이 일관된 방식의 기술로의 접근 환경을 제공하려는 추상화 구조
= 잘 만든 인터페이스

 

이는 POJO 원칙을 철저히 따른 Spring의 기능으로 Spring에서 동작할 수 있는 라이브러리들은 POJO 원칙을 지키게끔 PSA 형태의 추상화가 되어있음을 의미한다.

 

일반적인 Java 프레임워크에서 사용 가능한 라이브러리들은 Spring에서 지원하는 라이브러리와 다르다. Spring 프레임워크에서 추상화 시켜주기 때문에 개발자가 신경쓸 필요가 없다.

외부 라이브러리들은 Spring에서 사용할 때 내부 구현이 달라지더라도 동일한 인터페이스로 동일한 구동이 가능하게끔 설계되어 있으며 의존성을 고려할 필요가 없다.

Spring은 이렇듯 특정 기술에 직접적 영향을 받지 않게끔 객체를 POJO 기반으로 한번씩 더 추상화한 Layer를 갖고 있으며 이를 통해 일관성있는 서비스 추상화를 만들어낸다.

 

PSA가 적용된 코드라면 내 코드가 바뀌지 않고, 다른 기술로 간편하게 바꿀 수 있어 확장성이 좋고, 기술에 특화되어 있지 않은 코드를 의미한다.

Spring은 Spring Web MVC, Spring Transction, Spring Cache 등의 다양한 PSA를 제공한다.

 

출처

만렙 개발자 키우기 (nowwatersblog.com)

만렙 개발자 키우기 (nowwatersblog.com)

[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란? (tistory.com)

[Spring] 스프링 AOP (Spring AOP) 총정리 : 개념, 프록시 기반 AOP, @AOP (tistory.com)

Spring PSA(Portable Service Abstraction)의 개념 :: Jins' Dev Inside (tistory.com)

[Spring] PSA(Portable Service Abstraction)란? (tistory.com)

 

 

 

'1 week 1 conquer > Spring' 카테고리의 다른 글

[Spring] MVC 패턴  (0) 2023.04.20
Comments