Series 02 · Spring 기초 · 1/3
CONTROLLER
MAPPING
DTO
@Controller vs @RestController · URL 매핑 · 요청 데이터 처리 · DTO 설계
Spring MVC 구조의 핵심 — 1차·2차·3차 모두 여기서 시작
02a Controller·Mapping·DTO
02b Session·Service·Repository
02c Entity·JPA·연관관계
이 파일의 학습 목적
02a — @Controller vs @RestController · URL 매핑 · DTO
1차 프로젝트는 @Controller에 Thymeleaf, 2·3차는 @RestController에 JSON을 반환한다.
이 차이를 모르면 프로젝트가 왜 1차에서 2차로 바뀌었는지 이해가 안 된다.
02a는 Spring MVC 핵심 — 요청이 들어오면 어떻게 처리되는지, DTO가 왜 필요한지를 정리한다.
이 파일에서 배울 5가지
- ① @Controller vs @RestController — 뷰 vs JSON 반환
- ② URL 매핑 — @GetMapping·@PostMapping·@RequestMapping
- ③ @ModelAttribute vs @RequestBody — 1차 vs 2·3차 차이
- ④ DTO — Entity를 그대로 반환하지 않는 이유
- ⑤ Model · RedirectAttributes — 1차 Thymeleaf에서만 사용
프로젝트 단계별 변화
- · 1차 — @Controller + @ModelAttribute + Model
- · 2·3차 — @RestController + @RequestBody + ResponseEntity
- · DTO — 1차는 Entity 직접 / 2·3차는 BoardDto 변환
시리즈 순서:
01a·01b·01c Java 기초 →
02a ← 지금 여기
→ 02b Session·Service
→ 02c Entity·JPA
01 — @Controller vs @RestController
1차는 HTML 반환 / 2차·3차는 JSON 반환 — 우리 프로젝트의 가장 큰 전환점
🔀
핵심 차이
MVC
🍱 비유 — 배달 방식
@Controller = 완성된 도시락 배달 → 서버가 HTML까지 완성해서 브라우저에 전달
@RestController = 밀키트 배달 → 서버는 재료(JSON)만 전달, React가 화면을 만듦
1차 — @Controller (HTML 반환)
@Controller
public class BoardController {
@GetMapping("/board/list")
public String list(Model model) {
model.addAttribute("boards",
boardService.getList());
return "board/list";
}
}
2차·3차 — @RestController (JSON 반환)
@RestController
public class BoardController {
@GetMapping("/api/board/list")
public ResponseEntity<?> list() {
return ResponseEntity.ok(
boardService.getList());
}
}
💡 @RestController = @Controller + @ResponseBody
@ResponseBody = "반환값을 JSON으로 변환해서 HTTP 응답에 바로 담아라"
→ 2차부터 Thymeleaf 완전 제거, @RestController만 사용
| 구분 | @Controller | @RestController |
| 반환값 | 뷰 이름 (String) | 데이터 (JSON) |
| 화면 생성 | Thymeleaf (서버) | React (브라우저) |
| URL 패턴 | /board/list | /api/board/list |
| 사용 시점 | 1차 프로젝트 | 2차·3차 프로젝트 |
02 — URL 매핑 어노테이션
어떤 URL 요청을 어떤 메서드가 처리할지 연결 — HTTP 메서드별 의미
🗺️
@RequestMapping + HTTP 메서드 매핑
Routing
Java — 프로젝트 BoardController URL 구조
@RestController
@RequestMapping("/api/board")
public class BoardController {
@GetMapping("/list")
public ResponseEntity<?> list() { ... }
@PostMapping("/write")
public ResponseEntity<?> write() { ... }
@PutMapping("/edit/{id}")
public ResponseEntity<?> edit(@PathVariable Long id) { ... }
@DeleteMapping("/delete/{id}")
public ResponseEntity<?> delete(@PathVariable Long id) { ... }
}
| 어노테이션 | HTTP 메서드 | 의미 | 프로젝트 예시 |
| @GetMapping | GET | 조회 — 데이터 가져오기 | /api/board/list |
| @PostMapping | POST | 생성 — 새 데이터 만들기 | /api/board/write |
| @PutMapping | PUT | 수정 — 전체 교체 | /api/board/edit/{id} |
| @DeleteMapping | DELETE | 삭제 | /api/board/delete/{id} |
| @RequestMapping | 모두 | 클래스 레벨 공통 URL | /api/board (접두사) |
📌 {id} 경로 변수
URL에 /api/board/edit/5 요청 시 → @PathVariable Long id로 5를 받음
→ 자세한 내용은 03 웹/통신 기초에서 다룸
03 — @ModelAttribute vs @RequestBody
요청 데이터를 Java 객체로 받는 방법 — 1차(form)와 2차·3차(JSON)의 차이
📥
요청 데이터 처리 방식
Request
📮 비유 — 우체국 vs 택배
@ModelAttribute = 우체국 창구 (form 제출) — username=hong&password=1234 형식
@RequestBody = 택배 상자 (JSON) — {"username":"hong","password":"1234"} 형식
1차 — @ModelAttribute (form 데이터)
@PostMapping("/user/register")
public String register(
@ModelAttribute UserDto userDto) {
userService.register(userDto);
return "redirect:/login";
}
2차·3차 — @RequestBody (JSON)
@PostMapping("/api/user/register")
public ResponseEntity<?> register(
@RequestBody UserDto userDto) {
userService.register(userDto);
return ResponseEntity.ok("success");
}
| 구분 | @ModelAttribute | @RequestBody |
| 데이터 형식 | form 데이터 (key=value) | JSON ({key:value}) |
| 사용 상황 | 1차 HTML form 제출 | 2차·3차 React axios 요청 |
| Content-Type | application/x-www-form-urlencoded | application/json |
| 반환값 | 뷰 이름 (String) | ResponseEntity |
04 — DTO가 왜 필요한가
Entity를 직접 쓰면 안 되는 이유 — 보안 + 필요한 데이터만 전달
📋
DTO (Data Transfer Object)
Design
🏥 비유 — 진료 기록 vs 처방전
Entity = 전체 진료 기록 — 개인정보·병력·주민번호 다 있음
DTO = 처방전 — 필요한 정보만 골라서 약국에 전달 (주민번호 X)
→ Entity를 직접 API 응답에 쓰면 비밀번호가 노출될 수 있음
❌ Entity 직접 반환 (위험)
@GetMapping("/api/boards")
public List<Board> list() {
return boardRepository.findAll();
}
✅ DTO 변환 후 반환 (안전)
@GetMapping("/api/boards")
public List<BoardDto> list() {
return boardService.getList();
}
Java — 프로젝트 Entity vs DTO 실제 구조
@Entity
public class User {
private Long id;
private String username;
private String password;
private String nickname;
private String role;
}
@Getter @Setter
public class BoardDto {
private Long id;
private String title;
private String nickname;
private int viewCount;
private String createdAt;
}
private BoardDto toDto(Board board) {
BoardDto dto = new BoardDto();
dto.setTitle(board.getTitle());
dto.setNickname(board.getUser().getNickname());
return dto;
}
💡 DTO의 3가지 역할
1. 보안 — 민감 정보(password) 제외하고 전달
2. 데이터 최적화 — 필요한 필드만 담아서 전송 크기 줄임
3. Entity 보호 — 외부 요청이 Entity를 직접 건드리지 못하게 차단
📌 우리 프로젝트 DTO 목록
UserDto — 회원가입·로그인 요청 데이터
BoardDto — 게시글 목록·상세 응답 데이터
CommentDto — 댓글 응답 데이터
05 — Model · RedirectAttributes
1차 Thymeleaf에서 데이터를 HTML로 전달하는 방법
📤
1차 전용 — Controller → HTML 데이터 전달
1차만 사용
Java — Model / RedirectAttributes 실제 사용
@GetMapping("/board/list")
public String list(Model model) {
model.addAttribute("boardList", boardService.getList());
return "board/list";
}
@PostMapping("/user/register")
public String register(RedirectAttributes ra) {
try {
return "redirect:/user/login";
} catch (Exception e) {
ra.addFlashAttribute("error", e.getMessage());
return "redirect:/user/register";
}
}
⚠️ 2차·3차에서는 안 씀
Model·RedirectAttributes는 Thymeleaf(서버 렌더링) 전용.
2차부터 React로 전환 → ResponseEntity로 JSON 반환이 그 역할을 대체함.