[Spring] Java, Spring, Spring Boot의 차이
Java, Spring Framework, Spring Boot가 각각 무엇을 담당하는지와 세 개념이 어떻게 연결되는지 정리한 글
자바 백엔드 공부를 하다 보면 Java, Spring, Spring Boot라는 단어를 거의 항상 같이 보게 됩니다.
처음에는 셋이 비슷한 것처럼 느껴질 수 있습니다. 특히 채용 공고나 프로젝트 설명에서 "Java/Spring Boot 백엔드"처럼 묶어서 쓰는 경우가 많기 때문에, 각각이 정확히 무엇을 의미하는지 헷갈리기 쉽습니다.
하지만 세 개념은 같은 층 위의 기술이 아닙니다.
짧게 먼저 정리하면 다음과 같습니다.
| 구분 | 한 줄 설명 |
|---|---|
| Java | 애플리케이션을 작성하는 프로그래밍 언어이자 실행 생태계 |
| Spring | 평범한 Java 객체를 중심으로 객체 관리, 의존성 주입, 공통 관심사 분리를 돕는 프레임워크 |
| Spring Boot | Spring 기반 애플리케이션을 빠르게 구성하고 실행하게 해주는 도구 |
즉, Spring과 Spring Boot는 Java와 경쟁하는 기술이 아니라 Java 생태계 위에서 애플리케이션의 구조와 실행 환경을 더 쉽게 다루기 위한 기술이라고 볼 수 있습니다.
Java란?
Java는 프로그래밍 언어입니다.
개발자는 Java 문법으로 클래스를 만들고, 메서드를 작성하고, 객체를 생성하면서 프로그램의 동작을 표현합니다.
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
이 코드는 Spring이나 Spring Boot가 없어도 Java만으로 작성할 수 있습니다.
Java를 말할 때는 단순히 문법만 의미하지는 않습니다. 보통 다음 생태계까지 함께 떠올리게 됩니다.
- Java 언어 문법
- JDK(Java Development Kit)
- JVM(Java Virtual Machine)
- 표준 라이브러리
- Gradle, Maven 같은 빌드 도구
- 여러 서버, 프레임워크, 라이브러리 생태계
즉, Java는 애플리케이션을 만들기 위한 가장 기본적인 토대입니다.
다만 Java만으로도 서버를 만들 수는 있지만, 규모가 커질수록 반복되는 일이 많아집니다.
예를 들어 웹 요청을 받고, 데이터베이스와 연결하고, 객체 사이의 의존성을 관리하고, 트랜잭션을 처리하는 일을 매번 직접 구성해야 한다면 코드가 금방 복잡해집니다.
이런 문제를 줄이기 위해 등장한 대표적인 프레임워크가 Spring입니다.
Spring이란?
Spring은 Java 애플리케이션을 더 구조적으로 만들기 위한 프레임워크입니다.
여기서 중요한 점은 Spring이 단순히 "웹 서버를 만드는 도구"만은 아니라는 것입니다.
Spring의 출발점은 웹보다 더 넓은 객체 생성, 의존성 연결, 트랜잭션, 설정 관리, 공통 관심사 분리 같은 애플리케이션의 공통 문제를 해결하는 것에 가깝습니다.
그 위에 Spring MVC(Model-View-Controller) 같은 웹 기술을 얹으면 우리가 흔히 말하는 Spring 백엔드 서버가 됩니다.
Spring을 처음 볼 때는 적어도 아래 세 가지를 핵심으로 잡고 가는 것이 좋습니다.
| 핵심 개념 | 의미 |
|---|---|
| POJO(Plain Old Java Object) | 프레임워크에 과하게 묶이지 않은 평범한 Java 객체 중심으로 개발하는 방식 |
| IoC(Inversion of Control) / DI(Dependency Injection) | 객체 생성과 의존성 연결을 Spring 컨테이너가 관리하는 방식 |
| AOP(Aspect-Oriented Programming) | 트랜잭션, 로깅, 보안처럼 여러 곳에 반복되는 공통 관심사를 분리하는 방식 |
Spring의 핵심은 객체를 개발자가 직접 전부 생성하고 연결하는 대신, 프레임워크가 객체의 생성과 의존 관계를 관리해준다는 점입니다.
이 큰 흐름을 IoC, 즉 제어의 역전이라고 부릅니다.
그리고 IoC를 구현하는 대표적인 방식이 DI, 즉 의존성 주입입니다.
예를 들어 주문 서비스가 결제 클라이언트를 필요로 한다고 가정해보겠습니다.
public class OrderService {
private final PaymentClient paymentClient;
public OrderService(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
public void order() {
paymentClient.pay();
}
}
Java 코드만 놓고 보면 OrderService가 PaymentClient를 필요로 한다는 사실은 생성자를 통해 표현되어 있습니다.
Spring은 여기서 한 발 더 나아가, 필요한 객체를 컨테이너에 등록하고 적절한 객체를 주입해줍니다.
@Service
public class OrderService {
private final PaymentClient paymentClient;
public OrderService(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
public void order() {
paymentClient.pay();
}
}
@Service가 붙은 클래스는 컴포넌트 스캔 대상이 되고, Spring 컨테이너가 관리하는 객체로 등록될 수 있습니다. 이런 객체를 보통 Bean이라고 부릅니다.
POJO와 Spring Bean
Spring을 이해할 때 POJO도 같이 봐야 합니다.
POJO는 특정 프레임워크의 부모 클래스를 반드시 상속하거나, 특정 인터페이스를 반드시 구현해야만 동작하는 객체가 아니라 일반적인 Java 클래스로 작성한 객체를 뜻합니다.
예를 들어 아래 클래스는 Spring 코드 없이도 이해할 수 있는 평범한 Java 클래스입니다.
public class Order {
private final Long id;
private final int price;
public Order(Long id, int price) {
this.id = id;
this.price = price;
}
public int getPrice() {
return price;
}
}
Spring의 중요한 방향 중 하나는 이런 평범한 Java 객체를 중심으로 애플리케이션을 만들 수 있게 하는 것입니다.
비즈니스 로직을 작성하기 위해 Spring 전용 부모 클래스를 상속해야 한다면, 도메인 코드가 프레임워크에 강하게 묶이게 됩니다.
반대로 POJO 중심으로 작성하면 핵심 로직을 더 단순하게 테스트하고 이해할 수 있습니다.
여기서 헷갈리기 쉬운 점은 POJO와 Bean은 같은 말이 아니라는 것입니다.
- POJO는 객체가 어떤 방식으로 작성되었는지에 가까운 말입니다.
- Bean은 Spring 컨테이너가 생성하고 관리하는 객체를 가리키는 말입니다.
즉, 평범한 Java 클래스로 작성한 객체가 Spring 컨테이너에 등록되면 POJO 스타일의 Spring Bean이 될 수 있습니다.
반대로 모든 POJO가 자동으로 Spring Bean이 되는 것은 아닙니다. Spring이 관리하려면 @Component, @Service, @Bean 같은 방식으로 컨테이너에 등록되어야 합니다.
엄밀히 말하면 @Service 같은 Spring 어노테이션도 프레임워크 메타데이터입니다.
그래도 핵심은 비즈니스 클래스가 Spring 전용 부모 클래스를 상속하거나 Spring 전용 인터페이스를 구현하지 않아도 된다는 점입니다.
이런 성격 때문에 Spring은 흔히 POJO 기반 개발을 지원한다고 설명됩니다.
AOP는 왜 필요할까?
Spring을 설명할 때 AOP도 빠지면 안 됩니다.
애플리케이션에는 핵심 비즈니스 로직과 별개로 여러 곳에 반복해서 끼어드는 코드가 있습니다.
- 로깅
- 트랜잭션 시작, 커밋, 롤백
- 권한 검사
- 성능 측정
- 예외 처리
이런 코드는 주문, 결제, 회원, 게시글 같은 여러 기능에 반복해서 등장합니다.
문제는 이 코드들이 비즈니스 로직의 핵심은 아니지만, 여러 클래스와 메서드에 넓게 퍼진다는 점입니다.
이런 관심사를 횡단 관심사 또는 공통 관심사라고 부릅니다.
예를 들어 주문 생성 로직의 핵심은 주문을 만들고 저장하는 것입니다.
하지만 실제 서비스에서는 트랜잭션도 필요합니다.
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
}
}
여기서 createOrder() 메서드 안에는 트랜잭션을 시작하고 커밋하고 롤백하는 코드가 직접 보이지 않습니다.
개발자는 @Transactional을 붙이고, Spring은 이 메서드가 Spring Bean을 통해 호출될 때 트랜잭션 처리를 메서드 실행 앞뒤에 적용합니다.
이처럼 AOP는 핵심 로직을 직접 수정하지 않고도 공통으로 필요한 동작을 특정 지점에 끼워 넣는 방식입니다.
Spring에서는 보통 프록시를 이용해 Spring Bean의 메서드 실행을 감싸고, 그 앞뒤에 필요한 부가 동작을 적용합니다.
그래서 AOP를 이해하면 @Transactional이 단순한 표시가 아니라, Spring이 메서드 호출 흐름에 개입해서 트랜잭션이라는 공통 관심사를 처리하는 장치라는 점을 이해할 수 있습니다.
로깅, 보안, 캐시, 성능 측정 같은 기능도 비슷한 관점에서 볼 수 있습니다.
물론 Spring AOP가 모든 것을 가로챌 수 있는 완전한 AOP 도구라는 뜻은 아닙니다.
Spring AOP는 주로 Spring Bean의 메서드 실행 지점에 적용됩니다.
필드 접근까지 세밀하게 다루거나 더 강력한 AOP가 필요하다면 AspectJ 같은 별도 도구가 더 적합할 수 있습니다.
하지만 일반적인 Spring 백엔드 애플리케이션에서 트랜잭션, 로깅, 보안 같은 공통 관심사를 다루기에는 Spring AOP만으로도 충분한 경우가 많습니다.
다만 같은 클래스 내부에서 @Transactional이 붙은 메서드를 직접 호출하는 경우처럼 프록시를 거치지 않는 상황에서는 AOP가 기대처럼 적용되지 않을 수 있습니다.
이런 프록시 동작 방식이나 Bean 생명주기는 이 글의 핵심 주제인 Java, Spring, Spring Boot의 차이를 넘어서는 내용이므로 별도 글에서 더 자세히 다루는 편이 좋습니다.
Spring은 단순히 객체 생성만 도와주는 프레임워크는 아닙니다. 웹 개발, 데이터 접근, 트랜잭션, 보안, 테스트 등 애플리케이션에서 자주 필요한 기능과 프로젝트들이 함께 생태계를 이룹니다.
대표적으로 다음과 같이 나누어 볼 수 있습니다.
- Spring Framework Core: 객체 생성과 의존성 관리
- Spring AOP: DI와 별개로, 프록시를 통해 로깅과 트랜잭션 같은 공통 관심사 적용
- Spring MVC: 웹 요청과 응답 처리
- Spring Transaction: 트랜잭션 추상화
- Spring Test: 테스트 지원
- Spring Data: 데이터베이스 접근을 편하게 해주는 별도 Spring 프로젝트
- Spring Security: 인증과 인가를 다루는 별도 Spring 프로젝트
정리하면 Spring은 POJO를 중심으로 Java 애플리케이션의 객체를 만들고 연결하며, AOP를 통해 공통 관심사를 분리해서 처리하는 구조를 제공하는 프레임워크입니다.
백엔드 개발에서 자주 쓰이기 때문에 Spring을 서버 개발 도구로 먼저 접하지만, 핵심은 서버 자체보다 애플리케이션 구조를 잡는 데 있습니다.
그런데 Spring은 강력한 만큼 설정할 것도 많았습니다.
과거에는 XML 설정이나 복잡한 Java 설정을 직접 작성해야 하는 경우가 많았고, 어떤 라이브러리를 어떤 버전으로 조합해야 하는지도 개발자가 직접 신경 써야 했습니다.
이 지점을 편하게 만들어주는 것이 Spring Boot입니다.
Spring Boot란?
Spring Boot는 Spring 애플리케이션을 더 빠르게 만들고 실행할 수 있게 해주는 도구입니다.
Spring Boot는 Spring을 대체하는 기술이 아닙니다. 오히려 Spring을 기반으로 하면서, Spring을 사용할 때 필요한 설정과 실행 환경을 더 간단하게 만들어줍니다.
가장 대표적인 특징은 다음과 같습니다.
- 자동 설정(Auto Configuration)
- Starter 의존성 제공
- 내장 웹 서버 제공
- 실행 가능한 애플리케이션 구조 제공
- 운영 환경에서 필요한 기본 기능 제공
여기서 자동 설정은 "아무 설정도 필요 없다"는 뜻은 아닙니다.
프로젝트에 들어온 의존성, 클래스패스, 사용자가 직접 정의한 Bean 등을 보고 Spring Boot가 기본 구성을 추측해서 적용한다는 뜻에 가깝습니다.
필요하다면 개발자가 직접 설정을 추가해서 기본 설정을 바꿀 수 있습니다.
Spring Boot 애플리케이션의 시작점은 보통 다음과 같습니다.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication은 크게 세 가지 역할을 묶어둔 어노테이션입니다.
@SpringBootConfiguration: Spring Boot 애플리케이션의 설정 클래스 역할@EnableAutoConfiguration: 의존성과 조건을 바탕으로 자동 설정 적용@ComponentScan: 현재 패키지 하위의 컴포넌트 탐색
그래서 보통 메인 클래스에 @SpringBootApplication 하나만 붙여도 기본적인 Spring Boot 애플리케이션 구성이 시작됩니다.
또한 Spring Boot는 서블릿 기반 웹 애플리케이션에서 내장 Tomcat 같은 웹 서버를 함께 사용할 수 있게 해줍니다.
그래서 별도의 WAS(Web Application Server)에 배포하지 않아도, java -jar 명령으로 서버 애플리케이션을 실행할 수 있습니다.
java -jar demo-0.0.1-SNAPSHOT.jar
이 점 때문에 Spring Boot는 프로젝트를 빠르게 시작하기 좋습니다.
예를 들어 Spring MVC 기반 웹 서버를 만들고 싶다면 Starter를 추가하는 것만으로 Spring MVC, 내장 Tomcat, JSON 처리에 필요한 기본 의존성을 함께 가져올 수 있습니다.
Spring Boot 3 계열에서는 보통 spring-boot-starter-web을 많이 사용합니다.
Spring Boot 4 문서 기준으로는 spring-boot-starter-web이 deprecated 처리되고 spring-boot-starter-webmvc가 권장됩니다.
따라서 새 프로젝트라면 사용하는 Spring Boot 버전에 맞는 Starter 이름을 확인하는 것이 좋습니다.
// Spring Boot 3.x에서 자주 쓰던 방식
dependencies {
implementation "org.springframework.boot:spring-boot-starter-web"
}
// Spring Boot 4.x에서 Spring MVC와 Tomcat을 사용할 때 권장되는 방식
dependencies {
implementation "org.springframework.boot:spring-boot-starter-webmvc"
}
Starter는 기능을 직접 실행하는 코드라기보다, 관련 라이브러리 묶음을 한 번에 가져오기 위한 의존성 설명자에 가깝습니다.
개발자는 처음부터 모든 의존성과 설정을 직접 조합하기보다, Spring Boot가 제공하는 기본값 위에서 필요한 부분만 수정하면 됩니다.
Spring과 Spring Boot의 차이
Spring과 Spring Boot의 차이는 "기능이 있느냐 없느냐"보다 설정을 누가 얼마나 해주느냐에 가깝습니다.
Spring은 애플리케이션을 만들기 위한 핵심 프레임워크입니다.
Spring Boot는 그 Spring 애플리케이션을 더 쉽게 시작하고 운영할 수 있도록 기본 설정과 실행 방식을 제공하는 도구입니다.
| 구분 | Spring | Spring Boot |
|---|---|---|
| 역할 | POJO, DI, AOP를 바탕으로 Java 애플리케이션 구조를 잡는 프레임워크 | Spring 애플리케이션을 쉽게 시작하는 도구 |
| 설정 | 개발자가 직접 설정할 일이 많음 | 자동 설정과 기본값 제공 |
| 실행 | 외부 WAS 배포 방식도 가능 | 내장 서버로 단독 실행하기 쉬움 |
| 의존성 관리 | 필요한 Spring 모듈과 외부 라이브러리의 조합, 버전을 개발자가 더 많이 신경 씀 | Starter와 BOM(Bill of Materials)으로 검증된 의존성 묶음과 버전 조합 제공 |
| 목적 | 유연한 애플리케이션 구조 제공 | 빠른 개발과 간단한 실행 환경 제공 |
여기서 말하는 의존성 관리는 라이브러리를 전혀 직접 추가하지 않는다는 뜻이 아닙니다.
Spring Boot를 사용하더라도 JPA(Java Persistence API)가 필요하면 JPA 관련 의존성을 추가하고, Security가 필요하면 Security 관련 의존성을 추가합니다.
차이는 의존성을 추가하는 방식과 관리 범위에 있습니다.
Spring만 사용한다면 Spring MVC, Jackson, Tomcat, Hibernate, 커넥션 풀 같은 세부 라이브러리를 어떤 조합과 버전으로 가져갈지 개발자가 더 많이 판단해야 합니다.
반면 Spring Boot에서는 spring-boot-starter-data-jpa, spring-boot-starter-security 같은 Starter를 추가하면 관련 라이브러리 묶음을 가져오고, Spring Boot의 BOM이 권장 버전 조합을 관리합니다.
그래서 개발자가 기능 단위의 의존성은 선택하지만, 세부 라이브러리와 버전 호환성은 Spring Boot가 많이 덜어준다고 이해하면 됩니다.
예를 들어 아래처럼 작성했다면 개발자가 JPA 기능을 선택한 것은 맞습니다.
dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
}
다만 Hibernate, Spring Data JPA, 트랜잭션 관련 라이브러리, 기본 버전 조합을 하나하나 직접 맞추기보다 Spring Boot가 제공하는 Starter와 의존성 관리 체계를 이용하는 것입니다.
쉽게 말하면,
- Spring은 애플리케이션을 어떻게 구조화할 것인가에 가깝고
- Spring Boot는 그 Spring 애플리케이션을 어떻게 빠르게 시작하고 실행할 것인가에 가깝습니다.
세 개념을 계층으로 보면
Java, Spring, Spring Boot는 아래처럼 계층적으로 이해할 수 있습니다.
Spring Boot
└── Spring Framework
└── Java / JVM
Java는 가장 아래에서 언어와 실행 환경을 제공합니다.
Spring은 그 위에서 POJO 기반 객체 관리, 웹 요청 처리, 트랜잭션, AOP 같은 서버 애플리케이션 구조를 제공합니다.
Spring Boot는 Spring 위에서 자동 설정, 내장 서버, Starter 의존성, 운영 편의 기능 같은 구성을 제공합니다.
그래서 "Spring Boot를 쓴다"는 말은 보통 Java로 작성하고, Spring Framework를 기반으로 하며, Spring Boot의 자동 설정과 실행 방식을 사용한다는 의미에 가깝습니다.
비유로 정리해보기
조금 단순화해서 비유하면 다음과 같습니다.
- Java는 요리를 할 수 있는 기본 재료와 도구입니다.
- Spring은 재료를 어떤 방식으로 다루고 역할을 나눌지 정리해주는 주방 운영 시스템입니다.
- Spring Boot는 자주 쓰는 조리 환경을 미리 세팅해둔 주방 키트입니다.
Java만 있어도 요리는 할 수 있습니다.
하지만 식당처럼 규모가 커지면 재료 관리, 조리 순서, 역할 분담이 중요해집니다. 이때 Spring이 구조를 잡아줍니다.
그리고 매번 주방을 처음부터 세팅하지 않도록 도와주는 것이 Spring Boot입니다.
비유는 어디까지나 큰 그림을 위한 설명입니다.
정확히 말하면 Java는 언어와 실행 환경, Spring은 애플리케이션 프레임워크, Spring Boot는 Spring 기반 애플리케이션의 설정과 실행을 단순화하는 도구입니다.
그래서 무엇부터 공부해야 할까?
개인적으로는 아래 순서가 가장 자연스럽다고 생각합니다.
- Java 기본 문법과 객체 지향 개념
- HTTP와 웹 서버의 기본 흐름
- Spring의 핵심 개념인 POJO, IoC, DI, Bean
- AOP와
@Transactional이 해결하는 문제 이해하기 - Spring MVC로 요청과 응답 처리하기
- Spring Boot의 자동 설정과 Starter 이해하기
- 데이터베이스, 트랜잭션, 테스트로 확장하기
처음부터 Spring Boot만 외우면 "왜 이렇게 동작하는지"가 흐릿해질 수 있습니다.
반대로 Spring 설정을 너무 깊게 먼저 파고들면 실제 애플리케이션을 만들기 전에 지칠 수 있습니다.
그래서 Java와 웹의 기본 흐름을 잡고, Spring의 핵심 개념을 이해한 뒤, Spring Boot로 프로젝트를 만들어보는 흐름이 좋다고 생각합니다.
마무리
Java, Spring, Spring Boot는 비슷한 단어처럼 보이지만 역할이 다릅니다.
- Java는 프로그래밍 언어이자 실행 생태계입니다.
- Spring은 POJO 기반 객체 관리와 AOP를 통해 Java 애플리케이션을 구조적으로 만들기 위한 프레임워크입니다.
- Spring Boot는 Spring 애플리케이션을 빠르게 만들고 실행하기 위한 도구입니다.
Spring Boot를 쓴다고 해서 Spring을 모르는 상태로 넘어가도 되는 것은 아닙니다.
Spring Boot는 Spring을 숨기는 기술이 아니라, Spring을 더 편하게 사용할 수 있도록 기본값을 제공하는 기술에 가깝습니다.
따라서 세 개념을 경쟁 관계로 보기보다, Java 위에 Spring이 있고, Spring 위에 Spring Boot가 있다고 이해하면 훨씬 정리하기 쉽습니다.
참고 자료
- Spring Framework Reference - Introduction to the Spring IoC Container and Beans
- Martin Fowler - POJO
- Spring Framework Reference - AOP Concepts
- Spring Framework Reference - Spring AOP Capabilities and Goals
- Spring Framework Reference - Declarative Transaction Management
- Spring Boot Reference - Auto-configuration
- Spring Boot Reference - Using the @SpringBootApplication Annotation
- Spring Boot Reference - Starters