[F-lab 모각코 챌린지 58일차] TIL

JeongheeKim·2023년 7월 28일

TIL

목록 보기
58/66

학습계획


  • proxy - CGLIB, Dynamic Proxy맛보기
  • 토비의 스프링 - 2.테스트

Today I Learned


Proxy란?

클라이언트의 요청을 대신 받는 대리인 역할

실제 오브젝트인 타겟은 프로젝트를 통해 요청을 받아 처리함

타겟은 자신의 기능에만 집중하고, 부가기능은 프록시에게 위임한다.

Proxy의 목적

  1. 클라이언트가 타깃에 접근하는 방법을 제어
  2. 타깃에 부가적인 기능을 부여하기 위한 것

Proxy의 장단점

  • 장점
    • 기존 코드를 변경하지 않고 새로운 기능 추가 가능
    • 기존 코드가 해야할 일만 유지
  • 단점
    • 코드의 복잡도가 증가하거나 중복 코드가 발생한다.

위의 단점을 동적 프록시를 통해 해결할 수 있다.

스프링에서는 ProxyFactoryBean에서 인터페이스 유무를 확인하여 인터페이스가 있으면 JDK Dynamic Proxy를 쓰도록 하고 없으면 CGLIB를 쓰도록 결정한다.

JDK Dynamic Proxy

  • 프록시 직접 구현 필요없음
  • Invocation Handler를 통해 중복 코드 제거
    • Invocation Handler를 재정의한 invoke를 구현해줘야 부가 기능이 추가된다.
  • JDK에서 직접 제공하므로 외부 라이브러리에 의존하지않고 proxy생성 가능
  • reflection API를 사용한다.
  • 인터페이스가 반드시 있어야한다.

CGLIB

  • 바이트 코드를 조작해서 프록시를 생성한다.
  • MethodIntercepter를 재정의한 Intercept를 구현해야 부가기능이 추가된다.

토비의 스프링 - 2.테스트

  • TDD
    • 테스트 코드를 먼저 만들고, 테스트를 성공하게 해주는 코드를 작성하는 방식의 개발
    • 테스트를 작성하고 이를 성공시키는 코드를 만드는 작업의 주기를 가능한 짧게 가져가야한다.
  • 픽스쳐(fixture)
    • 테스트를 수행하는 데 필요한 정보나 오브젝트를 픽스처라고 한다.
    • 일반적으로 픽스처는 반복적으로 사용되기때문에 @Before 메소드를 이용해 생성해 두면 좋다.
  • 테스트는 가능한 독립적으로 매번 새로운 오브젝트를 만들어 사용하는것을 원칙으로 한다.
  • 이는 스프링에서 제공하는 Junit을 이용하는 테스트 컨텍스트 프레임워크를 통해 도움 받을 수 있다.
    • @RunWith
      • Junit프레임 워크의 테스트 실행 방법을 확장 시 사용
      • Junit이 테스트를 진행하는 중에 테스트가 사용할 어플리케이션 컨텍스트를 만들고 관리하는 작업을 진행해준다.
    • Application Context
      • Application Context이 주입되는 이유는 스프링 어플리케이션 초기화 시 자기 자신도 빈으로 등록하기 때문

기본생성자 없이 역직렬화 테스트

@RequestBody를 통해 JSON 응답에 대한 역직렬화 시 기본 생성자가 필요하다고 알고있었다. @Builder은 기본 생성자를 만들지 않기 때문에 정상 동작하지 않을거라고 예상했었다.

하지만 아래와 같이 @Builder가 붙어도 Dto 클래스의 역직렬화는 문제 없이 동작 했었다.

@Builder
@Getter
public class MemberRequestDto {

    @Pattern(regexp = RegexUtils.MEMBER_ID_REGEX, message = "id형식에 맞지 않습니다.")
    private String id;

    @Pattern(regexp = RegexUtils.PWD_REGEX, message = "비밀번호 형식에 맞지 않습니다.")
    private String password;

    @Pattern(regexp = RegexUtils.USER_NAME_REGEX, message = "사용자 이름 형식에 맞지 않습니다.")
    private String userName;

    @Pattern(regexp = RegexUtils.EMAIL_REGEX, message = "이메일 형식에 맞지 않습니다.")
    private String email;
}

@Builder, @Getter로 생성된 코드(.class파일에서 확인)

@Builder를 통해 모든 필드를 가진 생성자가 만들어졌습니다.

MemberRequestDto(final String id, final String password, final String userName, final String email) {
        this.id = id;
        this.password = password;
        this.userName = userName;
        this.email = email;
    }

    public static MemberRequestDtoBuilder builder() {
        return new MemberRequestDtoBuilder();
    }

    public String getId() {
        return this.id;
    }

    public String getPassword() {
        return this.password;
    }

    public String getUserName() {
        return this.userName;
    }

    public String getEmail() {
        return this.email;
    }

❓기본 생성자도 없는데 어떻게 정상 동작한것일까?

위의 코드로 작성 후 디버깅 하면 BeanDeserializer클래스의 deserialize메소드에 걸리게 된다.
deserialize 메소드가 리턴하는 deserializeFromObject을 확인해보자

기본 생성자가 있을때는 _nonStandardCreation = false여서 if 문을 타지 않는데, 기본생성자가 없을 경우 아래 조건문을 타게 된다.

_nonStandardCreation 필드는 역직렬화 시 객체가 표준적인 방식(기본 생성자)으로 바인딩 하지 않은 경우 사용되는 flag라고 한다.

if (_nonStandardCreation)에 만족하게 되면 기본생성자가 없는 경우는 deserializeFromObjectUsingNonDefault(메소드 이름에서부터 알 수 있다.)를 타게 되고, 기본 생성자가 있는 경우 조건문을 타지 않고 아래 메소드(_valueInstantiator.createUsingDefault(ctxt);)를 타게 된다.

deserializeFromObjectUsingNonDefault 내부에서는 delegateDeser와 메소드와 PropertyBasedCreator타입의 _propertyBasedCreator 검사 조건식으로 나뉜다.

  • delegateDeser

    Deserializer that is used iff delegate-based creator is to be used for deserializing from JSON Object.
    NOTE: cannot be final because we need to get it during resolve() method (and not contextualization).

    대리자를 통한 역직렬화 방식 체크 ( 대리자가 뭘 뜻하는지 아직 모르겠다.)
  • _propertyBasedCreator

    If the bean needs to be instantiated using constructor or factory method that takes one or more named properties as argument(s), this creator is used for instantiation. This value gets resolved during general resolution.

    프로퍼티 기반(필드명을 비교한 방식) 역직렬화 방식 체크

_deserializeUsingPropertyBased 의 역할은 프로퍼티(필드 이름을 기반하여)를 사용하여 객체 역직렬화 하는 메소드이다.

해당 메소드를 통해 propName에 필드값과, value필드에 값이 셋팅 된다.

propNamevalue를 통해 역직렬화 된 Object를 리턴하고있다.

❗마무리

  • 기본생성자가 없으면 컴파일 시 자동으로 생성해 준다. 하지만 다른 매개변수가 있으면 자동으로 생성하지 않는다.
  • Jackson library는 기본생성자 외에도 팩토리 메소드를 통해 직렬화가 가능하다.
  • @RequestBody에 명시된 Dto클래스에 기본 생성자가 없이 매개변수를 포함한 생성자가 있어도 역직렬화가 가능한다.
    • 단, 2개이상의 매개변수일 경우부터가능하다.
    • 1개일경우 에러가 나는데 이경우는 좀 더 찾아봐야겠다.

[참고]

https://www.youtube.com/watch?v=MFckVKrJLRQ

0개의 댓글