그전 블로그 https://velog.io/@mooh2jj/자바-리플랙션reflection에-대해서-알아보자
에서 확인해보았듯이, 리플렉션과 연관이 많이 된 기술이다.
Java 5(1.5)부터 등장한 기능으로, 한 마디로 요약하면 프로그램에 추가적인 정보를 제공해주는 메타 데이터이다.
여기서 메타 데이터
란 어플리케이션이 처리해야 할 데이터가 아니라 컴파일 과정과 런타임에서 코드를 어떻게 컴파일하고 처리할 것인지에 대한 정보를 말한다.
이 메타데이터를 잘 이용하면 비즈니스 로직과 분리하여
대상의 벨리데이션 체크, 값 주입, 역할 부여(기능 주입) 등을 수행
할 수 있어 체계가 잡혀있는 깔끔한 코드를 작성할 수 있게 된다.
어노테이션은 옵션에 따라 컴파일 전까지만 유효하도록 처리될 수도 있고, 컴파일 시기에 처리(컴파일러가 클래스를 참조할 때까지)될 수도 있고, 런타임 시기에 처리될 수도 있다.
Java의 리플렉션(실행중인 자바 클래스의 정보를 가져오는 기능)을 사용하여 런타임 시기에 어노테이션의 정보를 바탕으로 다양한 기능을 수행할 수 있으므로 어노테이션은 AOP(관점지향 프로그래밍)(코드를 주입, 런타임시)을 구성하는 데에 많은 도움을 줄 수 있다.
커스텀 어노테이션은 메타 어노테이션
을 사용하여 다음과 같은 구조를 가진다.
메타 어노테이션이란 커스텀 어노테이션을 구성할 때 시점, 위치등을 지정하기 위한 어노테이션이다.
어노테이션의 필드 타입은 enum, String이나 기본 자료형, 기본 자료형의 배열만 사용할 수 있다.
@Target({ElementType.[적용대상]})
@Retention(RetentionPolicy.[정보유지되는 대상])
public @interface [어노테이션명]{
public 타입 elementName() [default 값]
...
}
1) @Retention
컴파일러가 어노테이션을 다루는 방법을 기술, 어느 시점까지 영향을 미치는지를 결정, 주로 RUNTIME 을 많이 사용한다.
RetentionPolicy.SOURCE : 컴파일 전까지만 유효
RetentionPolicy.CLASS : 컴파일러가 클래스를 참조할 때까지 유효
RetentionPolicy.RUNTIME : 컴파일 이후 런타임 시기에도 JVM에 의해 참조가 가능(리플렉션)
2) @Target
어노테이션 적용할 위치 선택
주로, TYPE, FIELD, METHOD 를 많이 사용한다.
ElementType.PACKAGE : 패키지 선언
ElementType.TYPE : 타입 선언
ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언
ElementType.CONSTRUCTOR : 생성자 선언
ElementType.FIELD : 멤버 변수 선언
ElementType.LOCAL_VARIABLE : 지역 변수 선언
ElementType.METHOD : 메서드 선언
ElementType.PARAMETER : 전달인자 선언
ElementType.TYPE_PARAMETER : 전달인자 타입 선언
ElementType.TYPE_USE : 타입 선언
3) @Documented
해당 어노테이션을 Javadoc에 포함시킴
4) @Inherited
어노테이션의 상속을 가능하게 함
5) @Repeatable
Java8 부터 지원하며, 연속적으로 어노테이션을 선언할 수 있게 함
어토테이션 타입 선언은 특별한 종류의 인터페이스로 친다. 일반적인 인터페이스와 구분하기 위해
@interface를 붙여 선언
한다. 기술적으로 이 둘은 공백으로 분리가 가능하지만 스타일 문제로 분리하지 않는 것을 권장한다고 한다.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface CustomAnnotation {
public String value();
}
위의 커스텀 어노테이션을 JDK내장 javap 라는 역어셈블러를 사용해 확인해보면 java.lang.annotation.Annotation 클래스를 상속받음을 알 수 있고, 결과물은 다음과 같아진다.
public interface annotation.CustomAnnotation extends java.lang.annotation.Annotation {
public abstract java.lang.String value();
}
@interface는 자동으로 Annotation 클래스를 상속(확장)하며, 내부의 메소드들은 abstract 키워드가 자동으로 붙게 된다. 따라서 어노테이션 인터페이스는 extends절을 가질 수 없으며, 추가적으로 다음과 같은 제약이 존재한다.
(커스텀)어노테이션이 결과적으로 어떻게 이루어지는지는 알겠다. 그렇다면,
어노테이션이 해 주는 역할이 무엇인가? 라는 질문을 할 수 있겠다.
일단 어노테이션 그 자체로는 아무것도 해 주는 일이 없다.
어노테이션은 추가적인 정보를 제공해주는 메타데이터라고 했다.
어노테이션의 역할은 정보를 가짐으로써 끝났다고 볼 수 있다.
이제 이 정보를 이용하는 역할을 하는 다른 누군가가 필요한 것이다.
정보를 이용하려면 일단 어노테이션에 대한 접근이 필요하다.
클래스 메소드와 필드에 관한 어노테이션 정보를 얻고 싶으면, 리플렉션을 이용해서 얻어야 한다.
리플렉션을 이용해 가져온 정보를 토대로 다양한 작업을 수행할 수 있게 되는 것이다.
RestController + Swagger
https://mangkyu.tistory.com/130
자바 백엔드 개발자가 되려면 스프링, JPA 같은 기술은 필수로 배워야 한다. 그런데 처음 스프링이나 JPA 같은 기술을배우면, 기존에 자바 문법으로는 잘 이해가 안되는 마법 같은 일들이 벌어진다.
이러한 프레임워크들은 리플렉션과 애노테이션을 활용하여 다음의 "마법 같은" 기능들을 제공한다
- 의존성 주입 (Dependency Injection): 스프링은 리플렉션을 사용하여 객체의 필드나 생성자에 자동으로 의존
성을 주입한다. 개발자는 단순히 @Autowired 애노테이션만 붙이면 된다.- ORM (Object-Relational Mapping): JPA는 애노테이션을 사용하여 자바 객체와 데이터베이스 테이블 간의
매핑을 정의한다. 예를 들어, @Entity , @Table , @Column 등의 애노테이션으로 객체-테이블 관계를 설정한
다.- AOP (Aspect-Oriented Programming): 스프링은 리플렉션을 사용하여 런타임에 코드를 동적으로 주입하고,
@Aspect , @Before , @After 등의 애노테이션으로 관점 지향 프로그래밍을 구현한다.- 설정의 자동화: @Configuration , @Bean 등의 애노테이션을 사용하여 다양한 설정을 편리하게 적용한다.
- 트랜잭션 관리: @Transactional 애노테이션만으로 메서드 레벨의 DB 트랜잭션 처리가 가능해진다.
이러한 기능들은 개발자가 비즈니스 로직에 집중할 수 있게 해주며, 보일러플레이트(지루한 반복) 코드를 크게 줄여준다. 하지만 이 "마법"의 이면에는 리플렉션과 애노테이션을 활용한 복잡한 메타프로그래밍이 숨어 있다.
프레임워크의 동작 원리를 깊이 이해하기 위해서는 리플렉션과 애노테이션에 대한 이해가 필수다. 이를 통해 프레임워크가 제공하는 편의성과 그 이면의 복잡성 사이의 균형을 잡을 수 있으며, 필요에 따라 프레임워크를 효과적으로 커스터마이징하거나 최적화할 수 있게 된다.
😮 스프링이나 JPA 같은 프레임워크들은 이번에 학습한 리플렉션과 애노테이션을 극대화해서 사용한다.
리플렉션과 애노테이션을 배운 덕분에 여러분은 이런 기술이 마법이 아니라, 리플렉션과 애노테이션을 활용한 고급 프로그래밍 기법이라는 것을 이해할 수 있을 것이다. 그리고 이러한 이해를 바탕으로, 프레임워크의 동작 원리를 더 깊이파악하고 효과적으로 활용할 수 있게 될 것이다.
결론적으로, 자바 백엔드 개발자가 되기 위해 스프링과 JPA를 배우는 과정에서 리플렉션과 애노테이션은 더 이상 어렵고 낯선 개념이 아니라, 프레임워크의 동작 원리를 이해하고 활용하는 데 필수적인 도구임을 알게 될 것이다.
이러한 도구들을 잘 활용하면 개발자로서 한 단계 더 성장하게 될 것이며, 복잡한 문제 상황도 잘 해결할 수 있는 강력한 무기를얻게 될 것이다.