build.gradle
)DispatcherServlet
이라고 불리는 서블릿으로 전달한다.DispatcherServlet
에서 HandlerMapping
을 호출한다. HTTP요청의 URL과 매칭되는 Controller(Handler)를 선택한다.DispatcherServlet
은 HandlerAdapter
를 호출한다. 즉, Controller의 호출을 HandlerAdapter
에게 맡기는 것이다. 다양한 형태의 Controller를 지원해야 하므로 유연성과 확장성을 제공하기 위해 DispatcherServlet
이 직접 Controller를 호출하지 않고 다양한 방식을 추상화하고 통합하는 HandlerAdapter
에게 Controller를 호출하는 역할을 위임한다.DispatcherServlet
은 이 View이름을 가지고 ViewResolver
를 호출하여 실제 View를 찾아 렌더링한다. View가 아닌 JSON형태의 데이터를 반환해야하는 상황일 수도 있다. 이 경우에는 Controller는 View의 이름을 리턴하는 것이 아닌 HTTP Body에 JSON 데이터를 담아 리턴한다. DispatcherServlet
은 View이름이 아니므로 ViewResolver
를 호출하지 않고 그대로 반환한다.Model
+ View
+ Controller
로 이루어져 있다.@Controller
, 혹은 @RestController
로 Controller임을 명시한다.@Controller
애노테이션으로 Controller를 지정한 경우, 해당 Controller에 대응되는 View이름을 리턴한다. 이때 Controller에서는 해당 View에 사용될 데이터 혹은 로직을 Model에 담는다.@RestController
애노테이션으로 Controller를 지정한 경우, 해당 Contoller는 사용자에게 표시되는 화면인 View를 리턴하지 않는다. 대신, HTTP body에 JSON형태의 객체 데이터를 담아 반환한다. @RestController
는 @ResponseBody
와 @Controller
를 합친 애노테이션이다. 단순히 View가 아닌, JSON형태의 객체를 반환하고 싶을 때 @ResponseBody
를 사용한다.localhost:8080
에 접속하여 index.html
을 받는것이다. html은 정적 파일이지만 안에 들어가있는 데이터는 계속 변하는 동적 성질을 가진다고 가정하자. View를 반환하므로 Controller에서는 @Controller
애노테이션이 사용된다.localhost:8080
으로 시작되는 링크로 HTTP요청을 전송한다.DispatcherServlet
으로 전달한다.HandlerMapping
을 호출하여 실행해야할 Controller를 Mapping하여 DispatcherServlet
으로 돌아간다.DispatcherServlet
은 HandlerAdapter
을 호출하여 Controller를 실행한다.DispatcherServlet
으로 돌아가 ViewResolver
를 호출한다. ViewResolver
는 ViewName으로 실제 View의 위치를 찾아 매핑한다.DispatcherServlet
은 View를 렌더링한. 이 과정에서 View는 Model을 사용해 HTML을 생성한다. 이때 JSP, Thymeleaf 등의 템플릿 엔진이 사용된다.DispatcherServlet
은 렌더링 완료된 View를 클라이언트에 응답으로 반환한다.localhost:8080
을 요청했을 때{
"isSuccessed" : "true"
}
라는 간단한 JSON형식의 HTTP Body를 응답으로 받는다고 가정하자. 해당 JSON과 대응되는 자바 클래스를 Success라고 명명하겠다. 해당 내용은 db가 필요없지만 db에서 데이터를 가져와야한다고 가정하겠다. Java 객체를 반환하므로 Controller는 @RestController
애노테이션을 사용한다.DispatcherServlet
에서의 동작은 위와 동일하다.DispatcherServlet
은 해당 응답을 클라이언트에 반환한다.Bean Container
안에 컴포넌트들이 들어있음을 확인할 수 있다. Spring Container
라고 불리기도 한다. 이에 대해 바로 설명하기 보다 이를 왜 사용하게 됐는지부터 설명해보려고 한다.@SpringBootApplication
public class PracticeApplication {
public static void main(String[] args) {
SpringApplication.run(PracticeApplication.class, args);
}
}
main()
함수는 SpringApplication.run(PracticeApplication.class, args)
을 실행하여 스프링 부트를 실행한다. 해당 메서드는 애플리케이션을 실행하는데 필요한 객체를 생성한다.SpringApplication
을 생성하여 애플리케이션 초기 설정을 수행한다.
ApplicationContext
을 생성하여 애플리케이션의 모든 Bean
을 관리한다. 이는 조금 있다 후술할 예정이다.
등등…
여기에는 어떠한 Controller
, DispatcherServlet
, 설정 클래스 등 위에서 언급했던 수많은 컴포넌트들을 생성하는 코드는 전혀 없다.
반대로 생각해보면 당연하다고 느낄 수 있다. 개발자들이 개발을 진행하며 수많은 API와 View를 생성할 것이다. 즉, 수많은 컨트롤러들을 생성하게 될 것이다.
만약 100개의 Controller를 작성했다고 가정하자. 만약 애플리케이션을 실행하면 어떤 url로 어떤 Controller를 호출할지 모르니 이 모든 Controller를 직접 하드코딩하여 생성자를 이용해 생성해야 할 것이다.
또한 100개를 생성해놓고 HTTP요청에 매핑되어 사용될 컨트롤러는 단 한개 뿐이다. 리소스적으로 낭비가 아닐 수 없다.
ApplicationContext
만 생성하면 된다.ApplicationContext
)에 의해 관리되는 객체를 Bean
이라고 한다.@Configuration
, @Component
, @Controller
, @Service
, @Repository
, @Bean
등 모두 메서드, 혹은 클래스를 자동으로 생성 가능하도록 빈으로 등록하는 애노테이션이다.ApplicationContext
가 Spring Container의 구현체이다.ApplicationContext
를 생성하여 빈으로 등록된 클래스들을 관리한다.이러한 기술을 통해 모든 클래스를 생성하지 않고도 웹 애플리케이션 실행이 가능해지며 리소스 관리도 더욱 효율적으로 할 수 있다.