출시·고도화 중
Spring Boot 안내서 · 3/6
Spring Boot는 Spring Framework 위에서 동작합니다. 객체를 만들고 연결하는 일은 Spring의 IoC 컨테이너가 맡고, Spring Boot는 그 위에 스타터와 자동 구성을 더해 설정 작업을 크게 줄여 줍니다. 이 장에서는 빈과 의존성 주입, 자동 구성, 스타터, 웹 계층, 타입이 있는 설정을 다룹니다.
컨테이너가 관리하는 객체를 빈(bean) 이라고 합니다. 클래스에 스테레오타입 어노테이션을 붙이면 컴포넌트 스캔이 찾아 빈으로 등록합니다.
| 어노테이션 | 쓰임 |
|---|---|
@Component | 일반 컴포넌트 |
@Service | 비즈니스 로직 |
@Repository | 데이터 접근, 저장소 예외를 Spring 예외로 변환 |
@Controller / @RestController | 웹 요청 처리 |
빈이 다른 빈을 필요로 하면 생성자 주입으로 받습니다. 생성자가 하나뿐이면 @Autowired를 붙이지 않아도 됩니다. 필드를 final로 두면 테스트에서 직접 생성하기도 쉽습니다.
@Service
public class GreetingService {
private final Clock clock;
public GreetingService(Clock clock) {
this.clock = clock;
}
public String greet(String name) {
int hour = LocalTime.now(clock).getHour();
return (hour < 12 ? "Good morning, " : "Hello, ") + name;
}
}라이브러리 클래스처럼 어노테이션을 붙일 수 없는 객체는 @Configuration 클래스의 @Bean 메서드로 등록합니다.
@Configuration
public class AppConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}@SpringBootApplication은 세 가지 어노테이션을 합친 것입니다. @SpringBootConfiguration(설정 클래스), @ComponentScan(컴포넌트 스캔), 그리고 @EnableAutoConfiguration(자동 구성)입니다.
자동 구성은 클래스 경로와 이미 등록된 빈을 보고 필요한 빈을 대신 만듭니다. 예를 들어 클래스 경로에 Spring MVC와 Tomcat이 있으면 내장 웹 서버와 DispatcherServlet을, JDBC 드라이버와 spring.datasource.url이 있으면 DataSource를 구성합니다. 각 자동 구성은 @ConditionalOnClass, @ConditionalOnMissingBean 같은 조건으로 보호되므로, 같은 타입의 빈을 직접 정의하면 자동 구성은 물러납니다. 직접 정의한 빈이 항상 우선합니다.
어떤 자동 구성이 왜 적용되었는지 궁금하면 --debug로 실행해 조건 평가 보고서를 봅니다. 특정 자동 구성을 끄려면 exclude를 씁니다.
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DemoApplication { /* ... */ }스타터는 한 기능에 필요한 의존성을 묶은 패키지입니다. 의존성 하나로 라이브러리와 그에 맞는 자동 구성이 함께 들어옵니다.
spring-boot-starter-webmvc: Spring MVC, 내장 Tomcat, JSON(Jackson)spring-boot-starter-data-jpa: Spring Data JPA, Hibernatespring-boot-starter-validation: Bean Validationspring-boot-starter-security: Spring Securityspring-boot-starter-actuator: 상태 확인, 지표 같은 운영 기능@RestController의 메서드는 반환 값을 JSON으로 바꿔 응답합니다. 경로 변수는 @PathVariable, 요청 본문은 @RequestBody로 받습니다. Java 레코드는 요청·응답 객체로 쓰기 좋습니다.
@RestController
@RequestMapping("/api/todos")
public class TodoController {
private final TodoService service;
public TodoController(TodoService service) {
this.service = service;
}
@GetMapping("/{id}")
public TodoResponse get(@PathVariable Long id) {
return service.find(id);
}
@PostMapping
public ResponseEntity<TodoResponse> create(@RequestBody CreateTodo request) {
TodoResponse created = service.create(request.title());
return ResponseEntity.created(URI.create("/api/todos/" + created.id())).body(created);
}
public record CreateTodo(String title) {}
public record TodoResponse(Long id, String title, boolean done) {}
}@Value("${...}")로 값을 하나씩 받을 수도 있지만, 관련 설정이 여럿이면 @ConfigurationProperties로 묶는 편이 안전합니다. 접두어 아래의 키가 레코드 필드에 바인딩됩니다.
@ConfigurationProperties("app.mail")
public record MailProperties(String from, Duration timeout, boolean enabled) {}app:
mail:
from: no-reply@example.com
timeout: 5s
enabled: true진입점 클래스에 @ConfigurationPropertiesScan을 붙이면 이런 클래스를 찾아 빈으로 등록하므로, 다른 빈에서 생성자로 주입받을 수 있습니다. 5s처럼 쓴 값은 Duration으로 자동 변환됩니다.
Kotlin 클래스는 기본적으로 final이라 Spring의 프록시를 만들 수 없습니다. Initializr로 Kotlin 프로젝트를 만들면 kotlin("plugin.spring") 플러그인이 들어가, Spring 어노테이션이 붙은 클래스를 자동으로 열어 줍니다. 생성자 주입은 Kotlin의 주 생성자와 잘 맞습니다.
@Service
class GreetingService(private val clock: Clock) {
fun greet(name: String) = "Hello, $name"
}@ConfigurationProperties 레코드로 묶어 타입 안전하게 씁니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.