본문 바로가기

Spring Boot

RestInterceptor 개발기 - build() 메서드 제거와 v1.0 릴리즈

RestInterceptor 가 뭐지?  ->  https://dev-allday.tistory.com/90


 

기존에 아쉬웠던 점들과 사용성을 개선해 1.0 버전을 릴리즈했다.

UML

 

가장 큰 변화 - build()

가장 큰 변화는 RestInterceptorRegistry를 통해 RestInterceptor를 등록할 때 반드시 호출해야 했던 build() 메서드가 사라졌다는 것이다.

 

기존에 RestInterceptorRegistryRestInterceptorRegistry 리스트를 필드로 가지고 있다가 build() 메서드가 호출되어야만 RestInterceptor의 설정들을 실제 InterceptorRegistry로 반영했다.

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final RestTestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // Create RestInterceptorRegistry from InterceptorRegistry
        RestInterceptorRegistry restInterceptorRegistry = new RestInterceptorRegistry(registry);

        restInterceptorRegistry.addInterceptor(testInterceptor)
                .addRestfulPatterns(RestfulPattern.of("/memos", HttpMethod.GET)); // GET /memos

        restInterceptorRegistry.build(); // 명시적으로 build() 를 호출해야 했음.
    }
}

 

더보기
public class RestInterceptorRegistry {

    private final InterceptorRegistry registry;
    private final List<RestInterceptorRegistration> registrations = new ArrayList<>();

    public RestInterceptorRegistry(InterceptorRegistry registry) {
        this.registry = registry;
    }

    public RestInterceptorRegistration addInterceptor(RestInterceptor restInterceptor) {
        RestInterceptorRegistration registration = new RestInterceptorRegistration(restInterceptor);
        registrations.add(registration);
        return registration;
    }

    public void build() {
        this.registrations.forEach(registration -> {
            RestInterceptor restInterceptor = registration.getRestInterceptor();

            registry.addInterceptor(restInterceptor)
                    .addPathPatterns(restInterceptor.restfulPatterns.stream()
                            .map(RestfulPattern::getPath)
                            .collect(Collectors.toList()))
                    .order(registration.getOrder());
        });
    }
}

 

 때문에 사용자는 명시적으로 build() 메서드를 호출해야 했고, 프로그래밍 관점에서 절대 좋은 코드가 아니었다.

 

이를 해결하기 위해 RestInterceptorRegistration을 InterceptorRegistration과 RestInterceptor의 Adaptor로 디자인했다.

HandlerInterceptor를 등록하는 역할을 하는 Spring의 InterceptorRegistration 은 Path를 기반으로 HandlerInterceptor를 등록한다. 하지만, RestInterceptor는 Path 가 아닌 RestfulPattern을 기반으로 동작한다.

 

더보기

RestInterceptor

public abstract class RestInterceptor implements HandlerInterceptor {

    protected List<RestfulPattern> restfulPatterns = List.of();

    @Override
    public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (isPreFlightRequest(request) || shouldSkip(request)) {
            return true;
        }
        return doInternal(request, response, handler);
    }
    
    // ....
}

 

 RestfulPattern을 add 하면 RestInterceptor의 restfulPatterns에 추가함과 동시에 InterceptorRegistration의 addPathPatterns() 메서드에 해당 RestfulPattern의 Path를 등록하도록 구현했다.

public class RestInterceptorRegistration {

    private final RestInterceptor restInterceptor;
    private final InterceptorRegistration registration;

    RestInterceptorRegistration(RestInterceptor restInterceptor, InterceptorRegistry registry) {
        this.restInterceptor = restInterceptor;
        this.registration = registry.addInterceptor(restInterceptor);
    }
    
    public RestInterceptorRegistration addRestfulPatterns(RestfulPattern... restfulPatterns) {
        return addRestfulPatterns(Arrays.asList(restfulPatterns));
    }
    
    public RestInterceptorRegistration addRestfulPatterns(Collection<RestfulPattern> restfulPatterns) {
        restInterceptor.restfulPatterns = new ArrayList<>(restfulPatterns);
        registration.addPathPatterns(restfulPatterns.stream()
                .map(RestfulPattern::getPath)
                .toList()
        );
        return this;
    }

    public RestInterceptorRegistration order(int order) {
        registration.order(order);
        return this;
    }
}

 

 

이를 통해 서로 호환되지 않던 InterceptorRegistration과 RestInterceptor를 연결할 수 있었다.

 

1.0 버전부터는 불필요한 build() 메서드를 사용할 필요가 없다.

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final RestTestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // Create RestInterceptorRegistry from InterceptorRegistry
        RestInterceptorRegistry restInterceptorRegistry = new RestInterceptorRegistry(registry);

        restInterceptorRegistry.addInterceptor(testInterceptor)
                .addRestfulPatterns(RestfulPattern.of("/memos", HttpMethod.GET)); // GET /memos
    }
}

 

 

 

Repository

https://github.com/Dh3356/rest_interceptor