Publish:

νƒœκ·Έ:

μΉ΄ν…Œκ³ λ¦¬:

μ–΄λ…Έν…Œμ΄μ…˜ μœ μ§€μ •μ±…

μ–΄λ…Έν…Œμ΄μ…˜μ„ μ–Έμ œκΉŒμ§€ μœ μ§€ν•  것인가

CLASS(default)

1
2
3
4
@Retention(value=RetentionPolicy.CLASS)
public @interface MyAnnotation {
  String value() default "κΈ°λ³Έκ°’μ‚¬μš©";   // μ–΄λ…Έν…Œμ΄μ…˜ μ‚¬μš©ν•˜λŠ” μͺ½μ—μ„œ λ”°λ‘œ κ°’ μ„€μ • 없이 κΈ°λ³Έκ°’μœΌλ‘œ μ‚¬μš©.
}

컴파일 κ³Όμ •κΉŒμ§€ μœ μ§€ν•˜κ² λ‹€. .class νŒŒμΌμ— μ–΄λ…Έν…Œμ΄μ…˜μ„ ν¬ν•¨ν•œλ‹€. λ°”μ΄νŠΈμ½”λ“œλ‘œ μ–΄λ…Έν…Œμ΄μ…˜ 확인 κ°€λŠ₯. κ·ΈλŸ¬λ‚˜ λŸ°νƒ€μž„μ— λ©”λͺ¨λ¦¬μ— λ‘œλ”©λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ¦¬ν”Œλ ‰μ…˜ API둜 μ•Œμ•„λ‚Ό 수 μ—†λ‹€.

SOURCE

1
2
3
4
@Retention(value=RetentionPolicy.SOURCE)
public @interface MyAnnotation {
  String value();
}

μ†ŒμŠ€ νŒŒμΌμ—λ§Œ 남긴닀. .class νŒŒμΌμ— 포함 μ•ˆλœλ‹€. 컴파일 μ‹œ μ œκ±°λœλ‹€. λ°”μ΄νŠΈμ½”λ“œλ‘œ 확인 λΆˆκ°€λŠ₯ν•˜λ‹€.

RUNTIME

1
2
3
4
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
  String value();
}

.class νŒŒμΌμ—λ„ ν¬ν•¨λ˜κ³ , μ‹€ν–‰ν•  λ•Œλ„ λ©”λͺ¨λ¦¬μ— λ‘œλ”©λœλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— λ¦¬ν”Œλ ‰μ…˜μœΌλ‘œ 정보λ₯Ό μ•Œμ•„λ‚Ό 수 μžˆλ‹€.

λ¦¬ν”Œλ ‰μ…˜μœΌλ‘œ μ–΄λ…Έν…Œμ΄μ…˜ 정보 κ°€μ Έμ˜€κΈ°

1
2
3
4
@MyAnnotation
public class MyClass {
  
}
1
2
Class<?> clazz = MyClass.class;
clazz.getAnnotation(MyAnnotation.class);

μ–΄λ…Έν…Œμ΄μ…˜ 적용 λ²”μœ„

@Target을 μ‚¬μš©ν•˜μ—¬ μ• λ…Έν…Œμ΄μ…˜μ„ 뢙일 수 μžˆλŠ” λ²”μœ„λ₯Ό μ œμ–΄ν•  수 μžˆλ‹€.

μ’…λ₯˜ λ²”μœ„
ElementType.TYPE 클래슀, μΈν„°νŽ˜μ΄μŠ€μ— 뢙일 수 μžˆλ‹€.
ElementType.FIELD ν•„λ“œ μ„ μ–Έ μ•žμ— 뢙인닀.
ElementType.METHOD λ©”μ†Œλ“œ μ„ μ–Έ μœ„μ— 뢙인닀.
ElementType.LOCAL_VARIABLE 둜컬 λ³€μˆ˜ μ•žμ— 뢙인닀.
{ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.FIELD} λ°°μ—΄ ν˜•νƒœλ‘œ μ—¬λŸ¬κ΅°λ°μ„œ μ‚¬μš©ν•˜κ²Œ ν•  수 μžˆλ‹€.
1
2
3
@Target({ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.FIELD})
public @interface MyAnnotation {
}

μ–΄λ…Έν…Œμ΄μ…˜ ν™œμš©

RetentionPolicy.RUNTIME μœ μ§€ 정책을 가진 μ–΄λ…Έν…Œμ΄μ…˜μ„ ν™œμš©ν•˜λ©΄ 싀행쀑인 μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ νŠΉμ • 정보λ₯Ό μΆ”μΆœν•  수 μžˆλ‹€. μ•„λž˜μ™€ 같이 λ‘œκ·ΈμΈν•œ μœ μ €μ˜ μ„Έμ…˜(httpSession) 체크λ₯Ό μ–΄λ…Έν…Œμ΄μ…˜ 기반으둜 λ°”κΏ€ 수 μžˆλ‹€.

μœ μ € μ„Έμ…˜ 정보 확인

1
2
3
4
5
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {

}

μš°μ„  μ–΄λ…Έν…Œμ΄μ…˜μ„ μ •μ˜ν•œλ‹€. ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ„ νŒŒλΌλ―Έν„° μ•žμ— 뢙일 수 μžˆλ„λ‘ @Target을 μ§€μ •ν•˜κ³ , λŸ°νƒ€μž„ μ‹œμ—λ„ λ©”λͺ¨λ¦¬ 상에 μ–΄λ…Έν…Œμ΄μ…˜ 정보가 μœ μ§€λ˜λ„λ‘ @Retention λ²”μœ„λ₯Ό μ§€μ •ν•œλ‹€. 이λ₯Ό 톡해 λ¦¬ν”Œλ ‰μ…˜μœΌλ‘œ μ–΄λ…Έν…Œμ΄μ…˜ 정보λ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.

이제 μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•΄ 컨트둀러둜 λ„˜μ–΄μ˜€λŠ” DTO(Data Transfer Object) 객체λ₯Ό κ²€μ¦ν•˜κ²Œ λœλ‹€.

HandlerMethodArgumentResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {

    private final HttpSession httpSession;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // νŒŒλΌλ―Έν„°μ— LoginUser μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄ μžˆλŠ”κ°€?
        boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
        // νŒŒλΌλ―Έν„° νƒ€μž…μ΄ SessionUser 인가?
        /* if(parameter.getParameterType == LoginUser.class) {return true} */
        boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
        // λ‘˜λ‹€ 참일 경우 true λ°˜ν™˜
        return isLoginUserAnnotation && isUserClass;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 
      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        // μœ„ supportsParameter λ©”μ†Œλ“œ true 이면 μ‹€ν–‰
        return httpSession.getAttribute("user");
    }
}

HandlerMethodArgumentResolverλ₯Ό κ΅¬ν˜„ν•˜λ©΄ 컨트둀러 μ§„μž… μ‹œ νŒŒλΌλ―Έν„°λ₯Ό μ „μ²˜λ¦¬ ν•  수 μžˆλ‹€. μœ„ μ½”λ“œμ—μ„œλŠ” μœ μ € 정보λ₯Ό λ°›λŠ” DTO 객체의 μ–΄λ…Έν…Œμ΄μ…˜ νƒ€μž…μ„ ν™•μΈν•˜κ³ , λ©”μ†Œλ“œμ˜ ν˜„μž¬ νŒŒλΌλ―Έν„° νƒ€μž…μ΄ SessionUser인지 ν™•μΈν•œλ‹€. λ§¨μœ„μ—μ„œ μ •μ˜ν•œ μ–΄λ…Έν…Œμ΄μ…˜μ˜ κ·œμΉ™μ— λ§žλŠ”μ§€ λ‹€μ‹œ ν™•μΈν•˜λŠ” 것이닀. supportsParameterκ°€ true λ₯Ό λ¦¬ν„΄ν•˜κ²Œλ˜λ©΄, resolveArgumentμ—μ„œ μ‹€μ œ μ„Έμ…˜ 정보λ₯Ό κ°€μ Έμ˜¨λ‹€.

WebConfig

1
2
3
4
5
6
7
8
9
10
11
12
@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
  private final LoginUserArgumentResolver loginUserArgumentResolver;
  private final UserAgentArgumentResolver userAgentArgumentResolver;

  @Override
  public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    resolvers.add(loginUserArgumentResolver);
    resolvers.add(userAgentArgumentResolver);
  }
}

μœ„μ—μ„œ μ •μ˜ν•œ HandlerMethodArgumentResolverκ΅¬ν˜„μ²΄λ₯Ό μ•± μ‹œμž‘ μ‹œ μΈμ‹ν•˜κΈ° μœ„ν•΄ WebMvcConfigurerμΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄ λ„£μ–΄μ€€λ‹€.

IndexController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RequiredArgsConstructor
@Controller
public class IndexController {

  private final PostsService postsService;
  private final HttpSession httpSession;  // 이제 μ„Έμ…˜μ—μ„œ λ°”λ‘œ 꺼내지 μ•Šκ³  μ–΄λ…Έν…Œμ΄μ…˜μ„ μ΄μš©ν•΄ κΊΌλ‚Έλ‹€.

  @GetMapping("/")
  public String index(Model model, @LoginUser SessionUser user, @UserInfo String useragent) {
    model.addAttribute("posts", postsService.findAllDesc());
    // λ‘œκ·ΈμΈν•œ μœ μ € μ„Έμ…˜ κ°€μ Έμ˜€κΈ°
    //  -> μ–΄λ…Έν…Œμ΄μ…˜ 기반(LoginUserArgumentResolver) 으둜 λ³€κ²½ μ‹œ 컨트둀러둜 μ „λ‹¬λ˜λŠ” νŒŒλΌλ―Έν„°μ—μ„œ(user) λͺ¨λ‘ 검증이 λλ‚œ μƒνƒœλ‘œ κ°€μ Έμ˜΄.
    //     - νŒŒλΌλ―Έν„°λŠ” μ„Έμ…˜μ—μ„œ κ°€μ Έμ˜΄(resolveArgument λ©”μ†Œλ“œ 리턴 λΆ€λΆ„. httpsession.getAttribute)
    //     - μ •μ˜ν•œ μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄μžˆλŠ”μ§€ / νƒ€μž… 체크.

    //        SessionUser user = (SessionUser) httpSession.getAttribute("user");

    if (user != null) {
      model.addAttribute("userName", user.getName());
    }
    model.addAttribute("userAgent", useragent);
    return "index";
  }

둜그인 ν•œ μœ μ €κ°€ 인덱슀 νŽ˜μ΄μ§€ μ ‘κ·Ό μ‹œ κ°„λ‹¨ν•˜κ²Œ μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ μ„Έμ…˜ 정보λ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.

1
2
3
  ... 
HttpServletRequest req = (HttpServletRequest) webRequest.getNativeRequest();
req.getHeader("User-Agent");

μ„Έμ…˜ μ •λ³΄λΏλ§Œ μ•„λ‹ˆλΌ 접속 정보 등을 κ°€μ Έμ˜€λŠ” μ½”λ“œλ₯Ό resolver 에 등둝해 μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€.

reference

λ°©λ¬Έν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€! λŒ“κΈ€,지적,ν”Όλ“œλ°± μ–Έμ œλ‚˜ ν™˜μ˜ν•©λ‹ˆλ‹€πŸ˜Š

λŒ“κΈ€λ‚¨κΈ°κΈ°