Framework/Spring

[Spring] OOP와 비교하여 AOP 공부

JM Lee 2024. 4. 17. 21:00
728x90
OOP 복습

 

OOP(객체 지향 프로그래밍)은 객체를 중심으로 프로그래밍을 진행하는 것이다. 공통된 목적을 띈 데이터와 동작을 묶어 하나의 객체로 정의하는 것이 핵심으로, 특징으론 캡슐화(데이터와 그 데이터를 처리하는 메서드를 하나의 단위로 묶어), 은닉화(캡슐화의 목표. 내부 구조는 private하게 감춰두고 외부에서 조작할 수 있는 정보만 public으로 공개), 추상화(객체의 공통된 특성을 추출하여 모델링하는 과정), 상속(코드의 재사용성이 증가하고, 클래스 간의 계층 구조를 형성), 다형성( 동일한 메서드명이지만 다른 동작을 수행할 수 있도록 하는 기능), 동적 바인딩(다형성을 지원하는 매커니즘)이 있다.

 

Spring에서도 역시 MVC 구조는 @Controller, @Service, @Repository와 같이 관심사 별로 계층을 나눠 객체를 관리하게 된다.

 

그러나 AOP를 공부하게 된 계기로는 OOP의 단점을 느꼈기 때문이다.

 

그 중 가장 잘 느껴진 단점이 관심사의 분리, 프로그램을 개발할 때 특정 관심 영역(예: 트랜잭션 관리, 보안, 로깅 등)에 대한 코드가 서로 섞이는 것을 방지하여 코드의 가독성, 유지보수성, 재사용성을 향상시키는 것을 의미하는데, 이 분리가 OOP 프로그래밍의 경우에는 나타나지 않는다. 트랜잭션, 보안, 로깅 등은 개발자에게 있어 분명 필요한 부가 기능이지만, 객체에 같이 캡슐화되면 문제가 있는 기능이다. 비즈니스 클래스에 핵심 기능과 부가 기능이 같이 들어가면 비즈니스 코드 파악이 어렵게 된다.

 

AOP의 적용

 

AOP는 이러한 단점을 보완하기 위해 적용하는 방식이다. AOP(Aspect-Oriented Programming)은 관점 지향 프로그래밍으로, 프로그램을 핵심 기능과 공통 기능으로 분리한다. 공통 기능은 여러 모듈에 걸쳐 반복적으로 나타나는 기능으로, 로깅, 보안, 트랜잭션 관리 등이 있다. AOP는 이러한 공통 기능을 '관점'이라는 단위로 모듈화하고, 핵심 기능과 분리된 모듈로 적용할 수 있다. 이를 통해 핵심 기능에 영향을 주지 않으면서도 공통 기능을 추가하거나 변경할 수 있다. 스프링 부트는 AOP를 지원하여 관심사의 분리를 통해 모듈 간의 결합도를 낮추고, 코드의 재사용성과 유지보수성을 향상시킨다.

 

아래는 AOP로 수정하기 전 OOP만을 적용한 코드를 구현해 보았다.

public class User {
    private boolean authenticated;
    private List<String> roles;

    // 인증 상태 확인
    public boolean isAuthenticated() {
        // 인증 확인 로직
    }

    // 권한 검사
    public boolean hasRole(String role) {
        // 권한 검사 로직
    }

    // 다른 비즈니스 로직 메서드
    public void someBusinessLogic() {
        if (isAuthenticated() && hasRole("ADMIN")) {
            // 특정 작업 수행
        }
    }
}

 

한 눈에 봐도 객체를 둘러싼 기능들이 뒤죽박죽임을 알 수 있다. 인증을 확인하고 권한을 검사하는 로직들이 비즈니스 로직들과 뒤엉켜있다. 이를 해결하기 위해 지금의 AOP를 적용한 코드가 있다.

 

@Aspect
public class SecurityAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void authenticate(JoinPoint joinPoint) {
        // 인증 확인 로직
    }

    @Around("execution(* com.example.service.*.*(..))")
    public Object authorize(ProceedingJoinPoint joinPoint) throws Throwable {
        // 권한 검사 로직
        return joinPoint.proceed();
    }
}

 

메서드 실행 전 authenticate 로직을 수행하고, 메서드 실행 전후 authorize 로직을 수행하게끔 @Aspect(관점)를 통해 구분해두었다. 여기서 알아둘 수 있는 점은 Annotation들이 AOP의 특징 중 하나라는 것이다. 위에 @Config, @Interface, @Aspect 등으로 관심사를 표시하여 잘 모듈화했음을 보여준다. 우리가 왜 config, service, controller 등의 패키지를 나눠설정하는지를 공부하면서 깨달았다. 또한 이렇게 패키지,모듈화하면 결국 새로운 로직을 짤 때 불필요한 재사용 없이 딱 필요한 것만 재사용할 수 있게 된다.