DoController 클래스 설명
com.example.crud2.controller.DoController는 웹 요청(URL)을 받아 Service를 호출하고, Thymeleaf 화면으로 연결하는 MVC의 Controller 계층입니다.
전체 소스 코드
package com.example.crud2.controller;
import com.example.crud2.dto.DoDto;
import com.example.crud2.entity.DoIt;
import com.example.crud2.service.DoService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.List;
@Controller
public class DoController {
private final DoService doService;
public DoController(DoService doService) {
this.doService = doService;
}
@GetMapping("/mains/add")
public String addForm(Model model) {
model.addAttribute("doDto", new DoDto());
return "mains/add";
}
@GetMapping("/list/{num}")
public String detail(@PathVariable Long num, Model model) {
return doService.findById(num)
.map(doIt -> {
model.addAttribute("detail", doIt);
return "mains/detail";
})
.orElse("redirect:/list");
}
@GetMapping("/list")
public String list(Model model) {
List<DoIt> doList = doService.findAll();
model.addAttribute("DoList", doList);
return "mains/doList";
}
@GetMapping("/list/{num}/edit")
public String updateForm(@PathVariable Long num, Model model) {
return doService.findById(num)
.map(toDo -> {
model.addAttribute("editDto", new DoDto(toDo.getNum(), toDo.getTitle(), toDo.getContent()));
return "mains/edit";
})
.orElse("redirect:/list");
}
@GetMapping("/list/{num}/delete")
public String delete(@PathVariable Long num, RedirectAttributes rttr) {
if (doService.delete(num)) {
rttr.addFlashAttribute("msg", "삭제가 완료되었습니다.");
}
return "redirect:/list";
}
@PostMapping("/mains/create")
public String create(DoDto dto) {
DoIt saved = doService.create(dto);
return "redirect:/list/" + saved.getNum();
}
@PostMapping("/mains/update")
public String update(DoDto dto) {
return doService.update(dto)
.map(saved -> "redirect:/list/" + saved.getNum())
.orElse("redirect:/list");
}
}
한 줄 요약
웹 요청(URL)을 받아 Service만 호출하고, 처리 결과를 Model에 담아 Thymeleaf 뷰 이름을 반환하거나 redirect: 로 이동시킨다.
1. 클래스 선언과 @Controller
@Controller
public class DoController {
| 항목 |
설명 |
@Controller |
웹 요청을 처리하는 스프링 빈. 뷰 이름(문자열) 을 반환하면 Thymeleaf 등 ViewResolver가 HTML을 찾음. |
@RestController와 차이 |
@RestController는 기본적으로 JSON 등 HTTP 본문에 직접 씀. 화면 템플릿을 쓸 때는 @Controller가 일반적. |
2. Service 주입 (생성자 DI)
private final DoService doService;
public DoController(DoService doService) {
this.doService = doService;
}
- 생성자 주입: 스프링이
DoService 빈을 넣어 줌. final로 불변 참조 유지 가능.
- Controller는 Repository/DB에 직접 접근하지 않고 Service만 사용하는 구조가 유지보수에 유리함.
Controller → Service → Repository → DB
3. 글 작성 폼 화면
@GetMapping("/mains/add")
public String addForm(Model model) {
model.addAttribute("doDto", new DoDto());
return "mains/add";
}
| 항목 |
설명 |
| URL |
GET /mains/add |
Model |
뷰로 넘길 속성(map). 키 "doDto"로 빈 DoDto를 넣음. |
| 빈 DTO |
Thymeleaf th:object="${doDto}" 로 폼과 양방향 바인딩하기 좋음. |
| 반환값 |
"mains/add" → templates/mains/add.html |
4. 상세 보기 (READ, 단건)
@GetMapping("/list/{num}")
public String detail(@PathVariable Long num, Model model) {
return doService.findById(num)
.map(doIt -> {
model.addAttribute("detail", doIt);
return "mains/detail";
})
.orElse("redirect:/list");
}
- URL 예:
GET /list/1 → num = 1
@PathVariable: URL 경로의 {num} 값을 메서드 인자로 받음.
Optional 처리
| 경우 |
동작 |
findById에 값이 있음 |
detail을 모델에 넣고 "mains/detail" 뷰 |
| 없음 |
"redirect:/list" 로 목록으로 이동 |
IDE에서만 실행할 때 @PathVariable 오류가 나면 @PathVariable("num") 처럼 이름을 명시하거나, 컴파일 옵션 -parameters 사용을 검토.
5. 전체 목록 (READ, 목록)
@GetMapping("/list")
public String list(Model model) {
List<DoIt> doList = doService.findAll();
model.addAttribute("DoList", doList);
return "mains/doList";
}
GET /list: 전체 조회 후 "DoList" 키로 모델에 추가.
- 뷰:
templates/mains/doList.html에서 th:each 등으로 순회.
6. 수정 폼 화면
@GetMapping("/list/{num}/edit")
public String updateForm(@PathVariable Long num, Model model) {
return doService.findById(num)
.map(toDo -> {
model.addAttribute("editDto", new DoDto(toDo.getNum(), toDo.getTitle(), toDo.getContent()));
return "mains/edit";
})
.orElse("redirect:/list");
}
- Entity → DTO: 화면/폼에는
DoDto를 쓰기 위해 DoIt에서 필드를 꺼내 DoDto 생성.
- 없는
num이면 역시 목록으로 리다이렉트.
7. 삭제
@GetMapping("/list/{num}/delete")
public String delete(@PathVariable Long num, RedirectAttributes rttr) {
if (doService.delete(num)) {
rttr.addFlashAttribute("msg", "삭제가 완료되었습니다.");
}
return "redirect:/list";
}
RedirectAttributes.addFlashAttribute: 리다이렉트 직후 한 번만 모델에 담기는 값(세션 플래시). 새로고침 시 메시지가 반복되지 않게 할 때 자주 사용.
- 뷰에서는
${msg} 로 표시 가능(레이아웃에 th:if="${msg}" 등).
8. 글 등록 (CREATE)
@PostMapping("/mains/create")
public String create(DoDto dto) {
DoIt saved = doService.create(dto);
return "redirect:/list/" + saved.getNum();
}
| 단계 |
설명 |
| 폼 POST |
name="title", name="content" 등이 DoDto의 setter로 자동 바인딩 |
| Service |
DTO를 받아 Entity로 저장 로직 수행 |
| 응답 |
저장된 PK(num)로 상세 URL로 리다이렉트 |
9. 글 수정 (UPDATE)
@PostMapping("/mains/update")
public String update(DoDto dto) {
return doService.update(dto)
.map(saved -> "redirect:/list/" + saved.getNum())
.orElse("redirect:/list");
}
| 경우 |
동작 |
수정 성공 (Optional에 값 있음) |
해당 글 상세로 리다이렉트 |
실패 (예: 잘못된 num) |
목록으로 리다이렉트 |
수정 폼에서는 보통 hidden으로 num을 같이 보냄.
전체 요청 흐름
[사용자 요청]
↓
Controller (DoController)
↓
Service (DoService)
↓
Repository → DB
↓
Controller (결과를 Model에 담거나 redirect URL 결정)
↓
View (Thymeleaf) 또는 redirect 응답
자주 쓰는 매핑 정리
| 구분 |
설명 |
@GetMapping |
조회, 폼 화면 열기 |
@PostMapping |
폼 제출 등 데이터 변경 처리 |
@PathVariable |
/list/{num} 처럼 경로 변수 수신 |
Model |
뷰로 넘길 데이터 |
redirect: 접두사 |
브라우저 재요청으로 다른 URL 이동 (POST 후 PRG 패턴에도 사용) |
관련 문서