9.6 KiB
스프링 부트 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과 일치할 수 있습니다. 이 경우 스프링은 다음과 같은 우선순위에 따라 가장 적절한 핸들러를 선택합니다.
- 정확한 매칭: 정확히 일치하는 URL이 가장 높은 우선순위를 가집니다.
- 더 구체적인 패턴: 와일드카드 (
*,**)를 포함하는 패턴보다 정확한 세그먼트가 더 많은 패턴이 높은 우선순위를 가집니다. 예를 들어/products/*보다/products/details가 더 구체적입니다. - 와일드카드 순서:
*보다**가 낮은 우선순위를 가집니다. - HTTP 메서드: 요청의 HTTP 메서드와 정확히 일치하는 맵핑이 우선됩니다.
consumes,produces,params,headers조건: 이러한 조건이 더 구체적으로 명시된 맵핑이 우선됩니다.
5. URL 맵핑 예시
다음은 다양한 URL 맵핑 어노테이션 활용 예시입니다.
@Controller 사용 예시 (HTML 뷰 반환):
@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 데이터 반환):
@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 과 메서드 레벨 맵핑 조합:
@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 맵핑에 대한 깊이 있는 이해를 돕고, 실제 개발에 유용하게 활용될 수 있기를 바랍니다.