티스토리 뷰

http 서버 구현하기 미션을 하고 있다. 진행하면서 @GetMapping, @PostMapping 등으로 메소드 실행 분기처리를 하고 싶어졌다. 그래서 작성하는 Method 단위의 커스텀 어노테이션 만들기

 

1. 어노테이션 정의

  • @interface로 어노테이션을 정의한다.
  • 메타 어노테이션을 정의한다.
  • @Target: ElementType.METHOD, ElementType.FIELD 등으로 다양하게 어디에 적용할 어노테이션인지 정의할 수 있다.
  • @Retention: 런타임에도 참조할 수 있게 RetentionPolicy.RUNTIME 으로 해준다. 이것 역시 컴파일을 기준으로 언제 참조 가능한지 설정해 줄 수 있다.
  • CustomAnnotation 참고 블로그 
  • 이번 경우에서는 path라는 속성으로 uri을 정의해 줄 것이므로 해당 내용을 넣어준다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetMapping {
    String path() default "";
}

2. 어노테이션 사용할 곳에 붙이기

  • 메소드를 설정한 뒤, 해당 어노테이션을 붙이고 path에 지정할 uri 를 설정해 주었다.
public class Controller {

    private final HttpService service;

    public Controller() {
        this.service = new HttpService();
    }

    @GetMapping(path = "/")
    public String basic(String uri) {
        return ResponseEntity
                .responseBody("Hello world!")
                .build();
    }

    @GetMapping(path = "/login")
    public String login(String uri) throws IOException {
        return ResponseEntity
                .responseResource(uri)
                .build();
    }
 }

3. 어노테이션이 있는 메소드 실행하기

  • 메소드를 실행히길 설정을 해준다.
  • Controller.class.getDeclaredMethods() 로 Controller 클래스에 선언된 메소드들을 불러올 수 있다.
  • 해당 method 들을 돌면서 어노테이션이 존재하면 해당 path 와 일치하는지 여부를 확인한다.
  • method.getAnnotation(PostMapping.class).path() : 어노테이션에 path 로 설정했으므로 .path()로 해당 요소를 불러올 수 있다.
  • 메소드가 private 이라면 method.setAccessible(true); 를 해주어야 하지만 public 이므로 생략한다.
  • method.invoke(obj, ...args) 로 해당 메소드를 실행하고 반환값을 반환한다.
  • 매핑된 Controller가 없다면, 그냥 해당 소스를 찾아 리턴해주게 한다.
class FrontController{
	
    // ... 

    public String response() throws Exception {
        final String httpMethod = requestLine.getHttpMethod();
        final String uri = requestLine.getUri();
        Controller controller = new Controller();
        if (HttpMethod.GET.equals(httpMethod)) {
            return getMapping(uri, controller);
        }
        if (HttpMethod.POST.equals(httpMethod)) {
            return postMapping(uri, controller);
        }

        throw new HttpException("설정되어 있지 않은 http 메소드입니다.");
    }

    private String getMapping(String uri, Controller controller) throws Exception {
        for (Method method : Controller.class.getDeclaredMethods()) {
            if (matchGetMapping(method, uri)) {
                return (String) method.invoke(controller, uri);
            }
        }

        Set<String> declaredGetMappings = collectDeclaredGetMappings();
        if (!declaredGetMappings.contains(uri)) {
            return ResponseEntity
                    .responseResource(uri)
                    .build();
        }
        throw new HttpException("잘못된 http get 요청입니다.");
    }
    
    private boolean matchGetMapping(Method method, String uri) {
        if (method.isAnnotationPresent(GetMapping.class)) {
            String path = method.getAnnotation(GetMapping.class).path();
            return path.equals(uri);
        }
        return false;
    }
    // ...
}

참고

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함