메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

스프링 vs 스프링 부트 차이점과 Spring Boot 핵심 개념 3가지


일반적으로 규모가 크고 복잡한 애플리케이션을 개발할 때는 처음부터 끝까지 모든 부분을 개발하기보다는 프레임워크의 도움을 받게 됩니다. 특히 자바로 백엔드 애플리케이션을 개발할 때 개발자들에게 가장 인기 있는 프레임워크는 단연 스프링 프레임워크입니다. 스프링 프레임워크와 스프링 부트가 어떻게 다른지 살펴보고, 스프링 부트 애플리케이션의 구성을 이해하는 데 필요한 핵심 개념 3가지를 알아보겠습니다.

 

 

 

✅스프링, 스프링 프레임워크, 스프링 부트 비교

 

구분스프링
(Spring)
스프링 프레임워크 
(Spring Framework)
스프링 부트
(Spring Boot)
정의자바 기반 애플리케이션 개발을 위한 전체 생태계스프링 생태계의 핵심이 되는 기본 프레임워크스프링 프레임워크를 쉽고 빠르게 사용할 수 있게 해주는 도구
주요 특징- 포괄적인 생태계
- 다양한 프로젝트 제공
- 엔터프라이즈급 개발 지원
- 의존성 주입 (DI)
- 제어의 역전 (IoC)
- 관점 지향 프로그래밍 (AOP)
- 스프링 MVC
- 자동 구성 (Auto Configuration)
- 내장 서버 (Tomcat, Jetty 등)
- Starter 의존성
- 운영 기능 (Actuator)
설정 복잡도프로젝트에 따라 다름복잡한 설정 필요최소한의 설정으로 빠른 시작
프로젝트 시작선택한 프로젝트에 따라 다름수동으로 모든 설정 구성Spring Initializr로 몇 분 만에 프로젝트 생성
배포 방식프로젝트에 따라 다름외부 서버(Tomcat 등)에 WAR 파일 배포내장 서버로 JAR 파일 단독 실행 가능

 

 

 

 

스프링은 표준화된 프로그래밍 모델과 다양한 라이브러리를 제공함으로써 규모가 크고 복잡한 개발 프로젝트의 개발 시간을 단축시킬 뿐만 아니라 유지보수의 편의성까지 높여 줍니다. 그래서인지 스프링은 오랜 기간 자바 개발자들로부터 사실상의 업계 표준으로 받아들여질 만큼 큰 사랑을 받고 있습니다. 스프링은 다양한 프로젝트를 운영하고 있으며, 대표적으로 스프링 프레임워크 스프링 부트, 스프링 데이터, 스프링 시큐리티 등이 있습니다.

 

 

 

 

 

이 중 스프링 프레임워크는 모든 스프링 프로젝트의 기반이 되는 도구로, 의존성 주입과 제어의 역전을 바탕으로 하는 스프링 컨테이너, 스프링 MVC 모델, 그리고 데이터베이스 접근, 메시징, 트랜잭션과 같은 풍부한 기능을 제공합니다. 스프링 프레임워크는 자바 애플리케이션 개발의 대표적인 프레임워크로 자리매김했지만, 다양한 기능을 활용하기 위해 공부해야 할 내용이 많고 프로젝트 설정이 복잡하다는 단점이 있습니다. 그래서 스프링 프레임워크를 사용한 애플리케이션 개발이 쉽지 않죠.

 

스프링 부트는 스프링 프레임워크를 보다 쉽게 사용할 수 있는 자동 구성 및 설정 기능을 제공합니다. 개발에 필요한 기능들을 사용하기 위해 복잡하게 설정해야 했던 프로젝트 설정 과정을 패키지로 제공해주므로, 쉽고 빠르게 백엔드 애플리케이션을 개발할 수 있습니다. 또한 애플리케이션 내에 톰캣Tomcat 같은 웹 애플리케이션 서버를 포함해 단독으로 실행할 수 있어 배포와 운영이 한결 간편합니다.

 

 

 

 

즉, 스프링 프레임워크는 자바 개발 프레임워크의 핵심 코어 기능을 제공하며, 스프링 부트는 자동 설정 기능을 제공하고 웹 애플리케이션 서버를 내장하는 등 스프링 프레임워크를 쉽고 빠르게 이용할 수 있도록 합니다. 이렇게 스프링은 스프링 프레임워크, 스프링 부트와 기타 다양한 프로젝트를 아우르는 하나의 생태계라고 할 수 있습니다.

 

 

 

 

 


 

✅스프링 부트의 핵심 개념

 

자바로 프로그래밍을 할 때는 모든 로직을 하나의 클래스에 구현하기보다 기능에 따라 클래스를 나누어 작성하고, 프로그램의 흐름에 따라 하나의 클래스에서 다른 클래스를 생성해 호출하는 방식으로 프로그래밍을 합니다. 이때 클래스들 사이에는 클래스를 생성하고 사용하는 ‘의존성 관계’가 형성 되는데, 스프링에서는 의존성으로 인해 발생하는 개발 시간 및 유지보수의 효율을 높이기 위해 ‘의존성 주입’‘제어의 역전’이라는 기법을 사용합니다. 

 

모든 스프링 부트 애플리케이션은 개발자가 작성한 클래스 객체와 스프링이 제공하는 다양한 클래스 객체가 스프링 컨테이너 안에서 생성되고, 서로의 의존성이 주입되어 연결됩니다. 따라서 의존성 주입과 제어의 역전, 그리고 이와 관련해 관점 지향 프로그래밍이라는 개념을 이해하는 것이 매우 중요합니다. 

 

 

 

① 의존성 주입

자바로 클래스를 작성하다 보면 한 클래스에서 어떠한 동작을 위해 또 다른 클래스를 생성해 사용하는 경우가 빈번합니다. 만약 A라는 클래스가 동작하기 위해 B라는 클래스가 필요하다면 ‘A 클래스 가 B 클래스에 의존한다’고 표현합니다. 또는 ‘B 클래스가 A 클래스에게 필요한 의존성dependency’이 라고 표현하기도 하죠.
 

 

 

어떤 클래스가 다른 클래스의 기능을 사용해야 하는 상황이 자주 발생합니다. 예를 들어 커피를 내리는 CoffeeMaker는 커피 추출을 담당하는 CoffeeMachine이 있어야 제대로 작동하겠죠. 이렇게 다른 객체의 도움이 필요한 관계를 의존성이라고 부릅니다.

 

처음엔 CoffeeMaker 안에서 CoffeeMachine을 직접 만들어 사용할 수 있습니다. 하지만 이 방식은 나중에 커피 머신 종류가 바뀌거나 기능이 추가되면 CoffeeMaker도 같이 수정해야 합니다. 이런 구조는 변화에 약하고 유지보수가 어려운 코드를 만듭니다.

 

이 문제를 해결하려면 클래스 간 결합을 느슨하게 만들어야 합니다. 가장 좋은 방법은 공통 인터페이스를 만들고, 커피 머신들은 이 인터페이스만 따르게 하는 거죠. 그럼 CoffeeMaker는 특정 머신이 아니라 인터페이스만 바라보고, 실제 어떤 머신을 쓸지는 외부에서 결정하게 하면 됩니다. EspressoMachine과 DripCoffeeMachine 대신, 새롭게 모카 커피 머신을 만든다고 하더라도 커피 메이커를 수정할 필요 없이 간단하게 메인 클래스에서 모카 커피 머신을 커피 메이커에게 전달하기만 하면 됩니다.

 

이렇게 외부에서 필요한 객체를 주입해주는 방식이 바로 의존성 주입Dependency Injection입니다. 스프링은 이 구조를 바탕으로 돌아가며, 개발자가 직접 객체를 만들 필요 없이 필요한 의존성을 알아서 주입해 줍니다. 덕분에 코드가 훨씬 유연해지고, 테스트나 기능 확장도 쉬워지죠.

 

 

 

② 제어의 역전

이제 의존성 주입을 통해 새로운 커피 머신을 개발하더라도 커피 메이커의 소스코드를 변경하지 않고 그대로 사용할 수 있게 되었습니다. 어떤 커피 머신을 쓸지는 이제 CoffeeMaker가 직접 결정하지 않고 외부에서 주입받죠. 그런데 여전히 남은 문제가 있습니다. 그 객체를 '누가' 만들고, '어디서' 주입해 줄까요?

 

보통은 메인 코드에서 객체를 생성하고, 어떤 커피 머신을 쓸지 결정해 CoffeeMaker에 넘겨줍니다. 의존성 주입을 했더라도 결국 개발자가 연결고리를 직접 짜야 한다는 뜻이죠. 이런 과정을 프레임워크에게 넘겨주는 것이 바로 제어의 역전(IoC)Inversion of Control입니다. 메인 클래스에서 직접 의존성 생성과 주입을 위한 코드를 작성하는 것이 아니라 설정을 통해 클래스 객체를 만들고 필요한 의존성 객체를 주입해 줄 수 있는데, 스프링 프레임워크에서는 XML 파일이나 애노테이션을 통해 제어의 역전을 구현합니다.

 

  • 전통적인 방식: 개발자가 new로 객체를 만들고, 직접 메서드를 호출하며 흐름을 직접 제어합니다. 
  • IoC 방식: 프레임워크가 객체를 만들어서 필요한 곳에 주입하고 간접적으로 흐름을 제어합니다.

 

 

제어의 역전은 이 모든 흐름을 스프링에게 위임하는 방식입니다. 어떤 객체를 만들지 / 어떤 의존성을 주입할지 / 언제 초기화할지 이 모든 걸 스프링이 알아서 처리합니다. 개발자는 설정(혹은 애노테이션)만 해주면 됩니다. 예전에는 이런 설정을 XML 파일로 했지만, 지금은 거의 대부분 애노테이션을 사용합니다. 클래스에 @Component를 붙이면 스프링이 객체를 만들고, @Autowired로 의존성을 자동으로 주입하죠.

 

 

 

 

 

 

결과적으로, 제어의 역전은 우리가 객체를 직접 만들고 관리하지 않아도 되는 구조적 자유를 제공합니다. 의존성 주입이 객체 간의 관계를 유연하게 해줬다면, 제어의 역전은 그 관계를 구성하는 과정 자체를 프레임워크에게 맡기는 것입니다. 개발자는 기능 개발에만 집중하고, 스프링이 객체 생성부터 조립까지 알아서 처리해주는 거죠. 이게 가능한 이유가 바로, 스프링이 IoC 컨테이너로 동작하기 때문입니다.

 

 

 

 

 

③ 관점 지향 프로그래밍

자바 애플리케이션을 개발하다 보면, 여러 클래스에서 반복적으로 수행하는 공통 작업이 생기는 경우가 많습니다. 예를 들어 메서드 실행 시간을 측정하거나, 로그를 남기거나, 보안 체크를 하는 기능처럼 말이죠. 이런 기능들은 비즈니스 로직과는 별개지만, 거의 모든 서비스에서 공통적으로 필요한 작업입니다. 이처럼 여러 클래스에 흩어져 반복되는 공통 기능을  공통 관심사Cross-Cutting Concern라고 부릅니다.

 

전통적인 방식에서는 각 클래스나 메서드마다 이런 공통 기능을 일일이 구현하게 되는데, 그럴수록 코드 중복이 늘고 유지보수가 어려워집니다. 특히, 특정 기능 하나를 수정하려고 해도 그와 관련된 수많은 클래스들을 동시에 고쳐야 하는 문제가 생기죠.

 

 

 

 

이 문제를 해결하기 위한 개념이 바로 관점 지향 프로그래밍(AOP) Aspect Oriented Programming입니다. AOP는 핵심 비즈니스 로직과 공통 관심사를 분리해, 공통 기능은 따로 정의하고 필요한 곳에만 깔끔하게 끼워 넣을 수 있는 방식입니다. 예를 들어 어떤 메서드의 실행 시간을 알고 싶다면, 그 메서드 앞뒤로 시간 측정 코드를 넣는 대신, 단순히 애노테이션 하나만 붙이면 실행 시간 측정이 자동으로 이뤄지도록 만드는 식이죠. 이렇게 하면 공통 로직을 중복 없이 유지할 수 있고, 원하는 메서드에 쉽게 적용하거나 제거할 수 있습니다.

 

스프링에서는 이러한 AOP 개념을 아주 쉽게 구현할 수 있도록 지원합니다. 메서드 실행 전, 실행 후, 예외 발생 시점 등 다양한 위치에 공통 로직을 삽입할 수 있고, 별도의 상속 없이도 적용이 가능합니다. 스프링 부트에서는 애노테이션 기반 설정을 통해, 개발자가 만든 클래스 위에 간단한 표기만으로 AOP 기능을 적용할 수 있도록 해 줍니다.

 

예를 들어 @PrintExecutionTime이라는 애노테이션을 정의하고, 그 애노테이션이 붙은 메서드의 실행 시간을 자동으로 측정하게 만들 수 있습니다. 이 기능은 단 하나의 공통 클래스로 정의하고, 필요한 메서드에만 적용하면 되니 반복도 없고, 로직 수정도 한 군데만 고치면 됩니다. 실제로 스프링에서는 트랜잭션 처리용 @Transactional도 같은 방식으로 동작합니다. 이 애노테이션 하나만 붙이면 스프링이 알아서 트랜잭션을 시작하고, 정상적으로 끝나면 커밋하고, 예외가 발생하면 롤백까지 처리해 줍니다.

 

결국 AOP는 개발자가 핵심 로직에만 집중할 수 있도록 도와주는 구조적 장치입니다. 공통된 기능은 따로 분리해 관리하고, 핵심 로직은 최대한 깔끔하게 유지할 수 있게 해주죠. 이 덕분에 코드의 가독성과 재사용성이 높아지고, 유지보수는 더 쉬워집니다. 스프링 부트는 AOP를 매우 직관적으로 구현할 수 있게 해 주기 때문에, 프로젝트 규모가 커질수록 AOP의 가치는 더욱 커지게 됩니다.
 

 

 


 

위 콘텐츠는 『이것이 스프링 부트다 with 자바』의 내용을 재구성하여 작성되었습니다.

 

댓글

댓글 입력