IoC, DI, AOP는 스프링 프레임워크의 특징이며, 스프링 프레임워크의 구조를 이해하기 위해 반드시 알아야 하는 중요 개념이라고 할 수 있다.
IoC
IoC(Inversion of Control)란 우리말로 '제어의 역행'이라고 한다. 기존의 프로그램은 main()처럼 프로그램이 시작되는 곳에서 필요한 객체를 생성하고, 생성된 객체의 메서드를 호출하는 흐름을 가진다. 이와 달리 IoC는 작업을 수행하는 쪽에서 객체를 생성하는 일반적인 개념을 뒤집은 것으로, 프로그램 제어를 자신이 아닌 다를 곳에 위임하는 것을 말한다. 즉 컨테이너에 객체 생성과 공급을 위임하는 형태다.
IoC의 특징
- IoC에서는 객체가 자신이 사용할 객체를 생성하거나 선택하지 않는다.
- 객체는 자신이 어떻게 생성되고 어떻게 사용되는지 알 수 없다.
- 모든 객체는 제어 권한을 위임받은 특별한 객체(컨테이너)에 의해 만들어지고 사용된다.
스프링의 경우 스프링 컨테이너에서 객체를 생성하고 공급하는 역할을 담당한다. 또한 스프링 빈(Spring Bean)은 스프링 컨테이너에 의해 관리되는 스프링 객체를 말하는 것으로, 기본적으로 XML 기반의 설정 파일을 통해 객체와 관계를 정의하지만 지금은 주로 어너테이션을 통해 관련 설정을 한다.
DI
IoC를 시스템적으로 구현하는 방법으로 DI(Dependency Injection)와 DL(Dependency Lookup)이 있다. DI는 우리말로 의존성 주입이라 불리며, 클래스 간의 의존관계를 Bean 설정에 기반해 컨테이너가 자동으로 연결해주는 방식이다. 반면 DL은 저장소에 저장되어 있는 Bean에 접근하기 위해 개발자가 컨테이너에서 제공하는 API를 이용해 Bean을 찾는 방식이다.
따라서 DL은 컨테이너에 대한 의존성이 커지고 불필요한 코드 사용이 증가하므로 DI 방식이 좋다고 할 수 있다. 스프링 프레임워크에서는 기본적으로 DI 방식을 사용한다.
AOP
AOP(Aspect-Oriented Programming)는 관점 지향 프로그래밍이라고도 하며 스프링 프레임워크의 핵심요소 중 하나다. 제대로 내용을 이해하고 활용하려면 꽤나 많은 학습이 필요하며 생소한 용어도 다수 등장한다. 따라서 AOP 자체를 학습하는 데 많은 시간을 소요하기보다는 핵심 개념을 정리하고 스프링 프레임워크 등을 활용해 실제 구현에 하나씩 적용해가면서 이해도를 높이는 것이 좋다.
관점 지향 프로그래밍이란 횡단 관심사(Cross-cutting concern)의 분리를 허용함으로써 모듈성을 증가시키는 것이 목적이다. 따라서 코드 자체를 수정하지 않는 대신 기존 코드 기존 코드에 추가 동작인 어드바이스(Advise)를 정의하여 추가된 기능이 실행된다. 어느 코드가 포인트컷(Pointcut)(예를 들어 특정 이름으로 시작하는 메서드)을 통해 수정되는지를 별도로 지정하는 구조를 가지고 있다.
횡단 관심사란 여러 프로그램에 흩어져서 존재하는 공통 기능으로 이해할 수 있다. 꼭 필요한 기능인데 해당 기능을 사용하기 위해 동일하거나 비슷한 코드가 기계적으로 특정 위치에 들어가는 것이 해당한다. 대표적으로 인증과 로깅이 있다.
그렇다면 AOP는 실제 프로그램을 개발할 때 어떤 부분에 적용될까? 예를 들어 뉴스와 게시판 기능을 제공하는 서비스가 있다고 할 때 다음과 같이 적용할 수 있다.
- 뉴스와 게시판은 독립된 기능(모듈)이다.
- 두 모듈은 모두 사용자 인증을 통해 로그인 여부를 체크해야 한다.
- 어떤 뉴스 기사가 클릭되었는지 어떤 게시글이 클릭되었는지 로그를 기록해야 한다.
- 이때 모듈은 달라도 두 모듈은 사용자 인증과 로그를 처리하기 위한 코드가 구현되어야 한다.
- 이러한 구현의 추가는 코드에서 핵심 기능과 뒤섞이고 단순한 중복 코드를 생산한다.
- AOP는 핵심 기능과 횡단 관심사를 분리하고 코드를 일일이 구현하는 대신 어드바이스로 정의하고 코드의 특정 위치에 실행하기 위한 포인트컷을 정의한다.
AOP는 인증, 로깅, 트랜잭션 등 전체 시스템에 공통적으로 필요한 기능에 적용된다.
프로그램의 다양한 구현 방법
게임 프로그램에서 아이템 객체를 생성하고 사용하는 간단한 코드를 구현하는 다양한 방법을 살펴본다.(DI 구현 외에 다양한 구현 방법)
Java로 하는 일반적인 구현
일반 자바 프로그램 개발 방식으로 구현하는 경우다. ShotGun 클래스가 있고 Game 클래스에서는 ShotGun 클래스의 인스턴스를 만들고 fire 메서드 호출로 총을 발사하는 매우 일반적인 구조다.
class ShotGun {
void fire() { ... }
}
class Game {
public gameRun() {
ShotGun sg = new ShotGun();
sg.fire();
}
}
- 게임에 필요한 무기 중 샷건(ShotGun) 클래스를 생성한다.
- 게임 프로그램에서 필요할 때 샷건 인스턴스를 생성해 총을 발사한다.
프로그램 코드는 별다른 설명이 필요 없을 정도로 간단하고 아무런 문제가 없어 보이는데, 여기서 주의해야 할 부분은 바로 ShotGun sg = new ShotGun(); 행이다. 프로그램에서 직접 객체를 생성하기 때문에 클래스 간 종속성이 발생하는 문제가 있기 때문이다.
팩토리 패턴 구현
디자인 패턴 중 팩토리 패턴을 사용하는 방법이다. 좀 더 정밀하게 한다면 추상 클래스 형태로 정의해야 하지만 아래 코드에서는 간단한 구조를 위해 그냥 객체 생성을 위임하는 정도로 간단하게 패턴을 적용했다. ShotGun에도 속성이 다른 여러 객체가 존재할 수 있는데 객체 생성을 프로그램에서 하지 않고 팩토리 클래스를 통해 필요한 인스턴스를 얻어오는 구조다. 프로그램에서는 어떤 ShotGun의 인스턴스가 전달될지 알 필요가 없다. 즉 변화가 필요한 경우 게임 프로그램 자체는 수정하지 않고 Factory 객체의 변화를 관리하는 방식이다.
class Game {
public gameRun() {
ShotGun sg = Factory.getInstance();
sg.fire();
}
}
- Factory 클래스는 ShotGun 클래스의 인스턴스를 제공한다.
- 인스턴스는 싱글턴(Singleton)일 수도 있고 객체 풀 혹은 여러 방법으로 생성된 객체 중 하나일 수 있다.
- 객체 생성에 대한 부분이 캡슐화되지만 팩토리에 의존성이 생기고 필요에 따라 특정 객체를 요구하는 등에 대해서는 별도 구현이 필요하다.
DI 구현
순수 자바 코드로 DI 형태를 구현하는 방법이다. 게임 프로그램에 필요한 ShotGun 객체가 외부에서 공급되는 형태로 setter 메서드를 통해 필요한 객체가 외부에서 초기화되는 방식이다. setter 의존성 주입이라고도 하며 스프링에서도 사용하는 개념 중 하나다.
class Game {
private ShotGun sg;
public void setShotGun(ShotGun sg) {
this.sg = sg;
}
public gameRun() {
sg.fire();
}
}
- Game 클래스 외부에서 setShotGun() 메서드를 호출하고, 필요한 ShotGun 인스턴스를 공급한다.
- Game 클래스에서는 필요시 제공된 인스턴스를 사용하기만 하면 된다.
- 외부에서 ShotGun 객체를 제공하는 메커니즘은 별도로 구현되어야 한다.
스프링 프레임워크 구현
스프링 프레임워크를 사용하는 코드다. 위 DI 적용과 유사한 구조이지만 Annotation을 사용해 객체 생성과 주입을 처리한다. ShotGun 객체를 제공하는 메커니즘은 스프링 컨테이너가 제공한다. 아래 코드에서는 필드 의존성 주입을 사용하였지만 setter 혹은 생성자 주입 방식도 사용할 수 있다.
@Component
class ShotGun {
void fire() { ... }
}
class Game{
@Autowired
private ShotGun sg;
public gameRun() {
sg.fire();
}
}
- ShotGun 클래스에 @Component 어너테이션을 붙여 스프링 컨테이너가 해당 클래스의 인스턴스를 생성할 수 있도록 한다.
- @Autowired는 생성된 해당 클래스 타입 객체의 인스턴스를 연결한다.
'Programming > Web' 카테고리의 다른 글
[Web] JSTL(다운로드, 사용법, 정리), EL(정리) (1) | 2022.11.03 |
---|---|
[Web] WebMVC와 RestController 모듈 (0) | 2022.10.31 |
[Web] 스프링 프레임워크와 스프링 부트 (0) | 2022.10.27 |
[Web] REST 클라이언트 (0) | 2022.10.27 |
[Web] JAX-RS (0) | 2022.10.26 |