Spring Guide 시리즈는
스프링부트 핵심 가이드 - 스프링 부트를 활용한 애플리케이션 개발 실무
책을 통해 학습 및 정리한 내용을 담고 있습니다.
Spring Framework
스프링 부트 이전에, 스프링은 자바 기반의 어플리케이션 프레임워크이다. 스프링에는 목적에 따라 다양한 프로젝트가 존재하는데, 그 중 하나가 스프링 부트다. 쉽게 말해 스프링 부트는 자바로 특정 목적을 위한 어플리케이션을 쉽게 제작할 수 있도록 도와주는 친구라고 할 수 있다. 기존에 사용하던 Node.js & Express
와는 다른 구조와 특징을 가지고 있는데, 새로웠던 점들을 위주로 정리해보자.
특징과 구조
제어 역전 (IoC)
자바를 공부한다면 Class를 공부하지 않을 수가 없는데, 스프링에서는 이 Class를 보다 쉽게 다룰 수 있도록 도와준다. 일례로, JSP를 통해 프로젝트를 진행해보며, Class를 생성 및 사용할 일이 생겼다.
1
private final AService instance = new AService();
특정 객체를 사용하기 위해서 위와 같이 코드를 작성하였는데, 문제는 내가 직접 객체를 생성 및 사용하다보니 그 수가 늘어날 수록 관리하기가 어려워지는 경우가 생길 수 있었다.
스프링에서는 제어 역전 (IoC)
라는 특징을 가지고 있다. 덕분에 기존 자바를 이용해 객체를 생성, 사용하는 방식이 다르게 작동한다. 무려 객체를 직접 생성하지 않고 스프링 컨테이너
혹은 IoC 컨테이너
에게 객체의 모든 생명주기를 맡기면 되는 것이다.
이처럼 내가 직접 객체를 관리하던 것에서, 컨테이너에게 제어권을 넘겼기 때문에 제어 역전
이라고 부른다. 요 특징 덕분에 스프링은 뒤에 언급할 의존성 주입
이라던가, AOP
등이 가능해진다.
의존성 주입 (DI)
의존성 주입은 앞서 말한 IoC의 방법 중 하나이다. 내가 사용할 객체를 직접 생성하는 것이 아니라 외부 컨테이너가 생성해둔 객체를 그대로 주입받아서 사용하는 방법이다.
의존성 주입을 하는 방법에도 무려 3가지가 있다.
생성자를 이용해보자.
1
2
3
4
5
6
7
8
9
10
public class SomeClass {
AService aService;
@Autowired // 의존성 주입을 위한 어노테이션
public SomeClass(AService aService) {
this.aService = aService;
}
...
}
생성자를 통해 의존 주입을 하는 경우에는 레퍼런스 객체가 없으면 객체 자체를 초기화 할 수 없다는 이유로 Spring 공식 문서에서 권장하고 있다. 또한
@Autowired
라는 어노테이션을 생략할 수 있으나 가독성 측면에서 붙이는 것도 권장한다.
필드 객체를 선언해보자.
1
2
3
4
5
6
7
public class SomeClass {
@Autowired
private AService aService;
...
}
Setter를 사용해보자.
1
2
3
4
5
6
7
8
9
public class SomeClass {
AService aService;
public void setAService(AService aService) {
this.aService = aService;
}
}
관점 지향 프로그래밍 (AOP)
어떤 서비스 로직을 만든다는 상상을 해보도록 하자. 편의를 위해 함수 호출의 형태로 작성된 부분을 어떤 복잡한 자바 로직이라고 가정해보자.
1
2
3
4
5
6
7
8
loggingStart();
...
validateData();
saveData();
returnData();
...
loggingEnd();
우리는 여기서 관점을 핵심 기능과 부가 기능으로 나누어 볼 것이다. 여기서 핵심 기능은 validateData()
, saveData()
, returnData()
같은 특정 비즈니스 로직이 가지는 목적을 정확히 해내는 친구들을 말하고, 부가 기능은 그 외에 친구들(로그를 확인한다거나, 트랜잭션을 처리하는 등)이 해당한다.
조금 더 보기 쉽게 정리하면, 기본적으로 객체 지향 프로그래밍 방식에서는 아래와 같은 모습으로 동작의 흐름이 이어진다.
sequenceDiagram
autonumber
Service-->Repository: Logging Start
Service->>Repository: 핵심 로직 1
Repository->>Service: 핵심 로직 2
Service->>Repository: 핵심 로직 3
Service-->Repository: Logging End
위 다이어그램에서 점선으로 표기된 부분이 부가 기능에 해당한다. 이런 부가 기능이 하나의 비즈니스 로직에만 들어가는 것이 아니라, 우리가 앞으로 작성할 모든 비즈니스 로직에 똑같이 포함할 부가 기능이라면 코드의 중복이 늘어갈 것이다. 또한 A 작업
을 하는 비즈니스 로직은 A 작업
만을 수행하도록 하는 것이 바람직하기에 이러한 부가 기능을 담당하는 코드는 별도로 관리할 필요가 있다.
이렇게 관점에 따라 코드를 나누고, 반복적으로 등장하는 부가 기능을 하나의 공통 로직으로 모듈화하여 삽입/처리 하는 방식이 AOP이다. 중요한 것은, AOP가 재사용 가능한 코드를 만들어 반복적인 코드를 줄이고 우리가 핵심 비즈니스 로직에만 집중할 수 있도록 도와주는 목적을 가지고 있다는 것이다.
DI를 하는 방법에 3가지가 있던 것처럼 AOP를 구현하는 방법에도 3가지가 존재하지만, 스프링에서는
프록시 패턴
을 이용하는 방식을 채택했다.