> ## Documentation Index
> Fetch the complete documentation index at: https://blog.nvim.me/llms.txt
> Use this file to discover all available pages before exploring further.

# 왜 Spring과 Spring Boot가 필요했을까요?

> 객체를 직접 만들고 연결하던 코드가 왜 Spring의 컨테이너로 옮겨갔는지, 그리고 Spring Boot가 어떤 설정 부담을 덜어주는지 첫 장면부터 풀어봐요.

> 컨트롤러 클래스 하나 만들었을 뿐인데, 왜 서버가 뜨고 요청이 들어오고 JSON이 나갈까요?

처음 Spring Boot 프로젝트를 열면 묘한 느낌이 들어요.

내가 직접 `new`로 컨트롤러를 만든 적도 없고, 웹 서버를 띄운 적도 없고, JSON 변환기를 연결한 적도 없는데요.

```java theme={null}
package com.example.order;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
class OrderController {

    @GetMapping("/orders")
    String orders() {
        return "orders";
    }
}
```

이런 클래스 하나가 있으면 어느 순간 `/orders` 요청을 받을 수 있어요. 처음 보면 편한데, 동시에 좀 이상하죠.

> "내가 안 만든 객체는 누가 만들었지?"\
> "내가 안 연결한 서버는 어디서 나왔지?"\
> "설정이 적다는 건 내부 동작도 단순하다는 뜻일까?"

오늘은 이 질문에서 시작할게요. 여기서 다루는 **왜 Spring이 필요했고 Boot가 무엇을 덜어냈는지**라는 큰 그림은 특정 Spring Boot 버전 하나에 묶인 이야기가 아니라, Spring Boot 전반을 이해하는 출발점이에요.

참고로 공식 문서 기준으로는 Spring Framework의 [제어의 역전 컨테이너(IoC container)](https://docs.spring.io/spring-framework/reference/core/beans/introduction.html), Spring Boot의 [자동 설정(auto-configuration)](https://docs.spring.io/spring-boot/reference/using/auto-configuration.html), 그리고 [Spring Boot 프로젝트 소개](https://spring.io/projects/spring-boot)를 배경으로 잡고 읽어볼게요.

<Note title="이 글의 범위">
  여기서는 `@RestController`, `@SpringBootApplication`, `SpringApplication.run`의 모든 내부 단계를 파헤치지 않아요. 오늘 목표는 **Spring이 가져간 일**과 **Spring Boot가 줄여준 일**을 분리해서 보는 거예요. 세부 동작은 뒤 글에서 하나씩 열어볼게요.

  다만 "편해졌다"에서 끝내지는 않을게요. 뒤쪽에서는 Boot가 어떤 재료를 보고 기본값을 고르는지, 그리고 그 기본값이 실무에서 어떤 확인 지점으로 이어지는지도 같이 잡아볼 거예요.
</Note>

***

## 직접 다 만들던 시절의 코드를 떠올려볼게요

주문을 조회하는 작은 코드를 만든다고 해볼게요.

```java theme={null}
package com.example.order;

class OrderController {

    private final OrderService orderService;

    OrderController() {
        OrderRepository orderRepository = new OrderRepository();
        this.orderService = new OrderService(orderRepository);
    }

    String findOrders() {
        return orderService.findOrders();
    }
}
```

겉으로는 단순해요. 컨트롤러가 서비스도 만들고, 서비스가 쓸 저장소도 만들어요.

그런데 애플리케이션이 커지면 이 방식은 금방 부담이 돼요.

* `OrderService`가 `PaymentClient`, `InventoryClient`, `Clock`까지 필요해지면 생성 코드가 계속 커져요.
* 테스트에서는 진짜 `OrderRepository` 대신 가짜 저장소를 넣고 싶은데, 컨트롤러가 이미 직접 만들어버렸어요.
* 여러 곳에서 같은 객체를 써야 할 때, 누가 언제 만들고 공유할지 기준이 흐려져요.
* 트랜잭션, 보안, 로깅처럼 여러 기능에 걸치는 규칙을 매번 손으로 끼워 넣어야 해요.

처음에는 `new`가 자유로워 보이지만, 규모가 커질수록 **객체를 만드는 코드**와 **실제 업무 코드**가 뒤엉켜요.

컨트롤러는 주문 요청을 받아야 하는데, 어느 순간 객체 조립 담당자까지 하고 있는 셈이에요.

***

## 문제는 업무 코드와 조립 코드가 섞인다는 점이에요

컨트롤러가 서비스를 직접 만들고, 서비스가 저장소를 직접 만들면 처음에는 흐름이 눈에 잘 보여요. 하지만 이 구조에서는 클래스가 자기 일을 하기 전에 실행 환경을 조립하는 일까지 떠안아요.

Spring이 줄이려는 부담은 바로 이 지점이에요.

| 코드 안에 섞인 일                 | Spring을 쓰면 어디로 옮겨갈까요?               |
| -------------------------- | ----------------------------------- |
| 객체를 언제 만들지 결정함             | Spring 컨테이너가 빈(bean) 생성 시점을 관리해요    |
| 어떤 구현을 넣을지 직접 고름           | 설정, Annotation, 조건에 따라 빈 관계를 구성해요   |
| 여러 객체의 생성 순서를 맞춤           | 의존성 그래프를 보고 필요한 순서로 준비해요            |
| 트랜잭션, 보안, 로깅 같은 공통 규칙을 반복함 | 프록시(proxy)와 AOP 같은 구조로 메서드 주변에 적용해요 |

여기서 핵심은 **Spring이 업무를 대신한다**가 아니에요.

Spring이 주문 로직을 대신 작성해주는 건 아니에요. 대신 객체를 만들고, 필요한 객체를 연결하고, 공통 규칙을 적용하는 일을 맡아요. 그래서 개발자는 주문 조회, 결제, 재고 차감 같은 애플리케이션의 핵심 행동에 더 집중할 수 있어요.

***

## Spring은 객체 조립 책임을 가져갔어요

Spring Framework의 출발점은 거창한 마법보다 **객체를 누가 만들고 연결할 것인가**에 가까워요.

직접 만드는 방식은 이래요.

```java theme={null}
OrderRepository orderRepository = new OrderRepository();
OrderService orderService = new OrderService(orderRepository);
OrderController orderController = new OrderController(orderService);
```

Spring을 쓰면 흐름이 바뀌어요.

```java theme={null}
package com.example.order;

import org.springframework.stereotype.Service;

@Service
class OrderService {

    private final OrderRepository orderRepository;

    OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
}
```

`OrderService`는 이제 `OrderRepository`를 직접 만들지 않아요. 필요한 것을 생성자로 드러내고, 실제 연결은 Spring 컨테이너가 맡아요.

이걸 **의존성 주입(dependency injection)** 이라고 불러요. 더 큰 원칙으로는 **제어의 역전(inversion of control, IoC)** 이라고 해요.

말이 조금 딱딱하죠. 쉽게 말하면 이거예요.

> "내가 직접 만들던 객체들을, 이제 컨테이너가 만들고 꽂아준다."

```mermaid theme={null}
flowchart LR
    A[개발자 코드<br /><small>필요한 의존성을 생성자로 표현</small>]
    B[Spring 컨테이너<br /><small>객체 생성과 연결 담당</small>]
    C[실행 중 객체 그래프<br /><small>Controller -> Service -> Repository</small>]

    A --> B --> C
```

이 구조 덕분에 객체 생성 규칙이 한곳으로 모여요. 테스트할 때 다른 구현을 넣기도 쉬워지고, 공통 기능을 프록시(proxy)로 감싸는 일도 가능해져요.

여기까지가 먼저 Spring의 큰 역할이에요.

***

## 그런데 Spring만으로도 설정은 꽤 많았어요

Spring이 객체 조립 문제를 덜어줬다고 해서, 웹 애플리케이션 준비가 저절로 끝나는 건 아니었어요.

웹 서버를 붙이고, 요청을 컨트롤러로 보내는 장치를 등록하고, JSON 변환기를 연결하고, 데이터베이스 설정을 넣고, 운영에서 볼 상태 확인 기능을 켜는 일은 여전히 필요했어요.

대략 이런 질문들이 계속 따라와요.

| 하고 싶은 일     | 직접 정해야 하는 것                        |
| ----------- | ---------------------------------- |
| 웹 API 만들기   | 어떤 웹 라이브러리를 넣고, 요청을 누가 받을지         |
| JSON 응답 보내기 | 어떤 JSON 변환기를 쓰고, 어떻게 등록할지          |
| 데이터베이스 연결   | 드라이버, 커넥션 풀, 트랜잭션 설정을 어떻게 둘지       |
| 운영 상태 보기    | health, metrics 같은 엔드포인트를 어떻게 노출할지 |
| 배포하기        | 서버에 올릴지, 실행 가능한 애플리케이션으로 만들지       |

이 설정들은 대부분 프로젝트마다 완전히 새롭지 않아요.

웹 API를 만들면 보통 웹 관련 기본 구성이 필요하고, 데이터베이스 드라이버를 넣으면 보통 데이터소스 설정이 필요하고, Actuator를 넣으면 운영 확인 엔드포인트가 필요해요.

여기서 Spring Boot가 들어와요.

***

## Spring Boot는 반복 설정의 기본값을 가져왔어요

Spring Boot는 Spring을 대체하는 별도 프레임워크라기보다, **Spring 애플리케이션을 더 빨리, 더 일관되게 시작하게 해주는 층**에 가까워요.

Boot가 줄여주는 대표적인 부담은 세 가지예요.

### 1. 의존성을 묶어서 고르게 해줘요

웹 애플리케이션을 만들 때 필요한 라이브러리를 하나씩 고르면 버전 조합이 금방 피곤해져요.

그래서 Boot는 스타터(starter)라는 묶음을 제공해요. 예를 들어 웹 스타터를 넣으면 Spring MVC, 내장 웹 서버, JSON 처리에 필요한 기본 조합을 함께 가져오는 식이에요.

스타터는 "이 기능을 하려면 보통 이 조합이 필요하다"는 출발점을 만들어줘요.

### 2. 클래스패스를 보고 자동 설정을 시도해요

Boot의 자동 설정(auto-configuration)은 프로젝트에 들어온 라이브러리와 사용자가 이미 등록한 설정을 보고 기본 구성을 시도해요.

예를 들어 웹 관련 라이브러리가 있으면 웹 애플리케이션에 필요한 기본 설정을 준비하고, 사용자가 직접 같은 종류의 설정을 제공하면 그쪽을 우선할 수 있어요.

중요한 건 자동 설정이 **개발자 코드를 덮어쓰기 위한 기능이 아니라는 점**이에요. 보통은 기본값을 먼저 깔아주고, 필요하면 개발자가 명시한 설정으로 바꿔갈 수 있게 설계돼요.

### 3. 실행 가능한 애플리케이션 형태를 기본으로 잡아요

예전 Java 웹 애플리케이션은 별도 애플리케이션 서버에 배포하는 방식이 익숙했어요. Boot는 내장 서버를 사용해서 애플리케이션 자체를 실행하는 흐름을 자연스럽게 만들었어요.

그래서 `main` 메서드에서 애플리케이션을 시작하는 모양이 나와요.

```java theme={null}
package com.example.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
```

이 코드는 짧지만, 빈(bean) 등록, 환경 설정 읽기, 자동 설정 적용, 웹 서버 시작 같은 큰 흐름의 입구가 돼요.

***

## 그래서 "설정이 없다"가 아니라 "기본값이 있다"예요

Spring Boot를 처음 만날 때 가장 조심해야 할 오해가 있어요.

> "설정을 안 했으니 내부도 별로 없는 거 아닌가?"

아니에요. 오히려 반대에 가까워요.

Boot는 자주 쓰는 설정을 없앤 게 아니라, **합리적인 기본값으로 미리 배치**해둔 거예요. 그래서 초반에는 코드가 적어 보이고, 어느 순간부터는 "왜 이렇게 동작하지?"라는 질문이 생겨요.

```mermaid theme={null}
flowchart TD
    A[내가 작성한 코드<br /><small>Controller, Service, Repository</small>]
    B[Spring Framework<br /><small>객체 생성, 의존성 주입, 프록시, 컨테이너</small>]
    C[Spring Boot<br /><small>스타터, 자동 설정, 실행 진입점, 운영 기본값</small>]
    D[실행 중 애플리케이션<br /><small>요청 처리, JSON 응답, DB 연결, 상태 확인</small>]

    A --> B
    B --> C
    C --> D
```

처음에는 Boot 덕분에 빨리 시작해요. 하지만 제대로 운영하려면 결국 Spring이 무엇을 만들었는지, Boot가 어떤 기본값을 골랐는지 읽을 수 있어야 해요.

그래서 이 시리즈는 "Annotation을 외우는 글"이 아니라, **내가 쓴 코드와 프레임워크가 대신 한 일을 나눠보는 글**로 갈 거예요.

***

## 여기부터는 조금 더 깊게 볼게요

Boot의 기본값은 "Spring Boot가 알아서 잘해준다"라는 한 문장으로 끝내면 편해요. 그런데 실무에서는 그 문장이 너무 뭉뚱그려져서 문제를 만들 때가 많아요.

조금 더 정확히 보면 Boot는 보통 이런 재료를 함께 봐요.

| Boot가 보는 재료                 | 왜 중요할까요?                                          |
| --------------------------- | ------------------------------------------------- |
| 클래스패스(classpath)에 들어온 라이브러리 | 웹, 데이터베이스, JSON, 보안 같은 기능 후보를 판단해요                |
| 사용자가 직접 등록한 빈(bean)         | 이미 명시한 설정이 있으면 기본 설정이 물러날 수 있어요                   |
| 설정 파일과 환경 변수                | 포트, 프로필, 커넥션 정보처럼 실행 모양을 바꿔요                      |
| 조건(condition)               | "이 클래스가 있을 때만", "이 빈이 없을 때만" 같은 자동 설정의 적용 여부를 가려요 |

그래서 자동 설정(auto-configuration)은 "무조건 켜지는 마법"이 아니라, **조건을 만족할 때 적용되는 기본 설정 후보**에 가까워요.

```mermaid theme={null}
flowchart LR
    A[스타터 의존성<br /><small>클래스패스에 재료가 들어옴</small>]
    B[자동 설정 후보<br /><small>조건을 보고 적용 여부 판단</small>]
    C[사용자 설정<br /><small>직접 만든 빈과 설정값</small>]
    D[실행 중 컨텍스트<br /><small>최종 빈과 기본값 조합</small>]

    A --> B
    C --> B
    B --> D
```

이 그림에서 중요한 건 `자동 설정 후보`예요. Boot는 스타터가 있다고 해서 항상 같은 객체를 무조건 밀어 넣지 않아요. 조건을 보고, 이미 사용자가 만든 것이 있는지 보고, 최종적으로 컨텍스트에 무엇을 둘지 결정해요.

실무에서는 이 차이가 디버깅 기준이 돼요.

| 보이는 증상                   | 먼저 볼 질문                                         |
| ------------------------ | ----------------------------------------------- |
| 서버가 예상과 다른 포트로 떠요        | 어떤 설정값이 우선했나요? 명령줄 인자, 환경 변수, 설정 파일 중 무엇이 이겼나요? |
| 내가 만든 설정이 적용되지 않아요       | 빈이 실제로 등록됐나요? 자동 설정 조건 때문에 다른 구성이 들어왔나요?        |
| 라이브러리를 넣었더니 갑자기 동작이 생겼어요 | 스타터가 어떤 의존성을 클래스패스에 추가했나요?                      |
| 테스트에서는 되는데 실행하면 달라요      | 테스트 컨텍스트와 실제 실행 컨텍스트가 같은 빈과 설정을 쓰나요?            |

처음 읽을 때는 여기까지 전부 외울 필요 없어요. 다만 Spring Boot를 깊게 읽는 출발점은 이 문장으로 잡으면 좋아요.

> Boot는 설정을 없앤 게 아니라, 조건을 가진 기본 설정을 먼저 제안하고, 개발자가 명시한 선택과 합쳐 실행 컨텍스트를 만들어요.

***

## Spring과 Boot를 한 문장씩 나누면

헷갈릴 때는 이렇게 잡으면 좋아요.

| 이름               | 먼저 떠올릴 역할                                          |
| ---------------- | -------------------------------------------------- |
| Spring Framework | 객체를 만들고 연결하고, 공통 규칙을 적용하는 기반                       |
| Spring Boot      | Spring 애플리케이션을 빠르게 시작하도록 의존성, 자동 설정, 실행 방식을 정리한 도구 |

조금 더 실무적으로 말하면 이래요.

* Spring은 "이 객체들은 누가 만들고 어떻게 연결하지?"라는 질문에 답해요.
* Spring Boot는 "웹 서버, JSON, 설정 파일, 운영 기본값을 매번 어떻게 준비하지?"라는 질문에 답해요.
* 둘을 같이 쓰면, 개발자는 처음부터 모든 배선을 깔기보다 애플리케이션의 실제 행동부터 작성할 수 있어요.

하지만 그 편리함은 공짜가 아니에요. 대신 프레임워크가 무엇을 해줬는지 읽는 능력이 필요해져요.

***

## 자, 정리해볼까요?

<Info title="오늘 우리가 잡은 큰 그림">
  * 직접 `new`로 객체를 만들면 작은 코드에서는 편하지만, 규모가 커질수록 조립 코드와 업무 코드가 뒤섞여요.
  * Spring은 객체 생성과 연결을 컨테이너가 맡게 해서, 개발자 코드가 자기 책임에 집중하게 도와줘요.
  * Spring Boot는 스타터와 자동 설정으로 반복되는 Spring 애플리케이션 설정을 줄여줘요.
  * Boot가 설정을 줄여준다는 말은 내부 동작이 없다는 뜻이 아니라, 기본값을 먼저 준비해준다는 뜻이에요.
  * 조금 더 깊게 보면 Boot의 기본값은 클래스패스, 사용자 빈, 설정값, 조건을 보고 선택되는 실행 컨텍스트의 일부예요.
</Info>

이제 처음 질문으로 돌아가 볼게요.

> "컨트롤러만 만들었는데 왜 서버가 떠요?"

답은 이래요.

컨트롤러는 내가 만들었지만, 그 컨트롤러를 발견하고 객체로 만들고, 웹 요청과 연결하고, 내장 서버 위에서 실행되게 준비한 쪽은 Spring과 Spring Boot예요.

다음 글에서는 이 흐름을 직접 프로젝트 생성 장면에서 이어볼게요. Spring Initializr에서 고르는 옵션들이 실제로 어떤 파일과 실행 구조로 바뀌는지 볼 차례예요.


## Related topics

- [Spring Framework Reference: Introduction to the Spring IoC Container and Beans](https://docs.spring.io/spring-framework/reference/core/beans/introduction.html)
- [Spring Boot Reference: Auto-configuration](https://docs.spring.io/spring-boot/reference/using/auto-configuration.html)
- [Spring Boot Project Page](https://spring.io/projects/spring-boot)
