Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 MVC 1편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는 스프링 빈의 스코프에 대해서 다루워 보았다.
웹 서버와 어플리케이션 서버
웹 서버는 HTML, CSS, JS와 같은 정적인 파일을 그대로 HTTP 기반으로 응답해주는 서버를 의미한다.
이러한 서버는 내부 로직이 구동되지 않기 때문에 트래픽 내성이 강하다.
웹 어플리케이션 서버(WAS, Web Application Server)
동일하게 HTTP 기반으로 동작하지만 정적 리소스를 제공하고 내부 애플리케이션 로직까지 실행한다.
따라서 사용자 별로 서비스를 각자 제공할 수 있다.
대표적으로 서블릿, 스프링 MVC, JSP이 있다.
기본적인 웹 서버는 WAS와 DB로 구성될 수 있다.
하지만 WAS가 모든 역할을 담당하게 되면 과부화로 인해, 애플리케이션 로직을 수행하는 동안 정적 리소스 반환 처리가 늦어질 수 있다.
특히 WAS는 복잡한 프로그램 코드들을 수행하다 장애가 발생하기 쉽다.
이러한 경우 단순한 오류 페이지 조차 클라이언트에게 반환해 줄 수 없다.
따라서 정적 리소스를 처리하는 웹 서버를 거치고 WAS에 접근하도록 시스템을 구성해야 한다.
웹 서버는 트래픽에 강하기 때문에 WAS에 장애가 발생하더라도, 웹 서버가 오류 페이지를 반환시킬 수 있다.
서블릿
네트워크 계층 구조에 따라서 실제로 어플리케이션 계층이 소켓 통신으로 받게 되는 데이터는 HTTP이다.
데이터를 받기 위해서는 상대와 소켓 연결을 하고, 송신할 데이터는 HTTP 헤더로 캡슐화하고, 수신된 데이터 가공을 위해서는 헤더와 페이로드를 분리하는 파싱이 필요하다.
이런 여러가지 작업들을 대신 수행해 주는 것이 서블릿이다.
@WebServelt 어노테이션을 적용하고 HttpServlet을 상속하면, 해당 클래스에서 service() 함수를 통해 편리하게 HTTP를 사용할 수 있다.
서블릿 적용하면 WAS는 HttpServletRequest와 HttpServletResponse 객체를 만들어 서블릿 객체로 넘겨주기 때문이다.
서블릿 객체는 WAS의 서블릿 컨테이너에 생성되고, 호출, 종료등의 생명 주기가 관리된다.
요청마다 Request와 Response 객체는 모두 다르기 때문에 각각 생성이 필요하지만, 이 데이터를 넘겨받아 처리만 하는 서블릿 객체는 충분히 재사용할 수 있다.
따라서 스프링 컨테이너와 동일하게 서블릿 컨테이너도 객체를 싱글톤으로 관리한다.
물론 싱글톤이기 때문에 공유되는 변수의 사용은 주의가 필요하다.
무엇보다 멀티 쓰레드가 자동 지원된다.
멀티 쓰레드
워드 프로세스를 생각해보자.
사용자의 입력을 기다리는 루틴과, 입력을 화면에 출력하는 루틴, 자동 저장을 해주는 루틴 등의 동시에 여러 작업이 진행이 된다.
만약 모든 것이 단 하나의 메서드(하나의 쓰레드)에서 순차적으로 실행된다고 상상해보자.
타자를 치고 대기한 후 입력 가능 시간이 끝나면, 다음 루틴으로 넘어간다.
다음 루틴이 출력 루틴이라면 해당 글자가 화면에 글자가 출력될 때 동안에는 사용자의 입력을 받지 못 할 것이다.
따라서 동시에 여러 작업을 진행하기 위해 탄생한 것이 멀티 쓰레드이다.
쓰레드 별로 각각의 작업(메서드의 실행)을 동시에 실행이 가능하다.
서버에서도 만약 단 하나의 쓰레드가 있다면 순차적으로 요청 작업을 수행한 후, 다음 요청을 작업할 것이다.
하지만 WAS는 멀티 쓰레드를 지원하기 때문에, 서버는 각각의 요청마다 쓰레드를 할당하고 각 쓰레드는 서블릿 객체를 통해 요청에 응답한다. 따라서 개발자가 이를 직접 신경쓰지 않아도 된다.
하지만 요청마다 쓰레드를 생성하는 것은 cost(하드웨어적 처리량)가 높다.
거기에 OS의 스케줄링을 통해서 발생하는 문맥 교환비용이 발생한다.
(하드웨어의 쓰레드와 소프트웨어의 쓰레드는 다르다. 수많은 소프트웨어 쓰레드가 스케줄링을 통해 하드웨어의 쓰레드에 할당되어 돌아간다.)
요청마다 쓰레드가 생성된다면, 갑자기 늘어난 요청들마다 각각의 쓰레드를 생성한다.
하지만 실제 서버도 메모리에 제한이 있기 때문에 무한히 쓰레드를 생성할 수는 없다.
따라서 새로운 쓰레드를 생성하기 위해서는 기존의 프로세스를 강제 종료시키는 방식으로 새로운 쓰레드를 생성시킨다.
이 과정에서 서로가 메모리에 접근하기 위해 서로를 쫓아내는 DEADLOCK 같은 상황이 발생할 수 있다.
쓰레드 풀
따라서 쓰레드 풀(인기 식당의 테이블과 비슷)이라는 개념이 도입되었다.
요청이 오면 쓰레드가 이미 생성이 되어있는 쓰레드 풀에서 가져와 사용한다.
쓰레드 풀에는 개수가 한정되어 있고 더 많은 요청이 동시에 온다면, 다른 요청이 종료되어 쓰레드를 반납할 때까지 기다려야 한다. (대기 혹은 거절)
미리 쓰레드가 생성되어 있기 때문에 요청마다 생성, 종료되는 cost를 절약할 수 있고 반응도 빠르다.
그렇다면 WAS에서 중요한 것은 쓰레드 풀의 쓰레드 개수이다.
너무 적다면 기다리는 클라이언트가 많아지고, 너무 많다면 서버의 과부하가 발생하기 쉽다.
AWS와 같은 클라우드 서버는 더 필요한 쓰레드의 개수만큼 서버를 자동 증설하기 때문에 비용적인 측면에서도 서버의 성능을 최대한 사용하는 것이 중요하다.
데이터 전달 방식과 렌더링
정적 리소스 반환
서버가 저장 중인 파일(HTML, 이미지, 영상)을 바로 제공
HTML 페이지
WAS가 요청에 따라 동적인 HTML을 생성해 반환한다.
예를 들어 유튜브는 클라이언트마다 다른 영상 추천을 동적으로 만들어 HTML로 제공한다.
HTTP API
HTTP를 사용하면 본문에 HTML이 아닌 JSON과 같은 데이터를 포함시켜 전송이 가능하다.
웹 브라우징에 http:// 태그 URL에 들어가기 때문에 HTML을 보낼 때만 이용한다고 생각할 수 있지만,
HTTP는 범용적인, 네트워크의 애플리케이션 계층의 전송 프로토콜이기 때문에 서버-서버, 클라이언트 - 서버 간의 데이터 전송에도 이용된다.
Server Side Rendering(백엔드)
요청에 따른 최종적인 HTML을 서버에서 생성(렌더링)하여 HTML을 전달한다.
주로 복잡하지 않은 정적인 화면에 사용된다.
Client Side Rendering(프런트엔드)
HTML 코드를 JS를 이용해 클라이언트의 브라우저에서 생성한다.
보통 웹에서 일부만 동적으로 변경되는 경우 많이 사용된다.
초기에는 HTML와 JS를 받아와 렌더링 하지만, 이후 서버의 데이터가 필요하다면 HTTP API로 데이터를 요청한다.
최근에 인기가 많은 React, Vue.js 프레임 워크가 이런 방식으로 동작한다.
사실 하나의 렌더링 방식만 사용하기보단 각 방식의 둘 다 사용하여 서비스를 제공한다.
자바 뷰 템플릿
HTML을 탬플릿처럼 찍어서 뷰를 생성해주는 기술
Jsp에서 발전되고 스프링 MVC와 연결성이 강한 Thymeleaf(타임리프)를 많이 사용한다.
'개발 > Spring' 카테고리의 다른 글
[Spring] 14. 템플릿 엔진과 MVC 패턴 (0) | 2022.07.08 |
---|---|
[Spring] 13. Servlet, 서블릿 (0) | 2022.07.06 |
[Spring] 11. 스프링 빈의 스코프(Scope) (0) | 2022.07.02 |
[Spring] 10. 스프링 빈의 라이프 사이클 (0) | 2022.06.30 |
[Spring] 9. 의존 관계 주입 (0) | 2022.06.29 |