
Spring은 자바 기반의 애플리케이션 개발을 위한 대표적인 프레임워크로,
객체 지향 설계의 원칙을 실현하고 의존성 관리(DI), AOP, 트랜잭션, 보안 등 핵심 기능을 제공하는 강력한 플랫폼입니다.
Spring Boot는 이러한 Spring의 설정 복잡도를 줄이고, 자동 설정과 내장 서버를 제공하여 생산성을 극대화한 프레임워크입니다.
Client
└─HTTP─> [Spring MVC] Controller
│
▼
Service (@Transactional)
│
▼
Repository (JPA)
│
▼
DB/Cache/외부API
요청 경로(일반): Filter → Interceptor → @Controller → (AOP/프록시) Service → Repository
요청 경로(JWT): JwtAuthFilter가 가장 먼저 토큰 검증 → SecurityContext 설정 → 위와 동일
모니터링: Actuator/Micrometer → Prometheus → Grafana 시각화
| 기능 | 설명 |
|---|---|
| DI (Dependency Injection) | 객체 간 의존성을 외부에서 주입하여 재사용성과 테스트 용이성 향상 |
| IoC (Inversion of Control) | 객체의 생성과 관리 제어권을 개발자가 아닌 Spring 컨테이너가 담당 |
| AOP (Aspect-Oriented Programming) | 로깅, 보안, 트랜잭션 등 공통 관심사를 핵심 로직과 분리 |
| MVC 패턴 지원 | Model-View-Controller 구조로 웹 애플리케이션 설계 용이 |
| Transaction 관리 | 선언적 트랜잭션으로 안정적 DB 작업 보장 |
Spring Boot는 Spring의 복잡한 설정을 자동화하고 쉽고 빠른 개발 환경을 제공하는 확장 프레임워크입니다.
즉, Spring을 쉽게 쓰기 위한 도구로 볼 수 있습니다.
| 기능 | 설명 |
|---|---|
| 자동 설정 (Auto Configuration) | 설정 파일 최소화, 의존성 자동 등록 |
| 내장 서버 (Embedded Tomcat) | 별도의 WAS 설치 없이 실행 가능 |
| Starter 의존성 | 필요한 모듈을 묶어둔 패키지 제공 (예: spring-boot-starter-web) |
| Actuator | 서버 상태, 헬스체크, 메트릭 등을 모니터링 |
| Profile 분리 | dev, test, prod 등 환경별 설정 관리 용이 |
| 항목 | Spring | Spring Boot |
|---|---|---|
| 설정 방식 | XML 또는 Java Config 수동 설정 | 자동 설정 (AutoConfiguration) |
| 서버 실행 | 외부 WAS 설치 필요 | 내장 톰캣 제공 |
| 의존성 관리 | 개별 라이브러리 직접 추가 | Starter 종속성 제공 |
| 배포 형태 | WAR 파일 중심 | 단일 JAR 실행 가능 |
| 학습 난이도 | 상대적으로 높음 | 낮음 (초보자 친화적) |
권장 패턴: 생성자 주입 + final 필드 + @RequiredArgsConstructor
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository; // 불변 의존성
}
✅ 장점: 불변성, 순환참조 조기 감지, 테스트가 쉬움.
⚠️ 세터/필드 주입은 런타임 오류 발견 지연, 불변성 약화.
@Component 스캔(@Service, @Repository, @Controller 포함)@Configuration + @Bean@Configuration
public class AppConfig {
@Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}
final 메서드 불가.@Transactional)readOnly=true: 영속성 컨텍스트 읽기 최적화(더티체크 off) + 슬레이브 라우팅에 활용.REQUIRED(기본), REQUIRES_NEW, MANDATORY 등.READ_COMMITTED 등 DB 수준과 맞춰 설계.RuntimeException/Error 기본 롤백, 체크 예외는 명시 필요.@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
private final MemberRepository repo;
@Transactional // 쓰기
public Long register(Member m) { return repo.save(m).getId(); }
}
@RestController
@RequestMapping("/api/members")
public class MemberController {
private final MemberService service;
@GetMapping("/{id}") public MemberDto get(@PathVariable Long id) { return service.get(id); }
}
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<?> handle(MethodArgumentNotValidException e) { return ResponseEntity.badRequest().body("invalid"); }
}
팁: 검증은
@Valid, DTO 사용. 컨트롤러에서 엔티티 노출 지양.
@SpringBootApplication → @EnableAutoConfiguration 포함.META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(또는 예전 spring.factories)에 등록된 자동설정 클래스 로드.@ConditionalOnClass/Bean/MissingBean 등 조건부로 Bean 등록.@Bean 직접 정의), @ConditionalOnMissingBean 체크.spring-boot-starter-web, data-jpa, validation, security 등 조합.application.yml/properties + 우선순위(CLI args > env > 파일). @ConfigurationProperties(prefix="...")@ConfigurationProperties(prefix="storage.s3")
record S3Props(String bucket, String region) {}
java -jar 실행, Dockerize 용이.spring.profiles.active=dev 또는 --- 분리로 멀티 프로필.logging.level로 패키지별 로깅 제어, JSON 로거(ELK) 권장.| 스코프 | 설명 | 흔한 사용 |
|---|---|---|
| singleton | 애플 전체 1개(기본) | 대부분의 서비스/리포지토리 |
| prototype | 주입 시마다 새 인스턴스 | 상태가 있는 helper (드묾) |
| request | HTTP 요청 단위 | 요청 컨텍스트 보관 |
| session | HTTP 세션 단위 | 사용자별 상태 |
| application | 서블릿 컨텍스트 | 전역 캐시 등 |
⚠️ 싱글턴 빈은 상태 없어야 Thread-safe. 가변 필드 공유 금지.
@PostConstruct 또는 InitializingBean#afterPropertiesSet@PreDestroy 또는 DisposableBean#destroyApplicationContext
├─ BeanFactory(핵심 IoC)
├─ Environment(프로퍼티/프로필)
├─ MessageSource(i18n)
├─ ApplicationEventPublisher(이벤트)
└─ ResourceLoader(클래스패스/파일/Git 등 리소스)
@Component
class UserCreatedEventListener {
@EventListener
public void on(UserCreatedEvent e) { /* 알림/로그 */ }
}
<Base64Url(Header)>.<Base64Url(Payload)>.<HMAC/RS256 Signature>
POST /api/auth/login {username, password}
→ AuthenticationManager가 UserDetailsService로 사용자 조회
→ PasswordEncoder로 비밀번호 비교
→ 성공 시 AccessToken(짧음), RefreshToken(김) 발급
Bearer.csrf().disable() (단, 쿠키 사용 시 SameSite/CSRF 재검토).클라이언트가 Authorization: Bearer <access> 로 API 호출
→ JwtAuthFilter(OncePerRequestFilter)가 토큰 파싱/검증(exp, signature, blacklist 등)
→ 유효하면 UsernamePasswordAuthenticationToken 생성하여 SecurityContextHolder 저장
→ 컨트롤러/서비스에서 @PreAuthorize("hasRole('ADMIN')") 등으로 접근 제어
필터 체인 구성 예
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**", "/actuator/health").permitAll()
.requestMatchers(HttpMethod.GET, "/api/articles/**").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
JwtFilter 핵심
public class JwtFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
String token = resolve(req);
if (token != null && provider.validate(token)) {
Authentication auth = provider.getAuthentication(token); // UserDetails + 권한
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(req, res);
}
}
@EnableMethodSecurity
class SecConfig {}
@Service
class ReportService {
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
public Report getReport(String userId) { ... }
}
팁: 권한은
ROLE_prefix. 커스텀 권한은hasAuthority('SCOPE_read')등.
HttpOnly+SameSite=Strict.CorsConfigurationSource 등록, 허용 도메인만 화이트리스트.| 엔드포인트 | 용도 |
|---|---|
/actuator/health | 헬스체크 (DB/Redis/SMTP 등 HealthContributor) |
/actuator/metrics | Micrometer 노출, jvm.memory.used, http.server.requests |
/actuator/prometheus | Prometheus 스크레이프 대상 |
/actuator/loggers | 런타임 로깅 레벨 GET/POST 변경 |
/actuator/env | 외부설정 확인(민감 정보 주의) |
설정
management:
endpoints:
web.exposure.include: health,info,metrics,prometheus,loggers
endpoint.health.show-details: when_authorized
@Component
class ApiHealth implements HealthIndicator {
public Health health() {
boolean ok = pingExternal();
return ok ? Health.up().build() : Health.down().withDetail("api","down").build();
}
}
@Component
class BusinessMetrics {
private final MeterRegistry meter;
BusinessMetrics(MeterRegistry m) { this.meter = m; }
public void countOrder(){ meter.counter("biz.order.count").increment(); }
}
보안: 운영에선 Actuator 엔드포인트를 인증 뒤 노출,
/actuator/env등은 제한.
fetch join, @EntityGraph, 캐시.maximumPoolSize, connectionTimeout.WRITE_DATES_AS_TIMESTAMPS off, 모듈 캐시.@SpringBootTest(webEnvironment=RANDOM_PORT)@WebMvcTest(컨트롤러만), @DataJpaTest(JPA만)MockMvc 로 API 테스트, Testcontainers로 실DB 통합.readiness 프로브는 /actuator/health 사용.