์˜ค๋Š˜์€ ์Šคํ”„๋ง mvc ์—์„œ ์ œ๊ณตํ•˜๋Š” ArgumentResolver๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— Model, ServletRequest ๋“ฑ๋“ฑ ์ •์˜๋งŒ ํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” ์ด ArgumentResolver ๋•๋ถ„์ธ๋ฐ์š”, ์ปค์Šคํ…€ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

 

์ž๋ฃŒ๋ฅผ ์ฐพ๋‹ค ๋ณด๋‹ˆ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ณ  ์•ˆ๋ถ™์ด๊ธฐ๋„ ํ•˜๋Š”๋ฐ

์ œ ์ƒ๊ฐ์€ String, Integer ๋“ฑ ๋ฒ”์šฉ์ ์ธ ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ์‹๋ณ„์„ ์œ„ํ•ด ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๊ณ 

ํด๋ž˜์Šค๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„๋œ๋‹ค๋ฉด ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๋Š” ๊ฒƒ์€ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ช‡๊ฐ€์ง€ ์ถ”๊ฐ€ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

// build.gradle
// apache lang3์™€ user-agent-parser๋ฅผ ํ•จ๊ป˜ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

plugins {
    id 'org.springframework.boot' version '2.2.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.larry.blog'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    // https://mvnrepository.com/artifact/com.github.ua-parser/uap-java
    compile group: 'com.github.ua-parser', name: 'uap-java', version: '1.4.3'
    // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
    compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
}

test {
    useJUnitPlatform()
}

 

Ip๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ๊ธฐ๋Šฅ๊ณผ User-Agent ์ •๋ณด๋ฅผ ํŒŒ์‹ฑํ•œ ์ •๋ณด ๋‘ ๊ฐ€์ง€๋ฅผ ๊ฐ๊ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋‘ ๊ฐœ์˜ ArgumentResolver๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. 

public class ClientArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(Client.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Parser parser = new Parser();
        return parser.parse(webRequest.getHeader("User-Agent"));
    }
}


public class ParsedIpArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(ParsedIp.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();

        String ipAddress = request.getHeader("X-Forwarded-For");

        return StringUtils.defaultString(ipAddress,
                parameter.getParameterAnnotation(ParsedIp.class).defaultValue());
    }
}

 

 

๋ณดํ†ต ์›น์„œ๋ฒ„๋ฅผ ํ”„๋ก์‹œ๋กœ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ X-Forwarded-For๋กœ Ip๋ฅผ ๊ฐ€์ ธ์˜ค๋ฏ€๋กœ (์•„๋‹Œ ๊ฒฝ์šฐ๋„ ๋งŽ์Šต๋‹ˆ๋‹ค! IP๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”๊ฐ€ ํฌ์ŠคํŒ… ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.^^)

ํ—ค๋”๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋งŒ์•ฝ ์—†๋‹ค๋ฉด ๊ธฐ๋ณธ ๊ฐ’์„ ์ฃผ๋„๋ก ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ParsedIp {

    String defaultValue() default "UNKNOWN";

}

 

์Šคํ”„๋ง MVC Configuration ์— ๋“ฑ๋กํ•ด ์ค๋‹ˆ๋‹ค.

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        argumentResolvers.add(new ParsedIpArgumentResolver());
        argumentResolvers.add(new ClientArgumentResolver());
    }

}

 

์—ฌ๊ธฐ๊นŒ์ง€ ํ–ˆ๋‹ค๋ฉด ์‚ฌ์šฉํ•˜๋Š” ์ผ๋งŒ ๋‚จ์•˜์Šต๋‹ˆ๋‹ค!

@RestController
@Slf4j
public class MyController {

    @GetMapping
    public ResponseEntity<Void> ipTest(@ParsedIp(defaultValue = "Hello ip") String ip,
                                       Client client) {
      log.info("ip address: {}", ip);
      log.info("client: {}", client.toString());
      return ResponseEntity.ok().build();
    }

}

์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ์œ„์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๋“ฑ๋กํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

๊ฐ„๋‹จํžˆ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์š”์ฒญ์„ ํ•ด๋ณด๋ฉด (๋””๋ฐ”์ด์Šค๋Š” ์•„์ดํŒจ๋“œ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.)

์•„๋ฌด๊ฒƒ๋„ ํ•œ๊ฒŒ ์—†์œผ๋‹ˆ ๋ฐฑ์ง€์ฃ 

 

ํŒŒ์‹ฑ๋œ ๊ฒฐ๊ณผ๋ฌผ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•ด์„œ ๊ฐ์ž์˜ ํ”„๋กœ์ ํŠธ์— ์œ ์šฉํ•˜๊ฒŒ ์ ์šฉํ•˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค~๐Ÿ‘๐Ÿ‘

  • ๋„ค์ด๋ฒ„ ๋ธ”๋Ÿฌ๊ทธ ๊ณต์œ ํ•˜๊ธฐ
  • ๋„ค์ด๋ฒ„ ๋ฐด๋“œ์— ๊ณต์œ ํ•˜๊ธฐ
  • ํŽ˜์ด์Šค๋ถ ๊ณต์œ ํ•˜๊ธฐ
  • ๋ผ์ดํ”„์ฝ”๋ฆฌ์•„ํŠธ์œ„ํ„ฐ ๊ณต์œ ํ•˜๊ธฐ
  • shared
  • ์นด์นด์˜ค์Šคํ† ๋ฆฌ ๊ณต์œ ํ•˜๊ธฐ