[Java] 어노테이션을 사용하는 이유

Hyo Kyun Lee·2024년 12월 31일
0

Java

목록 보기
69/87

1. 개요

현재 진행 중인 프로젝트에서는 기본적으로 모든 클래스에 @AllArgsConstructor를 주입하여 필드생성자를 생성해주도록 하고, 매개변수 누락으로 인한 불완전한 Controller/Service 객체 생성을 방지한다.

그러면서 신기한 부분을 발견했는데, @AllArgsConstructor 어노테이션을 삭제할 경우 멤버 변수가 모두 완전히 있었음에도, 멤버 변수를 찾을 수 없다는 컴파일 오류가 발생하였다.

사실 어노테이션, 빈의 개념을 자바의 실행 주기나 생성 주기와 연관지어 이해하다보니 Runtime 관점에서만 바라보았던 것 같다.

컴파일 관점에서도 어노테이션은 중요한 의미를 지니며, 이에 대해 알게된 내용을 기록하여 남긴다.

2. 런타임 관점

일단 런타임 관점에서는 매우 유용하다 볼 수 있다. 하지만 양날의 검이다.

지금까지 무의식적으로 사용하였던 @AutoWired는 인터페이스의 의존성 주입 자동화, 즉 별도의 객체를 생성하지 않고도(인터페이스나 서비스 등의 메소드는 원칙적으로 외부에서 사용 불가능하지만), 객체 내 메소드를 외부에서 사용하고자 할 때 이를 가능할 수 있도록 한다.

현재 프로젝트에서는 @AutoWired를 query 실행을 위한 fetch 목적으로, 도메인 간 참조 자유도를 높이기 위해, 더 나아가 인터페이스나 추상 클래스를 사용하여 클래스 간의 순환 의존성을 피하기 위해 인터페이스의 쿼리 메서드에 해당 어노테이션을 설정하였다.

컨트롤러, 서비스에서는 해당 어노테이션을 사용하지는 않으나 다른 클래스에서 필드 메서드에 대해 @AutoWired 어노테이션을 사용하는 것은 매우 안좋다고 한다.

크게 3가지 이유인데,

  • 스프링 컨텍스트를 실행해야 주입받은 클래스 혹은 인터페이스에 대한 필드 의존성을 주입받을 수 있다. 즉, 런타임을 해야 효과가 있으므로 컴파일 단계에서 이루어지는 단위 테스트 작성은 어렵고, 별도의 모의 객체를 생성해야 테스트가 가능해진다.
  • 순환 의존 관계(서로의 클래스가 서로의 클래스를 참조하는 상태)일 경우 필드 인젝션을 사용할 경우 어느 한 쪽은 null을 주입받게 된다. 의존성을 분리하거나, 생성자 인젝션을 사용할 수 있다. 이 문제의 경우 런타임 시점에서도 문제가 될 수 있으므로 유의해야 한다.
  • 필드 인젝션을 사용할 경우 final 선언이 안되어, 객체가 생성된 이후에도 해당 필드를 변경할 수 있다는 문제점이 생긴다. 필드를 변경한다는 문제점은 비단 @AutoWired 뿐 만 아니라 getter, setter 등에도 적용되는 문제이며, 명확한 의존성 주입을 방해하는 요소이므로 피하는 것이 좋다.

런타임 관점에서 어노테이션은 자동화 및 간결성 등 편의적인 측면에서 장점이 많지만, 제대로 사용하지 않으면 오류 덩어리가 될 수 있으므로 주의해서 사용해야 한다.

더불어 어노테이션의 진가는 런타임 시점이 아니다. 컴파일 시점이다.

3. 컴파일 관점

기본적으로 어노테이션은 java compiler(javac)의 일부로 모든 처리를 컴파일에서 진행한다. 따라서 런타임 시점에서의 할 일을 경감하여 자바 실행 시 부담을 줄여줄 수 있다.

위 런타임 시점에서 필드 인젝션 자동 주입시, 테스트가 필요할 경우 리플렉션을 이용하여 테스트 시 생성자 주입을 할 수 있다고 알았다. 그러나 어노테이션 자체가 mirror API를 이용하여, 리플렉션 없이도 static 영역에 있는 클래스 정보(Stack 말하는 듯)를 사용할 수 있다는 것이 유용하다.

다시 말해, 어노테이션의 장점을 굳이 상쇄하면서까지 비효율적인 방법으로 로직이나 테스트 코드를 작성하기 보다는 컴파일 단계에서 어노테이션이 확보할 수 있는 장점을 활용해야한다는 점을 명심해야한다는 것이다.

위와 같이, 어노테이션은 컴파일 시점에서 처리할 어노테이션이 없어질 때까지 스캐닝 및 소스코드 생성을 반복한다.

이때 생성한 소스코드 중 비슷한 형태로 반복되는 코드를 보일러 플레이트 코드라 하는데, 어노테이션은 이 보일러 플레이트 코드의 자동 생성을 컴파일 시점에서 도와준다.

즉, 의존성 분리를 통해 인터페이스를 만들고 이에 대해 AutoWired를 선언했다면, 해당 인터페이스를 구체화하는 클래스를 만들고 이후에 객체를 생성하고...이런 반복적인 생성을 하지 않고도 AutoWired를 통해 한번에 해결할 수 있다는 의미이다.

여기서, 우리는 어노테이션은 컴파일 시점에서 어떠한 유리한 점이 있으며, 런타임보다는 컴파일 측면에서 그 장점을 백번 활용하는 방향으로 사용하는 것을 알아야 한다는 점을 알 수 있다.

@AllArgsConstructor도 마찬가지로, 컴파일 시점에서 해당 컨트롤러와 서비스의 생성자를 자동으로 만들어 주므로 완전한 상태의 객체 처리를 도와주고 전산 처리 시 필드의 누락을 막아준다는 관점에서 이해하는 것이 좋을 것 같다.

4. 참고자료

어노테이션 런타임 관점

어노테이션 컴파일 관점

0개의 댓글

관련 채용 정보