v0.5
★ MILESTONE · MVC

첫 종단간 흐름

DB 없이 끝까지 데이터를 흘려본다

학습 목표

  • 폼 입력이 Controller 로 어떻게 도달하는지 안다
  • Service 가 호출되고 결과를 반환하는 흐름을 본다
  • Model 에 담긴 데이터가 JSP 로 어떻게 도달하는지 안다
  • 흐름도의 4 박스를 손가락으로 짚을 수 있다
  • 데이터가 막히는 자리를 일부러 만들어보며 디버깅 감각을 익힌다

⚠️ 지금까지 배운 것의 단편

조각으로 흩어진 학습

Part 2-3 에서 우리는 부분 부분을 배웠습니다:

  • Controller 가 무엇인지 (어노테이션, RequestMapping)
  • Service 가 무엇인지 (비즈니스 로직 자리)
  • JSP 가 무엇인지 (화면을 만드는 곳)
  • DispatcherServlet, ViewResolver 같은 부품의 역할

하지만 — 한 번도 끝까지 연결해본 적이 없다.

왜 「DB 없이」 부터?

v1 에서 바로 DB 까지 한꺼번에 만들면 — 어디서 막혀도 원인을 찾기 어렵습니다.

흐름이 길수록 디버깅 거리도 길다. 그래서 오늘은 짧은 종단간 한 번 → 다음 Part 에서 DB 추가.

학습 전략 — 「선(先) 흐름, 후(後) DB」.

🛠️ 오늘 만들 것

「이름을 받아 인사하는」 가장 단순한 종단간

폼에 이름 입력 → 제출 → 서버가 인사말 응답 → 화면에 표시. DB 없이도 4 박스가 한 줄로 이어지는 종단간.

사용자 입력 [폼] name="홍길동" ↓ 브라우저 → Tomcat [HTTP POST /greet] ↓ DispatcherServlet → 어느 컨트롤러? ↓ GreetController.greet() │ name 받음 ↓ greetService.getGreeting(name) GreetService.getGreeting(name) │ "안녕하세요, 홍길동님!" 반환 ← 하드코딩 ↓ Controller : model.addAttribute("msg", ...) │ ↓ return "greet" ViewResolver → /WEB-INF/views/greet.jsp ↓ ${msg} 채움 "안녕하세요, 홍길동님!" 화면

① 폼 — 사용자 입력 받기


<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<head><title>인사 받기</title></head>
<body>
<h2>인사 받기</h2>
<form action="/greet" method="post">
    이름: <input type="text" name="name" />
    <button type="submit">인사받기</button>
</form>
</body>
</html>

① 폼의 핵심 포인트

  • action="/greet" — 어디로 보낼지
  • method="post" — POST 로 보냄 (바디에 데이터 숨김)
  • name="name"컨트롤러 메서드 파라미터 이름과 정확히 일치해야 함
⚠️ 가장 흔한 함정

JSP 의 name 속성과 컨트롤러 매개변수 이름이 한 글자라도 다르면 — 매개변수가 null. 그런데 에러는 안 남.

② Service — 비즈니스 로직


package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class GreetService {

    public String getGreeting(String name) {
        if (name == null || name.isBlank()) {
            return "안녕하세요, 손님!";
        }
        return "안녕하세요, " + name + "님!";
    }
}

👉 「하드코딩」 — 다음 Part 에서 이 자리에 DB 호출이 들어옵니다.

② 왜 Controller 에 이 로직을 안 두나

이 정도는 Controller 에 두어도 되지 않을까? — 처음엔 그렇게 보입니다. 하지만:

  • 나중에 「VIP 고객은 다른 인사말」 같은 규칙이 생김
  • 로직이 복잡해질수록 Controller 가 비대해짐
  • 비즈니스 로직 단위 테스트가 불가능 (HTTP 없이 못 부름)

처음부터 Service 로 분리해두면, 로직이 자라도 Controller 가 흔들리지 않음.

③ Controller — import / 선언


package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.service.GreetService;

@Controller
public class GreetController {

    @Autowired
    private GreetService service;
    // ... 메서드는 다음 슬라이드
}

③ Controller — 요청 응대 메서드


@Controller
public class GreetController {

    @Autowired private GreetService service;

    @GetMapping("/greet/form")
    public String form() {
        return "greet/form";   // 입력 폼 화면
    }

    @PostMapping("/greet")
    public String greet(String name, Model model) {
        String msg = service.getGreeting(name);
        model.addAttribute("msg", msg);
        return "greet/result";  // 결과 화면
    }
}

③ Controller 한 줄씩 짚기

  • @Controller → "이 클래스는 Bean 이고 요청을 처리한다"
  • @Autowired GreetService service → 컨테이너에서 자동 주입
  • @GetMapping("/greet/form") → GET /greet/form 요청 처리
  • @PostMapping("/greet") → form 의 method="post" 와 짝
  • String name → form 의 name="name" 입력값이 자동 바인딩
  • Model model → JSP 로 전달할 데이터 그릇
  • return "greet/result" → ViewName, ViewResolver 가 JSP 파일로 변환

④ 결과 JSP — 화면 그리기


<!-- /WEB-INF/views/greet/result.jsp -->
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<body>
<h1>${msg}</h1>
<a href="/greet/form">다시 인사받기</a>
</body>
</html>

👉 ${msg} = Controller 가 model.addAttribute("msg", ...) 한 그 키.

EL (Expression Language) 문법. JSP 의 모델 데이터에 접근하는 표준 방법.

박스 사이를 데이터가 어떻게 옮겨가나

[브라우저] │ name="홍길동" (POST 바디) ▼ [Controller.greet(String name, Model model)] │ name 매개변수에 자동 바인딩 │ service.getGreeting(name) 호출 ▼ [Service.getGreeting(name)] │ String 으로 결과 반환 │ return "안녕하세요, 홍길동님!" ▼ [Controller 가 받음] │ model.addAttribute("msg", ...) │ return "greet/result" (ViewName) ▼ [ViewResolver] │ "greet/result" → /WEB-INF/views/greet/result.jsp ▼ [JSP] ${msg} 자리에 모델 값이 들어감 │ ▼ [브라우저] HTML 받아 화면에 그림

데이터의 「변신」 추적

위치데이터의 형태
브라우저 inputHTML 폼의 텍스트
HTTP 메시지 바디name=홍길동
Controller 매개변수String name = "홍길동"
Service 매개변수String name = "홍길동"
Service 반환값String "안녕하세요, 홍길동님!"
Model 의 속성{ "msg" : "안녕하세요, 홍길동님!" }
JSP 의 ${msg}HTML 안의 텍스트로 변환
응답 HTTP 바디완성된 HTML 문서
브라우저 화면"안녕하세요, 홍길동님!" 글자

실험 ①: name 속성 일치 깨기

실험

JSP 의 name="name"name="userName" 으로 바꾸고 제출해보면?


<input type="text" name="userName" />
결과

화면에 "안녕하세요, 손님!" 이 뜸 (Controller 의 name 매개변수가 null).

에러는 안 나지만 데이터가 안 들어옴 — 가장 찾기 어려운 버그.

실험 ②: @Service 빼기


// @Service  ← 주석!
public class GreetService {
    public String getGreeting(String name) { ... }
}
결과

Tomcat 시작 시 에러:


NoSuchBeanDefinitionException:
No qualifying bean of type 'GreetService' available

컨테이너에 등록 안 된 Bean 은 주입 불가 → 시작부터 실패.

실험 ③: ViewName 깨기


return "greet/result_xxx";  // 이런 JSP 없음
결과

화면에 에러:


HTTP 404 Not Found
또는
"Could not resolve view with name 'greet/result_xxx'"

ViewResolver 가 prefix + ViewName + suffix 로 파일을 찾는데 그 파일이 없음.

실험 ④: ${msg} 키 깨기


<h1>${message}</h1>   <!-- 모델은 "msg" 키로 넣었는데 -->
결과

화면에 빈 <h1></h1> 만 보임. 에러 없음.

EL 은 없는 키를 만나도 빈 문자열로 처리. 가장 찾기 까다로운 종류의 버그.

실험 정리 — 4 가지 단절 자리

실험증상의심할 곳
① name 속성 불일치매개변수 nullJSP name ↔ Controller 매개변수
② @Service 빼기NoSuchBean 에러등록 어노테이션
③ ViewName 깨기404 또는 ViewResolver 에러JSP 파일 경로
④ EL 키 깨기빈 화면 (조용)model 키 ↔ JSP ${...}

👉 4 가지 자리를 외워두면 디버깅이 빨라집니다.

v0.5 의 의미

오늘이 우리 프로젝트의 두 번째 마일스톤입니다.

  • v0 — Hello Servlet (Part 1)
  • v0.5 — DB 없이 종단간 흐름 (오늘)
  • v1 — 첫 DB 연동 (Part 4)
  • v2~v∞ — 점진적 진화

v0.5 의 「하드코딩」 자리는 v1 에서 그대로 DB 호출로 교체됩니다. 흐름은 같고, 한 자리만 채워질 뿐.

🔄 Before / After

Part 3 시작

Controller·Service·View 가 무엇인지 따로따로 안다.

한 번도 끝까지 연결해본 적 없음.

v0.5 — Part 3 끝

폼 → Controller → Service → JSP 까지 데이터를 처음 끝까지 흘려봤다.

일부러 끊어보며 4 가지 단절 자리도 외운다.

📊 한 그림 정리

이번 차시의 데이터 흐름

브라우저 폼
Controller
Service
(하드코딩)
Model
JSP
모든 박스가 한 번에 등장 — 처음으로 흐름이 끝까지 이어졌습니다

다음 Part — Service 의 하드코딩 자리에 DB 호출이 들어옵니다.

정리

오늘 들고 가는 것

  • 4 박스(폼·Controller·Service·JSP)가 한 줄로 연결된다
  • 각 박스 사이 데이터의 「형태와 변환」 9 단계를 안다
  • 4 가지 단절 자리(name 속성·@Service·ViewName·EL 키)를 안다
  • 일부러 깨뜨려보며 디버깅 감각을 익힌다
  • v0.5 — 우리 프로젝트의 두 번째 마일스톤

다음 Part: 데이터와 MyBatis — Service 의 하드코딩 자리를 진짜 DB 로 채웁니다.