443 lines
13 KiB
Markdown
443 lines
13 KiB
Markdown
## Spring Boot 컨트롤러와 요청 처리
|
|
|
|
Spring Boot에서 **컨트롤러**는 클라이언트 요청을 처리하는 핵심 역할을 합니다. REST API든 웹 페이지든, 요청을 받아 적절히 응답하는 로직을 작성하는 게 컨트롤러의 임무죠. 이 글에서는 **Spring Boot 컨트롤러 작성법**, **요청 처리 방법**, 그리고 **실제 예제**를 통해 실무에서 바로 써먹을 수 있는 노하우를 다룹니다. "Spring Boot 컨트롤러 예제"나 "Spring Boot 요청 처리"로 검색한 개발자라면 이 글이 딱 맞아요. 바로 시작합시다!
|
|
|
|
### 1. 컨트롤러란? Spring Boot에서의 역할
|
|
Spring Boot에서 컨트롤러는 **HTTP 요청**을 받아 비즈니스 로직을 호출하고, 결과를 클라이언트에 반환합니다. `@Controller` 또는 `@RestController` 어노테이션을 사용하며, REST API 개발이 늘어나면서 `@RestController`가 더 자주 보이죠.
|
|
|
|
- `@Controller`: 뷰(HTML 등)를 반환할 때 사용.
|
|
- `@RestController`: JSON, XML 같은 데이터를 직접 반환 (REST API에 적합).
|
|
|
|
**실무 팁**:
|
|
- API와 웹을 분리한다면, `@RestController`는 `/api` 경로, `@Controller`는 `/web` 경로로 구분하세요.
|
|
|
|
### 2. 기본 컨트롤러 작성과 요청 처리
|
|
간단한 예제부터 시작해봅시다. 사용자를 조회하는 REST API를 만들어볼게요.
|
|
|
|
| 기능 | 어노테이션 | 설명 |
|
|
|------|----------|------|
|
|
| **기본 컨트롤러** | `@RestController` | REST API 컨트롤러 정의 |
|
|
| **GET 요청 처리** | `@GetMapping` | HTTP GET 요청 매핑 |
|
|
| **POST 요청 처리** | `@PostMapping` | HTTP POST 요청 매핑 |
|
|
| **쿼리 파라미터** | `@RequestParam` | URL 쿼리 매개변수 받기 |
|
|
| **URL 경로 변수** | `@PathVariable` | URL 경로에서 값 추출 |
|
|
| **JSON 요청 본문** | `@RequestBody` | 요청 본문을 객체로 매핑 |
|
|
| **요청 헤더 처리** | `@RequestHeader` | 요청 헤더 값을 읽기 |
|
|
| **응답 처리** | `ResponseEntity` | 상태 코드와 함께 응답 반환 |
|
|
| **예외 처리** | `@ExceptionHandler` | 예외 발생 시 응답 처리 |
|
|
|
|
#### 기본 예제
|
|
```java
|
|
@RestController
|
|
@RequestMapping("/api/users")
|
|
public class UserController {
|
|
|
|
@GetMapping("/{id}")
|
|
public ResponseEntity<String> getUser(@PathVariable Long id) {
|
|
return ResponseEntity.ok("User ID: " + id);
|
|
}
|
|
|
|
@PostMapping
|
|
public ResponseEntity<String> createUser(@RequestBody UserRequest request) {
|
|
return ResponseEntity.status(HttpStatus.CREATED).body("User created: " + request.getName());
|
|
}
|
|
}
|
|
|
|
@Data
|
|
public class UserRequest {
|
|
private String name;
|
|
private String email;
|
|
}
|
|
```
|
|
|
|
### 3. 다양한 요청 처리 방법
|
|
실무에서는 GET, POST 외에도 다양한 HTTP 메서드와 파라미터를 다룹니다. 예제를 통해 알아보죠.
|
|
|
|
#### 쿼리 파라미터 처리
|
|
```java
|
|
@GetMapping("/search")
|
|
public ResponseEntity<List<String>> searchUsers(@RequestParam String name,
|
|
@RequestParam(defaultValue = "10") int limit) {
|
|
List<String> users = Arrays.asList(name + "1", name + "2"); // 더미 데이터
|
|
return ResponseEntity.ok(users.subList(0, Math.min(limit, users.size())));
|
|
}
|
|
```
|
|
|
|
- `@RequestParam`: 쿼리 파라미터 (`?name=John&limit=5`) 처리.
|
|
- `defaultValue`: 값이 없으면 기본값 적용.
|
|
|
|
**실행**:
|
|
- GET `/api/users/search?name=John&limit=2` → `["John1", "John2"]`
|
|
|
|
#### 경로 변수와 혼합 사용
|
|
```java
|
|
@GetMapping("/{id}/details")
|
|
public ResponseEntity<String> getUserDetails(@PathVariable Long id,
|
|
@RequestParam String format) {
|
|
return ResponseEntity.ok("User " + id + " in " + format + " format");
|
|
}
|
|
```
|
|
|
|
**실행**:
|
|
- GET `/api/users/1/details?format=json` → "User 1 in json format"
|
|
|
|
#### 예외 처리
|
|
```java
|
|
@GetMapping("/{id}")
|
|
public ResponseEntity<String> getUser(@PathVariable Long id) {
|
|
if (id <= 0) {
|
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid ID");
|
|
}
|
|
return ResponseEntity.ok("User ID: " + id);
|
|
}
|
|
```
|
|
|
|
- `ResponseStatusException`: HTTP 상태 코드와 메시지를 반환.
|
|
|
|
### 4. 실무에서 유용한 컨트롤러 팁
|
|
컨트롤러를 더 효율적으로 작성하는 방법을 정리했어요.
|
|
|
|
#### 4.1. 서비스 레이어와 분리
|
|
컨트롤러는 요청/응답만 처리하고, 비즈니스 로직은 서비스로 분리하세요.
|
|
|
|
```java
|
|
@RestController
|
|
@RequestMapping("/api/users")
|
|
public class UserController {
|
|
private final UserService userService;
|
|
|
|
@Autowired
|
|
public UserController(UserService userService) {
|
|
this.userService = userService;
|
|
}
|
|
|
|
@GetMapping("/{id}")
|
|
public ResponseEntity<User> getUser(@PathVariable Long id) {
|
|
return ResponseEntity.ok(userService.findUserById(id));
|
|
}
|
|
}
|
|
|
|
@Service
|
|
public class UserService {
|
|
public User findUserById(Long id) {
|
|
return new User(id, "John Doe", "john@example.com"); // 더미 데이터
|
|
}
|
|
}
|
|
|
|
@Data
|
|
@AllArgsConstructor
|
|
public class User {
|
|
private Long id;
|
|
private String name;
|
|
private String email;
|
|
}
|
|
```
|
|
|
|
#### 4.2. 유효성 검사
|
|
요청 데이터의 유효성을 검사해 안정성을 높이세요.
|
|
|
|
```java
|
|
@PostMapping
|
|
public ResponseEntity<String> createUser(@Valid @RequestBody UserRequest request,
|
|
BindingResult result) {
|
|
if (result.hasErrors()) {
|
|
return ResponseEntity.badRequest().body(result.getAllErrors().toString());
|
|
}
|
|
return ResponseEntity.status(HttpStatus.CREATED).body("User created: " + request.getName());
|
|
}
|
|
|
|
@Data
|
|
public class UserRequest {
|
|
@NotBlank(message = "Name is required")
|
|
private String name;
|
|
@Email(message = "Invalid email")
|
|
private String email;
|
|
}
|
|
```
|
|
|
|
- `@Valid`: 요청 객체 검증.
|
|
- `BindingResult`: 오류 처리.
|
|
|
|
#### 4.3. 전역 예외 처리
|
|
`@ControllerAdvice`로 공통 예외를 처리하세요.
|
|
|
|
```java
|
|
@ControllerAdvice
|
|
public class GlobalExceptionHandler {
|
|
@ExceptionHandler(ResponseStatusException.class)
|
|
public ResponseEntity<String> handleResponseStatusException(ResponseStatusException ex) {
|
|
return ResponseEntity.status(ex.getStatus()).body(ex.getReason());
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 어노테이션 정리표
|
|
|
|
| 어노테이션 | 설명 |
|
|
|--------------------|-----------------------------------------|
|
|
| `@Controller` | Spring MVC의 컨트롤러 클래스를 정의 |
|
|
| `@RestController` | `@Controller` + `@ResponseBody`, JSON 응답을 기본으로 함 |
|
|
| `@RequestMapping` | URL 요청을 특정 컨트롤러 또는 메서드에 매핑 |
|
|
| `@GetMapping` | HTTP GET 요청을 특정 메서드에 매핑 |
|
|
| `@PostMapping` | HTTP POST 요청을 특정 메서드에 매핑 |
|
|
| `@PutMapping` | HTTP PUT 요청을 특정 메서드에 매핑 |
|
|
| `@DeleteMapping` | HTTP DELETE 요청을 특정 메서드에 매핑 |
|
|
| `@PatchMapping` | HTTP PATCH 요청을 특정 메서드에 매핑 |
|
|
| `@RequestParam` | 요청 파라미터를 메서드의 파라미터로 매핑 |
|
|
| `@PathVariable` | URL 경로 변수를 메서드의 파라미터로 매핑 |
|
|
| `@ModelAttribute` | 폼 데이터를 객체로 변환하여 전달 |
|
|
| `@RequestBody` | 요청 본문(JSON 등)을 객체로 변환하여 전달 |
|
|
| `@ResponseBody` | 반환 데이터를 JSON 형태로 응답 |
|
|
| `@ResponseStatus` | HTTP 응답 상태 코드를 지정 |
|
|
| `@ExceptionHandler` | 특정 예외 발생 시 처리할 메서드를 정의 |
|
|
| `@InitBinder` | 컨트롤러에서 요청 데이터를 변환하는 바인딩 설정을 정의 |
|
|
| `@CrossOrigin` | 다른 도메인에서 API 요청을 허용하도록 설정 |
|
|
|
|
---
|
|
|
|
## 2. 어노테이션 설명 및 예제
|
|
|
|
### 1) `@Controller`
|
|
Spring MVC 컨트롤러 클래스임을 나타냅니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@Controller
|
|
public class MyController {
|
|
@GetMapping("/hello")
|
|
public String hello() {
|
|
return "hello"; // hello.html을 렌더링
|
|
}
|
|
}
|
|
```
|
|
- `hello.html` 뷰 페이지를 반환합니다.
|
|
|
|
---
|
|
|
|
### 2) `@RestController`
|
|
`@Controller`와 `@ResponseBody`를 합친 역할을 합니다. 즉, JSON 응답을 기본으로 합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class MyRestController {
|
|
@GetMapping("/api/hello")
|
|
public String hello() {
|
|
return "Hello, World!";
|
|
}
|
|
}
|
|
```
|
|
- `"Hello, World!"`라는 문자열을 JSON 형식으로 반환합니다.
|
|
|
|
---
|
|
|
|
### 3) `@RequestMapping`
|
|
URL과 컨트롤러 메서드를 매핑합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@Controller
|
|
@RequestMapping("/users")
|
|
public class UserController {
|
|
@GetMapping("/{id}")
|
|
public String getUser(@PathVariable Long id) {
|
|
return "user"; // user.html 렌더링
|
|
}
|
|
}
|
|
```
|
|
- `/users/{id}` 경로로 들어오는 요청을 `getUser` 메서드가 처리합니다.
|
|
|
|
---
|
|
|
|
### 4) `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`
|
|
각 HTTP 메서드에 대한 매핑을 제공합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
@RequestMapping("/items")
|
|
public class ItemController {
|
|
|
|
@GetMapping("/{id}")
|
|
public String getItem(@PathVariable Long id) {
|
|
return "Item: " + id;
|
|
}
|
|
|
|
@PostMapping
|
|
public String createItem(@RequestBody String item) {
|
|
return "Created: " + item;
|
|
}
|
|
|
|
@PutMapping("/{id}")
|
|
public String updateItem(@PathVariable Long id, @RequestBody String item) {
|
|
return "Updated item " + id + " to " + item;
|
|
}
|
|
|
|
@DeleteMapping("/{id}")
|
|
public String deleteItem(@PathVariable Long id) {
|
|
return "Deleted item " + id;
|
|
}
|
|
}
|
|
```
|
|
- 각각 `GET`, `POST`, `PUT`, `DELETE` 요청을 처리하는 컨트롤러입니다.
|
|
|
|
---
|
|
|
|
### 5) `@RequestParam`
|
|
쿼리 파라미터를 매핑할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class ParamController {
|
|
@GetMapping("/search")
|
|
public String search(@RequestParam String query) {
|
|
return "Searching for: " + query;
|
|
}
|
|
}
|
|
```
|
|
- `/search?query=Spring` 요청 시 `"Searching for: Spring"` 반환.
|
|
|
|
---
|
|
|
|
### 6) `@PathVariable`
|
|
URL 경로 변수를 매핑할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class PathVariableController {
|
|
@GetMapping("/product/{id}")
|
|
public String getProduct(@PathVariable Long id) {
|
|
return "Product ID: " + id;
|
|
}
|
|
}
|
|
```
|
|
- `/product/100` 요청 시 `"Product ID: 100"` 반환.
|
|
|
|
---
|
|
|
|
### 7) `@ModelAttribute`
|
|
폼 데이터를 객체로 바인딩할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@Controller
|
|
public class FormController {
|
|
@PostMapping("/submit")
|
|
public String submit(@ModelAttribute User user) {
|
|
return "result"; // result.html 렌더링
|
|
}
|
|
}
|
|
|
|
class User {
|
|
private String name;
|
|
private int age;
|
|
// Getter & Setter 생략
|
|
}
|
|
```
|
|
- 폼에서 `name`과 `age` 값을 받아 `User` 객체로 변환.
|
|
|
|
---
|
|
|
|
### 8) `@RequestBody`
|
|
JSON 데이터를 객체로 변환할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class JsonController {
|
|
@PostMapping("/json")
|
|
public String receiveJson(@RequestBody User user) {
|
|
return "Received: " + user.getName();
|
|
}
|
|
}
|
|
```
|
|
- `{ "name": "Alice", "age": 25 }` 데이터를 `User` 객체로 변환.
|
|
|
|
---
|
|
|
|
### 9) `@ResponseBody`
|
|
메서드의 반환값을 HTTP 응답으로 직접 반환할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@Controller
|
|
public class ResponseController {
|
|
@ResponseBody
|
|
@GetMapping("/text")
|
|
public String textResponse() {
|
|
return "Hello, ResponseBody!";
|
|
}
|
|
}
|
|
```
|
|
- `"Hello, ResponseBody!"`가 그대로 반환.
|
|
|
|
---
|
|
|
|
### 10) `@ResponseStatus`
|
|
HTTP 응답 상태 코드를 설정할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class StatusController {
|
|
@ResponseStatus(HttpStatus.CREATED)
|
|
@PostMapping("/create")
|
|
public String create() {
|
|
return "Created successfully!";
|
|
}
|
|
}
|
|
```
|
|
- HTTP 201 Created 응답을 반환.
|
|
|
|
---
|
|
|
|
### 11) `@ExceptionHandler`
|
|
예외 발생 시 처리할 메서드를 정의합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
public class ExceptionController {
|
|
@GetMapping("/error")
|
|
public String error() {
|
|
throw new RuntimeException("Something went wrong!");
|
|
}
|
|
|
|
@ExceptionHandler(RuntimeException.class)
|
|
public String handleRuntimeException(RuntimeException e) {
|
|
return "Handled error: " + e.getMessage();
|
|
}
|
|
}
|
|
```
|
|
- `/error` 요청 시 `"Handled error: Something went wrong!"` 반환.
|
|
|
|
---
|
|
|
|
### 12) `@CrossOrigin`
|
|
CORS 문제를 해결할 때 사용합니다.
|
|
|
|
#### 예제:
|
|
```java
|
|
@RestController
|
|
@CrossOrigin(origins = "http://example.com")
|
|
public class CorsController {
|
|
@GetMapping("/data")
|
|
public String getData() {
|
|
return "CORS enabled";
|
|
}
|
|
}
|
|
```
|
|
- `http://example.com`에서 요청 가능.
|