@RequestMapping · 파라미터 · Model
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
private BoardService service;
@GetMapping("/view/{id}")
public String view(@PathVariable int id,
@RequestParam(defaultValue="false") boolean comment,
Model model,
HttpSession session) {
model.addAttribute("board", service.findById(id));
return "board/view";
}
}
단순해 보이지만 어노테이션·매개변수·반환값이 모두 의미가 다름. 한 줄씩 분해해서 봐야 합니다.
이 3 가지가 컨트롤러 메서드의 본질. Service 호출은 그 사이에 끼우는 「위임」 한 줄.
@Controller
public class BoardController { ... }
@Controller
@RequestMapping("/board") // 클래스 레벨 prefix
public class BoardController {
@GetMapping("/list") // GET /board/list
public String list() { ... }
@GetMapping("/view/{id}") // GET /board/view/3
public String view(@PathVariable int id) { ... }
@PostMapping("/write") // POST /board/write
public String write(Board b) { ... }
}
👉 클래스 + 메서드 어노테이션이 합쳐져 최종 URL.
| 어노테이션 | HTTP 메서드 | 전형적 용도 |
|---|---|---|
@GetMapping | GET | 조회 — 목록·상세 보기 |
@PostMapping | POST | 제출·생성 — 폼 처리 |
@PutMapping | PUT | 수정 (REST API 에서) |
@DeleteMapping | DELETE | 삭제 (REST API 에서) |
@RequestMapping | 전부 | 위 4 가지의 일반형 |
👉 옛 코드는 @RequestMapping(method=GET) 형식. 새 코드는 짧은 @GetMapping 권장.
@GetMapping("/search")
public String search(
@RequestParam("q") String query, // 필수
@RequestParam(defaultValue = "1") int page, // 기본값
@RequestParam(required = false) String category, // 선택
Model model
) {
// URL: /search?q=spring&page=2&category=java
// → query="spring", page=2, category="java"
}
?key=value 부분(쿼리스트링)을 받음required=false 또는 defaultValue 로 선택 처리("q") 생략 가능: @RequestParam String q
@GetMapping("/board/view/{id}")
public String view(@PathVariable int id) {
// URL: /board/view/3
// → id = 3
}
@GetMapping("/board/{boardId}/comment/{commentId}")
public String comment(
@PathVariable int boardId,
@PathVariable int commentId
) {
// URL: /board/3/comment/7
// → boardId=3, commentId=7
}
👉 REST API 스타일에서 자주 사용. 「리소스를 URL 로 표현」.
public class Board { // VO
private String title;
private String content;
// getter/setter
}
@PostMapping("/board/write")
public String write(Board board) { // ⭐ 자동 바인딩
service.insert(board);
return "redirect:/board/list";
}
<!-- write.jsp -->
<form action="/board/write" method="post">
<input name="title" /> <!-- ↔ board.title -->
<textarea name="content"></textarea>
<button>저장</button>
</form>
👉 form input 의 name 속성과 VO 필드명이 정확히 일치하면 Spring 이 자동 채워줌.
| 방식 | URL/Form 형태 | 코드 |
|---|---|---|
@RequestParam | ?id=3 | @RequestParam int id |
@PathVariable | URL /board/3 | @PathVariable int id |
| VO 자동 | form: name="title", "content" | Board board |
| @CookieValue | Cookie: theme=dark | @CookieValue String theme |
@GetMapping("/board/view")
public String view(@RequestParam int id, Model model) {
Board b = service.findById(id);
model.addAttribute("board", b); // ${board}
model.addAttribute("comments",
commentService.list(id)); // ${comments}
model.addAttribute("isOwner",
b.getWriterId() == 5); // ${isOwner}
return "board/view";
}
<!-- view.jsp -->
<h1>${board.title}</h1>
<c:if test="${isOwner}"><a>수정</a></c:if>
<c:forEach var="c" items="${comments}">
<p>${c.content}</p>
</c:forEach>
return "board/view";
// → ViewResolver 가 /WEB-INF/views/board/view.jsp 로 변환
return "redirect:/board/list";
// → 브라우저에 302 리다이렉트 응답
// 브라우저가 /board/list 로 다시 요청
return "forward:/board/list";
// → 서버 내부에서 다른 컨트롤러로 위임
// 브라우저는 변화 모름
👉 redirect: 와 forward: 가 가장 자주 보는 두 접두사.
@PostMapping("/board/write")
public String write(Board b) {
service.insert(b);
return "redirect:/board/list";
// ⭐ "redirect:" 의 이유?
// 1) 새로고침 시 글이 두 번 등록되는 것 방지
// 2) 브라우저 주소창의 URL 이 깔끔하게 바뀜
// 3) "Post-Redirect-Get(PRG) 패턴"
}
POST 후 곧바로 JSP 를 반환하면 → 새로고침 시 「양식을 다시 제출하시겠습니까?」 경고. redirect: 가 이 문제를 풀어줌.
public String list(
Model model, // 데이터 운반
HttpSession session, // 세션
HttpServletRequest request, // 원본 요청
HttpServletResponse response, // 원본 응답
RedirectAttributes ra, // 리다이렉트 시 데이터
@CookieValue String theme, // 쿠키
@RequestHeader("User-Agent") String ua // HTTP 헤더
) { ... }
👉 Spring 이 매개변수 타입을 보고 자동으로 채워줍니다. 순서·개수 자유.
@GetMapping("/board/view/{id}") // ① URL 매핑
public String view(
@PathVariable int id, // ② URL 의 {id} 자동 받기
Model model // ② 데이터 그릇
) {
Board b = service.findById(id); // ③ Service 호출
model.addAttribute("board", b); // ③ 그릇에 담기
return "board/view"; // ④ View 이름 반환
}
| 증상 | 원인 |
|---|---|
| 404 Not Found | URL 경로 / @GetMapping 오타 |
| 매개변수 null | form input name ↔ VO 필드명 불일치 |
| "Required parameter not present" | @RequestParam 필수인데 안 보냄 |
| JSP 에 ${} 그대로 표시 | EL 해석 안 됨 (옛 JSP 호환 옵션) |
| 새로고침 시 폼 재제출 경고 | POST 후 redirect: 안 씀 |
| "Could not resolve view" | return 의 ViewName 오타 |
| 실험 | 예상 결과 |
|---|---|
| @RequestParam 필수, 폼에서 안 보내기 | 400 Bad Request |
| VO 필드명 ↔ form name 다르게 | 매개변수 null (에러 X) |
| return "이상한값"; | "Could not resolve view" |
| @PathVariable 의 이름 다르게 | "Missing URI template variable" |
👉 의도적으로 깨뜨려보면 「어떤 오류 메시지 = 어떤 원인」 매핑이 머리에 박힙니다.
Controller 가 무엇인지 정도만 안다. 한 메서드 안의 코드가 무엇을 하는지는 흐릿.
Controller 메서드 한 개를 라인별로 짚는다. URL 매핑·파라미터·Model·반환을 따로따로 본다.
다음: Service 계층 — 비즈니스 로직과 트랜잭션의 자리.