개발지식/Backend Engineering

전략 패턴 + AOP로 정책 로직을 분리한 설계

우루쾅 2025. 12. 28. 20:16
728x90
반응형
SMALL

솔루션 관련 게시글들을 찾아보다가 흥미로운 방식으로 문제를 해결한 내용을 찾아서 블로그 게시글로 정리해서 올려봅니다!

AOP 예시

1. 문제

업무 시스템에서는 많은 파일들이 생성되고 엑셀, PDF, 정산 리포트, 통계 자료 등 다양한 산출물들이 매일같이 만들어지고 내려지는데요. 이런 파일들의 대부분은 보안 대상으로 취급됩니다.

그렇기에 많은 시스템이 특정 보안 솔루션을 통해 파일을 암호화하거나 보호하게 됩니다.

 

하지만 보안 정책이 변경되는 순간부터 기존 방식의 수정해야될 부분들이 많아지는데요

기존에는 설치형(에이전트 기반) 보안 솔루션을 사용하고 있지만 신규 클라우드 기반 보안 솔루션이 도입되었지만,

모든 사용자들에게 동일한 보안 방식을 적용시킬 수 없었고 결국 하이브리드 상황을 맞이하게 됩니다.

 

특정 권한 - 신규 보안 방식

그 외 사용자 - 기존 보안 방식 유지

 

즉, 로그인한 사용자의 권한에 따라 파일 보호 방식이 달라져야 하는 구조 가 된 것입니다.

 

2. 해결책 탐색

① if-else 분기 처리

두가지의 조건을 나누어야 하기 때문에 제일 간편한 if-else 문을 적용시킬 생각을 할 수 있습니다.

if (user.isInternal()) {
    cloudEncrypt(data);
} else {
    agentEncrypt(data);
}

 

하지만 파일 다운로드 API 가 많아질수록 문제가 명확해졌습니다.

- 동일한 분기 로직이 여기저기 흩어짐

- 새로운 보안 방식이 추가되면 모든 코드를 수정해야됨

- 한 군데라도 빠뜨리면 암호화되지 않은 파일이 그대로 노출됨

 

위 방식으로는 동작은 하긴 하지만 확장성안정성으로는 좋지않습니다..

 

② 별도 서비스 분리

암호화 로직을 별도 서비스로 분리하는 방식입니다.

fileEncryptionService.encrypt(data, user);

 

중복은 줄어들지만, 여전히 문제는 남아있습니다.

- 모든 다운로드 코드에서 반드시 호출해야됨

- 새 API를 만들 때 호출을 빼먹으면 사고로 이어짐

- 보안 로직이 비즈니스 코드에 계속 포함됨

 

보안은 선택이 아닌 필수 사항이기 때문에 이 구조는 적합하지 않습니다.

 

③ 팩토리 패턴 사용

 

위와 같은 상황에서 필요한 것은 객체를 새로 생성하는 것이 아닌, 이미 존재하는 구현체 중 하나를 선택하는 것이여야하기 때문에,

- Spring 빈으로 관리되고

- 다른 빈들에 의존하며

- 공통 정책(AOP)의 대상이 되어야 함

 

그렇기 때문에 자연스럽게 떠오른 해법이 전략 패턴이였습니다.

 

3. 전략 패턴으로 보안 로직 추상화

전략 패턴의 핵심은 단순합니다

 

"변하는 부분을 인터페이스로 추상화하고, 실행 시점에 적절한 구현체를 선택한다."

 

① 전략 인터페이스

public interface FileProtectionStrategy {
    String getType(); // 전략 식별자
    byte[] protect(byte[] input, String fileName);
    byte[] unprotect(byte[] protectedData);
}

 

② 보안 방식별 구현체

@Component
public class CloudFileProtectionStrategy implements FileProtectionStrategy {

    @Override
    public String getType() {
        return "CLOUD";
    }

    @Override
    public byte[] protect(byte[] input, String fileName) {
        // 클라우드 API 호출
        return input;
    }
}

 

@Component
public class AgentFileProtectionStrategy implements FileProtectionStrategy {

    @Override
    public String getType() {
        return "AGENT";
    }

    @Override
    public byte[] protect(byte[] input, String fileName) {
        // 에이전트 라이브러리 호출
        return input;
    }
}

 

이러한 상식으로 새로운 보안 솔루션이 추가되더라도 기존 코드 수정 없이 구현체만 추가함으로써 문제를 해결할 수 있습니다.

 

③ 전략 선택기

Spring의 다중 빈 주입을 통해 모든 전략을 한번에 등록합니다.

@Component
public class FileProtectionStrategyResolver {

    private final Map<String, FileProtectionStrategy> registry;

    public FileProtectionStrategyResolver(List<FileProtectionStrategy> strategies) {
        this.registry = strategies.stream()
                .collect(Collectors.toMap(
                        FileProtectionStrategy::getType,
                        Function.identity()
                ));
    }

    public FileProtectionStrategy resolve(String type) {
        FileProtectionStrategy strategy = registry.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported type: " + type);
        }
        return strategy;
    }
}

 

- 애플리캐이션 시작 시 1회만 Map 구성

- 런타임 전략 조회

- OCP 를 만족

 

4. AOP 로 보안 적용 누락 원천 차단

전략 패턴만 사용할 경우 여전히 개발자가 매번 protect() 를 호출해야한다는 문제가 있습니다.

그렇기 때문에 AOP 를 통해 보안을 강제하였습니다.

 

① 어노테이션

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class ProtectFile

 

② Aspect

@Aspect
@Component
class ProtectFileAspect(
    private val resolver: FileProtectionStrategyResolver
) {

    @Around("@annotation(ProtectFile)")
    fun protect(joinPoint: ProceedingJoinPoint): Any {
        val raw = joinPoint.proceed() as ByteArray

        val securityType = currentUserSecurityType()
        val strategy = resolver.resolve(securityType)

        return strategy.protect(raw, "result.pdf")
    }

    private fun currentUserSecurityType(): String {
        // SecurityContext 기반 권한 판별
        return "CLOUD"
    }
}

 

③ 사용하는 쪽

@ProtectFile
fun downloadReport(): ByteArray {
    return reportService.createPdf()
}

 

5. 구조 다이어그램 설명

- 비즈니스 로직 ▶ 기본 값만 계산

- AOP ▶ 반드시 정책 적용

- 전략 ▶ 정책 차이만 책임

 

6. 정리

전략패턴 + AOP 조합의 장점은 명확합니다

- 정책은 계속 늘어나도 구조는 흔들리지 않음

- 실수로 빠질 가능성을 원천 차단

- 비즈니스 로직은 끝까지 깔끔하게 유지

 

이 패턴은 정교한 설계라기 보다는 사고를 줄이는 설계에 가깝습니다!

 

 

출처

올리브영 테크블로그 - https://oliveyoung.tech/2025-12-26/protection-switch/

728x90
반응형
LIST