본 글은 학습 목적으로 작성되었으며, 스프링 프레임워크의 기능을 모방하여 구현한 것입니다.
그러므로, 실제 스프링 프레임워크와 완전히 동일하게 구현되지는 않았습니다.
1. 개요
이어서 어노테이션과 RequestMapping에 대해 더 깊이 있게 학습하려 합니다.
스프링의 RequestMapping 어노테이션은 메소드뿐만 아니라 클래스에도 적용할 수 있어 더욱 유연한 구조를 가집니다.
또한, 요청 URL 외에도 HTTP 요청 메소드 정보를 활용하여 더욱 세밀한 맵핑을 구성할 수 있습니다.
2. 사전 작업
2.1 RequestMethod Enum 타입 작성
public enum RequestMethod {
GET, POST
}
HTTP 요청 메소드 에 대한 Enum 클래스를 작성합니다.
2.2 어노테이션 작성
@Target({ElementType.TYPE, ElementType.METHOD}) // RequestMapping 어노테이션은 클래스와 메서드 두곳에서 사용될 것입니다.
@Retention(RetentionPolicy.RUNTIME) // 리플렉션을 이용해 런타임에 어노테이션 정보를 가져올 것 입니다.
public @interface RequestMapping {
String path() default "";
RequestMethod method() default RequestMethod.GET; // 요청 HTTP 응답의 정보도 활용할 것입니다.
}
RequestMapping 어노테이션은 다음과 같은 정보를 포함하고 있습니다.
- 맵핑할 URL에 대한 정보
- 맵핑할 HTTP 요청 메소드에 대한 정보
2.3 컨트롤러 작성
public interface Controller {
}
@RequestMapping(path = "/user") // UserController 을 "/user"으로 시작하는 URL과 맵핑할 것입니다.
public class UserController implements Controller{
@RequestMapping(path = "/create") // "/user/create" URL 과 GET 요청에 맵핑할 것입니다.
public void createByGet() {
System.out.println("I am user create method by GET");
}
@RequestMapping(path = "/create", method = RequestMethod.POST) // "/user/create" URL 과 POST 요청에 맵핑할 것입니다.
public void createByPost() {
System.out.println("I am user create method by POST");
}
}
@RequestMapping(path = "/article") // ArticleController 을 "/article"으로 시작하는 URL과 맵핑할 것입니다.
public class ArticleController implements Controller{
@RequestMapping(path = "/create") // "/article/create" URL 과 GET 요청에 맵핑할 것입니다.
public void createByGet() {
System.out.println("I am article create method by GET");
}
@RequestMapping(path = "/create", method = RequestMethod.POST) // "/article/create" URL 과 POST 요청에 맵핑할 것입니다.
public void createByPost() {
System.out.println("I am article create method by POST");
}
}
위에서 작성한 어노테이션을 각각의 클래스 및 메소드에 적용하였습니다.
3. 코드 구현
public class Main {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
//=============초기화 단계================
// 1. Request-Line은 "POST /user/create"라고 가정합니다.
String request = "POST /user/create";
String[] methodAndURL= request.split(" ");
String method = methodAndURL[0];
String URL = methodAndURL[1];
// 2. String 타입의 HTTP 요청 메소드를 RequestMethod 타입으로 변환합니다.
RequestMethod requestMethod = null;
if (method.equals("GET")) {
requestMethod = RequestMethod.GET;
}
if (method.equals("POST")) {
requestMethod = RequestMethod.POST;
}
// 3. 콜렉션 인터페이스 controllers에 UserController 타입과 ArticleController 타입이 있다고 가정합니다.
Collection<Controller> controllers = new ArrayList<>();
controllers.add(new UserController());
controllers.add(new ArticleController());
//=============================================
// 1. 요청 URL을 처리할 수 있는 Controller를 찾는다.
Controller mappingController = null;
String mappingControllerPath = null;
for (Controller controller : controllers) {
// 1.1 클래스 파일에 대한 정보를 가져온다. (리플렉션 사용)
Class<? extends Controller> controllerClass = controller.getClass();
// 1.2 유저 클래스의 RequestMapping 어노테이션을 가져온다.
RequestMapping controllerRequestMapping = controllerClass.getAnnotation(RequestMapping.class);
// 1.3 RequestMapping 에 맵핑되어 있는 경로를 가져온다.
String controllerPath = controllerRequestMapping.path();
// 1.4 컨트롤이 URL을 처리할 수 있는지 확인한다.
if (URL.startsWith(controllerPath)) {
mappingController = controller;
mappingControllerPath = controllerPath;
break;
}
}
// 2. 요청 URL과 HTTP method를 처리할 수 있는 메소드를 찾는다.
// 2.1 클래스 파일에 대한 정보를 가져온다. (리플렉션 사용)
Class<? extends Controller> controllerClass = mappingController.getClass();
// 2.2 클래스의 모든 메소드를 가져온다.
Method[] methods = controllerClass.getMethods();
Method mappingMethod = null;
for (Method m : methods) {
// 2.3 메소드의 RequestMapping 어노테이션을 가져온다.
RequestMapping methodAnnotation = m.getAnnotation(RequestMapping.class);
// 2.4 메소드가 요청 HTTP method를 처리할 수 있는지 확인한다.
if (methodAnnotation.method() != requestMethod) {
continue;
}
// 2.5 메소드에 맵핑되어 있는 경로를 가져온다.
String mappingMethodPath = methodAnnotation.path();
String path = mappingControllerPath + mappingMethodPath;
// 2.6 메소드가 URL을 처리할 수 있는지 확인한다.
if (URL.equals(path)) {
mappingMethod = m;
break;
}
}
// 3. 맵핑되어 있는 메서드를 실행한다.
mappingMethod.invoke(mappingController); // 출력 결과 : "I am user create method by POST"
}
}
4. 정리
이전 글에서 만들었던 커스텀 @RequestMapping 어노테이션을 컨트롤러에도 적용하였습니다.
이렇게 어노테이션과 리플렉션을 사용하면, HTTP Request-Line 과 컨트롤러의 메소드를 맵핑할 수 있다는 것을 학습했습니다.
다음으로 학습하고 싶은 것은 @Controller와 @Component 어노테이션입니다.
본 글의 메인 함수에서는 직접 Controller를 초기화하는 방식을 사용했습니다.
하지만 실제 스프링 프레임워크에서는 어노테이션 기반으로, Controller들을 자동으로 Bean으로 등록해주는 기능을 제공합니다.
이러한 기능을 학습하고 싶습니다.
'Spring' 카테고리의 다른 글
[스프링 기능 따라하며 공부하기] Annotation 과 RequestMapping (1/2) (0) | 2023.04.16 |
---|