[CS] Spring / Spring Boot

박원준·2025년 10월 14일

🧩 Spring / Spring Boot

Spring은 자바 기반의 애플리케이션 개발을 위한 대표적인 프레임워크로,
객체 지향 설계의 원칙을 실현하고 의존성 관리(DI), AOP, 트랜잭션, 보안 등 핵심 기능을 제공하는 강력한 플랫폼입니다.
Spring Boot는 이러한 Spring의 설정 복잡도를 줄이고, 자동 설정과 내장 서버를 제공하여 생산성을 극대화한 프레임워크입니다.


0) 큰 그림 한 장

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 시각화

✅ Spring이란?

  • 자바 기반의 엔터프라이즈 애플리케이션 개발을 위한 프레임워크(Framework)
  • 객체 간 의존성을 효율적으로 관리하여 결합도를 낮추고, 유지보수성을 향상
  • 다양한 모듈 제공 (Spring Core, MVC, JDBC, Security, Transaction 등)

💡 핵심 특징

기능설명
DI (Dependency Injection)객체 간 의존성을 외부에서 주입하여 재사용성과 테스트 용이성 향상
IoC (Inversion of Control)객체의 생성과 관리 제어권을 개발자가 아닌 Spring 컨테이너가 담당
AOP (Aspect-Oriented Programming)로깅, 보안, 트랜잭션 등 공통 관심사를 핵심 로직과 분리
MVC 패턴 지원Model-View-Controller 구조로 웹 애플리케이션 설계 용이
Transaction 관리선언적 트랜잭션으로 안정적 DB 작업 보장

✅ Spring Boot란?

Spring Boot는 Spring의 복잡한 설정을 자동화하고 쉽고 빠른 개발 환경을 제공하는 확장 프레임워크입니다.
즉, Spring을 쉽게 쓰기 위한 도구로 볼 수 있습니다.

💡 주요 특징

기능설명
자동 설정 (Auto Configuration)설정 파일 최소화, 의존성 자동 등록
내장 서버 (Embedded Tomcat)별도의 WAS 설치 없이 실행 가능
Starter 의존성필요한 모듈을 묶어둔 패키지 제공 (예: spring-boot-starter-web)
Actuator서버 상태, 헬스체크, 메트릭 등을 모니터링
Profile 분리dev, test, prod 등 환경별 설정 관리 용이

🔹 Spring vs Spring Boot 비교

항목SpringSpring Boot
설정 방식XML 또는 Java Config 수동 설정자동 설정 (AutoConfiguration)
서버 실행외부 WAS 설치 필요내장 톰캣 제공
의존성 관리개별 라이브러리 직접 추가Starter 종속성 제공
배포 형태WAR 파일 중심단일 JAR 실행 가능
학습 난이도상대적으로 높음낮음 (초보자 친화적)

1) Spring 핵심 (IoC/DI, AOP, MVC, TX)

1.1 IoC & DI

  • IoC: 객체 생성/수명/의존성 관리를 개발자가 아닌 컨테이너가 담당.
  • DI: 의존 관계를 외부에서 주입(생성자/세터/필드) → 결합도↓, 테스트 용이↑.

권장 패턴: 생성자 주입 + final 필드 + @RequiredArgsConstructor

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository; // 불변 의존성
}

✅ 장점: 불변성, 순환참조 조기 감지, 테스트가 쉬움.
⚠️ 세터/필드 주입은 런타임 오류 발견 지연, 불변성 약화.

1.2 Bean 등록 & 스캔

  • 자동: @Component 스캔(@Service, @Repository, @Controller 포함)
  • 수동: @Configuration + @Bean
@Configuration
public class AppConfig {
  @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}

1.3 AOP (프록시 기반)

  • Join point(메서드 실행), Pointcut(대상 선택), Advice(부가기능), Aspect(묶음).
  • 프록시 방식
    • JDK 동적 프록시: 인터페이스 필요.
    • CGLIB: 클래스 상속, final 메서드 불가.
  • 주의: 같은 클래스 내부 self-invocation은 프록시 우회 → 어드바이스 미적용.

1.4 트랜잭션 (@Transactional)

  • 경계: 서비스 public 메서드 권장.
  • 속성
    • readOnly=true: 영속성 컨텍스트 읽기 최적화(더티체크 off) + 슬레이브 라우팅에 활용.
    • 전파(Propagation): REQUIRED(기본), REQUIRES_NEW, MANDATORY 등.
    • 격리(Isolation): 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(); }
}

1.5 MVC & 예외 처리

@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 사용. 컨트롤러에서 엔티티 노출 지양.


2) Spring Boot 내부 (AutoConfig, Starter, 실행/환경)

2.1 AutoConfiguration 동작

  • @SpringBootApplication@EnableAutoConfiguration 포함.
  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(또는 예전 spring.factories)에 등록된 자동설정 클래스 로드.
  • 각 자동설정은 @ConditionalOnClass/Bean/MissingBean조건부로 Bean 등록.
  • 우선순위/오버라이드: 내 설정이 우선(@Bean 직접 정의), @ConditionalOnMissingBean 체크.

2.2 Starter 의존성 & 외부설정

  • 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) {}

2.3 실행/배포/프로필

  • 내장 서버java -jar 실행, Dockerize 용이.
  • spring.profiles.active=dev 또는 --- 분리로 멀티 프로필.
  • WAR 배포도 가능하나 최근엔 JAR + 컨테이너 표준.

2.4 관찰성(Observability)

  • Micrometer 기본 포함 → Prometheus, Datadog 등으로 내보내기.
  • logging.level로 패키지별 로깅 제어, JSON 로거(ELK) 권장.

3) Bean 스코프/컨텍스트/생명주기/이벤트

3.1 스코프

스코프설명흔한 사용
singleton애플 전체 1개(기본)대부분의 서비스/리포지토리
prototype주입 시마다 새 인스턴스상태가 있는 helper (드묾)
requestHTTP 요청 단위요청 컨텍스트 보관
sessionHTTP 세션 단위사용자별 상태
application서블릿 컨텍스트전역 캐시 등

⚠️ 싱글턴 빈은 상태 없어야 Thread-safe. 가변 필드 공유 금지.

3.2 생명주기 콜백

  • 초기화: @PostConstruct 또는 InitializingBean#afterPropertiesSet
  • 소멸: @PreDestroy 또는 DisposableBean#destroy

3.3 ApplicationContext 구성

ApplicationContext
 ├─ BeanFactory(핵심 IoC)
 ├─ Environment(프로퍼티/프로필)
 ├─ MessageSource(i18n)
 ├─ ApplicationEventPublisher(이벤트)
 └─ ResourceLoader(클래스패스/파일/Git 등 리소스)

3.4 이벤트 & 리스너

@Component
class UserCreatedEventListener {
  @EventListener
  public void on(UserCreatedEvent e) { /* 알림/로그 */ }
}

4) Spring Security — JWT 완전정복

4.1 JWT 구조

<Base64Url(Header)>.<Base64Url(Payload)>.<HMAC/RS256 Signature>

  • Header: alg, typ
  • Payload: sub, iat, exp, roles, custom claims
  • Signature: 무결성 보장

4.2 인증 플로우 (로그인)

POST /api/auth/login {username, password}
   → AuthenticationManager가 UserDetailsService로 사용자 조회
   → PasswordEncoder로 비밀번호 비교
   → 성공 시 AccessToken(짧음), RefreshToken(김) 발급
  • 저장: 쿠키(HTTPOnly, Secure) 또는 Authorization 헤더 Bearer.
  • CSRF: 세션리스 + JWT면 대개 csrf().disable() (단, 쿠키 사용 시 SameSite/CSRF 재검토).

4.3 인가 플로우 (요청 처리)

클라이언트가 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);
  }
}

4.4 Refresh 전략

  • Access 만료 시 RefreshToken으로 재발급 엔드포인트 호출.
  • 보안: 서버 저장(화이트리스트) or Redis/DB로 관리, 회전(Rotation) 적용 권장.
  • 로그아웃: RT 무효화(블랙리스트), AT는 짧은 만료로 방치.

4.5 Method Security

@EnableMethodSecurity
class SecConfig {}

@Service
class ReportService {
  @PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
  public Report getReport(String userId) { ... }
}

팁: 권한은 ROLE_ prefix. 커스텀 권한은 hasAuthority('SCOPE_read') 등.

4.6 흔한 보안 이슈 & 대책

  • 토큰 탈취: 만료 짧게, HTTPS, 쿠키라면 HttpOnly+SameSite=Strict.
  • CORS: CorsConfigurationSource 등록, 허용 도메인만 화이트리스트.
  • 공개 키 기반(JWT RS256): 키 롤테이션/JWKS 고려.

5) Actuator & 모니터링 — 운영 필수

5.1 엔드포인트

엔드포인트용도
/actuator/health헬스체크 (DB/Redis/SMTP 등 HealthContributor)
/actuator/metricsMicrometer 노출, jvm.memory.used, http.server.requests
/actuator/prometheusPrometheus 스크레이프 대상
/actuator/loggers런타임 로깅 레벨 GET/POST 변경
/actuator/env외부설정 확인(민감 정보 주의)

설정

management:
  endpoints:
    web.exposure.include: health,info,metrics,prometheus,loggers
  endpoint.health.show-details: when_authorized

5.2 커스텀 헬스/메트릭

@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(); }
}

5.3 대시보드 & 알람

  • Prometheus 스크레이프 → Grafana 대시보드
  • SLO: p95 지연시간, 에러율, QPS, GC Time, 스레드 수, DB 커넥션 사용률.

보안: 운영에선 Actuator 엔드포인트를 인증 뒤 노출, /actuator/env 등은 제한.


6) 성능/테스트/배포 베스트프랙티스

6.1 성능/지연

  • JPA N+1 → fetch join, @EntityGraph, 캐시.
  • 커넥션 풀 설정(HikariCP): maximumPoolSize, connectionTimeout.
  • JSON 직렬화 튜닝: Jackson WRITE_DATES_AS_TIMESTAMPS off, 모듈 캐시.

6.2 테스트

  • 통합: @SpringBootTest(webEnvironment=RANDOM_PORT)
  • 슬라이스: @WebMvcTest(컨트롤러만), @DataJpaTest(JPA만)
  • MockMvc 로 API 테스트, Testcontainers로 실DB 통합.

6.3 배포

  • Jib/Buildpacks로 컨테이너 이미지 빌드, 레이어드 JAR 최적화.
  • 무중단: 블루-그린/롤링, readiness 프로브는 /actuator/health 사용.

7) 체크리스트

  • DI/IoC: “객체 생성·수명·의존성 관리를 컨테이너에 위임, 생성자 주입으로 불변성 보장”
  • AOP: “프록시로 메서드 경계를 가로채 공통기능 삽입, JDK/CGLIB, self-invocation 주의”
  • TX: “서비스 public 메서드, 전파/격리/롤백 규칙, 읽기전용으로 최적화”
  • JWT: “AT 짧게, RT 길게+회전, 필터에서 인증 완료 후 Context 저장, 메서드 시큐리티”
  • Actuator: “health/metrics/prometheus로 관측성 확보, 민감 엔드포인트 보호”
  • Bean Scope: “싱글턴은 무상태로, request/session 스코프는 프록시로 주입 가능”

0개의 댓글