김영한의 실전 자바 - 고급 2편(어노테이션)

2025. 1. 29. 22:44·김영한의 실전 자바 - 고급 2편

어노테이션이란?

  • Annotation이라는 단어는 일반적으로 주석 또는 메모를 뜻하지만 자바에서는 주석 외에 추가 정보 제공 역할도 함.
  • 코드에게 메타데이터를 추가해주는 기능
  • 리플렉션은 메타데이터를 동적으로 조회 및 조작하는 기능을 제공하므로, 리플렉션을 통해 어노테이션을 활용 가능

 

어노테이션에 정의된 값은 어떻게 가져올 수 있는가?

@AnnoElement(value = "data", count = 10, tags = {"t1", "t2"})
	public class ElementData1 {
}

public static void main(String[] args) {
	Class<ElementData1> annoClass = ElementData1.class;
	AnnoElement annotation = annoClass.getAnnotation(AnnoElement.class);
	
    String value = annotation.value();
 	System.out.println("value = " + value);
 	...
  • 위와 같이 리플렉션을 통해 클래스정보를 가져옴
  • 가져온 클래스 정보에서 어노테이션을 가져와 내부의 값을 조회할 수 있음.

 

메타 어노테이션 Rentention, Target, Documented, Inherited에 대해 설명하시오

  • 어노테이션을 정의하는데 사용하는 특별한 어노테이션을 메타 어노테이션이라 함.
  • @Retention
    • 어노테이션의 생존 기간을 의미함
    • RetentionPolicy.SOURCE : 소스 코드에만 남아있다. 컴파일 시점에 제거된다.
    • RetentionPolicy.CLASS : 컴파일 후 class 파일까지는 남아있지만 자바 실행 시점에 제거된다. (기본 값)
    • RetentionPolicy.RUNTIME : 자바 실행 중에도 남아있다. 대부분 이 설정을 사용한다.
  • @Target
    • 어노테이션을 적용할 수 있는 위치를 지정함
    • 다음과 같은 타입을 지정할 수 있으며 일반적으로 TYPE, FIELD, METHOD를 사용함
    • TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE, MODULE, RECORD_COMPONENT;
  • @Documented
    • 자바 API 문서를 만들 때 해당 어노테이션이 함께 포함될 지를 지정하며 보통 함께 사용함
  • @Inherited
    • 자식 클래스가 어노테이션을 상속 받을 수 있게 함.

 

모든 어노테이션이 확장하는 인터페이스는?

public interface Annotation {
	boolean equals(Object obj);
	int hashCode();
	String toString();
	Class<? extends Annotation> annotationType();
}
  • 위와 같은 Annotation 인터페이스를 모든 어노테이션은 자동으로 확장함
  • @interface 키워드를 통해 정의하면 자동으로 확장하도록 함.
  • 어노테이션은 다른 어노테이션을 확장하는 기능은 없고 오직 Annotation 인터페이스만 확장함.

 

@Inherited의 상속에 대해 자세히 설명하시오

  • 해당 어노테이션을 적용한 클래스의 자식도 해당 어노테이션을 부여받도록 함
  • 이는 클래스 상속에서만 작동하며, 인터페이스의 구현체에는 적용되지 않음.
  • 상속은 자식이 부모의 특성을 이어받는 개념이기 때문에 적용됨
  • 하지만, 인터페이스는 메소드의 시그니처만 정의할 뿐 어떤 상태나 행위를 갖지 않기에 구현체가 어노테이션을 상속한다는 개념이 맞지 않은 것.
  • 또한, 인터페이스는 다중 구현이 가능하므로 이 경우 충돌이 발생할 수도 있음.

 

특정 필드가 Null 일 경우 오류를 내도록 하는 어노테이션을 구현하시오

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmpty {
	String message() default "값이 비어있습니다.";
}

public static void validate(Object obj) throws Exception {
	Field[] fields = obj.getClass().getDeclaredFields();
	for (Field field : fields) {
		field.setAccessible(true);
		if (field.isAnnotationPresent(NotEmpty.class)) {
			String value = (String) field.get(obj);
			NotEmpty annotation = field.getAnnotation(NotEmpty.class);
			if (value == null || value.isEmpty()) {
				throw new RuntimeException(annotation.message());
			}
        }
		...
  • @Target을 FIELD로 지정
  • @Rentention은 RUNTIME으로 지정해야 코드 실행시 적용 가능
  • 특정 객체의 필드들을 가져와 setAccessible을 true로 만들어 private도 접근가능하도록 설정
  • 해당 필드가 NonEmpty 어노테이션을 갖고 있는지 확인하고 만약 맞다면 null 체크를 하고 NonEmpty의 메시지를 출력하도록 함.
  • 이 어노테이션을 null 체크가 필요한 필드에 모두 입력한 후 null 체크 필요할 때마다 validate 함수를 호출.
  • 이는 클래스나 필드가 달라도 모두 적용 가능함.
  • 새로 추가되는 클래스나 필드에도 적절히 확장 가능함.
  • 코드 가독성에도 좋음

 

자바 기본 어노테이션 Override, Deprecated, SuppressWarning에 대해 설명하시오

  • @Override
    • 매서드 재정의가 정확하게 잘 되었는지 컴파일러가 체크하는데 사용
    • 컴파일러단에서 체크하기 때문에 실수 방지를 위해서라도 재정의할때는 사용하는게 좋음
    • RententionPolicy가 SOURCE이므로 컴파일 이후 어노테이션은 제거됨
  • @Deprecated
    • 더 이상 사용되지 않는다는 의미로, 이 어노테이션이 적용된 기능은 사용을 권장하지 않음.
    • 해당 요소를 사용하면 오류 발생 가능성이 있거나 향후 변경 또는 제거될 가능성이 있음
    • 혹은, 더 나은 대체제로 대체되었거나 현재 더이상 사용중이지 않다는 것을 의미할 수도.
    • @Deprecated(since = "2.4", forRemoval = true)
      • 2.4 버전 이후 더 이상  사용하지 않게 되었고 미래 버전에 코드가 제거될 예정을 의미
    • Deprecated만 있는 경우 경고를 IDE가 보여주며, forRemoval true 일 경우 심각한 경고를 의미
  • SuppressWarnings
    • 경고를 억제하는 어노테이션으로, 자바 컴파일러가 문제를 경고하지만 개발자가 문제에 대해 잘 알기에 더는 경고하지 말라고 지시하는 어노테이션
    • ex) @SuppressWarnings({"rawtypes", "unchecked"})
    • all: 모든 경고를 억제
    • deprecation: 사용이 권장되지 않는(deprecated) 코드를 사용할 때 발생하는 경고를 억제
    • unchecked: 제네릭 타입과 관련된
    • unchecked 경고를 억제
    • serial: Serializable 인터페이스를 구현할 때 serialVersionUID 필드를 선언하지 않은 경우 발생하는 경고를 억제
    • rawtypes: 제네릭 타입이 명시되지 않은(raw) 타입을 사용할 때 발생하는 경고를 억제
    • unused: 사용되지 않는 변수, 메서드, 필드 등을 선언했을 때 발생하는 경고를 억제

 

어노테이션을 활용하여 특정 URI Path가 요청으로 들어오면 해당 요청에 맵핑되는 함수를 호출되도록 하는 코드를 작성하시오

public class AnnotationController {

    @Mapping("/")
    public void home(HttpRequest request, HttpResponse response) {
    }

    @Mapping("/site1")
    public void site1(HttpRequest request, HttpResponse response) {
    }

    @Mapping("/site2")
    public void site2(HttpRequest request, HttpResponse response) {
    }
}
public void service(HttpRequest request, HttpResponse response) throws IOException {
    String path = request.getPath();
    
    for (Object controller : controllers) {
        Method[] methods = controller.getClass().getDeclaredMethods();
        
        for (Method method : methods) {
            if (method.isAnnotationPresent(Mapping.class)) {
                Mapping mapping = method.getAnnotation(Mapping.class);
                String value = mapping.value();
                
                if (value.equals(path)) {
                    invoke(controller, method, request, response);
                    return;
                }
            }
        }
    }
    
    throw new PageNotFoundException("request=" + path);
}
  • 컨트롤러의 클래스 정보에서 모든 메소드를 불러옴
  • 메소드가 Mapping이라는 어노테이션을 가지고 있는지 확인하고, 해당 어노테이션의 value가 요청 들어온 path와 가은지 확인
  • 만약 같으면 해당 함수를 호출하는 메소드 실행

 

위 코드의 문제점을 제시하고, 개선할 수 있는 방안을 제시하시오.

  • 모든 메소드를 뒤져야 하므로 O(n) 번의 복잡도를 갖고 있어 성능 문제가 있음.
  • 똑같은 value를 가지고 있는 Mapping 어노테이션이 있을 수 있는 문제가 있음.
  • 따라서, HashMap을 통해 미리 uri와 함수를 맵핑하는 맵을 만들고 성능을 개선하고 중복이 발생할 경우 예외를 던지는 기능을 추가하며 됨.
  • Map을 만드는 기능은 생성자에 만들어서 처음에 한 번만 실행되도록 함.
public class AnnotationServletV3 implements HttpServlet {

    private final Map<String, ControllerMethod> pathMap;

    public AnnotationServletV3(List<Object> controllers) {
        this.pathMap = new HashMap<>();
        initializePathMap(controllers);
    }

    private void initializePathMap(List<Object> controllers) {
        for (Object controller : controllers) {
            for (Method method : controller.getClass().getDeclaredMethods()) {
                if (method.isAnnotationPresent(Mapping.class)) {
                    String path = method.getAnnotation(Mapping.class).value();

                    // 중복 경로 체크
                    if (pathMap.containsKey(path)) {
                        ControllerMethod controllerMethod = pathMap.get(path);
                        throw new IllegalArgumentException("경로 중복 등록, path="
                                + path + ", method=" + method + ", 이미 등록된 메서드=" + controllerMethod.method);
                    }

                    pathMap.put(path, new ControllerMethod(controller, method));
                }
            }
        }
    }
}

 

 

 

'김영한의 실전 자바 - 고급 2편' 카테고리의 다른 글

김영한의 실전 자바 - 고급 2편(리플렉션)  (1) 2025.01.29
김영한의 실전 자바 - 고급 2편(HTTP서버)  (2) 2025.01.29
김영한의 실전 자바 - 고급 2편(네트워크 예외)  (2) 2025.01.28
김영한의 실전 자바 - 고급 2편(네트워크 자원정리)  (1) 2025.01.28
김영한의 실전 자바 - 고급 2편(네트워크, TCP, IP, UDP, DNS)  (1) 2025.01.28
'김영한의 실전 자바 - 고급 2편' 카테고리의 다른 글
  • 김영한의 실전 자바 - 고급 2편(리플렉션)
  • 김영한의 실전 자바 - 고급 2편(HTTP서버)
  • 김영한의 실전 자바 - 고급 2편(네트워크 예외)
  • 김영한의 실전 자바 - 고급 2편(네트워크 자원정리)
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (242)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (118)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      페치 조인
      프록시 팩토리
      락
      프록시
      jdk 동적 프록시
      양방향 맵핑
      gesingleresult
      requset scope
      @discriminatorvalue
      조회 성능 최적화
      자바
      @discriminatorcolumn
      hibernate5module
      버퍼
      김영한
      WAS
      JPQL
      Target
      jpq
      typequery
      cglib
      고급
      빈 후처리기
      reentarantlock
      Thread
      단방향 맵핑
      스레드
      @within
      @args
      log trace
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    김영한의 실전 자바 - 고급 2편(어노테이션)
    상단으로

    티스토리툴바