BACKEND/SPRING

02. 개발에 앞서 알면 좋은 기초 지식 - 서버간 통신 및 스프링 동작 방식

우진하다 2023. 7. 22. 14:38

애플리케이션이 어떻게 동작하는지, 왜 이렇게 구성되는지 생각하며 실습하기 위한 배경 지식을 알아봅시다.

 

서버 간 통신

단일 서비스 아키텍처와 마이크로서비스 아키텍처는 둘 다 소프트웨어 시스템을 구축하는 방법에 대한 접근 방식을 나타내는 용어입니다.

단일 서비스 아키텍처 (Monolithic Architecture)
단일 서비스 아키텍처는 하나의 큰 애플리케이션으로 구성되는 전통적인 아키텍처입니다.
모든 기능과 로직이 단일 애플리케이션 내에 포함되어 있으며, 모놀리식(모노리딕) 애플리케이션으로도 불립니다.
주로 모노리딕 애플리케이션은 단일 코드 베이스로 구성되며, 코드의 모든 부분이 하나로 통합됩니다.
애플리케이션의 규모가 커지면 코드 베이스가 점점 복잡해지고 유지보수가 어려워질 수 있습니다.

마이크로서비스 아키텍처 (Microservices Architecture)
마이크로서비스 아키텍처는 애플리케이션을 작은, 독립적으로 배포 가능한 여러 개의 작은 서비스로 나누는 아키텍처 패턴입니다.
각각의 서비스는 특정 비즈니스 기능을 담당하며, 서로 다른 데이터베이스나 독립적인 데이터 저장소를 사용할 수 있습니다.
마이크로서비스는 각각 독립된 프로세스로 실행되고, 네트워크를 통해 서로 통신합니다.
각각의 마이크로서비스는 느슨한 결합을 가지므로, 개별적으로 스케일링하고 업데이트할 수 있습니다.
마이크로서비스는 DevOps, CI/CD 등과 같은 현대적인 개발 방법론과 잘 어울립니다.

만약 마이크로 서비스 아키텍처로 만들어진 애플리케이션을 개발하게 되면 각 서비스 간에 통신해야 하는 경우가 발생합니다.
예로 사용자 관리 서비스, 상품 카탈로그 서비스, 주문 처리 서비스 등이 각각의 마이크로서비스로 구현되고, 서로 통신하여 하나의 웹 애플리케이션으로 작동한다면 사용자 로그인 서비스를 거쳐야만 위와 같은 서비스를 이용할 수 있는 상황이 있습니다.

서버 간 통신은 한 서버가 다른 서버에 통신을 요청하는 것을 의미하며, 한 대는 서버, 다른 한 대는 클라이언트가 되는 구조 입니다.
즉, 서버 간 통신은 네트워크를 통해 서로 다른 두 개 이상의 서버가 데이터나 메시지를 주고받는 것을 말합니다. 이는 다양한 목적으로 사용되며, 웹 서비스, 마이크로서비스 아키텍처, 분산 시스템 등에서 일반적으로 발생합니다.

서버 간 통신을 구현하는 방법은 여러 가지가 있습니다. 일반적으로 사용되는 주요 통신 방법은 다음과 같습니다:

HTTP 통신
HTTP는 HyperText Transfer Protocol의 약자로, 웹 기반의 통신에 가장 일반적으로 사용됩니다.
서버 간 통신에서는 주로 REST API를 통해 JSON 또는 XML 형식의 데이터를 주고받습니다.
클라이언트(요청을 보내는 서버)와 서버(요청을 받는 서버) 간의 요청(Request)과 응답(Response) 방식으로 통신합니다.

RPC (Remote Procedure Call)
RPC는 원격 프로시저 호출을 의미하며, 클라이언트가 원격 서버의 함수를 호출하여 결과를 받아오는 방식입니다.
클라이언트와 서버 간의 통신은 마치 로컬 함수 호출처럼 추상화되어 사용됩니다.
주로 gRPC, Apache Thrift, Apache Dubbo 등의 프레임워크를 사용하여 구현할 수 있습니다.

메시지 큐 (Message Queue)
메시지 큐는 메시지 브로커를 통해 서버 간 비동기적으로 메시지를 주고받는 방식입니다.
메시지 큐를 이용하면 메시지를 전달하고 받는 서버 간의 결합도를 낮출 수 있습니다.
대표적인 메시지 큐 시스템으로는 RabbitMQ, Apache Kafka 등이 있습니다.

웹 소켓 (WebSocket)
웹 소켓은 양방향 통신을 제공하는 프로토콜로, 클라이언트와 서버 간에 실시간으로 데이터를 주고받을 수 있습니다.
주로 실시간 채팅, 알림, 게임 등에서 사용됩니다.

이외에도 gRPC, MQTT, ZeroMQ 등 다양한 통신 프로토콜과 기술들이 있으며, 
상황과 요구에 맞게 적절한 통신 방법을 선택하여 서버 간 통신을 구현할 수 있습니다.


스프링 부트의 동작 방식

스프링 부트에서는 spring-boot-starter-web 모듈을 사용해 기본적으로 톰캣(Tomcat)을 사용하는 스프링 MVC 구조를 기반으로 동작합니다. 웹 요청이 들어오면 아래와 같은 그림으로 스프링 부트는 동작합니다.

Servlet은 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술입니다.
일반적으로 서블릿은 서블릿 컨테이너에서 관리하며
서블릿 컨테이너는 서블릿 인스턴스를 생성하고 관리하는 역할을 수행하는 주체로
톰탯은 WAS의 역할과 서블릿 컨에티너의 역할을 수행하는 대표적인 컨테이너 입니다.

서블릿 컨테이너의 특징으로는
- 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기를 관리
- 서블릿 객체는 싱글톤 패턴으로 관리
- 멀티 스레딩 지원
입니다.

스프링에서는 DispatcherServlet이 서블릿의 역할을 수행하며 스프링은 톰캣을 임베드해 사용합니다.
그렇기 때문에 서블릿 컨테이너와 디스패처서블릿은 자동 설정된 web.xml 의 설정값을 공유합니다.

DispatcherServlet 동작을 간략히 알아봅시다.

① 디스패처서블릿으로 요청이 들어오면 핸들러 맵핑을 통해 요청 URI에 매핑된 핸들러를 탐색합니다.
여기서 핸들러는 컨트롤러를 의미합니다.
② 핸들러 어댑터로 컨트롤러를 호출하고
③ 핸들러 어댑터에 컨트롤러의 응답이 돌아오면 ModelandView 로 응답을 가공해 반환합니다.
④ 뷰 형식으로 리턴하는 컨트롤러를 사용할 때는 뷰 리졸버를 통해 뷰를 받아 리턴 합니다.

핸들러 매핑은 요청 정보를 기준으로 어떤 컨트롤러를 사용할지 선정하는 인터페이스입니다.
핸들러 매핑 인터페이스는 여러 구현체를 가지며, 대표적인 구현 클래스는 다음과 같습니다.

BeanNameUrlHandlerMapping
요청 URL과 일치하는 빈의 이름을 기준으로 핸들러를 매핑합니다.
스프링 빈의 이름과 요청 URL을 매핑하는 가장 간단한 방법입니다.

RequestMappingHandlerMapping
@RequestMapping 어노테이션을 이용하여 핸들러 매핑을 수행하는 가장 일반적인 핸들러 매핑입니다.
메소드 레벨의 @RequestMapping, @GetMapping, @PostMapping 등과 클래스 레벨의 @RequestMapping 어노테이션을 분석하여 핸들러를 매핑합니다.

SimpleUrlHandlerMapping
사용자가 직접 URL 패턴과 핸들러를 매핑하는 것을 지원하는 핸들러 매핑입니다.
properties 파일 등을 통해 URL과 핸들러를 일대일로 매핑할 수 있습니다.

ControllerClassNameHandlerMapping은 컨트롤러 클래스의 이름을 기반으로 핸들러를 매핑하는 방식입니다. 보통 이 핸들러 매핑은 컨트롤러 클래스의 이름을 가지고 URL 패턴을 자동으로 생성하여 핸들러 매핑합니다.

예를 들어, 컨트롤러 클래스의 이름이 "UserController"인 경우, 이 핸들러 매핑은 "/user"와 같은 URL 패턴에 해당 컨트롤러를 매핑합니다. 컨트롤러 클래스 이름의 일부를 기반으로 URL 패턴을 생성하고 싶을 때 유용하게 사용될 수 있습니다.

이러한 핸들러 매핑은 주로 특수한 요구사항이나 프로젝트의 특정 구성에 따라 사용되며,
대부분의 경우RequestMappingHandlerMapping과 같은 일반적인 핸들러 매핑이 더 많이 사용됩니다.
하지만 스프링의 유연성과 다양한 기능들을 고려하면서 컨트롤러 클래스 이름 핸들러 매핑을 사용할 수도 있습니다.

DefaultAnnotationHandlerMapping (Deprecated)
이전에 사용되던 핸들러 매핑 방식으로, @Controller, @RequestMapping 등의 어노테이션을 이용하여 핸들러를 매핑했습니다.
현재는 더 이상 권장되지 않으며, 대신 RequestMappingHandlerMapping을 사용하는 것이 일반적입니다.

이 외에도 커스텀한 핸들러 매핑 인터페이스를 구현하여 사용할 수 있습니다. 스프링은 다양한 핸들러 매핑을 제공하여 개발자들이 편리하게 웹 애플리케이션의 핸들러를 매핑하고 실행할 수 있도록 지원하고 있습니다.

뷰 리졸버는 뷰의 렌더링 역할을 담당하는 뷰 객체를 반환합니다.

뷰가 없는 애플리케이션이라면 REST 형식의 @ResponseBody를 사용해
MessageConverte를 거쳐 JSON 형식으로 반환하여 응답합니다.

 

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ HttpMessageConverter.class, WebMvcConfigurer.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnMissingBean(WebMvcConfigurer.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@EnableConfigurationProperties(HttpProperties.class)
public class HttpMessageConvertersAutoConfiguration {
    //...
}

@Configuration: 이 클래스는 스프링의 설정 클래스임을 나타냅니다.
@ConditionalOnClass({ HttpMessageConverter.class, WebMvcConfigurer.class }): HttpMessageConverter 및 WebMvcConfigurer 클래스가 클래스패스에 존재할 때에만 해당 설정을 활성화합니다.
@ConditionalOnWebApplication(type = Type.SERVLET): 웹 애플리케이션의 유형이 서블릿 기반 웹 애플리케이션일 경우에만 설정이 활성화됩니다.
@ConditionalOnMissingBean(WebMvcConfigurer.class): WebMvcConfigurer 빈이 없을 경우에만 설정을 적용합니다. 이는 사용자가 별도의 WebMvcConfigurer를 등록하지 않으면 스프링 부트의 기본 설정을 사용하도록 합니다.
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10): 이 설정의 우선순위를 가장 높게 설정합니다.
HttpMessageConvertersAutoConfiguration 클래스는 위와 같은 조건들을 충족하는 경우에만 활성화되어 자동으로 HttpMessageConverter 빈들을 설정합니다. 설정된 HttpMessageConverter들은 웹 애플리케이션에서 HTTP 요청과 응답의 메시지 변환에 사용됩니다.