[1일차] 인프런 워밍업 스터디 클럽 0기- BE

[질문]

  • 어노테이션을 사용하는 이유 (효과) 는 무엇일까?

  • 나만의 어노테이션은 어떻게 만들 수 있을까?


1일차에 위와 같은 질문을 받았습니다.

어노테이션을 사용하는 이유는 어노테이션 하나로 마법과 같은 일이 일어나기 때문이라고 하셨습니다. ( 강의 중에.. )

예를 들면 @PostMapping의 경우 이 어노테이션 하나로 http통신 방법을 정의할 수 있습니다.

아래는 @PostMapping 어노테이션의 일부 코드입니다. ( 솔직히 어떻게 작동하는지 모르겠습니다 )

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {

	/**
	 * Alias for {@link RequestMapping#name}.
	 */
	@AliasFor(annotation = RequestMapping.class)
	String name() default "";

	/**
	 * Alias for {@link RequestMapping#value}.
	 */
	@AliasFor(annotation = RequestMapping.class)
	String[] value() default {};

	/**
	 * Alias for {@link RequestMapping#path}.
	 */
	@AliasFor(annotation = RequestMapping.class)
	String[] path() default {};

	/**
	 * Alias for {@link RequestMapping#params}.
	 */
	@AliasFor(annotation = RequestMapping.class)
	String[] params() default {};

그럼 이제 나도 마법같은 나만의 어노테이션을 만들어 볼까? 라는 생각이 들었습니다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AnnotationCal {
    int[] numbers() default {};
}

어떤 어노테이션을 만들어 볼까 하다가 배열을 넣으면 계산을 해주는 어노테이션을 만들기로 하였습니다.

위와 같이 어노테이션을 선언 하였습니다. 간단하게 numbers 배열로 만들었습니다.

public class AnnotationUtil {
    static int calculateSumForAnnotation(Class<?> clazz, String methodName) throws NoSuchMethodException {
        int sum = 0;
        Method method = clazz.getMethod(methodName);
        AnnotationCal annotationCal = method.getAnnotation(AnnotationCal.class);
        for (int number : annotationCal.numbers()) {
            sum += number;
        }
        return sum;
    }
}

어노테이션 유틸에 덧셈 계산을 하는 로직을 만들어 주었습니다. 근데 @PostMapping의 경우는 저런 구현체가 없던데...

@RestController
public class AnnotationCalculator {

    @GetMapping("/annotationSum")
    @AnnotationCal(numbers = {1, 2, 3, 4, 5})
    public int annotationSum() throws NoSuchMethodException {
        return AnnotationUtil.calculateSumForAnnotation(getClass(), "annotationSum");
    }
}

위와 같이 어노테이션 배열로 숫자를 넣어 주면 자동으로 합계를 구해주도록 만들었습니다!!

위 API를 호출하면 15가 출력됩니다!

[ 깃 허브 ]

여기서 더 궁금한 점이 생겼습니다. 위에서 말한 구현체 관련된 부분입니다.

스프링 어노테이션의 경우 위와같이 어노테이션을 불러서 로직을 만들어 주는 부분이 어디 있는지 모르겠습니다. 저처럼 어딘가에 만들어 놓지 않았다면 절대 동작할 일이 없습니다.

저도 어노테이션만 선언하면 사용자는 세부 로직을 모르고 단순히 사용만 가능하도록 로직을 만들어 보고 싶습니다.

그렇지만 저는 검색 바보라서 그런 것을 찾는 것이 너무 어렵습니다ㅠㅠ

아시는 분은 좀 알려주세요~

 

1일차 과제 끝 ~~

댓글을 작성해보세요.

  • 최태현
    최태현

    안녕하세요 jhc님! 🙂 말씀해주신 것처럼 스프링 어노테이션의 경우 PostMapping / GetMapping 등을 식별하고 해당 로직을 처리해주는 코드가 존재합니다. spring-mvc의 https://github.com/spring-projects/spring-framework/blob/main/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java 코드를 보시면, PostMapping / GetMapping 의 전신인 RequestMapping 을 처리해주는 로직이 존재하죠. (아마 스프링 MVC는 정보를 이렇게 처리해 기억하고 있다가, 요청이 들어오면 갖고 있는 정보를 토대로 특정 메소드로 요청을 보내줄겁니다)

     

    우리도 이렇게 어노테이션만 선언했을 때 사용자는 세부 로직을 모르고 단순히 사용만 가능하도록 로직을 만들려면 몇 가지 방법을 사용해 볼 수 있긴 합니다. 가장 쉽게 쓰려면 스프링의 AOP를 적용해볼 수 있을 것 같네요! (return 타입도 Object로 바꾸고, 몇 가지 번거로운 작업도 해줘야 할거에요..) 다만, 작성해주신 사례처럼 특정 메소드의 반환을 어노테이션 기반으로 처리하는 경우는 무척 드물어서 이 사례에 fit하게 적용 가능할지는 잘 모르겠습니다 ㅎㅎ..

    @GetMapping("/annotationSum")
    @AnnotationCal(numbers = {1, 2, 3, 4, 5})
    public int annotationSum() throws NoSuchMethodException {
        return AnnotationUtil.calculateSumForAnnotation(getClass(), "annotationSum");
    }

    예를 들면, 위의 코드 보다는 아래 코드가 성능상 이점도 있고, 코드도 깔끔하죠!

    @GetMapping("/annotationSum")
    public int annotationSum() throws NoSuchMethodException {
        return NumberUtils.getSum(1, 2, 3, 4, 5);
    }

     


    jhc
    jhc

    우와 답글까지.... 정말 감동입니다 ;ㅁ; 깃헙 링크도 너무 감사합니다!!

    찬찬히 살펴 보겠습니다.

채널톡 아이콘