반응형
해당 글에서는 Spring Framework의 주요 특징인 DI, IoC, POJO, AOP 대해서 알아봅니다.
1) Spring Framework와 Spring Boot의 관계
💡 Spring Framework와 Spring Boot의 관계
- Spring Boot는 Spring Framework 위에 구축되어 Spring 애플리케이션을 생성하고 구성하는 간소화된 방법을 제공합니다. 즉, Spring Framework 기반으로 Spring Boot가 수행되는 프레임워크를 의미합니다.
- Spring Boot 자동 구성과 주관적인 기본 설정을 제공하여 개발자가 보일러플레이트 코드 대신 비즈니스 로직 작성에 집중할 수 있도록 개발 과정을 간소화하는 것을 목표로 합니다.
[ 더 알아보기 ]
💡 Spring Framework의 기능을 Spring Boot 내에서 모두 사용할 수 있는가?
- Spring Framework의 기능은 Spring Boot에서 모두 사용할 수 있습니다.
- Spring Boot는 Spring Framework를 기반으로 하며, 추가적인 기능과 개발 생산성을 높이기 위한 설정을 제공합니다.
💡 보일러플레이트 코드(Boilerplate code)
- 반복적이고 일반적으로 발생하는 작업을 수행하는 데 사용되는 초기 코드 또는 템플릿 코드입니다. 이러한 코드는 일반적으로 데이터베이스 연결, 로깅, 예외 처리 등과 같은 작업에 사용됩니다.
- 보일러플레이트 코드는 코드의 재사용성과 유지 보수성을 향상하는 데 도움이 됩니다.
// 보일러플레이트 코드 예시
public class Main {
public static void main(String[] args) {
// 데이터베이스 연결
DatabaseConnection.connect();
// 로깅
Logger.log("Application started");
try {
// 예외 처리
// 코드 실행
} catch (Exception e) {
// 예외 처리
}
// 데이터베이스 연결 해제
DatabaseConnection.disconnect();
}
}
2) 제어의 역전 :IOC(Inversion of Control)
💡 제어의 역전 :IOC(Inversion of Control)
- 소프트웨어 디자인 패턴 중 하나로 제어 흐름이 일반적인 프로그램의 흐름과는 반대로 ‘제어의 주체가 역전’되는 것을 의미합니다.
- 일반적으로 프로그램은 ‘사용자 또는 개발자’가 직접 제어 흐름을 조작하고 결정합니다. 하지만 IOC 패턴을 사용하면 제어 흐름의 결정 권한이 Spring 컨테이너에게 객체 생성 및 관리(객체 간의 의존성)를 위임할 수 있습니다.
[ 더 알아보기 ]
💡 그래서 어떤 제어를 하는 건가?
- ‘컨테이너가 의존성을 주입하고 객체의 라이프사이클을 관리’함으로써 흐름을 관리하는 제어를 수행합니다
💡 제어 흐름의 결정 권한이 ‘프레임워크나 컨테이너’에 있다면 무엇이 좋은가?
1. 객체 간의 결합도를 낮출 수 있습니다.
2. 유연한 코드를 작성할 수 있습니다.
3. 테스트 용이성을 향상할 수 있습니다.
1. 컨테이너(Container), IOC 컨테이너(Inversion of Control Container), 스프링 컨테이너(Spring Container)
💡 컨테이너(Container)
- 객체의 인스턴스를 생성하고 관리하는 역할을 합니다. 또한 필요한 객체를 생성하고 객체 간의 의존성을 주입하는 등의 작업을 수행합니다.
💡IOC 컨테이너(Inversion of Control Container)
- 컨테이너의 일종으로, 제어의 역전 개념을 기반으로 동작합니다. 또한 애플리케이션의 ‘제어 흐름’을 관리하며, ‘객체의 생성과 의존성 주입’을 자동으로 처리합니다.
- 이를 통해 애플리케이션의 코드는 객체 생성에 집중하지 않고, 의존성 주입을 통해 객체를 사용할 수 있게 됩니다.
💡스프링 컨테이너(Spring Container)
- 스프링 프레임워크에서 제공하는 IOC 컨테이너의 구현체 중 하나입니다. 스프링 컨테이너는 POJO의 생성과 의존성 주입을 담당하며, 스프링 프레임워크에서 제공하는 다양한 기능과 모듈을 활용하여 애플리케이션을 개발할 수 있도록 도와줍니다
분류 | 컨테이너 (Container) |
IOC 컨테이너 (Inversion of Control Container) |
스프링 컨테이너 (Spring Container) |
정의 | 소프트웨어 컴포넌트를 저장, 관리 및 실행하는 환경 | 컴포넌트의 생명주기와 의존성 관리를 자동으로 처리하는 컨테이너 | 스프링 프레임워크에서 제공하는 IOC 컨테이너 구현체 |
주요 기능 | 컴포넌트 인스턴스화 및 관리 | 객체의 생성, 의존성 주입, 설정 및 관리 | 객체의 생성, 의존성 주입, 설정 및 관리 |
의존성 관리 | 수동으로 수행해야 함 | 자동으로 수행됨 | 자동으로 수행됨 |
확장성 | 제한적 | 높음 | 높음 |
사용 예시 | Java EE, .NET 등에서 사용 | Spring Framework 등에서 사용 | Spring Framework 등에서 사용 |
2. IOC 컨테이너의 흐름
💡 IOC 컨테이너의 흐름
1. Configuration Meta(Annotation-based, Java-based, XML-based) → Spring IoC Container
- IOC 컨테이너에게 ‘POJO의 생성 및 의존성 주입에 대한 정보’를 제공합니다.(Annotation-based, Java-based, XML-based)
- 이 정보를 기반으로 IOC 컨테이너는 POJO를 생성하고 필요한 의존성을 주입하여 객체 간의 관계를 설정합니다.
- IOC 컨테이너가 POJO의 라이프사이클을 관리하고 의존성을 처리하는 데 도움을 줍니다.
2. Business Objects(POJOs) → Spring IoC Container
- IOC 컨테이너는 POJO를 관리하고 필요한 ‘의존성을 주입’하여 ‘객체 간의 관계’를 설정합니다.
- 이를 통해 POJO는 의존성을 직접 관리하지 않고 느슨한 결합도를 갖는 구조를 유지할 수 있습니다.
3. Spring IoC Container → Fully Configured Application Ready For Use
- Configuration Metadata를 통해 POJO의 생성 및 의존성 주입에 대한 정보를 받은 뒤, POJO에서는 의존성을 주입하고 객체 간의 관계를 설정합니다.
- 이렇게 구성된 설정에 따라 애플리케이션의 구성을 완전히 설정합니다. 즉 애플리케이션을 구성하고 사용할 수 있는 상태로 만들어주는 역할을 합니다.
3. Bean, BeanFactory, ApplicationContext
💡 Bean
- 스프링 프레임워크에서 관리되는 ‘객체’를 의미합니다. 이러한 객체는 클래스 기반으로 생성하고 ‘빈 컨테이너’에 의해 생성, 관리, 검색됩니다.
- 빈은 애플리케이션에서 사용되는 주요한 구성 요소로 여러 기능을 수행하고 다른 빈들과 협력합니다.
- 빈은 XML, Java Config, Anntation을 기반으로 Bean 설정하고 구성할 수 있습니다. XML을 기반으로 기존의 빈의 속성과 의존성을 정의하며 어노테이션을 이용하여 Java 클래스에 직접 어노테이션을 작성하여 빈을 설정할 수 있습니다.
💡 BeanDefinition
- 스프링 프레임워크에서 빈(bean)의 정의를 나타내는 구성 메타데이터입니다. 이는 빈의 클래스, 범위, 라이프사이클 콜백, 의존성 및 기타 구성 세부 정보에 대한 정보를 제공합니다.
- 스프링 컨테이너가 제공된 구성에 따라 빈 인스턴스를 생성하고 관리하는 데 사용됩니다. 이는 런타임에서 빈 객체를 생성하기 위한 역할을 합니다.
💡 BeanFactory
- 스프링의 핵심 인터페이스로, 빈의 생성, 관리, 검색 등을 담당합니다.
- 빈을 등록하고 필요한 시점에 빈을 가져와서 사용할 수 있습니다. 빈의 지연 로딩을 지원하고, XML 또는 애노테이션 기반으로 빈을 설정할 수 있습니다.
💡 ApplicationContext
- BeanFactory의 하위 인터페이스로, 빈 컨테이너의 기능을 확장한 것입니다.
- BeanFactory의 모든 기능을 포함하며, 추가적인 기능을 제공합니다. 예를 들어, ApplicationContext는 메시지 리소스 번들, 이벤트 발행 및 처리, AOP(Aspect-Oriented Programming) 등의 기능을 지원합니다.
- 또한 ApplicationContext는 다양한 형식으로 빈을 설정할 수 있습니다. XML, 애노테이션, 자바 설정 클래스 등을 사용하여 빈을 정의하고 관리할 수 있습니다.
[ 더 알아보기 ]
💡 BeanFactory, ApplicationContext는 서로 무슨 관계인가?
- BeanFactory는 스프링의 핵심적인 역할을 담당하며, ApplicationContext는 BeanFactory의 확장된 기능을 제공하여 더 편리한 개발 환경을 제공합니다.
3) 의존성 검색 : DL(Dependency Lookup) & 의존성 주입 :DI(Dependency Injection)
1. 의존성 검색 : DL(Dependency Lookup)
💡 의존성 검색 : DL(Dependency Lookup)
- 객체가 직접 ‘의존하는 객체를 검색하고 반환받는 방식’을 의미합니다.
- 의존성 조회는 객체가 필요로 하는 의존성을 직접 알고 있어야 하며, 의존하는 객체가 변경되면 코드를 수정해야 합니다.
💡 사용 예시
1. ApplicationContext 인터페이스를 구현한 ClassPathXmlApplicationContext 클래스의 인스턴스인 context를 생성합니다.
2. context를 사용하여 myBean이라는 이름의 빈을 가져옵니다. getBean 메서드는 빈의 이름을 인자로 받고, 해당하는 빈 객체를 반환합니다. 이때, 반환되는 객체는 Object 타입이므로 캐스팅을 통해 MyBean 타입으로 사용할 수 있습니다.
3. myBean 객체를 사용하여 doSomething 메서드를 호출합니다. 이는 MyBean 클래스에 구현된 메서드입니다.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// DL을 사용하여 Bean을 가져오기
MyBean myBean = (MyBean) context.getBean("myBean");
// Bean 사용
myBean.doSomething();
}
}
2. 의존성 주입 :DI(Dependency Injection)
💡 의존성 주입 :DI(Dependency Injection)
- 소프트웨어 디자인 패턴 중 하나로 컴포넌트 간 ‘의존성을 외부에서 주입’하는 것을 의미합니다. 이를 통해 직접 의존하는 객체를 생성하거나 관리하지 않고 의존성을 주입받아 사용할 수 있습니다.
- 의존성 주입을 사용하면 컴포넌트 간의 결합도를 낮추고 테스트 용이성, 재사용성, 유지보수성을 향상합니다.
2.1. 생성자 주입(Constructor Injection)
💡 생성자 주입(Constructor Injection)
- 종속성을 ‘클래스의 생성자’를 통해 의존성을 주입하는 방법을 의미합니다.
- 해당 주입 방식은 의존성을 인스턴스화하는 동안에만 사용이 가능합니다.
- 클래스가 필요로 하는 모든 종속성을 생성자 매개변수로 명시적으로 선언하여 의존성을 주입하는 방식입니다. 이를 통해 의존성을 명확하게 정의하고 클래스의 결합도를 낮추어 유연하고 테스트 가능한 코드를 작성할 수 있습니다.
💡 예시 설명
- 해당 UserService 클래스에서는 UserRepository라는 종속성을 필요로 합니다.
1. 종속성을 위해 생성자 매개변수로 받아와서 클래스 내부에서만 사용됩니다.
2. 이러한 생성자 주입을 사용하여 UserService 클래스는 UserRepository에 직접적으로 의존하게 되며, 이를 통해 클래스 간의 결합도를 낮출 수 있습니다
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 다른 메서드들...
}
[ 더 알아보기 ]
💡 인스턴스화
- 클래스를 기반으로 객체를 생성하는 과정을 말합니다. 클래스는 객체의 설계도이며, 인스턴스는 클래스를 기반으로 실제로 생성된 객체를 의미합니다.
ex) Person person = new Person();로 객체를 구성하는 과정 하며 객체를 할당하는 과정입니다.
2.2. 필드 주입(Field Injection)
💡 필드 주입(Field Injection)
- 종속성을 ‘클래스의 필드’에 직접 의존성을 주입하는 방법을 의미합니다.
- 의존성을 클래스의 속성으로 선언하고, 해당 속성에 직접적으로 의존성을 할당하는 방식으로 동작합니다. 이는 생성자 주입과는 달리 생성자 매개변수를 통해 의존성을 주입하지 않고, 클래스의 필드에 직접적으로 주입하는 차이점이 있습니다.
💡 사용 예시
- @Autowired 어노테이션을 사용하여 의존성 주입을 수행하는 방법을 보여줍니다.
- UserService 클래스에서의 필드 주입은 UserRepository를 필드로 선언하고 @Autowired 어노테이션을 붙여 의존성을 주입하는 방식입니다.
public class UserService {
@Autowired
private UserRepository userRepository;
// ...
}
2.3. 수정자 주입(Setter Injection)
💡 수정자 주입(Setter Injection)
- 종속성을 ‘클래스의 Setter 메서드’를 통해 의존성을 주입하는 방법을 의미합니다.
- Setter 주입은 클래스의 속성을 의존성으로 선언하고, 해당 속성에 Setter 메서드를 통해 의존성을 주입하는 방식으로 동작합니다.
생성자 주입과는 달리 생성자 매개변수를 통해 의존성을 주입하지 않고, 클래스의 Setter 메서드를 통해 주입하는 차이점이 있습니다.
💡 사용예시
- 수정자 주입을 사용하여 의존성 주입을 수행하는 방법을 보여줍니다
- OrderService 클래스에서 수정자 주입은 setPaymentService 메서드를 정의하고 @Autowired 어노테이션을 붙여 의존성을 주입하는 방식입니다. 이를 통해 PaymentService를 주입하여 사용할 수 있습니다.
public class UserService {
private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 다른 메서드들...
}
2.4. 각각의 주입방식에 대한 비교
주입 방법 | 설명 | 장점 | 단점 | 사용빈도 |
생성자 주입 | 객체를 생성할 때 생성자를 통해 의존성을 주입하는 방법입니다. | 코드의 일관성과 안정성을 제공합니다. 객체 생성 이후에는 변경할 수 없습니다. | 생성자의 매개변수가 많아질 경우 코드의 가독성이 떨어질 수 있습니다. | 상 |
필드 주입 | 의존성을 클래스의 필드에 직접 주입하는 방법입니다. | 코드의 간결성을 유지할 수 있습니다. | 테스트하기 어렵고, 외부에서 주입된 의존성을 변경할 수 있습니다. | 중 |
수정자 주입 | 의존성을 설정 메서드(Setter 메서드)를 통해 주입하는 방법입니다. | 유연성이 있고, 선택적으로 의존성을 주입할 수 있습니다. | 객체 생성 이후에도 의존성을 변경할 수 있습니다. 수정자 메서드를 호출하지 않을 경우 의존성이 설정되지 않을 수 있습니다. | 하 |
[ 더 알아보기 ]
💡 IntelliJ - Field Injection is not recommended
- 해당 필드 주입 방식은 아래와 같은 이유로 추천을 하지 않고 있습니다.
1. 테스트 용이성 감소: 필드 주입은 주입된 의존성을 가진 클래스에 대한 단위 테스트 작성을 어렵게 만듭니다. 테스트 중에 주입된 의존성을 목 객체로 대체하거나 모의 객체로 대체하기 어려울 수 있습니다.
2. 숨겨진 의존성: 필드 주입을 사용하면 의존성이 생성자나 메서드 매개변수에 명시적으로 선언되지 않습니다. 이로 인해 클래스의 의존성을 이해하기 어려워지며, 숨겨진 의존성이 발생할 수 있습니다.
3. 강한 결합: 필드 주입은 클래스와 해당 의존성 간에 강한 결합을 만듭니다. 이로 인해 코드베이스가 유연성이 떨어지고 유지보수가 어려워질 수 있습니다. 의존성에 변경이 발생하면 해당 의존성을 사용하는 클래스를 수정해야 할 수도 있습니다.
4. 제어 부족: 필드 주입은 의존성의 수명 주기를 제어하는 데 제한적인 제어를 제공합니다. 의존성의 생성과 소멸을 관리하기 어려워지며, 이로 인해 메모리 누수나 리소스 관리 문제가 발생할 수 있습니다.
Why Is Field Injection Not Recommended? | Baeldung
[ 더 알아보기 ]
💡 그럼 어떤 방식으로 의존성 주입을 하는 것이 좋을까?
- IntelliJ 추천으로는 필드 의존성 주입보다는 ‘생성자 의존성 주입’ 방식을 추천해주고 있습니다.
4) Plain Old Java Object (POJO)
💡 Plain Old Java Object (POJO)
- 오래된 방식의 자바 오브젝트라는 말로 프레임워크에 종속된 ‘무거운’ 객체를 만들게 된 것에 대해 반발해서 사용되는 용어를 의미합니다.
- 데이터를 캡슐화하고 해당 데이터에 대한 접근 및 수정을 위한 getter 및 setter 메서드를 제공하는 간단한 Java 클래스를 지칭합니다.
- 애플리케이션에서 데이터 객체를 표현하는 데 사용되며, Hibernate나 Spring과 같은 프레임워크와 함께 자주 사용됩니다.
💡 사용예시
- Person 클래스는 데이터를 캡슐화하고 name과 age에 대한 접근 및 수정을 위한 getter와 setter 메서드를 제공합니다
- 이 클래스는 POJO의 특징을 따르며, 외부 라이브러리나 프레임워크에 대한 의존성이 없는 간단한 Java 클래스입니다. 이 클래스를 사용하면 애플리케이션에서 사람에 대한 데이터를 쉽게 표현하고 조작할 수 있습니다.
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
5) AOP(Aspect-Oriented Programming)
💡 Spring AOP란?
- 스프링 프레임워크에서 제공하는 기능 중 하나로 관점 지향 프로그래밍을 지원하는 기술입니다. Spring AOP는 로깅, 보안, 트랜잭션 관리 등과 같은 공통적인 관심사를 모듈화 하여 코드 중복을 줄이고 유지 보수성을 향상하는데 도움을 줍니다.
💡 관점 지향 프로그래밍(Aspect-Oriented Programming, AOP) 이란?
- 객체 지향 프로그래밍 패러다임을 보완하는 기술로 메서드나 객체의 기능을 핵심 관심사(Core Concern)와 공통 관심사(Cross-cutting Concern)로 나누어 프로그래밍하는 것을 말합니다. “핵심 관심사”는 각 객체가 가져야 할 본래의 기능이며, “공통 관심사”는 여러 객체에서 공통적으로 사용되는 코드를 말합니다.
- 여러 개의 클래스에서 반복해서 사용하는 코드가 있다면 해당 코드를 모듈화 하여 공통 관심사로 분리합니다. 이렇게 분리한 공통 관심사를 Aspect로 정의하고 Aspect를 적용할 메서드나 클래스에 Advice를 적용하여 공통 관심사와 핵심 관심사를 분리할 수 있습니다. 이렇게 AOP에서는 공통 관심사를 별도의 모듈로 분리하여 관리하며, 이를 통해 코드의 재사용성과 유지 보수성을 높일 수 있습니다.
1. AOP 상세히 알아보기
💡 관점 지향 프로그래밍의 정의를 보면 핵심 관심사와 공통 관심사를 분리하여 프로그래밍하는 것을 의미합니다.
💡 위에 이미지와 같이 3개의 A, B, C의 클래스가 있다고 가정합니다.
- 클래스 A에서는 주황, 파랑, 빨간색 블록으로 구성이 되어 있고 클래스 B에서는 빨강, 주황 블록으로 구성되어 있으며 클래스 C에서는 주황, 파랑 블록으로 구성되어 있습니다. 해당 색은 클래스 A, B, C에서 동일하게 사용되는 코드를 의미합니다.
- 예를 들어서 클래스 A에서 주황색 블록을 수정을 하게 되면 클래스 B, C에서도 수정을 해야 합니다. 이렇게 되면 유지보수 차원에서 모든 코드를 수정해야 하니 불편한 점이 있습니다. 그래서 Aspect X에서는 공통 관심사인 주황색 블록을 묶어서 모듈화를 시켜서 코드의 재 사용성과 유지 보수성을 강화하였습니다. 이렇듯, 관점 지향 프로그래밍에서는 소스코드에서 반복적으로 사용하는 코드를 하나로 묶어서 모듈화 하여 재사용성과 유지 보수성을 높일 수 있는 강점을 가지고 있습니다.
💡 [참고] 더 상세한 AOP에 대해 궁금하시면 아래의 글이 도움이 됩니다.
오늘도 감사합니다. 😀
반응형
'Java > 아키텍처 & 디자인 패턴' 카테고리의 다른 글
[Java] 람다식(Lambda Expression), 함수형 인터페이스(Functional Interface) 이해하기 (1) | 2023.12.05 |
---|---|
[Java/디자인패턴] 싱글턴 패턴(Signleton Pattern) 이해하기 -1 : 정의 및 종류 (1) | 2023.12.02 |
[Java] MacOS 환경에서 Java JDK 설정 및 변경하기 : homebrew, 다운로드 파일 (2) | 2023.06.28 |
[Java] Gradle 버전 확인 및 변경 방법 (2) | 2023.06.27 |
[Java] 개발 환경에 따라 각각 환경 파일 구성 방법: application.properties (0) | 2023.06.07 |