Spring Boot는 편한데, 편한 만큼 내가 안 한 일을 누가 했는지가 잘 안 보일 때가 있어요.컨트롤러 하나 만들었을 뿐인데 서버가 뜨고, JSON이 나가고, 설정 파일이 읽히고, 데이터베이스 연결까지 준비되는 장면을 보면 이런 생각이 들죠.
“이거 편하긴 한데, 내부에서 뭐가 움직이는 거지?”이 Spring Boot 글들은 그 질문을 따라가려고 만든 길이에요. Annotation 이름을 먼저 외우기보다, 내가 작성한 코드와 Spring Boot가 대신 준비한 일을 나눠서 보는 데 집중할게요. 처음 읽는 분은 “아, 그래서 서버가 뜨는구나” 정도만 잡아도 충분해요. 하지만 이 카테고리는 거기서 멈추지 않을 거예요. 같은 장면을 한 번 더 내려가서 누가 결정했는지, 어느 시점에 객체가 만들어지는지, 어떤 경계에서 기대와 다르게 동작하는지, 실무에서는 무엇을 확인해야 하는지까지 같이 볼 거예요.
지금은 여기서 시작하면 돼요
글이 많아 보여도 앞에서 잡은 개념이 뒤 글의 기준점이 되도록 이어져 있어요. 처음이라면 아래 순서대로 읽는 흐름이 가장 자연스러워요.- 왜 Spring과 Spring Boot가 필요했을까요?
객체를 직접 만들고 연결하던 코드가 왜 Spring 컨테이너로 옮겨갔는지, 그리고 Spring Boot가 어떤 반복 설정을 덜어주는지 큰 그림부터 잡아요. - Spring Boot 프로젝트는 처음에 무엇을 만들어줄까요? Spring Initializr와
spring init으로 만든 첫 프로젝트의 선택지, 빌드 파일,main클래스, 패키지 구조, DevTools의 역할을 살펴봐요. - IoC, DI, AOP는 왜 Spring을 읽는 세 가지 기준점일까요? 제어의 역전, 의존성 주입, 관점 지향 프로그래밍을 어려운 정의보다 먼저 코드 책임의 이동으로 잡아봐요.
- main 메서드 하나가 어떻게 실행 중인 앱이 될까요?
SpringApplication.run이 환경을 준비하고 컨테이너를 만들고 웹 서버를 띄우는 큰 흐름을 따라가요. - ApplicationContext와 Bean은 왜 Spring이 소유한 객체일까요? Spring 컨테이너가 빈 정의를 모으고 실제 객체를 만들고 연결하는 흐름을, 생명주기와 scope까지 이어서 살펴봐요.
- 컴포넌트 스캔과 Bean 등록은 왜 가끔 내 클래스를 못 찾을까요? 컴포넌트 스캔,
@Bean, 조건부 등록이 각각 어떤 방식으로 빈을 등록하는지 보고, Spring Boot가 클래스를 못 찾는 흔한 이유를 정리해요. - 의존성 주입은 실제 코드에서 어떻게 읽어야 할까요? 생성자 주입, 선택적 의존성, 여러 빈 후보, 순환 의존성을 실제 서비스 코드 흐름으로 읽고 field injection이 오래 갈수록 불리해지는 이유를 살펴봐요.
- AOP 프록시와 Annotation은 왜 기대와 다르게 동작할까요?
@Transactional같은 Annotation이 단순한 표시가 아니라 프록시를 통과하는 호출에서 동작하는 이유와 self-invocation 함정을 살펴봐요. - 스타터와 자동 설정은 왜 버전과 설정을 대신 맞춰줄까요? starter, BOM, dependency management, auto-configuration, 조건 평가 리포트를 통해 의존성 하나가 어떻게 실행 설정으로 이어지는지 살펴봐요.
- application.yml과 profile은 설정을 어떻게 바꿔줄까요? 외부 설정, Config Data, profile, 환경 변수, Configuration Properties, 검증, secret 경계를 한 흐름으로 읽어봐요.
- Spring Boot 4와 3은 무엇이 달라졌을까요? Spring Boot 4.x와 3.5.x의 기준선을 Java, Spring Framework, Jakarta EE, Servlet 컨테이너, starter, 테스트 의존성 관점에서 비교해요.
- 웹 요청은 Spring MVC 안에서 어떤 순서로 지나갈까요? DispatcherServlet, HandlerMapping, argument resolution, validation, HttpMessageConverter, exception handling까지 Spring MVC 요청 흐름을 한 번에 따라가요.
- REST API 설계와 에러 계약은 왜 컨트롤러 코드보다 먼저일까요? DTO, request와 response 모양, Bean Validation, Problem Details, error code, API versioning을 클라이언트와 서버의 약속으로 읽어봐요.
- JSON과 Jackson 3는 왜 API 호환성의 핵심일까요? Spring Boot 4.x의 Jackson 3 기본 흐름, JsonMapper, Jackson 2 마이그레이션, 날짜와 enum과 unknown field가 API 계약에 주는 영향을 살펴봐요.
- Todo API 첫 커밋은 어떻게 차근차근 만들까요? Spring CLI로 Java 21, Gradle wrapper 기반 프로젝트를 만들고 생성된 파일 확인부터 controller, service, repository, validation, 테스트까지 첫 커밋 흐름으로 따라가요.
- Spring MVC와 WebFlux는 언제 다르게 선택해야 할까요? Spring MVC와 WebFlux를 구버전/신버전 관계가 아니라 blocking I/O, non-blocking I/O, thread model, 라이브러리 선택의 차이로 읽어봐요.
- WebSocket과 STOMP는 언제 실시간 연결에 필요할까요? HTTP 요청-응답과 오래 열린 WebSocket 연결의 차이, STOMP destination, message broker,
@MessageMapping, SockJS fallback을 Spring Boot 흐름으로 읽어봐요. - RestClient, WebClient, HTTP Interface는 언제 다르게 써야 할까요? 서버가 다른 서버를 호출할 때
RestClient,WebClient, HTTP Interface를 어떻게 고르고, timeout과 retry를 어디에 두어야 하는지 outbound API 경계로 읽어봐요. - 데이터 접근은 JDBC, JPA, R2DBC 중 무엇을 골라야 할까요? JDBC와
JdbcClient, Spring Data JDBC, JPA, Spring Data repository, R2DBC를 최신순이 아니라 문제 모양과 실행 모델로 고르는 법을 살펴봐요. - JPA Entity 상태와 영속성 컨텍스트는 왜 헷갈릴까요? Entity가 새 객체, 관리되는 객체, 분리된 객체, 삭제 예정 객체로 바뀌는 흐름을 영속성 컨텍스트, dirty checking, lazy loading, N+1, auditing과 함께 읽어봐요.
- 트랜잭션 경계와 롤백은 왜 Annotation 하나로 끝나지 않을까요?
@Transactional이 시작되는 위치, commit과 rollback이 결정되는 시점, propagation과 self-invocation이 기대를 흔드는 이유를 service 경계와 테스트까지 이어서 살펴봐요. - Schema migration은 왜 Flyway와 Liquibase로 관리할까요? 테이블과 컬럼 변경을 서버 코드처럼 버전으로 남겨야 하는 이유를 Flyway, Liquibase, migration history, 배포 순서, rollback 기대까지 이어서 살펴봐요.
- 복잡한 query는 Querydsl과 Specification 중 무엇으로 풀어야 할까요? Spring Data JPA에서 derived query method,
@Query, Specification, Querydsl을 언제 나눠 쓰는지 검색 조건, join, pagination, sorting 경계로 살펴봐요. - Redis cache와 session은 왜 성능을 올리면서 버그도 만들까요? Spring Boot에서 Redis cache와 Spring Session Redis를 같은 저장소처럼 보되, TTL, invalidation, key prefix, 분산 session 경계로 나누어 읽어봐요.
- MongoDB와 Elasticsearch는 왜 JPA보다 빠른 DB가 아닐까요? MongoDB, Elasticsearch, document model, search index를 JPA 대체재가 아니라 서로 다른 데이터 문제를 푸는 도구로 나누어 읽어봐요.
- Spring Security는 왜 filter chain부터 봐야 할까요? Spring Boot에 Security starter를 넣었을 때 모든 요청이 왜 갑자기 잠기고, 인증과 인가와 CSRF와 CORS가 filter chain 안에서 어떻게 나뉘는지 살펴봐요.
- JWT와 OAuth2 Resource Server는 왜 로그인 구현과 다를까요? JWT access token, OAuth2 login, resource server, issuer, JWK Set, scope, refresh token 경계를 Spring Security 요청 흐름으로 나누어 읽어봐요.
- Method Security와 도메인 권한은 어디에 둬야 할까요? URL 규칙만으로 부족해지는 소유권 검사, method security,
@PreAuthorize,@PostAuthorize, 도메인 권한 판단을 service 경계에서 읽어봐요. - Auth API를 만들어요: domain과 application Spring Boot shell, HTTP 계약, schema, domain port,
IdentityFacade, JWT 설정을 compile 가능한 순서로 만들어요. - Auth API에 JWT와 Security를 연결해요 JDBC와 Security adapter, 두
SecurityFilterChain, controller를 연결하고 15개 test와 실제 HTTP 요청으로 확인해요. - 테스트는 unit, slice, integration을 어떻게 나눠야 할까요? Spring 없는 unit test, MVC와 JDBC slice, 전체 bean graph를 여는 integration test, 실제 port를 여는 server test가 각각 무엇을 증명하고 놓치는지 살펴봐요.
- Testcontainers와 WireMock은 무엇을 진짜로 바꿔줄까요? 실제 PostgreSQL을 띄우는 Testcontainers, 외부 HTTP를 재현하는 WireMock, 서비스 사이 약속을 검증하는 contract test를 서로 다른 test 경계로 나누어 읽어봐요.
- Actuator의 health와 metrics는 운영에서 무엇을 알려줄까요? Health, info, metrics, Prometheus endpoint를 서로 다른 운영 질문으로 나누고, liveness와 readiness와 안전한 공개 범위를 살펴봐요.
- Micrometer tracing과 correlation ID는 한 요청을 어떻게 찾을까요? Metric에서 발견한 이상 징후를 trace와 log로 좁히고, trace ID, span ID, MDC, context propagation, sampling으로 여러 service를 지난 요청 하나를 찾아가요.
- Auth API에 metrics와 tracing을 실제로 어떻게 붙일까요? 기존 Auth API에 Actuator, Prometheus Counter, OpenTelemetry tracing, Jaeger, 작은 downstream을 붙이고, async context 단절과 장애 추적을 실제 실행 결과로 확인해요.
- Application Event는 같은 앱 안에서 무엇을 느슨하게 만들까요?
ApplicationEventPublisher,@EventListener,@TransactionalEventListener로 component를 분리하고, 동기 실행과 transaction phase와 전달 한계를 함께 살펴봐요. - Kafka와 RabbitMQ는 event를 어떻게 다르게 전달할까요? Spring Boot에서 process 밖으로 message를 보내고, Kafka의 partition과 RabbitMQ의 exchange·queue를 비교하면서 idempotency, retry, dead-letter, ordering을 함께 설계해요.
이 길에서 계속 붙잡을 질문
Spring Boot를 읽을 때는 기능 이름보다 질문을 먼저 잡는 편이 덜 헷갈려요.- 내가 만든 클래스는 누가 객체로 만들까요?
- 필요한 객체는 누가 생성자에 넣어줄까요?
main메서드 하나가 왜 웹 서버 시작으로 이어질까요?- 스타터(starter)를 넣으면 왜 여러 라이브러리가 같이 따라올까요?
- 자동 설정(auto-configuration)은 언제 적용되고, 언제 물러날까요?
- 테스트에서는 왜 실행할 때와 다른 컨텍스트가 뜰까요?
- Annotation 하나를 붙였는데 실제로는 프록시(proxy), 조건(condition), 컨텍스트(application context) 중 어디가 움직인 걸까요?
- 운영에서 문제가 났을 때 로그, 설정, 빈 목록, 조건 평가 리포트 중 무엇부터 봐야 할까요?
1
내가 작성한 코드를 구분해요
Controller, Service, Repository처럼 우리가 직접 책임지는 클래스부터 찾아요. 각 클래스가 필요한 의존성과 업무 규칙을 어떻게 표현하는지 읽어요.
2
Spring 컨테이너의 일을 추적해요
어떤 객체를 빈으로 만들고, 생성자 의존성을 어떻게 연결하는지 살펴봐요. 객체 생명주기와 프록시 경계도 컨테이너의 책임에서 이어져요.
3
Spring Boot가 더한 기본값을 확인해요
starter가 어떤 라이브러리를 가져오고 자동 설정이 어떤 조건으로 적용되는지 봐요. 편리한 기본값과 우리가 명시한 설정의 경계를 구분해요.
4
실행 중 애플리케이션에서 검증해요
HTTP 요청, 데이터 접근, 보안, 관측 지표처럼 실제 동작으로 앞선 설정을 확인해요. 코드와 컨테이너와 Boot의 선택이 운영 결과로 어떻게 나타나는지 연결해요.
자, 정리해볼까요?
- 처음이라면 위 목록을 차례대로 읽는 게 가장 자연스러워요.
- 이 카테고리는 Annotation을 외우기보다, 프레임워크가 대신 처리한 일을 이해하는 데 집중합니다.
- 각 글은 익숙한 장면에서 시작해 내부 메커니즘과 실패할 수 있는 경계까지 함께 살펴봅니다.