Spring Framework VS Spring Boot / Spring Boot의 동작 과정

사람·2025년 9월 11일
0

Backend

목록 보기
9/11

Spring Framework의 특징

POJO 기반의 경량 컨테이너 제공

Spring Framework에서 개발자는 POJO 클래스를 개발하고 스프링 컨테이너는 이 POJO 객체(스프링 빈)를 관리한다.
POJO 객체는 특정 기술에 종속되지 않는 순수 자바 객체를 의미한다. Spring Framework에서는 프레임워크의 메서드가 사용자 클래스에 구현되지 않아 프레임워크 코드와 사용자 코드 간의 결합이 느슨해진다. 개발자가 개발하는 클래스는 프레임워크의 코드와 분리되어 있어 개발자는 비즈니스 로직을 구현하는 데 집중할 수 있다.

복잡한 비즈니스 영역의 문제를 쉽게 개발하고 운영 가능하게 하는 설계 철학

제어의 역전(IoC, Inversion of Control)

객체의 생명 주기를 관리할 책임을 프레임워크(스프링 컨테이너, IoC 컨테이너)에 위임함으로써 개발자가 비즈니스 로직에 집중할 수 있도록 한다.

의존성 주입(DI, Dependency Injection)

제어 역전의 방법 중 하나이다. 사용할 객체를 직접 생성하지 않고 외부 컨테이너가 생성한 객체를 주입받아 사용하는 방식이다. 이렇게 하면 클래스 간의 복잡한 관계를 개발자가 직접 관리할 필요가 없게 된다.

스프링에서 의존성 주입을 받는 방법은 다음의 세 가지가 있다.

  • 생성자 주입
  • 필드 주입
  • setter 주입

이중에서 스프링에서 권장하는 의존성 주입 방법은 생성자 주입인데, 그 이유는 다음과 같다.

  1. 주입된 객체에 대한 불변성 확보
    생성자 주입은 생성자의 호출 시점에 1회만 수행되는 것이 보장된다. 그래서 주입된 객체의 불필요한 변경을 막을 수 있다.
  2. 테스트 코드 작성 용이
    생성자 주입을 사용하면 스프링의 @Autowirde 없이도 컴파일 시점에 객체를 주입받아 테스트 코드를 작성할 수 있다. 즉, 프레임워크에 의존하지 않더라도 순수 자바 코드만으로 테스트 코드를 작성할 수 있게 된다.
  3. final 키워드 사용 가능
    final로 선언된 필드는 생성자에서 반드시 초기화가 이뤄져야 한다. 다른 주입 방법은 생성자 호출이 끝난 후 주입이 이뤄지기에 필드 객체를 final로 선언할 수 없는 반면, 생성자 주입을 사용하면 필드 객체에 final 키워드를 사용할 수 있어 컴파일 시점에 누락된 의존성을 감지할 수 있다.

관점 지향 프로그래밍(AOP, Aspect Oriented Programming)

AOP는 관점을 기준으로 묶어 개발하는 방식을 의미한다. 어떤 기능을 구현할 때 그 기능을 '핵심 기능'과 '부가 기능'으로 구분해 각각을 다른 관점으로 보는 것이다. 스프링은 부가 기능을 핵심 기능과 분리하는 기능을 제공함으로써 코드의 복잡성을 낮추고 재사용성을 높인다.

  • 핵심 기능
    비즈니스 로직이 처리하고자 하는 목적에 해당하는 기능이다.
    상품 정보를 데이터베이스에 저장하고, 저장된 상품 정보 데이터를 보여주는 코드 등이 여기에 속한다.
  • 부가 기능
    비기능적 요구 사항에 해당하는 기능이다.
    핵심 기능을 구현하기 위해 부수적으로 추가되는 기능이다.
    비즈니스 로직 사이의 로깅 처리나 트랜잭션 처리 코드 등이 여기에 속한다.

AOP의 관점이 적용되지 않은 경우, 각 핵심 기능을 수행하는 로직마다 부가 기능을 수행하는 로직이 함께 구현이 되어 있을 것이다. 하지만 부가 기능이 수행해야 하는 로직은 모든 핵심 기능에 공통적으로 적용될 확률이 높기에 코드의 중복이 발생하게 된다.

반면 AOP의 관점에서는 이렇게 반복되는 부가 기능을 하나의 공통 로직으로 처리할 수 있도록 모듈화한다.어떤 핵심 기능이 수행되는지와 무관하게 로직이 수행되기 전 또는 후에 부가 기능이 수행될 수 있도록 하는 것이다. 이렇게 하면 모듈화된 객체를 편하게 적용할 수 있게 되어 개발자가 비즈니스 로직을 구현하는 데에만 집중할 수 있게 된다.
스프링에서는 프록시 패턴(Proxy Pattern)을 통해 AOP 기능을 제공하고 있다.

서비스 추상화(PSA, Portable Service Abstraction)

스프링 프레임워크는 수많은 기능을 일정한 수준의 추상화를 한 클래스로 제공하여 어떤 기능(구현체)를 사용하든지 간에 일관된 방식으로 접근할 수 있도록 한다.
대표적인 예가 트랜잭션 기능이다. 스프링 프레임워크에서는 데이터를 저장하여 객체를 계속 유지할 수 있도록 하는 다양한 영속성 프레임워크를 붙여 사용할 수 있다. 스프링 프레임워크가 RDB의 트랜잭션 기능을 정리한 PlatformTransaction 인터페이스를 제공함으로써 각 영속성 프레임워크에 적합한 구현 클래스를 제공하기 때문이다. 따라서 개발자는 어떤 구현체 혹은 프레임워크를 사용하더라도 추상화되어 있는 PlatformTranscationManager의 메서드만 사용하면 된다.

모듈식 프레임워크

스프링 프레임워크는 애플리케이션을 개발할 수 있는 여러 기능을 포함하고 있으며, 기능들을 성격에 따라 분류하여 '모듈'이라는 단위로 관리한다. 개발자는 필요한 모듈들을 조합하여 필요한 기능만 사용할 수 있다.
스프링 프레임워크는 약 20여개의 모듈로 구성되어 있다. 스프링 부트에서는 스프링 프레임워크의 모든 모듈을 기본으로 포함한다.

높은 확장성과 범용성

스프링 프레임워크는 여러 가지 기술과 연동 및 확장할 수 있는 다양한 형태의 프로젝트를 제공한다. 따라서 스프링 프레임워크를 사용하면 확장이 용이한 범용적인 애플리케이션을 만들 수 있다.

  • Spring Batch
  • Spring Security
  • Spring Data

Spring Boot의 특징

스프링 부트의 목적

스프링 부트는 빠른 개발을 목적으로 '설정보다 관례'라는 패러다임을 채택한 프레임워크이다. 그래서 스프링 부트 프로젝트에서는 가장 보편적으로 많이 사용하는 형태로 스프링 애플리케이션을 미리 설정해 두었다. 직접 설정하는 대신 관례(CoC)에 맞게 코드를 작성하면 미리 설정된 형태로 애플리케이션을 개발할 수 있다.
기존 스프링 프레임워크로 개발할 때는 필요한 라이브러리를 찾고 애플리케이션에 적합한 버전을 선택하며 이를 설정하고 테스트하는 데 많은 시간이 필요했다. 스프링 부트는 컨트리뷰터와 커미터가 이미 적절한 라이브러리를 선택하고 이를 일반적인 형태로 설정하고 테스트까지 완료했기 때문에, 개발자는 그저 사용하기만 하면 되어 시간이 훨씬 단축된다.

스프링 부트에서 추가적으로 제공하는 기능들

모든 스프링 프로젝트는 스프링 프레임워크를 반드시 포함한다. 따라서 위에 작성한 스프링 프레임워크의 특징 역시 스프링 부트에도 적용된다. 그리고 스프링 프레임워크에서 제공하는 모든 기능을 스프링 부트에서도 똑같은 방법으로 사용할 수 있다. 그렇다면 스프링 부트가 추가적으로 제공하는 기능에는 무엇이 있을까?

단독 실행 가능한 스프링 애플리케이션

스프링 부트 프로젝트는 빌드 플러그인을 제공하고, 이를 실행하면 단독 실행이 가능한 JAR 파일을 만들 수 있다. 그리고 java 명령어와 -jar 옵션을 사용하면 간단하게 애플리케이션을 실행할 수 있다. 그래서 전통적인 배포 방법에 비해 매우 간단하고 빠르게 배포할 수 있다는 장점이 있다.

starter 의존성 제공

스프링 부트 프로젝트는 기능별로 라이브러리 의존성을 포함한 스타터(starter)를 제공한다. Gradle의 경우, build.gradle에 다음과 같이 의존성을 추가함으로써 사용할 수 있다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
}

스타터 내부에 라이브러리 의존성 설정이 포함되어 있어 한 줄만 추가하면 기능을 사용하는 데 필요한 모든 라이브러리를 한 번에 추가할 수 있다.

Auto Configuration 제공

스프링 부트의 모듈인 spring-boot-autoconfigure가 제공하는 자동 구성 기능은 특정 조건들이 충족되면 미리 설정된 자바 설정 클래스가 동작하고 애플리케이션을 구성할 수 있도록 한다. 자동 구성을 충족하는 특정 조건들은 여러 가지 형태이다. 예를 들어 애플리케이션에 특정 스프링 빈이 있거나 class path에 특정 라이브러리가 포함되어 있거나 환경 설정 값이 있으면 실행되는 방식이다. 그리고 이러한 특정 조건들은 다양하게 조합하여 사용할 수 있다.

health-check를 위한 actuator 제공

스프링 부트를 이용해서 애플리케이션을 개발했다면 기본 모니터링 지표와 헬스 체크 기능을 기본으로 제공한다. 그래서 모니터링 솔루션을 이용해서 각 서버들의 상태와 지표를 수집하기가 매우 쉽다. 이 기능을 제공하는 모듈이 spring-boot-actuator이다.

애플리케이션에 내장된 WAS

스프링 부트의 spring-boot-starter-web 스타터를 이용하여 웹 애플리케이션을 개발한 경우 톰캣이 내장되어 있다. 내장된 WAS 덕분에 앞서 언급한 단독 실행이 가능한 애플리케이션 배포가 가능하다. 톰캣 대신 다른 WAS가 필요하다면 쉽게 교체할 수 있다.

Spring Boot의 동작 과정

스프링 부트에서 spring-boot-starter-web 스타터를 사용하면 기본적으로 톰캣을 사용하는 스프링 MVC 구조를 기반으로 동작한다.

서블릿(Servlet)과 서블릿 컨테이너(Servlet Container)

서블릿은 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술이다. 서블릿을 사용하여 웹 페이지를 동적으로 생성할 수 있다. 그리고 이 서블릿이 관리되는 곳이 서블릿 컨테이너이다.

서블릿 컨테이너의 특징은 다음과 같다.

  • 서블릿 인스턴스를 생성, 초기화, 호출, 종료하는 생명주기를 관리한다.
  • 서블릿 객체는 싱글톤 패턴으로 관리된다.
  • 멀티 스레딩을 지원한다.
  • 톰캣은 WAS와 서블릿 컨테이너의 역할을 수행하는 대표적인 컨테이너이다.

DispatcherServlet

들어온 요청을 가장 먼저 받고, 요청을 적절하게 처리할 컨트롤러를 찾아서 정해주는 역할을 하는 서블릿이다. 다음과 같은 과정을 거쳐 동작한다.

  1. DispatcherServlet에 요청(HttpServletRequest)이 들어온다.
  2. DispatcherServlet은 핸들러 매핑(Handler Mapping)을 통해 요청 URI에 매핑된 핸들러(Controller)를 탐색한다.
  3. 핸들러 어댑터(HandlerAdapter)를 통해 탐색된 컨트롤러를 호출한다.
  4. 핸들러 어댑터에서 응답이 돌아오면 ModelAndView로 응답을 가공해 반환한다.
  5. 뷰 형식으로 리턴하는 컨트롤러를 사용한 경우 뷰 리졸버(View Resolver)를 통해 뷰를 받아 리턴한다.
    @ResponseBody를 사용하면 뷰 리졸버를 호출하지 않고 MessageConverter를 거쳐 JSON 형식으로 변환해 리턴한다.
  • 핸들러 매핑(Handler Mapping)
    요청 정보를 기준으로 어떤 컨트롤러를 사용할지 선정하는 인터페이스이다. 여러 구현체를 가진다.
  • 뷰 리졸버(View Resolver)
    ModelAndView 객체에 대해 응답 생성에 적절한 뷰를 결정하고 매핑하는 역할을 한다.
  • 메시지 컨버터(MessageConverter)
    요청과 응답에 대해 Body 값을 변환하는 역할을 수행한다. 스프링 부트는 자동 설정을 통해 HttpMessageConverter 인터페이스를 빈으로 등록해 사용한다. 해당 인터페이스를 기반으로 하는 구현체 클래스는 다양하며, Content-Type을 참고해서 이중에서 적절한 Converter를 선정한다.
profile
알고리즘 블로그 아닙니다.

0개의 댓글