Spring Framework: "안녕하세요 Spring 입니다."

IToriginal·2023년 5월 30일
0

남정네들: Web Study

목록 보기
4/7
post-thumbnail
post-custom-banner

스프링이 어떻게 등장하게 되었고, 이전에는 어떠한 문제점이 있었는지 스프링의 등장 배경에 대해서 알아보았고, Spring의 기본 개념에 대해 알아보고자 한다.
이전 포스트: 🚀 Spring Framework: Spring의 탄생

스프링(Spring)과 스프링 부트(Spring Boot)

스프링(Spring) 이란?

스프링 프레임워크는 자바 엔터프라이즈 애플리케이션 개발을 단순하게 해주는 오픈소스 경량급 애플리케이션 프레임워크이다.

🤔 엔터프라이즈 애플리케이션 개발을 단순하게?

애플리케이션의 비즈니스 로직은 사용자의 요구사항을 해결하기 위한 실질적 코드로 스프링이 등장하기 이전에는 비즈니스 로직을 구현하기 위해 기술 자체에 대한 공부를 추가적으로 해야 했다. 비즈니스 로직을 구현하는 기술 자체가 복잡하고 어려웠기 때문이였다.

그러나, 스프링이 등장하면서 스프링을 사용하면 개발자는 애플리케이션의 비즈니스 로직을 작성하는데 신경을 더욱 집중할 수 있어서 생산성이 좋아졌다.

🤔 오픈소스

스프링은 모든 사용자에게 무료로 열려 있다. 즉, 어떤 기업도 개인도 스프링을 사용해서 웹 애플리케이션을 개발할 수 있고, 필요하다면 스프링의 코드 일부를 수정해서 사용할 수 있다.

오픈 소스의 특징은 뚜렷하게 정해진 인원이 프로젝트의 개발과 관리를 맡는 것이 아니기 때문에 프로젝트의 개발과 개선이 안정적이지 못하는 단점을 가질 수 밖에 없는데 스프링은 오픈소스 프레임워크이지만, 안정적인 개발과 개선이 보장된다.

왜냐면, 스프링은 오픈소스로 배포되어 누구나 이용이 가능하지만, 스프링소스(SpringSource)라는 IT기업에서 관리하고 있어서 코드를 수정하거나 개선하는 일에는 그 기업의 한정된 인원만 참여할 수 있기 때문이다.

🤔 경량급

스프링은 수십개의 세부 모듈 및 수십만줄의 방대한 코드로 이루어져있는 프레임워크이다.

그런데 방대한 프레임워크를 왜 경량급이라고 정의하는 것일까?

스프링의 경량급이라는 말은 기존에 스프링 대신 사용하던 기술들과 비교하여, 스프링을 사용했을 때 개발자가 작성해야하는 코드가 상대적으로 단순하는 것을 표현하기 위함이다.

따라서, 스프링을 사용하면서 불필요하고 불가피하게 작성된 코드를 제거할 수 있었고, 코드를 제거함으로서 복잡성을 낮출 수 있었기 때문에 경량급이라는 표현을 사용해 정의한다.

스프링 부트(Spring Boot) 란?

스프링부트(Spring Boot)는 스프링(Spring) 을 더 쉽게 이용하기 위한 도구

스프링을 이용해 개발을 진행할 때, 이것 저것 세팅을 해야할 요소들이 많은데, 이러한 세팅을 처음 배우려는 사용자에게는 어려울 수 있다. 하지만, 스프링 부트를 사용하면 매우 간단하게 프로젝트를 설정할 수 있으므로, 개발을 조금 더 쉽게 만들어주는 역할을 한다.

스프링을 이용한 xml 설정 파일과 스프링 부트를 이용해 복잡한 설정 내용을 간략하게 줄여준 것을 확인할 수 있다. 이와같이 사용자는 스프링 부트를 이용해 설정을 보다 쉽게 할 수 있는 것이다.

스프링 모듈

스프링은 담당하는 애플리케이션 개발 요소에 따라 여러 모듈로 구성된다.
아래의 그림은 공식 문서에 소개되어 있는 스프링 프레임워크의 모듈 아키텍처의 이미지이다.

이 이미지를 보면 스프링이 엔터프라이즈 애플리케이션 개발의 모든 측면을 다룬다는 사실을 알 수 있을 것이다.

스프링을 사용하면 웹 애플리케이션을 개발하고 데이터베이스에 접근하며, 트랜잭션을 관리하고 단위 테스트와 통합테스트를 개발하는 등의 일을 할 수 있다.
이미지처럼 스프링의 여러 모듈은 애플리케이션에 꼭 필요한 부분만 포함하도록 설계한다.

예를 들어서, 애플리케이션에서 DI기능을 사용하기 위해서는 핵심 컨테이너(Core Container) 그룹 모듈만 포함하면 되는 것이다.

✨ 스프링 배포판 명명 규약

spring-<짧은 모듈 이름>-<스프링 버전>.jar
  • <짧은 모듈 이름>: aop, beans, context, expressions etc..
ex) spring-aop-5.0.1.RELEASE.jar, spring-beans-5.0.1.RELEASE.jar

스프링 프레임워크의 핵심

스프링 프레임 워크의 핵심은 Bean의 생명 주기와 설정 그리고 처리방법을 관리하는 코어 컨테이너이다.

이 이미지는 스프링 모듈 사이의 상호 의존 관계를 보여주고 있다. 스프링 중심에 핵심 컨테이너 그룹에 속한 모듈이 있고, 다른 모듈이 이 그룹에 의존한다. 모든 스프링 모듈의 기반이 된다.

스프링 IoC 컨테이너와 빈


위의 코드를 보면 main함수에서 그냥 run()만 불렀을 뿐이고 내가 작성한 코드를 호출하는 부분이 없다. 그럼 내가 작성한 코드(객체)는 언제 생성되고 초기화하고 실행되며 소멸될까?
이것을 알아서 처리해주는 것이 프레임워크의 컨테이너이다.
프레임워크를 사용하지 않고 직접 실행하게 된다면, 여러 요청에 따라 스레드를 여러개로 빼거나, 비동기 처리하는 복잡한 일거리가 생기게 되는 것이다. (main에서 시작된 실행 흐름을 직접 while과 같은 코드로 제어해야하기 때문)

IoC: Inversion of Control

IoC는 의존 관계 주입(DI: Dependency Injection)이라고도 한다. 어떤 객체가 사용하는 의존 객체를 직접 만들어서 사용하는 것이 아니라, 어떤 장치를 사용해서(생성자, setter, 필드 인젝션 등) 주입 받아 사용하는 방법을 말한다.

이 처럼 프로그램의 실행 흐름이나 객체의 생명 주기를 개발자가 직접 제어하는 것이 아니라, 컨테이너로 제어권이 넘어가는 것을 Inversion of Control(IoC)라고 하고, 이러한 방식 때문에 Spring IoC Container라고 부르는 것 같다.

Bean

컨테이너가 관리하는 객체를 빈(Bean)이라고 부른다. 빈은 기본적으로 싱글턴이다. (싱글턴에 대해서는 다음에 한 주제로 알아볼 예정이다.)
아래와 같이 4가지의 어노테이션을 사용하면 해당 클래스를 자동으로 빈 등록해준다.

  • @Controler
    Persentation layer에서 Controller임을 명시
  • @Service
    Business layer에서 Service를 명시
  • @Repository
    Persentation layer에서 DAO임을 명시
  • @Component
    기타 자동 등록 하고 싶은 것

이외에 @Bean이라는 어노테이션이 존재하는데 이는 내가 직접 작성한 클래스가 아닌 외부 라이브러리 객체를 빈으로 등록하고 싶을 때 사용하게 된다.

DI: Dependency Injection

스프링에서는 빈의 생성 소멸 등 생명 주기를 컨테이너가 관리하며, 해당 타입의 객체를 사용하는 곳에 명시하면 알아서 DI 해준다.

의존성 주입 방법

앞서 스프링 컨테이너를 설명하면서 DI에 대해 언급하게 되었는데, Spring Framework의 핵심 기술 중 하나가 바로 DI이다. Spring Framework를 이용하면 다양한 의존성 주입을 이용할 수 있는데 이에 대해서 아래와 같이 알아보자.

Constructor-based DI

생성자를 통해 의존 관계를 주입하는 방법이다.

생성자 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다. 그렇기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있다.
Spring Framework는 생성자 주입을 적극 지원하고 있기 때문에, 생성자가 1개만 있을 경우에는 @Autowired를 생략해도 주입이 가능하다.

Setter-based DI

수정자 주입(Setter Injection)은 필드 값을 변경하는 Setter를 통해서 의존 관계를 주입하는 방법이다. Setter 주입은 생성자 주입과는 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용하는데 실제로 변경이 필요한 경우는 극히 드물다.

@Autowired로 주입할 대상이 없는 경우에는 Error가 발생한다. 주입할 대상이 없어도 동작하도록 하려면 @Autowired(required = false) 를 통해서 설정이 가능하다.

Field based DI(@Autowired)

필드 주입(Field Injection)은 필드에 바로 의존 관계를 주입하는 방법이다.

필드 주입을 이용하면 코드가 간결해져서 상당히 많이 이용되었던 방식이였다. 하지만 필드 주입은 외부에서 접근이 불가능해서 테스트 코드의 중요성이 부각됨에 따라 필드의 객체를 수정할 수 없는 필드 주입은 거의 사용하지 않게 되었다. 따라서 요즘은 필드 주입의 경우 사용을 지양하고 있고, 애플리케이션의 실제 코드와 무관한 테스트 코드나 설정을 위해 불가피한 경우에만 사용한다.

일반 메소드 주입(Method Injection)

일반 메소드를 통해 의존관계를 주입하는 방법. 수정자 주입과 동일하며 마찬가지로 사용할 필요가 거의 없는 주입 방법이다.

생성자 주입을 사용해야하는 이유

[1] 객체의 불변성 확보

실제로 개발을 하다 보면 의존 관계의 변경이 필요한 상황이 거의 없다. 하지만 Setter 주입이나 일반 메서드 주입을 이용하면 불필요하게 수정의 가능성을 열어두어 유지보수성을 떨어뜨린다. 그러므로 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 바람직하다.

[2] 테스트 코드의 작성

테스트가 특정 프레임워크에 의존하는 것은 침투적이므로 좋지 않다. 그러므로 가능한 순수 자바를 통해서 테스트를 작성하는 것이 좋은데 생성자 주입이 아닌 다른 주입 방식으로 작성된 코드는 순수 자바 코드로 단위 테스트를 작성하는 것이 어렵다.

[3] final 키워드 작성 및 Lombok과의 결합

생성자 주입을 사용하면 필드 객체에 final 키워드를 사용할 수 있으며, 컴파일 시점에 누락된 의존성을 확인할 수 있다. 반면에 다른 주입 방법들은 객체의 생성(생성자 호출) 이후에 호출이 되므로 final 키워드를 사용할 수 없게 된다.
final 키워드를 붙히게 되면 Lombok과 결합되어 코드를 간결하게 작성할 수 있는 장점을 가지게 된다.

[4] 스프링에 비침투적인 코드 작성

필드 주입을 사용하려면 @Autowired 를 이용해야 하는데 이것은 스프링이 제공하는 어노테이션이다. 그러므로 @Autowired 를 사용하면 아래와 같이 UserService 클래스에 스프링 의존성이 침투하게 된다.

우리가 사용하는 프레임워크는 언제 바뀔지 모를 뿐만 아니라, 사용자와 관련된 책임을 가지는 UserService에 스프링 코드가 박혀버리는 것은 바람직하지 않다. 프레임워크는 비즈니스 로직을 작성하는 서비스 계층에서 알아야 할 대상이 아니다. 물론 이는 필요한 자바 파일을 import해야 하는 정적 언어인 Java 언어의 한계이기도 하다. 그래도 가능한 스프링 없이 코드가 작성된다면 더욱 유연한 코드를 확보하게 되는 것이다. 스프링 프레임워크가 자주 바뀌는 것이 아니라서 스프링 코드가 침투하는 것이 큰 문제는 되지 않지만, 생성자 주입과 같이 좋은 방법을 두고 굳이 사용할 필요는 없다.

[5] 순환 참조 에러 방지

생성자 주입을 사용하면 애플리케이션 구동 시점(객체의 생성 시점)에 순환 참조 에러를 예방할 수 있다. A클래스와 B클래스가 서로 호출한다면 A,B 클래스의 서로의 메소드는 서로 호출을 계속 하게 되고, 메모리에 CallStack이 쌓이게 되면서 StackOverflow Error를 발생하게 되는 것이다.
이러한 문제는 결국 서버를 죽이게 되고, 서버가 죽는 것은 치명적인 문제를 발생시키는 것이다.

하지만, 생성자 주입을 이용하면 순환 참조 에러는 방지할 수 있다. 어플리케이션 구동 시점에 에러가 발생하기 때문이다.

@Autowired 를 이용한 필드 주입에서 이러한 문제가 어플리케이션 구동 시점에 에러가 발생하지 않는 이유는 빈의 생성과 조립 시점이 분리되어 있기 때문이다. 생성자 주입은 객체의 생성과 조립이 동시에 실행되기 때문에 순환 참조 에러 같은 문제를 사전에 잡을 수 있는 것이다.

[5] 순환 참조 에러 방지는 Spring Boot 2.6 버전 이후 부터는 순환 참조가 기본적으로 허용되지 않도록 변경되어 필드 주입을 받아도 순환 참조가 발생한다면 사전에 알 수 있게 되었다. 하지만, 버전에 얽매이지 않기 위해서는 생성자 주입을 사용하는 것이 좋다 생각한다.


Reference

📄 스프링 공식 문서
📚 배워서 바로 쓰는 스프링 프레임워크
📰 CodeStates 블로그
📰 우테코 둔덩님의 블로그: Reflection API 간단히 알아보자
👨🏻‍💻 Kai6666님의 블로그: 스프링 프레임워크 모듈 구성
👨🏻‍💻 망나니개발자님의 블로그: 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유

profile
👾ISTP의 개발자 도전기🧐
post-custom-banner

0개의 댓글