## 스프링 부트 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<User> getAllUsers() {
        // 모든 사용자 정보 조회 로직
        return Arrays.asList(new User(1, "John Doe"), new User(2, "Jane Smith"));
    }

    @GetMapping("/{userId}")
    public ResponseEntity<User> getUserById(@PathVariable Long userId) {
        // 특정 사용자 정보 조회 로직
        User user = findUser(userId);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User newUser) {
        // 새로운 사용자 생성 로직
        User createdUser = saveUser(newUser);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
    }

    @PutMapping("/{userId}")
    public ResponseEntity<User> 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<Void> 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<Product> getAllProducts() {
        // ...
        return null;
    }

    @GetMapping("/{productId}")
    public Product getProductById(@PathVariable Long productId) {
        // ...
        return null;
    }

    @PostMapping("/search")
    public List<Product> 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 맵핑에 대한 깊이 있는 이해를 돕고, 실제 개발에 유용하게 활용될 수 있기를 바랍니다.