이 글은 김영한님의
스프링 MVC 1편
강의를 수강하고 정리한 내용입니다.
강의 보러가기
클라이언트에서 서버로 데이터를 전송할 때, 서버에서 클라이언트로 데이터를 응답할 때 모두 HTTP 프로토콜을 기반으로 통신한다.
거의 모든 형태의 데이터를 전송할 수 있다.
HTTP 기반으로 동작한다.
정적인 HTML, CSS, JS, 이미지, 영상 같은 리소스들을 제공한다.
대표적으로 Nginx, Apache가 있다.
웹 서버와 마찬가지로 HTTP 기반으로 동작한다.
웹 서버의 기능을 포함하고 있으며 애플리케이션 로직을 수행할 수 있다는 특징이 있다.
대표적으로 Tomcat, Jetty, Undertow가 있다.
이 두 용어의 경계는 모호하다. 웹 서버도 프로그램 코드를 실행하는 기능을 포함하기도 하며 WAS도 웹 서버의 기능을 제공하기 때문이다.
간단하게 웹 서버는 정적 리소스를 제공하는 것, WAS는 애플리케이션 로직까지 수행할 수 있는 것이라고 기억해 두자.
WAS, DB만으로 웹 시스템을 구성한다면?
앞에서 말했듯 WAS가 정적 리소스, 애플리케이션 로직까지 모두 제공이 가능해 WAS와 DB만으로도 웹 시스템을 구성할 수 있다.
하지만 이 경우는 WAS가 너무 많은 일을 담당하고 있어 서버 과부하의 위험이 있다.
WAS에 장애가 생길 경우 오류 화면조차 제공이 불가능하므로 심각한 문제가 될 수 있다.
WEB, WAS, DB로 웹 시스템 구성!
웹 서버가 정적 리소스를 처리, WAS가 애플리케이션 로직을 처리하게끔 역할을 분담한다.
효율적으로 리소스를 관리할 수 있다.
복잡한 로직이 동작하는 WAS가 잘 죽기 때문에 WAS, DB 장애 시 웹 서버가 오류 화면을 제공할 수 있다.
서블릿이 도입되면서 초록색 박스를 제외한 나머지 업무는 자동으로 처리가 되었다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) {
//애플리케이션 로직
}
}
HTTP 요청 메시지를 직접 파싱해서 사용하는 건 너무 번거로운 일이다.
개발자가 HTTP 스펙을 편리하게 다룰 수 있게 해준다.
WAS가 request, response 객체를 만들어 서블릿 객체를 호출한다.
개발자는 request / response에서 각각 HTTP 요청/응답 정보를 편리하게 사용할 수 있다.
WAS는 response 객체에 담겨있는 내용으로 HTTP 응답 정보를 생성한다.
웹 브라우저에 응답 메시지가 전달된다.
톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너라고 한다.
서블릿 컨테이너가 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기를 관리해준다.
서블릿 객체는 싱글톤으로 관리가 된다.
고객의 요청이 들어올 때마다 객체를 생성하는건 매우 비효율적이다.
최초 로딩 시점에 서블릿 객체를 미리 만들어두고 재활용한다.
모든 고객 요청은 동일한 서블릿 객체 인스턴스에 접근한다.
서블릿 컨테이너 종료 시 서블릿 객체도 같이 종료된다.
JSP도 서블릿으로 변환되어 사용된다
❗동시 요청을 위한 멀티 쓰레드 처리를 지원한다.❗
💡 바로 이 쓰레드가 서블릿 객체를 호출해 준다.
애플리케이션 코드를 하나하나 순차적으로 실행하는 것이 쓰레드이다.
자바 메인 메서드를 처음 실행하면 main이라는 이름의 쓰레드가 실행된다.
쓰레드가 없다면 자바 애플리케이션 실행이 불가능하다.
쓰레드는 한번에 하나의 코드 라인만 수행한다.
동시 처리가 필요하면 쓰레드를 추가로 생성한다.
그럼 이제 쓰레드를 사용한 요청/응답 과정을 생각해 보자.
먼저 클라이언트 요청1이 들어오면 쓰레드가 서블릿 객체를 호출해 요청1을 처리한다.
그런데 그 처리가 어떠한 이유로 지연이 되는 사이에 요청2가 들어왔다고 가정하자.
요청2는 요청1의 처리가 끝날 때까지 기다려야 되는 문제가 발생하는 것이다.
그럼 위에 적혀 있듯 동시 처리가 필요한 상황이니
요청마다 쓰레드를 생성해서 처리하면 되는거 아니야?
요청1 처리가 지연되는 동안 요청2는 신규 쓰레드를 생성해 처리를 진행하면서 문제가 해결되었다.
위 방식에는 장단점이 존재한다.
동시 요청을 처리할 수 있다.
리소스(CPU, 메모리)가 허용할 때까지 처리가 가능하다.
하나의 쓰레드가 지연 되어도, 나머지 쓰레드는 정상 동작한다.
쓰레드는 생성 비용은 매우 비싸다.
쓰레드는 컨텍스트 스위칭 비용이 발생한다.
쓰레드 생성에 제한이 없다.
컨텍스트 스위칭 비용이란?
예를 들어 CPU 코어수가 4개라고 가정해보자.
코어는 쓰레드를 동시에 수행하지 못하기 때문에 여러 쓰레드를 번갈아가며 수행한다.
이렇게 다른 쓰레드로 전환할 때 발생하는 비용을 컨텍스트 스위칭 비용이라고 한다.
WAS에서는 쓰레드를 요청마다 생성할 때의 단점을 보완할 수 있는
쓰레드풀
이라는 것으로 구현이 되어있다.
필요한 쓰레드를 쓰레드 풀에 보관하고 관리한다.
쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리한다.
-> 톰캣은 최대 200개가 기본 설정이다. (변경 가능)
쓰레드가 필요하면, 이미 생성되어 있는 쓰레드를 쓰레드 풀에서 꺼내서 사용한다.
사용을 종료하면 쓰레드 풀에 해당 쓰레드를 반납한다.
최대 쓰레드가 모두 사용중이라 쓰레드 풀에 쓰레드가 없으면?
쓰레드가 미리 생성되어 있으므로, 쓰레드를 생성하고 종료하는 비용(CPU)이 절약되고, 응답 시간이 빠르다.
생성 가능한 쓰레드의 최대치가 있으므로 너무 많은 요청이 들어와도 기존 요청은 안전하게 처리할 수 있다.
WAS의 주요 튜닝 포인트는 최대 쓰레드(max thread)수이다.
이 값을 너무 낮게 설정하면?
동시 요청이 많다면, 서버 리소스는 여유롭지만 클라이언트는 금방 응답 지연
예를 들어
최대 쓰레드를 10개로 설정해둔 상태에서 요청이 100개가 들어온다고 가정해보자.
그러면 응답 지연은 금방되는데 CPU는 5%밖에 사용 안하고 있는 상황이 생길 수 있다.
이 값을 너무 높게 설정하면?
장애 발생시?
클라우드면 일단 서버부터 늘리고, 이후에 튜닝한다.
클라우드가 아니면 바로 튜닝한다.
애플리케이션 로직의 복잡도, CPU, 메모리, IO 리소스 상황에 따라 모두 다르다.
최대한 실제 서비스와 유사하게 성능 테스트를 시도하는 방법이 있다.
멀티 쓰레드에 대한 부분은 WAS가 처리한다.
개발자가 멀티 쓰레드 관련 코드를 신경쓰지 않아도 된다.
개발자는 마치 싱글 쓰레드 프로그래밍을 하듯이 편리하게 소스 코드를 개발하면 된다.
멀티 쓰레드 환경이므로 싱글톤 객체(서블릿, 스프링 빈)는 주의해서 사용해야 한다.
-> 웹 서버에서 정적인 HTML 파일, CSS, JS, 이미지, 영상 등을 제공하면 된다.
-> WAS에서 뷰 템플릿을 가지고 동적으로 필요한 HTML 파일을 생성해서 전달한다.
HTML처럼 보여줄 화면을 전달해 주는 것이 아니라 데이터를 전달해 주는 방식이다.
데이터 형식은 주로 JSON 형식을 사용한다.
웹 브라우저에 넘겨줄 때 쓰는게 아니라 다양한 시스템에서 호출한다.
( 어차피 웹 브라우저는 HTML을 렌더링해서 화면을 띄워야 하는데 JSON이 들어오면 해석 못한다. )
웹/앱 클라이언트나 서버간 통신이 필요할 때 사용한다.
데이터만 주고 받는다.
주로 JSON 형태로 데이터 통신한다.
UI 클라이언트 접점
앱 클라이언트(아이폰, 안드로이드, PC 앱)
웹 브라우저에서 자바스크립트를 통한 HTTP API 호출
React, Vue.js 같은 웹 클라이언트
서버 to 서버
주문 서버 -> 결제 서버
기업간 데이터 통신
HTML을 만드는 과정은 서버에서 모두 끝내고, 웹 브라우저는 생성된 HTML을 렌더링한다.
최종적으로 HTML을 서버에서 다 만들기 때문에 서버 사이드 렌더링이라고 불린다.
주로 정적인 화면에 사용된다.
관련기술: JSP, 타임리프 -> 백엔드 개발자
웹 브라우저에서 HTML을 동적으로 생성해 적용한다.
-> 자바스크립트 사용
클라이언트쪽에서 HTML을 만들기 때문에 클라이언트 사이드 렌더링이라고 불린다.
주로 동적인 화면에 사용, 웹 환경을 마치 앱 처럼 필요한 부분을 변경할 수 있다.
관련기술: React, Vue.js -> 웹 프론트엔드 개발자
애노테이션 기반의 스프링 MVC 등장
@Controller
MVC 프레임워크의 춘추 전국 시대 마무리!
스프링 부트의 등장
스프링 부트는 서버를 내장하고 있다. (Tomcat)
과거에는 서버에 WAS를 직접 설치하고, 소스는 War 파일을 만들어서 설치한 WAS에 배포했다.
스프링 부트는 빌드 결과(Jar)에 WAS를 포함시켜 빌드 배포를 단순화시켰다!
Web Servlet 기반의 Spring MVC -> 보통의 경우 이걸 사용!
Web Reactive 기반의 Spring WebFlux -> 기술적 난이도가 매우 높아 사용하기 어렵다.
JSP
프리마커(Freemarker), Velocity(벨로시티)
타임리프(Thymeleaf)
내추럴 템플릿: HTML의 모양을 유지하면서 뷰 템플릿 적용 가능
스프링 MVC와 강력한 기능 통합
타임리프 선택이 베스트!
(단 성능은 프리마커, 벨로시티가 더 빠름)