[멋사 클라우드 5기] Day 22 - SpringBoot 그리고 웹 서버

2026. 2. 27. 02:37·Learning Log

1. Servlet — 자바가 HTTP를 처리하는 방법

Spring은 Servlet 위에서 동작한다.

Servlet은 자바에서 HTTP 요청을 받아 처리하고 응답을 돌려주는 기술이다.
Node.js의 express에서 라우터 하나와 비슷한 역할을 한다.

// Node.js/Express 방식
app.get('/hello', (req, res) => {
    res.send("Hello World");
});

// Java Servlet 방식
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) {
        res.getWriter().write("Hello World");
    }
}

구조는 비슷하지만 Servlet은 훨씬 더 많은 설정이 필요하다.
초기에는 각 URL마다 Servlet 클래스를 하나씩 만들어야 했다고 한다.

/login    → LoginServlet.java
/users    → UsersServlet.java
/orders   → OrdersServlet.java

URL이 100개면 Servlet도 100개가 필요했고, 인증 체크나 에러 처리 같은 공통 로직을 모든 Servlet에 반복해서 작성해야 했다.
이 불편함을 해결하기 위해 등장한 것이 Front Controller 패턴이다.


2. Web Server vs Web Application Server

Web Server — 정적인 것만 처리

Web Server는 정적 콘텐츠를 처리한다.
클라이언트가 요청하면 디스크에서 파일을 찾아 그대로 돌려준다.
별도의 로직 실행 없이 단순히 파일을 서빙하는 역할이다.

클라이언트 요청: GET /logo.png
Web Server 응답: (파일 찾아서 그대로 반환)

Web Application Server (WAS) — 동적인 것도 처리

WAS는 정적 콘텐츠뿐만 아니라 동적 콘텐츠도 처리한다.
요청마다 코드를 실행하고, DB를 조회하고, 그 결과로 응답을 만들어낸다.

클라이언트 요청: GET /users/1
WAS 처리:        코드 실행 → DB 조회 → 사용자 데이터 조합 → 응답 생성
구분 Web Server WAS
처리 대상 정적 콘텐츠 (HTML, CSS, 이미지) 동적 콘텐츠 (비즈니스 로직)
처리 방식 파일을 그대로 반환 코드를 실행해서 응답 생성
대표 제품 Nginx, Apache Tomcat, Jetty

💡 WAS도 정적 파일을 처리할 수 있다.
하지만 정적 파일만큼은 Web Server가 훨씬 빠르고 효율적이기 때문에 역할을 나눠 사용한다.


3. Front Controller 패턴

Servlet의 문제를 다시 떠올려보자. URL마다 Servlet을 만들어야 하고, 공통 로직이 반복된다.

Front Controller 패턴은 이 문제를 "입구를 하나로 만들자"는 아이디어로 해결한다.

// 기존 방식 — 입구가 여러 개
/login   → LoginServlet   (인증 체크, 로깅, 공통처리 + 실제 로직)
/users   → UsersServlet   (인증 체크, 로깅, 공통처리 + 실제 로직)
/orders  → OrdersServlet  (인증 체크, 로깅, 공통처리 + 실제 로직)

// Front Controller 방식 — 입구가 하나
모든 요청 → FrontController (공통처리) → 각 Controller에 위임

 

// FrontController — 공통 처리는 여기서 한 번만
public class FrontController extends HttpServlet {
    doGet(request, response) {
        checkAuth(request);   // 인증 체크 — 한 곳에서
        logging(request);     // 로깅 — 한 곳에서

        // URL을 보고 적절한 Controller로 위임
        String url = request.getRequestURI();
        if (url.equals("/login"))  new LoginController().process();
        if (url.equals("/users"))  new UsersController().process();
    }
}

Spring의 DispatcherServlet이 바로 이 Front Controller의 구현체이다.
Spring을 사용하면 이 역할을 프레임워크가 대신 해주기 때문에 우리는 실제 비즈니스 로직에만 집중할 수 있다.


4. Spring MVC Flow

 

실제로 HTTP 요청이 들어왔을 때 Spring 내부에서 어떤 일이 일어나는지 순서대로 살펴보자

① Request
② HandlerMapping
③ HandlerAdapter
④ Controller (+ Service → Repository → DB)
⑤ view name 반환
⑥ ViewResolver
⑦ View 렌더링
⑧ Response

① Request — 요청 진입

클라이언트가 GET /users/1을 요청하면 가장 먼저 DispatcherServlet이 받는다.
모든 요청은 예외 없이 DispatcherServlet을 거친다.

② HandlerMapping — "누가 처리할 거야?"

DispatcherServlet은 HandlerMapping에게 요청으로 들어온 URL을 처리할 수 있는 Controller를 묻는다.

@RestController
public class UserController {
    @GetMapping("/users/{id}")    // ← HandlerMapping이 이 정보를 기억해둡니다
    public User getUser(...) { }
}

HandlerMapping은 애플리케이션이 시작될 때 모든 @GetMapping, @PostMapping 등을 스캔해서
URL과 메서드의 매핑 정보를 저장해둔다.
요청이 들어오면 이 정보를 참고해서 어느 Controller가 처리할지 알려준다.

③ HandlerAdapter — "어떻게 호출할 거야?"

HandlerMapping이 Controller를 찾았는데, 왜 바로 호출하지 않고 Adapter가 필요할까?

Spring은 오랜 역사를 가진 프레임워크라 Controller를 만드는 방식이 시대에 따라 여러 가지였다.
@RequestMapping 방식, Controller 인터페이스 방식, HttpRequestHandler 방식 등이 공존한다.
각 방식마다 호출하는 방법이 다르기 때문에, HandlerAdapter가 그 차이를 흡수해서
DispatcherServlet이 항상 동일한 방식으로 Controller를 호출할 수 있게 해준다.

④ Controller 실행

개발자가 작업하는 영역이다. Controller는 직접 비즈니스 로직을 처리하지 않고 Service에게 위임한다.

@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id, Model model) {
    User user = userService.findById(id);    // Service에 위임
    model.addAttribute("user", user);        // 결과를 Model에 담기
    return "user/detail";                    // view name 반환
}

⑤ view name 반환

Controller가 반환하는 "user/detail"은 아직 실제 파일 경로가 아니다. 그냥 이름이다.

⑥ ViewResolver — "그 이름이 실제 어떤 파일이야?"

ViewResolver는 view name을 실제 뷰 파일 경로로 변환한다.

# application.properties
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
view name:  "user/detail"
               ↓ ViewResolver 변환
실제 경로:  /WEB-INF/views/user/detail.jsp

⑦⑧ View 렌더링 → Response

ViewResolver가 찾은 파일(JSP 등)에 Model 데이터를 주입해서 HTML을 완성하고 클라이언트에게 반환한다.


REST API

요즘은 JSP 대신 JSON을 반환하는 REST API 방식을 주로 쓴다. 이 경우 View와 ViewResolver 과정이 생략된다.

@RestController    // @Controller + @ResponseBody
public class UserController {
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}
  @Controller @RestController
반환 방식 view name → HTML 객체 → JSON
View/ViewResolver 필요 불필요
주요 용도 서버 사이드 렌더링 REST API

5. JSP — 서버 사이드 렌더링의 과거

MVC Flow에서 View 역할을 담당하는 JSP(JavaServer Pages)는 HTML 안에 Java 코드를 작성할 수 있는 템플릿 기술이다.

React의 JSX와 비교하면 방향이 정반대이다.

// JSX — JavaScript 안에 HTML
function UserPage({ user }) {
    return (
        <div>
            <h1>{user.name}</h1>
        </div>
    );
}
<!-- JSP — HTML 안에 Java -->
<div>
    <h1><%= user.getName() %></h1>
</div>

JSP의 문제는 HTML과 Java 코드가 뒤섞이면서 코드가 복잡해진다는 점이다.


6. Spring Boot의 내장 서버

Spring Boot 이전에는 서버를 따로 설치하고 배포하는 과정이 복잡했다.

// Spring Boot 이전 방식
1. Tomcat 별도 설치
2. WAR 파일로 프로젝트 빌드
3. Tomcat에 WAR 배포
4. Tomcat 재시작

Spring Boot는 Tomcat을 애플리케이션 안에 내장했다. 덕분에 배포가 크게 단순해졌다.

// Spring Boot 방식
1. JAR 파일로 빌드
2. java -jar myapp.jar
   → 끝. 서버가 자동으로 8080 포트에서 실행된다.
<!-- spring-boot-starter-web 의존성 하나에 Tomcat이 포함되어 있다 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

서버 설정은 application.properties 파일 하나로 처리 가능하다.

server.port=9090                       # 포트 변경 (기본값: 8080)
server.servlet.context-path=/api       # 컨텍스트 패스 설정

애플리케이션 시작 흐름

java -jar myapp.jar 실행
        ↓
SpringApplication.run() 호출
        ↓
내장 Tomcat 자동 시작 (8080 포트)
        ↓
DispatcherServlet 등록
        ↓
HTTP 요청 수신 대기
        ↓
요청 → DispatcherServlet → Controller → Service → Repository → DB

 


7. 실무 배포 구조 (Nginx + Spring Boot)

Spring Boot 내장 서버만으로도 운영이 가능하지만, 실무에서는 앞단에 Nginx를 함께 사용한다.

[클라이언트 브라우저]
        ↓
[Nginx — 80/443 포트]
  ├─ 정적 파일 요청 → React 빌드 파일 직접 서빙 (빠름)
  └─ /api/** 요청 → Spring Boot(8080)로 전달
        ↓
[Spring Boot — 8080 포트]
        ↓
[DB]

 

Nginx를 쓰는 이유

  • 성능: 정적 파일(이미지, JS, CSS)은 Nginx가 훨씬 빠르게 처리.
  • 보안: 8080 포트를 외부에 직접 노출하지 않아도 된다.
  • HTTPS: SSL 인증서 처리를 Nginx 한 곳에서 담당.
  • 로드 밸런싱: Spring Boot 서버가 여러 대일 때 요청을 분산.

'Learning Log' 카테고리의 다른 글

[멋사 클라우드 5기] Day 24 - Spring Data JPA (2)  (0) 2026.03.04
[멋사 클라우드 5기] Day 23 - Spring Data JPA (1)  (1) 2026.03.03
[멋사 클라우드 5기] Day 21 - 제어의 역전(IoC)과 의존성 주입(DI)  (0) 2026.02.26
[멋사 클라우드 5기] Day 20 - JavaScript의 비동기  (0) 2026.02.25
[멋사 클라우드 5기] Day 19 - Object.assign과 structuredClone에 대하여  (0) 2026.02.24
'Learning Log' 카테고리의 다른 글
  • [멋사 클라우드 5기] Day 24 - Spring Data JPA (2)
  • [멋사 클라우드 5기] Day 23 - Spring Data JPA (1)
  • [멋사 클라우드 5기] Day 21 - 제어의 역전(IoC)과 의존성 주입(DI)
  • [멋사 클라우드 5기] Day 20 - JavaScript의 비동기
allluck777
allluck777
allluck777
    • 분류 전체보기 (41)
      • AWS (0)
      • Network (0)
      • Linux (0)
      • Docker (0)
      • Project (4)
        • CloudNote (4)
      • Learning Log (34)
      • Lecture (3)
        • 스프링 입문 - 코드로 배우는 스프링 부트, 웹 .. (3)
  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.6
allluck777
[멋사 클라우드 5기] Day 22 - SpringBoot 그리고 웹 서버
상단으로

티스토리툴바