Spring MVC 게시판 만들기 2편: 컨트롤러와 요청 흐름 완벽 이해
💡 이 글에서 배울 것
Spring MVC에서 사용자의 요청이 어떻게 Controller를 거쳐 처리되는지, 그 전체 흐름을 파헤쳐봅니다.
🎯 한눈에 보기
| 주제 | 핵심 내용 |
|---|---|
| DispatcherServlet | 모든 요청의 시작점, 안내 데스크 역할 |
| @Controller | 웹 요청을 처리하는 클래스 표시 |
| @GetMapping/@PostMapping | HTTP 메서드별 요청 처리 |
📌 이번 편에서 배울 것
이번 편에서는 사용자의 요청이 어떻게 처리되는지 알아볼 거예요:
- DispatcherServlet이 뭘까?
@Controller와@RequestMapping의 역할- 게시글 목록, 등록, 조회, 수정, 삭제가 어떻게 처리되는지
🚪 모든 요청의 시작점: DispatcherServlet
Spring MVC에서 모든 웹 요청은 DispatcherServlet이라는 특별한 친구를 거쳐갑니다. 마치 회사의 안내 데스크처럼, 어떤 부서(Controller)로 가야 할지 안내해주는 역할이에요!

flowchart LR
User[👤 사용자] --> |HTTP 요청| DS[DispatcherServlet]
DS --> |어디로 갈까?| HM[Handler Mapping]
HM --> |BoardController로!| DS
DS --> |요청 전달| BC[BoardController]
BC --> |결과 반환| DS
DS --> |화면은?| VR[View Resolver]
VR --> |list.jsp로!| DS
DS --> |최종 결과| User
web.xml 설정 코드:
<!-- 모든 요청을 DispatcherServlet이 받도록 설정 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern> <!-- 모든 경로 처리 -->
</servlet-mapping>
🎯 BoardController 한눈에 보기
BoardController.java는 게시판 관련 모든 요청을 담당합니다.
@Controller // "나는 웹 요청을 처리하는 클래스야!"
@Log4j2 // 로그 출력용
@RequestMapping("/board") // /board로 시작하는 모든 요청 담당
@RequiredArgsConstructor // 생성자 자동 생성 (DI용)
public class BoardController {
private final BoardService boardService; // 서비스 주입
// 여기에 각종 요청 처리 메서드들...
}
📋 게시글 목록 보기 (GET /board/list)
가장 많이 사용하는 기능! 게시글 목록을 가져오는 과정을 보겠습니다.
sequenceDiagram
participant 📱 as 브라우저
participant 🎮 as BoardController
participant 🔧 as BoardService
participant 📊 as Model
participant 📄 as list.jsp
📱->>🎮: GET /board/list?page=1&size=10
🎮->>🔧: getList(1, 10, null, null)
🔧-->>🎮: BoardListPaginDTO
🎮->>📊: model.addAttribute("dto", list)
🎮-->>📄: return "board/list"
📄-->>📱: HTML 화면
실제 코드:
@GetMapping("/list")
public void list(
@RequestParam(name="page", defaultValue = "1") int page,
@RequestParam(name="size", defaultValue = "10") int size,
@RequestParam(name="types", required = false) String types,
@RequestParam(name="keyword", required = false) String keyword,
Model model) {
log.info("========== board list 호출 ==========");
// 서비스에서 게시글 목록 + 페이징 정보 가져오기
BoardListPaginDTO list = boardService.getList(page, size, types, keyword);
// JSP에서 사용할 수 있도록 Model에 담기
model.addAttribute("dto", list);
// 메서드 반환타입이 void면 → 경로 그대로 JSP 찾음
// /board/list → /WEB-INF/views/board/list.jsp
}
💡 핵심 포인트:
@RequestParam: URL의 파라미터를 변수로 받음 (?page=1 → page 변수에 1)defaultValue: 값이 없으면 기본값 사용Model: JSP에 데이터를 전달하는 “가방” 역할
✍️ 게시글 등록 (GET + POST /board/register)
등록은 두 단계로 이루어집니다:
- GET: 빈 입력 폼 보여주기
- POST: 사용자가 작성한 데이터 저장하기
flowchart TB
subgraph "1️⃣ 등록 폼 요청"
A[사용자] --> |GET /board/register| B[Controller]
B --> |빈 폼 화면| C[register.jsp]
end
subgraph "2️⃣ 등록 처리"
D[사용자가 폼 작성] --> |POST /board/register| E[Controller]
E --> |데이터 저장| F[Service]
F --> |성공| G[redirect:/board/list]
end
GET - 빈 폼 보여주기:
@GetMapping("/register")
public void register() {
log.info("board register");
// void 반환 → /board/register → register.jsp
}
POST - 데이터 저장하기:
@PostMapping("/register")
public String registerPost(BoardDTO dto, RedirectAttributes rttr) {
log.info("board register post");
// 게시글 등록하고 등록된 번호 받기
Long boardNo = boardService.register(dto);
// 등록 완료 메시지 (1회용 데이터)
rttr.addFlashAttribute("result", boardNo);
// 목록 페이지로 이동
return "redirect:/board/list";
}
💡 RedirectAttributes란?
redirect할 때 데이터를 전달하는 방법addFlashAttribute: 1회용 데이터 (새로고침하면 사라짐)- URL에 노출되지 않아 보안상 안전!
👀 게시글 상세 보기 (GET /board/read/{boardNo})
게시글 번호를 URL 경로에 넣어서 요청합니다.
/board/read/42 ← 42번 게시글 보기
코드:
@GetMapping("/read/{boardNo}") // {boardNo}는 경로 변수
public String read(
@PathVariable("boardNo") Long boardNo, // URL에서 boardNo 추출
@RequestParam(name="page", defaultValue = "1") int page,
@RequestParam(name="size", defaultValue = "10") int size,
@RequestParam(name="types", required = false) String types,
@RequestParam(name="keyword", required = false) String keyword,
Model model) {
// 게시글 1건 조회
BoardDTO dto = boardService.read(boardNo);
// JSP에 전달
model.addAttribute("board", dto);
model.addAttribute("page", page); // 목록으로 돌아갈 때 사용
model.addAttribute("size", size);
model.addAttribute("types", types);
model.addAttribute("keyword", keyword);
return "/board/read";
}
💡 @PathVariable vs @RequestParam:
@PathVariable: URL 경로의 일부/read/{boardNo}→/read/42@RequestParam: URL의 쿼리 파라미터?page=1
✏️ 게시글 수정 (GET + POST /board/modify)
수정도 등록과 마찬가지로 두 단계!
flowchart LR
A[목록] --> |수정 클릭| B[GET /board/modify/42]
B --> |기존 데이터로 폼 표시| C[modify.jsp]
C --> |수정 버튼 클릭| D[POST /board/modify]
D --> |저장 완료| E[redirect:/board/read/42]
GET - 수정 폼 보여주기:
@GetMapping("/modify/{boardNo}")
public String modifyGet(@PathVariable("boardNo") Long boardNo, Model model) {
BoardDTO dto = boardService.read(boardNo); // 기존 데이터 불러오기
model.addAttribute("board", dto);
return "board/modify";
}
POST - 수정 처리:
@PostMapping("/modify")
public String modifyPost(BoardDTO dto) {
boardService.modify(dto);
return "redirect:/board/read/" + dto.getBoardNo(); // 상세보기로 이동
}
🗑️ 게시글 삭제 (POST /board/remove)
삭제는 POST 요청으로만 처리합니다. (보안상 GET 삭제는 위험!)
@PostMapping("/remove")
public String remove(
@RequestParam("boardNo") Long boardNo,
RedirectAttributes rttr) {
boardService.remove(boardNo);
rttr.addFlashAttribute("result", boardNo); // 삭제 알림용
return "redirect:/board/list";
}
🔀 요청 처리 방식 비교
| 기능 | HTTP 메서드 | URL | 반환 방식 |
|---|---|---|---|
| 목록 | GET | /board/list | void (경로=JSP) |
| 등록폼 | GET | /board/register | void |
| 등록처리 | POST | /board/register | redirect |
| 상세보기 | GET | /board/read/{boardNo} | String (JSP경로) |
| 수정폼 | GET | /board/modify/{boardNo} | String |
| 수정처리 | POST | /board/modify | redirect |
| 삭제 | POST | /board/remove | redirect |
🎨 View Resolver의 역할
Controller가 "board/list"를 반환하면, 어떻게 list.jsp 파일을 찾을까요?
servlet-context.xml 설정:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
Controller 반환값: "board/list"
↓
prefix + 반환값 + suffix
↓
"/WEB-INF/views/" + "board/list" + ".jsp"
↓
결과: /WEB-INF/views/board/list.jsp ✓
📝 정리
| 어노테이션 | 역할 |
|---|---|
@Controller | 웹 요청 처리 클래스 |
@RequestMapping | URL 경로 매핑 (클래스/메서드) |
@GetMapping | GET 요청 처리 |
@PostMapping | POST 요청 처리 |
@RequestParam | URL 파라미터 받기 |
@PathVariable | URL 경로 변수 받기 |
🚀 다음 편 예고
**3편: 서비스 계층과 비즈니스 로직 구현하기**에서는:
@Service어노테이션의 의미- 페이징 계산 로직 분석
- 검색 조건 처리 방법
을 알아볼 예정입니다!
📚 시리즈 목차
- [1편] 프로젝트 구조와 아키텍처 완벽 정리
- [2편] Spring MVC 컨트롤러와 요청 흐름 이해하기 ← 현재 글
- [3편] 서비스 계층과 비즈니스 로직 구현하기
- [4편] MyBatis와 데이터베이스 연동 완벽 가이드
- [5편] 페이징과 검색 기능 구현하기
- [6편] 댓글 기능과 예외 처리 마스터하기