Files
spring-boot-examples/docs/04_컨트롤러와 요청 처리.md
2025-04-08 19:56:24 +09:00

13 KiB

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 예외 발생 시 응답 처리

기본 예제

@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 메서드와 파라미터를 다룹니다. 예제를 통해 알아보죠.

쿼리 파라미터 처리

@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"]

경로 변수와 혼합 사용

@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"

예외 처리

@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. 서비스 레이어와 분리

컨트롤러는 요청/응답만 처리하고, 비즈니스 로직은 서비스로 분리하세요.

@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. 유효성 검사

요청 데이터의 유효성을 검사해 안정성을 높이세요.

@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로 공통 예외를 처리하세요.

@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 컨트롤러 클래스임을 나타냅니다.

예제:

@Controller
public class MyController {
    @GetMapping("/hello")
    public String hello() {
        return "hello"; // hello.html을 렌더링
    }
}
  • hello.html 뷰 페이지를 반환합니다.

2) @RestController

@Controller@ResponseBody를 합친 역할을 합니다. 즉, JSON 응답을 기본으로 합니다.

예제:

@RestController
public class MyRestController {
    @GetMapping("/api/hello")
    public String hello() {
        return "Hello, World!";
    }
}
  • "Hello, World!"라는 문자열을 JSON 형식으로 반환합니다.

3) @RequestMapping

URL과 컨트롤러 메서드를 매핑합니다.

예제:

@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 메서드에 대한 매핑을 제공합니다.

예제:

@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

쿼리 파라미터를 매핑할 때 사용합니다.

예제:

@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 경로 변수를 매핑할 때 사용합니다.

예제:

@RestController
public class PathVariableController {
    @GetMapping("/product/{id}")
    public String getProduct(@PathVariable Long id) {
        return "Product ID: " + id;
    }
}
  • /product/100 요청 시 "Product ID: 100" 반환.

7) @ModelAttribute

폼 데이터를 객체로 바인딩할 때 사용합니다.

예제:

@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 생략
}
  • 폼에서 nameage 값을 받아 User 객체로 변환.

8) @RequestBody

JSON 데이터를 객체로 변환할 때 사용합니다.

예제:

@RestController
public class JsonController {
    @PostMapping("/json")
    public String receiveJson(@RequestBody User user) {
        return "Received: " + user.getName();
    }
}
  • { "name": "Alice", "age": 25 } 데이터를 User 객체로 변환.

9) @ResponseBody

메서드의 반환값을 HTTP 응답으로 직접 반환할 때 사용합니다.

예제:

@Controller
public class ResponseController {
    @ResponseBody
    @GetMapping("/text")
    public String textResponse() {
        return "Hello, ResponseBody!";
    }
}
  • "Hello, ResponseBody!"가 그대로 반환.

10) @ResponseStatus

HTTP 응답 상태 코드를 설정할 때 사용합니다.

예제:

@RestController
public class StatusController {
    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping("/create")
    public String create() {
        return "Created successfully!";
    }
}
  • HTTP 201 Created 응답을 반환.

11) @ExceptionHandler

예외 발생 시 처리할 메서드를 정의합니다.

예제:

@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 문제를 해결할 때 사용합니다.

예제:

@RestController
@CrossOrigin(origins = "http://example.com")
public class CorsController {
    @GetMapping("/data")
    public String getData() {
        return "CORS enabled";
    }
}
  • http://example.com에서 요청 가능.