[Java] Aop 적용법

정석환·2025년 4월 23일

Java Aop 적용법

1. Anotation 생성

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckTeam {

}

어노테이션을 생성하는 것은 간단하다.
다만 중요한게 Retention 과 Traget이다.

Retention(RetentionPolicy.RUNTIME)
→ 어노테이션을 런타임까지 유지하도록 설정한다. 즉, 실행 중 리플렉션 등을 통해 어노테이션 정보를 읽을 수 있다.

Target(ElementType.METHOD)
→ 이 어노테이션은 메서드에만 적용 가능하다는 뜻이다. 클래스, 필드 등 다른 요소엔 붙일 수 없다.

2. Aop의 기본 틀


@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class MyAspect {

    @Around("@annotation(내어노테이션)")
    public Object adviceMethod(ProceedingJoinPoint joinPoint, 내어노테이션 어노인스턴스) throws Throwable {
    
        // 1. 전처리 로직 (Before)
        Object result = joinPoint.proceed(); // 대상 메서드 실행

        // 2. 후처리 로직 (After)
        return result;
    }
}

기본 틀은 이렇다 그러면 어떠한 어노테이션이 적용 되었는지 보자.

Aspect 이 클래스가 AOP 역할을 한다는 선언
Component 스프링 빈으로 등록
Around 대상 메서드 실행 전후로 로직 삽입
annotation(적용할 어노테이션) 어노테이션에서 적용

그리고 parameter을 좀 보면 ProceedingJoinPoint 라는 것이 있는데
이는 대상 메서드를 가로 챌 수 있게 한다고 생각하면된다.

joinPoint.proceed()는 가로챈 원래 메서드를 실제로 실행한다.

이러한 형태가 기본적인 형태이며 ProceedingJoinPoint를 통해서 아래와 같은 행동을 할 수있다.

`getArgs()`	: 메서드에 전달된 매개변수들 가져오기
`getSignature()` : 메서드 이름, 클래스, 리턴타입 등의 정보 가져오기
`proceed()`	: 실제로 그 메서드를 실행 시킴 (즉, 본래 로직으로 진입)

그렇다면 이제는 예시를 보자.

3. Aop의 예시

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class TeamCheckAspect {

	//의존성 주입
    private final TeamService teamService;
    private final TeamMemberService teamMemberService;
    private final MemberService memberService;
	
    //어노테이션 설정
    @Around("@annotation(checkTeam)")
    public Object teamCheck(ProceedingJoinPoint joinPoint, CheckTeam checkTeam) throws Throwable {

		
        Long teamId = null;
		//joinPoint.getSignature()를 하면 메소드에 대한 정보를 가지고 있다.
        //MethodSignature로 캐스팅하면, 메서드 이름, 파라미터 정보 등을 더 쉽게 가져올 수 있다.
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        
        //실제로 실행될 메서드 객체를 가져온다.
		//이 method 객체로부터 파라미터 정보를 가져올 수 있다.
        Method method = methodSignature.getMethod();

		// 메서드의 파라미터중 id값을 가져오기 위한 작업
        //getParameters()는 Parameter 객체 배열을 리턴하고,
		//getName()은 각 파라미터의 변수 이름(예: teamId)을 가져온다
        for (int i = 0; i < method.getParameters().length; i++) {

            String parameterName = method.getParameters()[i].getName();
			
            //파라미터의 이름이 teamId이면 Teamid를 할당 해준다.
            if (parameterName.equals("teamId")) {
                teamId = (Long) joinPoint.getArgs()[i];
                break;
            }

        }
		
        // team id가 없다면 예외 처리
        if (teamId == null) {
            throw new KanbanBoardTeamIdNotProvidedException(ExceptionMessage.Kanbanboard.KANBANBOARD_TEAM_ID_NOT_PROVIDED);
        }

        //Security Util을 이용해서 멤버의 id를 가져온다.
        Long currentMemberId = SecurityUtil.getCurrentMemberId();

        //Member을 조회한다.
        Member member = memberService.getByMemberId(currentMemberId);

        //Team을 조회한다.
        Team team = teamService.getTeamById(teamId);
        //Team Member을 조회한다. 아직 가입이 안된지 확인을 위함
        TeamMember teamMember = teamMemberService.getTeamMemberByMemberAndTeam(member, team);
		//team에 가입되어 있지 않다면 기존 메서드를 실행한다.
        if (teamMember != null) {
            return joinPoint.proceed();
        }
        
        //아니면 예외 처리
        throw new SecurityException("접근 권한이 없습니다. " + " 팀에 소속되지 않았습니다.");

    }

}

필자는 위의 코드와 같이 member을 Team에 초대하는데 기존 teamMember로 가입이 되어 있지 않다면 예외 처리를 하고 가입이 되어있다면 기존 메서드를 실행 할 수 있게 하였다.

4. 왜 aop를 사용하는가?

  1. 공통된 로직을 중복 없이 관리하기 위해
  2. 핵심 비즈니스 로직과 분리
  3. 유지보수와 확장성 향상
profile
자바,스프링 백엔드 개발자를 꿈꾸는 초보아빠

0개의 댓글