Study · open-source/backend/java/spring-boot

[Study] Spring Boot — main() 한 줄이 HTTP 응답 한 번이 되기까지

무엇을 켤지 정하고, 빈을 만들어 담고, 프록시로 감싸고, 톰캣을 올리고, 요청 하나를 받아 JSON으로 돌려주기까지를 8단계로 나눴다. 단계마다 페이지가 하나이고, 장면은 버튼을 눌러 한 스텝씩 넘기며 본다.

값은 example/spring-boot/example을 --debug로 한 번 돌려 얻은 실측이다(Spring Boot 4.0.6 · Tomcat 11.0.21 · H2 + HikariCP · JDK 21.0.10 · 포트 18080). 버전이 다르면 조건 개수와 빈 개수가 달라진다.

1단계부터 읽기 ›

8단계 지도

올릴 때 — 서버를 띄울 때 한 번

요청마다 — 요청이 들어올 때마다

요청 한 번이 지나가는 소스를 디버거로 멈춰 세워 쌓았다

6 · 7 · 8단계는 요청 한 번을 스레드 · 주소 표 · 응답으로 나눠 봤다. 나눠 보면 그 요청이 실제로 어느 프로젝트의 소스를 어떤 차례로 지나가는지는 한눈에 안 보인다. 내 앱은 내가 쓴 코드만으로 돌지 않는다. build.gradle 에 적은 톰캣 · 스프링 · DB 같은 남의 코드(jar)가 같은 프로그램 안에 들어 있고, 요청은 그 코드들을 번갈아 지나간다.

내가 쓴 코드는 메서드 두 개다. 요청을 받는 OrderController.place와, @Transactional이 붙어 DB 에 주문 한 줄을 넣고 개수를 세는 OrderService.place다. 여기에 POST /orders?item=book(책 한 권을 주문으로 넣어 달라는 HTTP 요청)를 한 번 보냈다.

보내기 전에 예제 앱을 디버그 모드로 띄우고 디버거를 연결해 두었다. 그리고 요청이 지나가리라 짐작한 7곳에 중단점을 걸었다 — 연결을 받는다 · 읽을 것이 온 연결을 일꾼 스레드에 넘긴다 · 내 메서드에 들어선다 · insert 를 DB 에 보낸다 · 트랜잭션을 커밋한다 · JSON 바이트를 톰캣에 넘긴다 · 바이트가 소켓으로 나간다. 짐작이 틀렸다면 그 자리는 안 걸렸을 텐데, 7곳 모두 걸렸다. 걸릴 때마다 그곳을 지나던 스레드를 멈춰 세우고, 그 순간의 스택을 맨 아래 칸부터 전부 읽었다. 앱 코드는 한 줄도 고치지 않았다.

첫 그림에서 막대 한 칸이 스택의 한 칸이다. 맨 위 칸이 그 순간 실행 중인 메서드이고, 그 아래 칸들은 바로 위 칸을 부르고 돌아오기를 기다리는 메서드다. 위로 쌓일수록 더 깊이 부른 메서드이고, 막대 색이 그 메서드의 소스가 든 프로젝트다. CGLIB 색만 예외로, 소스 없이 스프링이 실행 중에 만든 클래스(4단계)다.

멈춘 차례마다 한 열씩 오른쪽으로 나아간다. 같은 스레드의 앞 차례와 아래부터 같은 메서드인 칸은 이어진 것으로 보고 옆 열까지 막대를 이었고, 달라진 칸부터 새로 쌓았다. 그 사이에 한 번 돌아왔다가 같은 메서드를 다시 불렀을 수도 있지만, 멈춘 순간만으로는 둘을 가를 수 없다. 같은 까닭으로 그림은 멈춘 일곱 순간만 안다. 그 사이에 불렸다가 이미 돌아온 메서드는 그림에 없다. 「다음」을 누르면 한 차례씩 나아가고, 내 메서드에 들어설 때까지의 긴 첫 차례만 네 번에 나눠 올라간다.

이 화면에서는 2D 그림을 그리지 못했다. 아래 시퀀스 다이어그램 밑의 로그(「멈춘 7번의 스택」)가 같은 내용이다.

가로의 열 하나가 멈춘 한 번(1–7)의 스택이고, 열 아래에 그 차례의 짧은 이름과 스레드 이름(같은 스레드끼리 묶었다)이 있다. 세로는 아래에서 몇 번째 칸인지, 막대 색은 그 소스가 든 프로젝트다(아래 범례). 같은 스레드의 앞 열과 아래부터 같은 메서드인 칸은 이어진 것으로 보고 옆 열까지 막대를 길게 이었다 — 그 사이에 돌아왔다가 같은 메서드를 다시 불렀는지는 가르지 못한다. 앞 열의 막대가 다음 열로 안 이어지고 끝나면 그 칸은 두 차례 사이에 돌아와 스택에서 빠진 것이고, 다음 열에서 그 높이부터 새로 시작하는 막대는 새로 부른 메서드다. 스레드가 바뀌는 자리는 스택이 따로라 바닥부터 끊기는데, 이것은 돌아왔다는 뜻이 아니다. 지나간 열 위의 수는 그 차례의 칸 수다. 지금 보는 열에는 밝은 띠를 깔고, 맨 위 칸에 테두리와 「스레드 · N칸 · 메서드 :줄」 이름표를 단다. 막대에 마우스를 올리면 프로젝트 · 아래에서 몇 번째 칸인지 · 메서드와 열마다의 줄 번호가 뜬다. 줄 번호가 없는 칸은 그 클래스에 줄 번호 표가 없어서다(스프링이 만든 클래스, JDK 가 만든 LambdaForm 같은 것). 어느 프로젝트에 어느 색을 줬는지와 범례의 차례는 보기 좋게 정한 것이라 뜻은 없다.
잰 때 2026-10-13 10:56 · OpenJDK 64-Bit Server VM 21.0.9 · POST /orders?item=book → 200(성공). 1–8단계의 값은 JDK 21.0.10 으로 쟀지만, 이 그림을 잰 날 이 컴퓨터에서는 21.0.10 을 찾지 못해 위의 JVM 으로 쟀다. 나온 프로젝트의 버전은 톰캣 11.0.21 · 스프링 프레임워크 7.0.7 · HikariCP 7.0.2 · H2 2.4.240 · Jackson 3.1.2 · JDK 21.0.9이다.

이 화면에서는 시퀀스 다이어그램을 그리지 못했다. 바로 아래 로그(「멈춘 7번의 스택」)를 차례로 견줘 보면 누가 누구를 불렀는지 나온다. 그 아래 설명은 그 그림을 읽는 법이다.

로그 — 멈춘 7번의 스택. 맨 위가 그 순간 실행 중이던 메서드, 맨 아래가 스레드가 시작한 곳이고, 이름:숫자의 숫자는 그 소스 파일의 줄 번호다. 줄을 누르면 위 시퀀스 다이어그램이 그 순간으로 간다.

1. 연결을 받는다 http-nio-18080-Acceptor · 4칸 · 멈춘 곳 org.apache.tomcat.util.net.NioEndpoint.setSocketOptions

  1. 톰캣org.apache.tomcat.util.net.NioEndpoint.setSocketOptions:71
  2. 톰캣org.apache.tomcat.util.net.Acceptor.run:147
  3. JDKjava.lang.Thread.runWith:1596
  4. JDKjava.lang.Thread.run:1583

2. 읽을 것이 온 연결을 일꾼 스레드에 넘긴다 http-nio-18080-Poller · 5칸 · 멈춘 곳 org.apache.tomcat.util.net.AbstractEndpoint.processSocket

  1. 톰캣org.apache.tomcat.util.net.AbstractEndpoint.processSocket:1575
  2. 톰캣org.apache.tomcat.util.net.NioEndpoint$Poller.processKey:845
  3. 톰캣org.apache.tomcat.util.net.NioEndpoint$Poller.run:812
  4. JDKjava.lang.Thread.runWith:1596
  5. JDKjava.lang.Thread.run:1583

3. 내 메서드에 들어선다 http-nio-18080-exec-2 · 68칸 · 멈춘 곳 com.example.example.service.OrderService.place

  1. 내 코드com.example.example.service.OrderService.place:18
  2. JDKjava.lang.invoke.LambdaForm$DMH/0x000002d7c0002800.invokeVirtual
  3. JDKjava.lang.invoke.LambdaForm$MH/0x000002d7c05fc800.invoke
  4. JDKjava.lang.invoke.Invokers$Holder.invokeExact_MT
  5. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl:154
  6. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invoke:103
  7. JDKjava.lang.reflect.Method.invoke:580
  8. 스프링 프레임워크org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection:359
  9. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint:190
  10. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.proceed:158
  11. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation:133
  12. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction:371
  13. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionInterceptor.invoke:130
  14. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.proceed:179
  15. 스프링 프레임워크org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept:719
  16. 스프링이 만든 클래스com.example.example.service.OrderService$$SpringCGLIB$$0.place
  17. 내 코드com.example.example.controller.OrderController.place:25
  18. JDKjava.lang.invoke.LambdaForm$DMH/0x000002d7c0158c00.invokeVirtual
  19. JDKjava.lang.invoke.LambdaForm$MH/0x000002d7c0369800.invoke
  20. JDKjava.lang.invoke.Invokers$Holder.invokeExact_MT
  21. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl:154
  22. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invoke:103
  23. JDKjava.lang.reflect.Method.invoke:580
  24. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.doInvoke:252
  25. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest:184
  26. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle:117
  27. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod:934
  28. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal:853
  29. 스프링 프레임워크org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle:86
  30. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doDispatch:963
  31. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doService:866
  32. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.processRequest:1000
  33. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.doPost:903
  34. 톰캣jakarta.servlet.http.HttpServlet.service:649
  35. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.service:874
  36. 톰캣jakarta.servlet.http.HttpServlet.service:710
  37. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:128
  38. 톰캣org.apache.tomcat.websocket.server.WsFilter.doFilter:53
  39. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  40. 스프링 프레임워크org.springframework.web.filter.RequestContextFilter.doFilterInternal:100
  41. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  42. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  43. 스프링 프레임워크org.springframework.web.filter.FormContentFilter.doFilterInternal:93
  44. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  45. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  46. 스프링 프레임워크org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal:110
  47. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  48. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  49. 스프링 프레임워크org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal:199
  50. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  51. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  52. 톰캣org.apache.catalina.core.StandardWrapperValve.invoke:165
  53. 톰캣org.apache.catalina.core.StandardContextValve.invoke:77
  54. 톰캣org.apache.catalina.authenticator.AuthenticatorBase.invoke:492
  55. 톰캣org.apache.catalina.core.StandardHostValve.invoke:113
  56. 톰캣org.apache.catalina.valves.ErrorReportValve.invoke:83
  57. 톰캣org.apache.catalina.core.StandardEngineValve.invoke:72
  58. 톰캣org.apache.catalina.connector.CoyoteAdapter.service:341
  59. 톰캣org.apache.coyote.http11.Http11Processor.service:397
  60. 톰캣org.apache.coyote.AbstractProcessorLight.process:63
  61. 톰캣org.apache.coyote.AbstractProtocol$ConnectionHandler.process:903
  62. 톰캣org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun:1801
  63. 톰캣org.apache.tomcat.util.net.SocketProcessorBase.run:52
  64. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker:946
  65. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run:480
  66. 톰캣org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run:57
  67. JDKjava.lang.Thread.runWith:1596
  68. JDKjava.lang.Thread.run:1583

4. insert 를 DB 에 보낸다 http-nio-18080-exec-2 · 77칸 · 멈춘 곳 org.h2.jdbc.JdbcPreparedStatement.executeUpdate

  1. H2org.h2.jdbc.JdbcPreparedStatement.executeUpdate:170
  2. HikariCPcom.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate:61
  3. HikariCPcom.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate
  4. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate.lambda$update$0:971
  5. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate$$Lambda/0x000002d7c05f99b8.doInPreparedStatement
  6. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate.execute:670
  7. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate.update:966
  8. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate.update:1010
  9. 스프링 프레임워크org.springframework.jdbc.core.JdbcTemplate.update:1020
  10. 내 코드com.example.example.service.OrderService.place:18
  11. JDKjava.lang.invoke.LambdaForm$DMH/0x000002d7c0002800.invokeVirtual
  12. JDKjava.lang.invoke.LambdaForm$MH/0x000002d7c05fc800.invoke
  13. JDKjava.lang.invoke.Invokers$Holder.invokeExact_MT
  14. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl:154
  15. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invoke:103
  16. JDKjava.lang.reflect.Method.invoke:580
  17. 스프링 프레임워크org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection:359
  18. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint:190
  19. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.proceed:158
  20. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation:133
  21. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction:371
  22. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionInterceptor.invoke:130
  23. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.proceed:179
  24. 스프링 프레임워크org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept:719
  25. 스프링이 만든 클래스com.example.example.service.OrderService$$SpringCGLIB$$0.place
  26. 내 코드com.example.example.controller.OrderController.place:25
  27. JDKjava.lang.invoke.LambdaForm$DMH/0x000002d7c0158c00.invokeVirtual
  28. JDKjava.lang.invoke.LambdaForm$MH/0x000002d7c0369800.invoke
  29. JDKjava.lang.invoke.Invokers$Holder.invokeExact_MT
  30. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl:154
  31. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invoke:103
  32. JDKjava.lang.reflect.Method.invoke:580
  33. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.doInvoke:252
  34. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest:184
  35. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle:117
  36. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod:934
  37. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal:853
  38. 스프링 프레임워크org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle:86
  39. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doDispatch:963
  40. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doService:866
  41. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.processRequest:1000
  42. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.doPost:903
  43. 톰캣jakarta.servlet.http.HttpServlet.service:649
  44. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.service:874
  45. 톰캣jakarta.servlet.http.HttpServlet.service:710
  46. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:128
  47. 톰캣org.apache.tomcat.websocket.server.WsFilter.doFilter:53
  48. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  49. 스프링 프레임워크org.springframework.web.filter.RequestContextFilter.doFilterInternal:100
  50. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  51. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  52. 스프링 프레임워크org.springframework.web.filter.FormContentFilter.doFilterInternal:93
  53. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  54. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  55. 스프링 프레임워크org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal:110
  56. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  57. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  58. 스프링 프레임워크org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal:199
  59. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  60. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  61. 톰캣org.apache.catalina.core.StandardWrapperValve.invoke:165
  62. 톰캣org.apache.catalina.core.StandardContextValve.invoke:77
  63. 톰캣org.apache.catalina.authenticator.AuthenticatorBase.invoke:492
  64. 톰캣org.apache.catalina.core.StandardHostValve.invoke:113
  65. 톰캣org.apache.catalina.valves.ErrorReportValve.invoke:83
  66. 톰캣org.apache.catalina.core.StandardEngineValve.invoke:72
  67. 톰캣org.apache.catalina.connector.CoyoteAdapter.service:341
  68. 톰캣org.apache.coyote.http11.Http11Processor.service:397
  69. 톰캣org.apache.coyote.AbstractProcessorLight.process:63
  70. 톰캣org.apache.coyote.AbstractProtocol$ConnectionHandler.process:903
  71. 톰캣org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun:1801
  72. 톰캣org.apache.tomcat.util.net.SocketProcessorBase.run:52
  73. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker:946
  74. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run:480
  75. 톰캣org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run:57
  76. JDKjava.lang.Thread.runWith:1596
  77. JDKjava.lang.Thread.run:1583

5. 트랜잭션을 커밋한다 http-nio-18080-exec-2 · 64칸 · 멈춘 곳 org.h2.jdbc.JdbcConnection.commit

  1. H2org.h2.jdbc.JdbcConnection.commit:481
  2. HikariCPcom.zaxxer.hikari.pool.ProxyConnection.commit:378
  3. HikariCPcom.zaxxer.hikari.pool.HikariProxyConnection.commit
  4. 스프링 프레임워크org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit:343
  5. 스프링 프레임워크org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit:794
  6. 스프링 프레임워크org.springframework.transaction.support.AbstractPlatformTransactionManager.commit:757
  7. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning:687
  8. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction:408
  9. 스프링 프레임워크org.springframework.transaction.interceptor.TransactionInterceptor.invoke:130
  10. 스프링 프레임워크org.springframework.aop.framework.ReflectiveMethodInvocation.proceed:179
  11. 스프링 프레임워크org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept:719
  12. 스프링이 만든 클래스com.example.example.service.OrderService$$SpringCGLIB$$0.place
  13. 내 코드com.example.example.controller.OrderController.place:25
  14. JDKjava.lang.invoke.LambdaForm$DMH/0x000002d7c0158c00.invokeVirtual
  15. JDKjava.lang.invoke.LambdaForm$MH/0x000002d7c0369800.invoke
  16. JDKjava.lang.invoke.Invokers$Holder.invokeExact_MT
  17. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl:154
  18. JDKjdk.internal.reflect.DirectMethodHandleAccessor.invoke:103
  19. JDKjava.lang.reflect.Method.invoke:580
  20. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.doInvoke:252
  21. 스프링 프레임워크org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest:184
  22. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle:117
  23. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod:934
  24. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal:853
  25. 스프링 프레임워크org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle:86
  26. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doDispatch:963
  27. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doService:866
  28. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.processRequest:1000
  29. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.doPost:903
  30. 톰캣jakarta.servlet.http.HttpServlet.service:649
  31. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.service:874
  32. 톰캣jakarta.servlet.http.HttpServlet.service:710
  33. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:128
  34. 톰캣org.apache.tomcat.websocket.server.WsFilter.doFilter:53
  35. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  36. 스프링 프레임워크org.springframework.web.filter.RequestContextFilter.doFilterInternal:100
  37. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  38. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  39. 스프링 프레임워크org.springframework.web.filter.FormContentFilter.doFilterInternal:93
  40. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  41. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  42. 스프링 프레임워크org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal:110
  43. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  44. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  45. 스프링 프레임워크org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal:199
  46. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  47. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  48. 톰캣org.apache.catalina.core.StandardWrapperValve.invoke:165
  49. 톰캣org.apache.catalina.core.StandardContextValve.invoke:77
  50. 톰캣org.apache.catalina.authenticator.AuthenticatorBase.invoke:492
  51. 톰캣org.apache.catalina.core.StandardHostValve.invoke:113
  52. 톰캣org.apache.catalina.valves.ErrorReportValve.invoke:83
  53. 톰캣org.apache.catalina.core.StandardEngineValve.invoke:72
  54. 톰캣org.apache.catalina.connector.CoyoteAdapter.service:341
  55. 톰캣org.apache.coyote.http11.Http11Processor.service:397
  56. 톰캣org.apache.coyote.AbstractProcessorLight.process:63
  57. 톰캣org.apache.coyote.AbstractProtocol$ConnectionHandler.process:903
  58. 톰캣org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun:1801
  59. 톰캣org.apache.tomcat.util.net.SocketProcessorBase.run:52
  60. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker:946
  61. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run:480
  62. 톰캣org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run:57
  63. JDKjava.lang.Thread.runWith:1596
  64. JDKjava.lang.Thread.run:1583

6. JSON 바이트를 톰캣에 넘긴다 http-nio-18080-exec-2 · 54칸 · 멈춘 곳 org.apache.catalina.connector.CoyoteOutputStream.write

  1. 톰캣org.apache.catalina.connector.CoyoteOutputStream.write:97
  2. 스프링 프레임워크org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.write:396
  3. 스프링 프레임워크org.springframework.util.StreamUtils$NonClosingOutputStream.write:311
  4. Jacksontools.jackson.core.json.UTF8JsonGenerator._flushBuffer:2341
  5. Jacksontools.jackson.core.json.UTF8JsonGenerator.flush:1235
  6. Jacksontools.jackson.databind.ObjectWriter.writeValue:984
  7. 스프링 프레임워크org.springframework.http.converter.AbstractJacksonHttpMessageConverter.writeInternal:436
  8. 스프링 프레임워크org.springframework.http.converter.AbstractSmartHttpMessageConverter.write:116
  9. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters:342
  10. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue:213
  11. 스프링 프레임워크org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue:77
  12. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle:135
  13. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod:934
  14. 스프링 프레임워크org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal:853
  15. 스프링 프레임워크org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle:86
  16. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doDispatch:963
  17. 스프링 프레임워크org.springframework.web.servlet.DispatcherServlet.doService:866
  18. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.processRequest:1000
  19. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.doPost:903
  20. 톰캣jakarta.servlet.http.HttpServlet.service:649
  21. 스프링 프레임워크org.springframework.web.servlet.FrameworkServlet.service:874
  22. 톰캣jakarta.servlet.http.HttpServlet.service:710
  23. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:128
  24. 톰캣org.apache.tomcat.websocket.server.WsFilter.doFilter:53
  25. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  26. 스프링 프레임워크org.springframework.web.filter.RequestContextFilter.doFilterInternal:100
  27. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  28. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  29. 스프링 프레임워크org.springframework.web.filter.FormContentFilter.doFilterInternal:93
  30. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  31. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  32. 스프링 프레임워크org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal:110
  33. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  34. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  35. 스프링 프레임워크org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal:199
  36. 스프링 프레임워크org.springframework.web.filter.OncePerRequestFilter.doFilter:116
  37. 톰캣org.apache.catalina.core.ApplicationFilterChain.doFilter:107
  38. 톰캣org.apache.catalina.core.StandardWrapperValve.invoke:165
  39. 톰캣org.apache.catalina.core.StandardContextValve.invoke:77
  40. 톰캣org.apache.catalina.authenticator.AuthenticatorBase.invoke:492
  41. 톰캣org.apache.catalina.core.StandardHostValve.invoke:113
  42. 톰캣org.apache.catalina.valves.ErrorReportValve.invoke:83
  43. 톰캣org.apache.catalina.core.StandardEngineValve.invoke:72
  44. 톰캣org.apache.catalina.connector.CoyoteAdapter.service:341
  45. 톰캣org.apache.coyote.http11.Http11Processor.service:397
  46. 톰캣org.apache.coyote.AbstractProcessorLight.process:63
  47. 톰캣org.apache.coyote.AbstractProtocol$ConnectionHandler.process:903
  48. 톰캣org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun:1801
  49. 톰캣org.apache.tomcat.util.net.SocketProcessorBase.run:52
  50. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker:946
  51. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run:480
  52. 톰캣org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run:57
  53. JDKjava.lang.Thread.runWith:1596
  54. JDKjava.lang.Thread.run:1583

7. 바이트가 소켓으로 나간다 http-nio-18080-exec-2 · 25칸 · 멈춘 곳 sun.nio.ch.SocketChannelImpl.write

  1. JDKsun.nio.ch.SocketChannelImpl.write:533
  2. 톰캣org.apache.tomcat.util.net.NioChannel.write:129
  3. 톰캣org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite:1441
  4. 톰캣org.apache.tomcat.util.net.SocketWrapperBase.doWrite:747
  5. 톰캣org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking:713
  6. 톰캣org.apache.tomcat.util.net.SocketWrapperBase.flush:698
  7. 톰캣org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.end:558
  8. 톰캣org.apache.coyote.http11.filters.IdentityOutputFilter.end:121
  9. 톰캣org.apache.coyote.http11.Http11OutputBuffer.end:230
  10. 톰캣org.apache.coyote.http11.Http11Processor.finishResponse:1237
  11. 톰캣org.apache.coyote.AbstractProcessor.action:386
  12. 톰캣org.apache.coyote.Response.action:197
  13. 톰캣org.apache.catalina.connector.OutputBuffer.close:241
  14. 톰캣org.apache.catalina.connector.Response.finishResponse:386
  15. 톰캣org.apache.catalina.connector.CoyoteAdapter.service:370
  16. 톰캣org.apache.coyote.http11.Http11Processor.service:397
  17. 톰캣org.apache.coyote.AbstractProcessorLight.process:63
  18. 톰캣org.apache.coyote.AbstractProtocol$ConnectionHandler.process:903
  19. 톰캣org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun:1801
  20. 톰캣org.apache.tomcat.util.net.SocketProcessorBase.run:52
  21. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker:946
  22. 톰캣org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run:480
  23. 톰캣org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run:57
  24. JDKjava.lang.Thread.runWith:1596
  25. JDKjava.lang.Thread.run:1583
같은 데이터를 시퀀스 다이어그램(누가 누구를 어떤 차례로 부르는지 위에서 아래로 적는 그림)으로 그렸다.
  • 그림 바로 밑의 로그가 이 그림의 손잡이다. 머리줄(「1. 연결을 받는다」 같은 줄)을 누르면 그 차례에 멈춘 순간으로 가고, 그 아래 줄(스택의 칸 하나)을 누르면 같은 순간에서 그 칸이 든 막대에 테두리를 두르고 멈춤 줄 옆에 그 칸의 이름을 단다. 이전 · 다음으로 단계를 넘기면 로그도 지금 차례의 머리줄로 굴러간다.
  • 맨 위 이름 칸이 참여자(프로젝트)다. 칸의 차례와 색은 위 2D 그림의 범례와 같고, 차례에는 뜻이 없다. 칸에서 내려오는 회색 세로 점선은 자리를 잇는 줄일 뿐이다.
  • 아래로 갈수록 나중이다. 다만 세로 간격은 일마다 같게 두어서, 막대 길이는 걸린 시간이 아니다.
  • 실선 화살표는 다른 프로젝트의 메서드를 부른 것이다. 화살표 위 이름은 화살표가 가리키는 프로젝트에 들어가 처음 실행된 메서드다. 같은 프로젝트 안에서 부른 것은 그리지 않아서, 화살표 수가 스택의 칸 수보다 훨씬 적다.
  • 점선 화살표는 그 프로젝트의 메서드가 다 돌아온 것이다.
  • 세로 막대는 그 프로젝트의 메서드가 스택에 있는 동안이다 — 실행 중이거나, 자기가 부른 메서드가 돌아오기를 기다리는 중이다. 한 참여자 밑에 막대가 조금씩 비켜 겹쳐 있으면 다른 프로젝트를 거쳐 다시 들어온 것이다. 점선 화살표 없이 상자 바닥까지 가는 막대는, 마지막으로 멈춘 뒤를 안 봐서 돌아왔는지 모르는 것이다.
  • 바깥 상자 하나가 스레드 하나다. 상자마다 첫 화살표는 왼쪽 끝의 점, 곧 스레드가 시작한 곳(Thread.run)에서 나온다. Acceptor 가 받은 연결을 Poller 가 지켜보다 일꾼 스레드(exec-2)에게 넘기는데, 이것은 메서드 호출이 아니라서 상자 사이에 화살표가 없다.
  • 노란 점선 「멈춤 N · M칸」은 디버거가 멈춘 순간이고, M 은 그때 스택 전체의 칸 수다(같은 프로젝트 안의 칸까지 센다). 그 줄 위의 노란 점은 그때 스택 꼭대기의 메서드가 든 막대에 찍었다.
  • 단계를 넘기면 그림이 그 단계의 화살표까지 아래로 따라가고, 지난 단계의 화살표와 돌아온 막대는 흐리게 둔다. 마지막 단계는 전체를 줄여 보인다.
  • 보통의 시퀀스 다이어그램은 실제로 지나간 호출을 다 적어 그리지만, 이 그림은 멈춘 순간의 스택을 차례로 견줘 맞춘 것이다. 앞 차례에만 있던 칸은 그 사이에 돌아왔고(스택 꼭대기부터), 새로 생긴 칸은 그 뒤에 불렸다(바닥 쪽부터). 그래서 멈춘 사이에 부르고 돌아온 호출은 안 보이고, 막대가 이어져 보여도 그 사이에 돌아왔다가 다시 불렸을 수 있다.
  • 화살표나 막대에 마우스를 올리면 클래스의 전체 이름이 뜬다.

마지막 단계는 멈춘 7번을 한데 모은 모습과, 프로젝트마다 칸이 몇 개 나왔는지(이어진 칸은 한 번만 센다)를 보여 준다. 거기서 스프링 부트 소스는 한 칸도 안 나온다. 스프링 부트와 스프링 프레임워크는 다른 프로젝트다. 빈 · 웹 · 트랜잭션을 실제로 처리하는 코드는 프레임워크에 있고, 부트는 그것을 설정 없이 켜 주는 쪽이다(1–5단계). 그렇다고 「부트는 요청 때 아무 일도 안 한다」로 읽지는 않는다. 이 요청은 성공한 요청 하나다. 7단계의 /nope처럼 404가 나면 부트의 BasicErrorController가 답한다. 확인한 것은 이 요청의 이 7순간에는 부트 소스의 줄이 스택에 없었다는 데까지다.