[Spring] 웹 애플리케이션 아키텍처 이해: WEB, WAS, 서블릿, Spring MVC

tech_bae·2025년 3월 26일

Spring

목록 보기
1/1
post-thumbnail

WAS(Web Application Server)

정적 페이지

  • Web Server가 요청이 들어오면 해당 요청에 해당하는 파일을 반환한다.
  • 이 파일은 항상 같은 페이지(밀키트 자판기라고 생각해보자. 밀키트 자판기는 밀키트를 조리해서 주지 않는다. 그냥 밀키트를 줄 뿐)
    • WEB이 정적자원을 처리한다.

동적 페이지

  • 클라이언트의 요청에 맞게 동적인 데이터를 반환한다.(원하는 대로 조리를 해서 준다 와 개이득)
  • Web Server에서 실행되는 프로그램의 연산을 통한 결과가 필요
    • 이 동적인 페이지 생성하기 위한 프로그램이 Servlet
    • 이러한 프로그램(Servlet)이 실행되는 서버가 WAS




웹 서버(Web Server)

  • 클라이언트의 HTTP 요청을 해석하여 이에 맞는 데이터로 응답
  • 이 HTTP를 해석하고 그에 맞는 데이터 형식으로 보내 준다.
  • 정적데이터들을 다룬다. ⇒ 처리속도가 빠르고 트래픽 과부하를 잘 처리

왜 웹서버는 미들웨어가 아닌가?

⇒ 웹서버는 WAS와는 다르게 비지니스 로직을 담당하지 않기 때문에




어플리케이션 서버(Application Server)

  • 데이터를 가공, 연산하여 처리하는 비지니스 로직을 처리하는 서버
  • HTTP를 통해 컴퓨터나 장치에 비지니스 로직을 수행해주는 미들웨어로 볼 수 있다. ⇒http는 요청과 응답에 규약이있음. 이 데이터를 자바(or 다른 언어)로 쓸수 있게 바꾸어줌.
  • 동적데이터를 처리, 주로 DB서버와 같이 수행한다.


WAS(Web Application Server)

  • WAS는 JAVA EE스펙을 구현하여, 서블렛이나 JSP로 작성된 어플리케이션을 실행하는 소프트웨어(어플리케이션 서버)

어플리케이션 서버와 WAS는 같은 것 인가?

⇒ JAVA EE를 구현하여 서블릿으로 작성된 어플리케이션을 실행 할 수 있으면 WAS

즉, WAS는 어플리케이션 서버의 한 종류

Jakarta EE

분산 어플리케이션 개발 목적의 산업 표준 플랫 폼

Servlet(서블릿), JSP, EJB, JDBC, JNDI, JMX, JTA등의 기술을 포함




Servelet Container(Web Container)

CGI(Common Gateway Interface)

웹 서버가 동적인 데이터를 반환하기 위해선 이를 위한 비지니스로직을 처리하는 프로그램이 필요하다. 이 데이터를 처리하는 프로그램에 요청을 적절히 전달해주는 중간자 역할을 하는 프로그램이 CGI프로그램이다.

(클라이언드 → CGI Process → Program)

하지만 CGI는 실체가 있는 도구가 아니라 웹 서버와 외부 프로그램 사이에서 정보를 주고 받는 방법, 즉 표준 스펙이자 인터페이스입니다.

⇒ 다양한 언어(PHP, Python …)로 CGI를 구현(적용) 할 수 있다.

JAVA에선 CGI와 유사한 방식으로 구현된 서블릿(Servlet)이라는 프로그램이 존재

서블릿

서블릿은 JAVA를 사용하여 웹 요청(HTTP)을 처리하고 응답을 생성하는 서버 측 프로그램

  • JAVA EE(Jakarta EE)에서 제공하는 웹 기술
  • 클라이언트가 보낸 요청을 받고 서버에서 응답을 생성해서 돌려주는 역할

💡 Jakarta EE의 스펙(인터페이스, 명세) 중 하나인 Servlet을 구현 → 이를 WAS로 배포

서블릿은 싱글톤 패턴

싱글톤이란?

하나의 클래스에 대해 하나의 인스턴스만 존재하도록 보장하는 디자인 패턴

  1. 서블릿은 싱글톤으로으로 동작한다. → 모든 요청에 대해서 하나의 같은 인스턴스를 공유

  2. 여러 요청이 동시에 들어온다면?

    • 하나의 서블릿 인스턴스에 대해 여러 스레드가 동시에 접근
    • 멀티스레드 환경이 자동으로 만들어진다(서블릿 컨테이너가 함)
  3. 이 때 서블릿 내부에 멤버 변수가 있으면?

    private int count = 0;
    
    protected void doGet(...) {
        count++; // Thread-Unsafe!
    }
    
    • 경합(race condition)이 발생가능
      • 둘 이상의 스레드(또는 프로세스)가 동시에 같은 자원에 접근하거나 변경할 때, 실행 순서에 따라 프로그램의 결과가 달라질 수 있는 상황(가능성이 현실이 됨..)
    • Thread-Safe하지 않음
      • Thread-unsafe라는 말은 여러 스레드가 동시에 어떤 공유 자원에 접근하거나 조작할 때, 예상치 못한 결과나 오류가 발생할 수 있는 상황을 의미(가능성)

💡서블릿의 Thread-Safe를 보장하려면 다음과 같은 방법을 적용해야 한다.

  • 무상태(stateless) : 멤버변수를 사용하지 않음(로컬 변수만 사용)
  • 읽기 전용(read-only) : 상수처럼 변하지 않는 값만 사용
  • 동기화 처리 : synchronized, AtomicInteger 와 같은 별도의 Thread-Safe구조를 사

서블릿 컨테이너

⚡웹 컨터이너와 서블릿 컨테이너는 같은 말

  • 서블릿 자체로 작동하는 것이 아니고 이를 생성, 소멸 및 관리해주는 것이 서블릿 컨테이너
  • 서블릿이 역할을 수행하는 정의서라면, 서블릿 컨테이너는 이 정의서를 보고 수행한다.

💡Tomcat은 서블릿 컨테이너이면서도 WAS의 역할도 어느정도 가진다. 하지만 Tomcat은 JAVA EE의 스펙을 전부 구현하지 않기 때문에 → 서블릿 컨테이너라고 할 수 있다.

서블릿 컨테이너는 IoC(Inversion of Control: 제어의 역전)DI(Dependency Injection: 의존성주입)

을 할 수 있으며 서블릿 컨테이너의 역할은 다음과 같다.

(IoC와 DI는 아래에서 자세히 알아보자)

서블릿 컨테이너의 기능

  • 웹 서버와 통신 지원
    • 서블릿과 Web Server가 통신할 수 있도록 한다.
    • 이는 서블릿 컨테이너가 알아서 하기 때문에 개발자는 통신에 신경을 쓸 필요가 없음
  • 서블릿 생명주기 관리
    • 서블릿의 생성/호출/소멸을 관리
    • 요청이 들어오면 해당 서블릿을 찾고 없으면 초기화(web.xml을 읽는다.)
    • 서블릿의 메소드를 호출 → 실제 기능을 하도록 한다
    • 서블릿의 역할이 끝나면 GC가 관련 인스턴스를 소멸
  • 멀티 쓰레딩 관리
    • 서블릿 컨테이너는 요청이 들어올때마다 새로운 쓰레드를 생성하여 멀티 쓰레딩 방식으로 처리
  • JSP 지원
    • JSP를 서블릿으로 변환시켜 처리

IoC와 DI

이 이름만 들어도 어지러운 두개는 도대체 무엇일까? 알아보자

  • IoC 프로그램의 제어 흐름을 개발자가 아닌, 외부(프레임워크)가 맡는 것 ⇒ 즉, 개발자가 객체 만들고 흐름을 제어하는 것이 아니고, 이를 외부 시스템이 대신 함 ㄱㅇㄷ.
  • DI 어떤 객체가 필요로 하는 의존 객체를 직접 만들지 않고, 외부에서 전달받는 방식 ⇒ 필요한걸 내가 안만듦, 누가 대신 만들어서 내한테 줌. ❌ DI없는 코드
    class Engine {
        void start() { System.out.println("엔진 시작"); }
    }
    
    class Car {
        private Engine engine = new Engine();  // Car가 직접 Engine을 만듬
    
        void start() {
            engine.start();
        }
    }
    • Car는 항상 Engine에 강하게 묶여있다. (강한 결합)
      - 테스트 어렵고, 의존하는 객체(Engine )을 바꾸기도 어렵다. (구현객체를 참조)

      ⭕ DI 적용 코드

      class Car {
          private Engine engine;
      
          // 외부에서 의존성(Engine)을 주입받음
          public Car(Engine engine) {
              this.engine = engine;
          }
      
          void start() {
              engine.start();
          }
      }
    • 이제 Car는 어떤 Engine이든 의존이 가능(유연성 증가)(인터페이스를 참조 → 약한 결합)

      • 테스트, 유지보수, 확장성이 좋아짐



Spring MVC

MVC는 Model-View-Controller의 약자

Spring MVC는 이 구조를 기반으로 웹 어플리케이션을 구성하는 프레임워크이다.(Spring 프레임워크의 하위 모듈)

MVC구조

  • Model - 데이터와 비지니스 로직을 처리
  • View - 사용자에게 보여지는 화면
  • Controller - 사용자의 요청을 받아 처리, 결과를 모델과 뷰로 전달

Spring MVC의 동작 흐름

  1. DispatcherServlet이 요청을 받는다. (스프링의 프론트 컨트롤러)
  2. 요청을 HandlerMapping에 넘겨준다
  3. HandlerMapping은 해당 요청을 처리할 Handler(Controller)를 탐색
  4. Handler실행 할 수 있는 HandlerAdapter를 탐색한다
  5. 찾은 HandlerAdapter를 사용하여 Handler의 메소드를 실행
  6. Handler의 반환값은 ModelView
  7. View 이름을 ViewResolver에게 전달, ViewResolver는 해당 View객체를 전달
  8. DispatcherServletView에게 Model을 전달. (이때 Model이 null이면 그대로 View사용, 아니면 View에 Model데이터를 렌더링)
  9. DispatcherServletView의 결과를 응답



Spring Container(IoC 컨테이너, DI 컨테이너)

일단 스프링컨테이너, IoC컨테이너, DI컨테이너 모두 같은 것을 지칭하는 용어다.

왜 용어가 통일이 안되는가 헷갈리게..

자 스프링 컨테이너를 이해하기 위해선 일단 Bean이 무엇인지를 알아야합니다.

Bean이란 간단히 Spring이 관리하는 객체입니다. 즉 앞으로 알아볼 스프링 컨테이너가 생성하고 관리하는 객체가 Bean입니다.

앞에서 IoC에 대해서 간단하게 소개했었는데 이 Bean을 스프링 컨테이너가 IoC를 적용해서 관리합니다.(알아서 관리한다는 소리, 개발자가 직접 생성하고 관리하지 않는다.)

즉, 스프링컨테이너는 이 Bean의 생명 주기를 관리하는 역할을 합니다.

  • DispatcherServlet이라는 서블릿이 Spring Container을 사용하여 요청을 처리합니다.

💡이 IoC컨테이너를 상속하여 부가 기능을 추가한 것이 ApplicationContext이다.

정리하자면 서블릿컨테이너 안에 Spring MVC의 DispatcherServlet이라는 서블릿이 등록되어 있고,

DispatcherServletSpring컨테이너(ApplicationContext)을 통해 Bean들을(@Controller, @Service 등) 관리하여 요청에 의한 비지니스로직을 처리합니다.




웹 어플리케이션 요청-응답 흐름 정리

  1. 클라이언트가 HTTP요청을 보낸다.

  2. 이 요청을 WEB이 받는다.

    2-1. 정적 자원 요청이면 WEB이 직접 응답한다.

    2-2. 동적자원 요청이면 이른 WAS에 전달한다.

  3. WAS(Servlet Container)가 요청을 받아 Spring MVC의 DispatcherServlet를 호출

  4. DispatcherServlet이 요청에 따라 처리할 Controller Bean을 결정

  5. Spring Container(ApplicationContext)가 등록된 Bean들을 이용해 로직 처리

  6. 처리 결과를 View 또는 JSON의 형태로 응답




느낀점

웹 애플리케이션 아키텍처를 정리하면서 처음 보는 용어도 많고, 같은 개념을 여러 방식으로 부르는 경우도 많아 많이 헷갈렸던 것 같습니다.
하지만 개념을 하나씩 정리해가다 보니, 각 구성 요소의 역할과 흐름이 조금씩 보이기 시작했고, 웹의 전반적인 구조를 이해하는 데 큰 도움이 되었습니다.

단순히 코드를 따라치는 것만으로는 구현은 가능하겠지만, 구조와 개념에 대한 이해가 없다면 그것은 진짜 '이해한 것'이 아니라고 느꼈습니다.
앞으로도 무언가 어렵거나 개념이 잡히지 않을 때는, 스스로에게 설명하듯 글로 정리하며 학습하는 습관을 가져야겠다는 생각이 들었습니다.

글이 다소 길고 두서없을 수 있지만, 저에게는 분명히 정리가 되는 소중한 시간이었습니다.
혹시 잘못된 내용이나 부족한 부분이 있다면 언제든지 알려주십쇼!
저에게 정말 큰 도움이 될 겁니다.

참조

[Spring] Spring MVC - Servlet, Servlet Container, Spring Container 에 대해
[Spring] Servlet, Servlet Container, Spring MVC 정리

profile
전 아무고토 몰루고 아무고토 못해여

0개의 댓글