Day 24: Spring Boot 입문
Spring Boot는 Java 웹 애플리케이션 개발의 사실상 표준 프레임워크입니다. 복잡한 설정 없이 빠르게 애플리케이션을 만들 수 있습니다. 레스토랑에 비유하면, Spring은 주방 장비 세트이고 Spring Boot는 모든 장비가 이미 세팅된 턴키 주방입니다.
Spring Boot 프로젝트 시작
Spring Initializr(https://start.spring.io)로 프로젝트를 생성하거나 직접 설정합니다.
// build.gradle.kts
/*
plugins {
java
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
sourceCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.test {
useJUnitPlatform()
}
*/
// 메인 애플리케이션 클래스
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 자동 설정 + 컴포넌트 스캔 + 설정 클래스
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
// 내장 톰캣 서버가 8080 포트에서 시작됨
}
}
IoC/DI (제어의 역전과 의존성 주입)
Spring의 핵심 원리를 이해합니다. 객체 생성과 관리를 개발자가 아닌 Spring 컨테이너가 담당합니다.
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
// 인터페이스 정의
interface GreetingService {
String greet(String name);
}
// 구현체 1: @Service로 Spring Bean 등록
@Service
class KoreanGreetingService implements GreetingService {
@Override
public String greet(String name) {
return name + "님, 안녕하세요!";
}
}
// 구현체 2 (필요 시 @Primary나 @Qualifier로 선택)
// @Service
// class EnglishGreetingService implements GreetingService {
// @Override
// public String greet(String name) {
// return "Hello, " + name + "!";
// }
// }
// 의존성 주입 받는 클래스
@Component
class WelcomeProcessor {
private final GreetingService greetingService;
// 생성자 주입 (권장 방식)
// @Autowired 생략 가능 (생성자가 하나일 때)
WelcomeProcessor(GreetingService greetingService) {
this.greetingService = greetingService;
}
String processWelcome(String name) {
String greeting = greetingService.greet(name);
return "[환영] " + greeting;
}
}
// DI의 장점:
// 1. 느슨한 결합: 구현체 교체가 쉬움
// 2. 테스트 용이: Mock 객체 주입 가능
// 3. 코드 재사용: 같은 빈을 여러 곳에서 사용
간단한 웹 컨트롤러
HTTP 요청을 처리하는 컨트롤러를 만듭니다.
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.time.LocalDateTime;
import java.util.Map;
@RestController // JSON 응답을 반환하는 컨트롤러
@RequestMapping("/api") // 공통 경로 접두사
class HelloController {
private final GreetingService greetingService;
HelloController(GreetingService greetingService) {
this.greetingService = greetingService;
}
// GET /api/hello
@GetMapping("/hello")
Map<String, Object> hello() {
return Map.of(
"message", "Hello, Spring Boot!",
"timestamp", LocalDateTime.now().toString()
);
}
// GET /api/hello/홍길동
@GetMapping("/hello/{name}")
Map<String, String> helloName(@PathVariable String name) {
return Map.of(
"greeting", greetingService.greet(name)
);
}
// GET /api/search?keyword=java&page=1
@GetMapping("/search")
Map<String, Object> search(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page) {
return Map.of(
"keyword", keyword,
"page", page,
"results", "'" + keyword + "' 검색 결과 (페이지 " + page + ")"
);
}
// POST /api/echo
@PostMapping("/echo")
ResponseEntity<Map<String, Object>> echo(@RequestBody Map<String, Object> body) {
return ResponseEntity.ok(Map.of(
"received", body,
"echo", true,
"timestamp", LocalDateTime.now().toString()
));
}
}
application.properties 설정
Spring Boot 애플리케이션 설정 파일입니다.
// src/main/resources/application.properties
/*
# 서버 설정
server.port=8080
server.servlet.context-path=/
# 로깅 설정
logging.level.root=INFO
logging.level.com.example=DEBUG
# JSON 설정
spring.jackson.serialization.indent-output=true
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# 데이터베이스 (H2 인메모리)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# H2 콘솔 활성화 (개발용)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
*/
// application.yml 형식 (대안):
/*
server:
port: 8080
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
logging:
level:
root: INFO
com.example: DEBUG
*/
// 프로파일별 설정:
// application-dev.properties (개발)
// application-prod.properties (운영)
// 활성화: spring.profiles.active=dev
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
class AppConfig {
@Value("${server.port:8080}")
private int port;
@Value("${app.name:MyApp}")
private String appName;
void printConfig() {
System.out.println("포트: " + port);
System.out.println("앱 이름: " + appName);
}
}
오늘의 연습문제
-
프로필 API:
/api/profile엔드포인트를 만드세요. GET은 프로필 정보(이름, 이메일, 자기소개)를 반환하고, POST는 프로필을 업데이트합니다. 데이터는 메모리(Map)에 저장하세요. -
환율 계산기 API:
/api/exchange?from=USD&to=KRW&amount=100형태의 GET 요청을 처리하는 컨트롤러를 만드세요. 환율 데이터는 Map으로 하드코딩하고, 없는 통화 요청 시 적절한 에러 응답을 반환하세요. -
DI 실습:
NotificationService인터페이스와EmailNotification,SmsNotification구현체를 만들고,@Qualifier로 원하는 구현체를 주입받는 컨트롤러를 작성하세요.