## 스프링 부트 URL 맵핑 완벽 가이드: 요청과 코드를 연결하는 핵심 원리 스프링 부트 애플리케이션의 핵심은 사용자의 웹 요청을 적절한 코드 영역으로 연결하는 **URL 맵핑**입니다. URL 맵핑을 제대로 이해하고 활용하는 것은 웹 애플리케이션 개발의 기초이자 효율적인 API 설계의 핵심입니다. 이 글에서는 스프링 부트의 URL 맵핑에 대한 모든 것을 상세하게 알아보겠습니다. ### 1. URL 맵핑이란 무엇일까요? URL 맵핑은 클라이언트(웹 브라우저, 모바일 앱 등)가 특정 URL로 요청을 보냈을 때, 해당 요청을 처리할 서버 측의 특정 핸들러 메서드(주로 컨트롤러 클래스 내의 메서드)를 연결해주는 과정입니다. 스프링 부트는 다양한 어노테이션과 설정을 통해 이러한 맵핑을 간편하게 구현할 수 있도록 지원합니다. ### 2. 주요 URL 맵핑 어노테이션 스프링 MVC (Model-View-Controller) 프레임워크에서 URL 맵핑을 정의하는 데 가장 많이 사용되는 어노테이션은 다음과 같습니다. * **`@Controller`**: 해당 클래스가 웹 요청을 처리하는 컨트롤러임을 명시합니다. 주로 HTML 뷰를 반환하는 데 사용됩니다. * **`@RestController`**: `@Controller`와 `@ResponseBody` 어노테이션을 결합한 것으로, 해당 클래스의 모든 메서드가 HTTP 응답 본문에 직접 데이터를 써서 반환함을 의미합니다. 주로 RESTful API 개발에 사용됩니다. * **`@RequestMapping`**: 특정 URL 패턴과 HTTP 메서드에 대한 맵핑을 정의하는 가장 기본적인 어노테이션입니다. 클래스 레벨과 메서드 레벨 모두에서 사용할 수 있습니다. * **`value` 또는 `path`**: 맵핑할 URL 패턴을 지정합니다. 여러 개의 패턴을 배열 형태로 지정할 수도 있습니다. * **`method`**: 맵핑할 HTTP 메서드 (GET, POST, PUT, DELETE 등)를 지정합니다. 여러 개의 메서드를 배열 형태로 지정할 수 있습니다. * **`consumes`**: 요청의 Content-Type 헤더를 기반으로 맵핑을 제한합니다. * **`produces`**: 응답의 Content-Type 헤더를 기반으로 맵핑을 제한합니다. * **`params`**: 요청 파라미터의 존재 여부나 특정 값을 기반으로 맵핑을 제한합니다. * **`headers`**: 요청 헤더의 존재 여부나 특정 값을 기반으로 맵핑을 제한합니다. * **`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`**: `@RequestMapping` 어노테이션에 특정 HTTP 메서드를 명시적으로 지정한 축약형 어노테이션입니다. 코드의 가독성을 높여줍니다. * `@GetMapping(value = "/users")` 은 `@RequestMapping(value = "/users", method = RequestMethod.GET)` 과 동일합니다. ### 3. URL 패턴 매칭 규칙 `@RequestMapping` 또는 축약형 어노테이션의 `value` 또는 `path` 속성에 지정하는 URL 패턴은 다음과 같은 규칙을 따릅니다. * **정확한 매칭**: `/users` 와 같이 정확한 URL 경로를 지정하면 해당 경로로의 요청만 매핑됩니다. * **와일드카드 (`*`)**: 하나의 경로 세그먼트 내에서 모든 문자와 매칭됩니다. 예를 들어 `/products/*` 는 `/products/1`, `/products/abc` 등과 매칭됩니다. * **이중 와일드카드 (`**`)**: 0개 이상의 경로 세그먼트와 매칭됩니다. 클래스 레벨에서 주로 사용되며, 하위의 모든 경로를 포괄합니다. 예를 들어 `@RequestMapping("/api/**")` 로 설정된 컨트롤러는 `/api/users`, `/api/products/1`, `/api/admin/settings` 등 모든 `/api/` 하위 경로와 매칭됩니다. * **경로 변수 (`{variableName}`)**: URL 경로의 특정 부분을 변수로 추출하여 메서드 파라미터로 전달할 수 있습니다. `@PathVariable` 어노테이션과 함께 사용됩니다. 예를 들어 `/users/{userId}` 로 맵핑된 경우, `/users/123` 요청 시 `123` 이 `userId` 변수에 담겨 메서드로 전달됩니다. ### 4. URL 맵핑 우선순위 여러 개의 맵핑 규칙이 하나의 URL과 일치할 수 있습니다. 이 경우 스프링은 다음과 같은 우선순위에 따라 가장 적절한 핸들러를 선택합니다. 1. **정확한 매칭**: 정확히 일치하는 URL이 가장 높은 우선순위를 가집니다. 2. **더 구체적인 패턴**: 와일드카드 (`*`, `**`)를 포함하는 패턴보다 정확한 세그먼트가 더 많은 패턴이 높은 우선순위를 가집니다. 예를 들어 `/products/*` 보다 `/products/details` 가 더 구체적입니다. 3. **와일드카드 순서**: `*` 보다 `**` 가 낮은 우선순위를 가집니다. 4. **HTTP 메서드**: 요청의 HTTP 메서드와 정확히 일치하는 맵핑이 우선됩니다. 5. **`consumes`, `produces`, `params`, `headers` 조건**: 이러한 조건이 더 구체적으로 명시된 맵핑이 우선됩니다. ### 5. URL 맵핑 예시 다음은 다양한 URL 맵핑 어노테이션 활용 예시입니다. **`@Controller` 사용 예시 (HTML 뷰 반환):** ```java @Controller public class HomeController { @GetMapping("/") public String home(Model model) { model.addAttribute("message", "Welcome to the homepage!"); return "home"; // home.html 뷰 반환 } @GetMapping("/about") public String about() { return "about"; // about.html 뷰 반환 } } ``` **`@RestController` 사용 예시 (JSON 데이터 반환):** ```java @RestController @RequestMapping("/api/users") public class UserApiController { @GetMapping public List getAllUsers() { // 모든 사용자 정보 조회 로직 return Arrays.asList(new User(1, "John Doe"), new User(2, "Jane Smith")); } @GetMapping("/{userId}") public ResponseEntity getUserById(@PathVariable Long userId) { // 특정 사용자 정보 조회 로직 User user = findUser(userId); if (user != null) { return ResponseEntity.ok(user); } else { return ResponseEntity.notFound().build(); } } @PostMapping public ResponseEntity createUser(@RequestBody User newUser) { // 새로운 사용자 생성 로직 User createdUser = saveUser(newUser); return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); } @PutMapping("/{userId}") public ResponseEntity updateUser(@PathVariable Long userId, @RequestBody User updatedUser) { // 특정 사용자 정보 업데이트 로직 User user = updateUser(userId, updatedUser); if (user != null) { return ResponseEntity.ok(user); } else { return ResponseEntity.notFound().build(); } } @DeleteMapping("/{userId}") public ResponseEntity deleteUser(@PathVariable Long userId) { // 특정 사용자 삭제 로직 deleteUser(userId); return ResponseEntity.noContent().build(); } private User findUser(Long userId) { // ... 구현 생략 return null; } private User saveUser(User newUser) { // ... 구현 생략 return null; } private User updateUser(Long userId, User updatedUser) { // ... 구현 생략 return null; } private void deleteUser(Long userId) { // ... 구현 생략 } } ``` **클래스 레벨 `@RequestMapping` 과 메서드 레벨 맵핑 조합:** ```java @RestController @RequestMapping("/api/products") public class ProductApiController { @GetMapping public List getAllProducts() { // ... return null; } @GetMapping("/{productId}") public Product getProductById(@PathVariable Long productId) { // ... return null; } @PostMapping("/search") public List searchProducts(@RequestBody SearchCriteria criteria) { // ... return null; } } ``` 위 예시에서 `/api/products` 로 시작하는 모든 요청은 `ProductApiController` 에서 처리하며, 메서드 레벨의 `@GetMapping`, `@PostMapping` 등을 통해 더 구체적인 URL과 매핑됩니다. 예를 들어 `/api/products` 에 대한 GET 요청은 `getAllProducts()` 메서드가 처리하고, `/api/products/{productId}` 에 대한 GET 요청은 `getProductById()` 메서드가 처리합니다. ### 6. 스프링 부트 자동 구성과 URL 맵핑 스프링 부트는 개발자가 직접 많은 설정을 하지 않아도 기본적인 URL 맵핑 기능을 제공합니다. `@SpringBootApplication` 어노테이션이 포함된 메인 클래스를 실행하면 스프링 MVC가 자동으로 구성되고, 컨트롤러 어노테이션이 붙은 클래스들의 메서드에 정의된 URL 맵핑 정보를 스캔하여 등록합니다. ### 7. 사용자 정의 URL 맵핑 설정 때로는 어노테이션 방식 외에 프로그래밍 방식으로 URL 맵핑을 설정해야 할 경우가 있습니다. 이럴 때는 `WebFluxConfigurer` 또는 `WebMvcConfigurer` 인터페이스를 구현하여 `addResourceHandlers()`, `addViewControllers()` 등의 메서드를 오버라이드하여 사용자 정의 맵핑 규칙을 추가할 수 있습니다. ### 결론 스프링 부트의 URL 맵핑은 웹 애플리케이션의 동작 방식을 결정하는 중요한 요소입니다. 다양한 어노테이션과 패턴 매칭 규칙을 이해하고 적절하게 활용하면 효율적이고 유지보수하기 쉬운 웹 애플리케이션을 구축할 수 있습니다. 이 가이드가 스프링 부트 URL 맵핑에 대한 깊이 있는 이해를 돕고, 실제 개발에 유용하게 활용될 수 있기를 바랍니다.