본문 바로가기

Backend/Spring

Spring Webflux Annotated Controller @ClientIp

먼저 Spring Webflux Annotated Controller 방식에서 client ip를 가져와볼게요.

그 다음 MVC 방식에서는 어떻게 설정하면 되는지도 다뤄볼게요.

@ClientIp 어노테이션을 만들어서 비즈니스 로직 단에서는 매우 간단하게 처리하고

IP를 받아오는 로직은 Resolver에 존재하도록 하겠습니다.

 

일단 목표는 Controller에서 아래와 같이 사용하는 거에요.

Controller

@RestController
@RequiredArgsConstructor
@Slf4j
public class BookController {

  private final BookService bookService;

  @GetMapping("/books/{bookId}")
  public Mono<BookResponse> getBook(@PathVariable String bookId,
                                    @ClientIp String clientIp) {
    log.info(clientIp);
    return bookService.getBook(bookId);
  }
}

@ClientIp

어노테이션을 하나 만들어줍니다.

 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClientIp {
  
}

ClientIpArgumentResolver

IP 받아오는 부분을 아주 간단하게 작성하였습니다.

혹시 IP가 이상하게 나올 경우나, 기타 상황에 따라 맞게 IP 받아오는 로직 잘 수정하시면 됩니다.

 

import org.springframework.core.MethodParameter;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.net.InetSocketAddress;

public class ClientIpArgumentResolver implements HandlerMethodArgumentResolver {

  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.getParameterType().equals(String.class) &&
        parameter.hasParameterAnnotation(ClientIp.class);
  }

  @Override
  public Mono<Object> resolveArgument(MethodParameter parameter, 
                                      BindingContext bindingContext, 
                                      ServerWebExchange exchange) {
    InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
    if (remoteAddress != null) {
      InetAddress address = remoteAddress.getAddress();
      return Mono.just(address.getHostAddress());
    }
    return Mono.empty();
  }
}

CustomWebFluxConfigurer

위의 ClientIpArgumentResolver를 추가해줍니다.

 

import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;

@Configuration
public class CustomWebFluxConfigurer implements WebFluxConfigurer {

  @Override
  public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
    configurer.addCustomResolver(new ClientIpArgumentResolver());
    WebFluxConfigurer.super.configureArgumentResolvers(configurer);
  }
}

MVC

MVC Controller에서도 비슷하게 구현할 수 있습니다.

위에서 사용한 HandlerMethodArgumentResolver와 WebFluxConfigurer가 reactive 패키지에 있는 인터페이스인데요.

이걸 MVC 환경에서 동일한 걸 찾아서 똑같은 방식으로 메서드 오버라이드하여 구현하시면 됩니다.

HandlerMethodArgumentResolver

import org.springframework.web.method.support.HandlerMethodArgumentResolver;

WebMvcConfigurer

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;