Application 기동 → DI → AOP Proxy → Security 설정 → HTTP 요청 → Filter Chain → Controller → Service → DB → 응답까지
모든 단계의 코드 실행 순서와 JVM 메모리 상태 변화 전체
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 단계 │ 실행 내용 │ 메모리 변화 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ① JVM 시작, main() 호출 Stack: main 프레임 ② SpringApplication.run() Heap: ApplicationContext 생성 시작 ③ 클래스 스캔 Method Area: .class 파일들 로딩 @Component/@Service/@Repository @Controller/@Aspect/@Configuration ④ ──── DI 단계 ──── Repository Bean 생성 Heap: UserRepository, BoardRepository 생성 Service Bean 생성 Heap: UserService(userRepo 주입), BoardService 생성 Controller Bean 생성 Heap: UserController, BoardController 생성 Util Bean 생성 Heap: JwtUtil(secretKey 주입) 생성 Security Bean 생성 Heap: CustomUserDetailsService, JwtAuthFilter 생성 ⑤ ──── AOP 단계 ──── @Aspect 감지 Method Area: LogAspect 클래스 Pointcut 분석 → service 패키지 대상 확인 Proxy 생성 Heap: UserService$$CGLIB (Proxy) 생성 Heap: BoardService$$CGLIB (Proxy) 생성 Heap: AdminService$$CGLIB (Proxy) 생성 ApplicationContext 교체 → 원본 Bean 대신 Proxy Bean 등록 (@Transactional 대상도 포함) ⑥ ──── Security 단계 ──── SecurityConfig.filterChain() Heap: SecurityFilterChain 생성 Filter 등록 → [CorsFilter → JwtAuthFilter → AuthorizFilter] 순서 등록 CORS 설정 Heap: CorsConfigurationSource 생성 권한 규칙 등록 → permitAll/hasRole/authenticated 규칙 저장 ⑦ ──── DB 단계 ──── DataSource 생성 Heap: HikariDataSource (커넥션 풀) EntityManagerFactory 생성 Heap: JPA 핵심 객체 ddl-auto=update DB: 테이블 검증/생성 ⑧ Tomcat 시작 Thread: Accept 스레드 생성 포트 8083 LISTEN ThreadPool 초기화 (최대 200 스레드) ⑨ "Started BoardApplication" 기동 완료 — 요청 대기 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Bean (Singleton):
UserRepository, BoardRepository, AdminRepository,
UserService Proxy, BoardService Proxy, AdminService Proxy,
UserController, BoardController, AdminController,
JwtUtil, CustomUserDetailsService, JwtAuthenticationFilter,
LogAspect, BCryptPasswordEncoder
Infrastructure:
SecurityFilterChain, CorsConfigurationSource,
HikariDataSource, EntityManagerFactory,
ApplicationContext (이 모든 Bean들을 담는 컨테이너)
이 객체들은 서버가 살아있는 한 Heap에 상주합니다.
요청마다 새로 만들지 않습니다.
3차 기준. React에서 "글쓰기" 버튼을 누르는 순간부터 화면에 성공 메시지가 뜨기까지 모든 코드가 실행되는 순서와 그 시점의 JVM Stack/Heap 상태를 전부 추적합니다.
handleWrite() 실행 →localStorage.getItem("token") →config.headers.Authorization = "Bearer eyJ..." 자동 세팅 →POST /api/board/write HTTP 요청 전송 (title, content JSON body)
Access-Control-Allow-Origin 헤더 응답에 추가 예약
jwtUtil.validateToken(token) → 서명 검증 →jwtUtil.getUsername(token) → "hong" → Stack에 username 지역변수 →customUserDetailsService.loadUserByUsername("hong") → DB SELECT →new CustomUserDetails(user) 생성 →new UsernamePasswordAuthenticationToken(userDetails, null, authorities) 생성 →SecurityContextHolder.getContext().setAuthentication(auth) →
Thread-Local에 auth 저장
BoardController.write()로 매핑 →@RequestBody BoardDto: JSON body 역직렬화 → Heap에 new BoardDto() 생성@AuthenticationPrincipal: Thread-Local에서 auth.getPrincipal() → CustomUserDetails 꺼냄
userDetails.getUser() → Heap의 User 객체 참조 꺼냄 →boardService.write(boardDto, user) 호출 →joinPoint.proceed() → 실제 BoardService.write() 프레임 push
Board.builder().title(...).content(...).user(user).build() →boardRepository.save(board) 호출
@PrePersist 실행: createdAt = LocalDateTime.now() →INSERT INTO board (title, content, user_id, created_at) VALUES (?, ?, ?, ?) 실행 →Map.of("message", "글쓰기 성공") → Heap에 Map 생성 →{"message":"글쓰기 성공"} →SecurityContextHolder.clearContext() →
Thread-Local에서 auth 삭제 →response.data.message →navigate("/board/list") → 목록 페이지로 이동
| 메모리 관점 | 1차 Thymeleaf+Session | 2차 React+JWT | 3차 Spring Security |
|---|---|---|---|
| 인증 저장 위치 | Heap — HttpSession 객체 (서버 메모리 30분 점유) |
Heap — request 객체 속성 (요청 처리 동안만) |
Thread-Local — SecurityContext (요청 처리 동안만, 요청마다 독립) |
| 클라이언트 저장 | 브라우저 쿠키 (JSESSIONID) |
localStorage (token) |
localStorage (token + role) |
| 요청마다 Stack | Controller → Service → Repository | JwtFilter → Controller → Service → Repository | CorsFilter → JwtAuthFilter → AuthorizFilter → Controller → AOP Proxy → Service → Repository |
| AOP Proxy | @Transactional 대상만 | @Transactional 대상만 | @Transactional + @Aspect 로깅 대상 |
| 서버 확장 시 | Session 공유 문제 발생 (서버 A Session을 서버 B 모름) |
문제 없음 (서버가 상태 저장 안 함) |
문제 없음 (STATELESS) |
| HTML 생성 위치 | 서버 Heap (Thymeleaf 렌더링) |
브라우저 (React 렌더링) |
브라우저 (React 렌더링) |
| 로그인 유저 꺼내기 | session.getAttribute() — 직접 | request.getAttribute() — 직접 | @AuthenticationPrincipal — 자동 주입 (ArgumentResolver가 Thread-Local에서 꺼냄) |
기동 시:
모든 Bean은 Heap에 딱 한 번 만들어지고 재사용된다. AOP Proxy가 Bean을 감싸고, Security는 FilterChain을 Heap에 등록한다.
요청 시:
스레드마다 독립적인 Stack이 생기고, 메서드 호출마다 프레임이 push/pop된다.
Thread-Local(SecurityContext)은 같은 요청 내 어디서든 인증 정보를 꺼낼 수 있게 해준다.
요청 완료 후 Stack은 비워지고 Thread-Local도 정리된다. Heap의 임시 객체는 GC가 처리한다.