μ˜€λŠ˜μ€ μŠ€ν”„λ§ 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
  • μΉ΄μΉ΄μ˜€μŠ€ν† λ¦¬ κ³΅μœ ν•˜κΈ°