
소개
Spring Boot는 Spring 생태계에서 빠른 애플리케이션 개발을 목표로 한 프레임워크입니다. 반복되는 설정을 자동화하고 스타터 의존성으로 초기 구성을 단순화합니다. 본문에서는 핵심 개념을 정리하고, 실제로 프로젝트에 적용할 때 마주하는 트레이드오프를 설명한 뒤 간단한 REST 서비스 예제를 통해 실무 적용 방법을 제시합니다.
핵심 개념 요약
Spring Boot를 이해하는 데 중요한 개념은 다음과 같습니다.
- Auto-Configuration: 애플리케이션의 classpath와 설정을 보고 필요한 빈을 자동으로 등록합니다.
- Starters: 자주 함께 쓰이는 의존성을 모아 제공(starter-web, starter-data-jpa 등).
- Embedded Server: Tomcat/Jetty/Undertow를 내장해 실행 가능한 JAR을 생성합니다.
- Actuator: 헬스, 메트릭, 환경 정보 등 운영에 유용한 엔드포인트를 제공합니다.
- Configuration Properties: 타입 안전한 설정 바인딩(@ConfigurationProperties).
- AOT / Native: 빠른 시작과 낮은 메모리 사용을 위한 AOT 또는 GraalVM Native Image 지원(설정/제약 존재).
주요 트레이드오프
편의성과 자동 설정은 생산성을 크게 높이지만 모든 상황에서 최선은 아닙니다. 주요 트레이드오프는 다음과 같습니다.
- 생산성 vs 제어권
자동 설정과 스타터는 초기 개발 속도를 올립니다. 반면 복잡한 커스터마이징을 하려면 자동 설정을 비활성화하거나 빈을 명시적으로 정의해야 할 수 있습니다.
- 클래스패스 스캐닝 편의성 vs 시작 시간/보안 표면적
컴포넌트 스캔은 설정을 줄여주지만 빈이 많아지면 시작 시간이 늘어나고 의도치 않은 빈 등록으로 보안 위험이나 충돌이 발생할 수 있습니다.
- Fat JAR(단일 실행 JAR) vs 다층 이미지
Fat JAR은 배포가 단순하지만 이미지 크기가 큽니다. 컨테이너 환경에서는 레이어 분리(또는 Spring Boot의 레이어드 JAR)를 사용해 빌드 캐시를 활용하는 것이 더 효율적입니다.
- JVM 이미지 vs Native 이미지
Native image는 시작 시간과 메모리 사용을 크게 줄입니다. 그러나 리플렉션이나 동적 클래스 생성 같은 기능에 제약이 있고 빌드 시간이 길며 디버깅이 더 어렵습니다.
- Actuator 편의성 vs 정보 노출
운영 정보를 쉽게 얻을 수 있지만, 엔드포인트 접근 제어, 노출 항목 최소화 등 보안 설정이 필수입니다.
실전 예제: 간단한 REST 서비스
아래 예제는 Maven 기반의 최소 Spring Boot 애플리케이션입니다. 기본적인 컨트롤러와 설정, Dockerfile을 포함합니다.
pom.xml (핵심 부분)
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
애플리케이션 진입점 (DemoApplication.java)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
간단한 컨트롤러 (HelloController.java)
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
public Map<String, String> hello() {
return Map.of("message", "Hello, Spring Boot");
}
}
application.properties (운영 관련 예)
server.port=8080
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=when-authorized
Dockerfile (멀티스테이지, JVM 기반)
# Build stage
FROM maven:3.8.8-eclipse-temurin-17 AS build
WORKDIR /workspace
COPY pom.xml .
COPY src ./src
RUN mvn -e -DskipTests package
# Run stage
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /workspace/target/demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app/app.jar"]
빌드와 실행
기본 JVM 빌드와 실행:
mvn package
java -jar target/demo-0.0.1-SNAPSHOT.jar
컨테이너 빌드:
docker build -t demo-app .
docker run -p 8080:8080 demo-app
Native 이미지(AOT) 관련: 빠른 시작이 필요하면 Spring AOT와 GraalVM native-image 빌드를 고려할 수 있습니다. 그러나 리플렉션, 동적 프록시, 런타임 생성 코드를 사용하는 라이브러리는 추가 설정(구성·등록)이 필요하고 빌드 시간이 늘어나므로 장단점을 검토하세요.
운영 및 확장 관점에서의 고려사항
- Actuator: 반드시 인증/권한 제어를 적용해 민감한 정보를 보호합니다.
- 로깅/모니터링: 로그 포맷(예: JSON), 분산 추적, 메트릭 수집을 초기부터 설계합니다.
- 컨테이너 최적화: 레이어드 JAR나 빌드 캐시를 활용하면 이미지를 작게 유지하고 CI 시간을 단축할 수 있습니다.
- 테스트 전략: 슬라이스 테스트(@WebMvcTest 등)로 빈 단위 테스트와 통합 테스트를 구분합니다.
결론
Spring Boot는 생산성을 크게 높여주지만, 자동화된 편의 기능이 항상 최선은 아닙니다. 애플리케이션 특성(초기화 시간, 메모리 제약, 운영 보안 요구사항)에 맞춰 자동 설정을 부분적으로 비활성화하거나 AOT/native 전략을 선택해야 합니다. 위 실전 예제는 빠르게 시작하는 데 필요한 최소 구성과 배포 방식을 보여주며, 이후에는 보안, 모니터링, 빌드 최적화 등을 단계적으로 적용하면 됩니다.