소개 배경
현재 진행중인 프로젝트에서 평소와 같이 필요한 기능을 구현하기 위해 Service Layer에서 CRUD를 제작하던 도중 항상 고정적으로 Import하게 되는 어노테이션들이 갑자기 거슬렸다.
설계되는 도메인에 따라 Service 클래스는 그만큼 증가되게 되는데 그에 따라 impot 문이 기하급수적으로 늘어나 클래스의 로딩시간이 지연되거나 코드의 전체적인 가독성이 떨어진다고 생각했다.
그래서 이를 줄이기위해 CustomAnnotation을 사용하는 방법을 생각했다.
기술 적용
CustomAnnotation을 제작하는 것은 생각보다 간단하다.
우선 @interface 타입의 클래스를 하나 생성한다. Custom Annotation(사용자 정의) 타입의 클래스를 생성하기 위해 사용된다.
public @interface CustomAnnotation {
그리고 @Traget을 설정한다. @Target 어노테이션의 경우 적용될 대상을 지정하는데 사용된다.
@Target 타입에는 여러가지가 존재하는데 몇 가지만 소개하자면 다음과 같다.
1. @Target(ElementType.TYPE) : 클래스,인터페이스,이넘 클래스에 적용하는 범위로 사용된다.
2. @Target(ElementType.METHOD) : 메소드에 적용하는 범위
3. @Target(ElementType.CONSTRUCTOR) : 생성자에 적용하는 범위
여기서 나는 Service Layer에서 자주 적용되는 @Service,@Transactional과 같은 어노테이션을 커스텀 할 것이기 때문에 @Target의 범위를 TYPE으로 지정하여 진행했다.
@Target({ElementType.TYPE})
💡 아니! @RequiredArgsConstructor 나 @Log4j2도 저는 많이 사용하는데요?
그렇다 해당 어노테이션들의 경우 나도 거의 필수적으로 사용하는 어노테이션이지만 @RequiredArgsConstrucotr의 경우 클래스의 생성자에 적용가능하기에 @Target의 범위를 CONSTRUCTOR로 지정해주어야 하고 @Log4j2의 경우는 @lombok.extern.log4j.Log4j2 is legal only on classes and enums 에러가 발생하게 된다.
이는 Lombok에서 제공하는 @Log4j2 어노테이션의 경우 클래스와 enum에 직접 적용해야하므로 컴파일 단계에서 오류가 발생하게된다.
@Target의 설정을 마친 뒤에는 @Retention 즉, 커스텀 어노테이션의 Life Cycle(유효 범위)를 설정해야한다.
나의 경우 실제 Runtime 환경에서 작성한 커스텀 어노테이션이 작동되어야하기에 인자값을 아래와 같이 주었다.
@Retention(RetentionPolicy.RUNTIME)
다른 인자값으로는 CLASS와 SOURCE가 존재한다.
CLASS의 경우 컴파일 단계까지만 유효하며, 런타임에는 반영되지 않으나 Logging 작업을 할 때 유용하다.
SOURCE의 경우 소스코드 범위까지 유효하며, 바이트 코드나 런타임에 영향을 전혀미치지 않는다.
이후 Custom에 필요한 어노테이션들을 기입해주면 Custom Annotation 사용이 성공적으로 완료된다.
실제 소스코드
package com.example.customantotation.custom;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional(readOnly = true)
public @interface EssentialServiceLayer {
}
위의 코드를 보면 알겠지만 해당 CustomAnotation 클래스에서는 import 문이 필수적으로 생기게된다. 하지만 Service Layer를 보게되면 가독성이 좋아지는 부분을 볼 수 있다.
package com.example.customantotation.member.service;
import com.example.customantotation.custom.EssentialServiceLayer;
import com.example.customantotation.member.dto.MemberDTO;
import com.example.customantotation.member.repo.MemberRepository;
import lombok.RequiredArgsConstructor;
@EssentialServiceLayer
@RequiredArgsConstructor
public class MemberService {
훨씬 간결해진 모습을 볼 수 있다.
결론
물론 예시에서는 사용되는 어노테이션의 수가 적으나 서비스의 규모가 커질 수록 반복되는 import는 상당히 많아질 것이다.
그렇기에 자주 사용되는 어노테이션이 존재한다면 이를 사용자 정의화시켜 클래스의 로딩시간을 단축하고 가독성을 높이는 코드를 지향해보자
'Spring-boot' 카테고리의 다른 글
OSIV(Open-Session-In-View) 사용 (0) | 2023.03.16 |
---|---|
profile을 적용하여 yaml 파일을 목적에 맞게 분리 (0) | 2023.03.06 |
엔티티의 생명주기와 변경감지, @Transactional 이슈 (0) | 2023.02.26 |
JPA 1차 캐시와 2차 캐시 (1) | 2023.02.26 |
SpringBoot 외부 Rest API 호출 방식(JAVA) - RestTemplate (0) | 2023.02.25 |