해당 포스트를 작성하려다가 문득..

 "도대체 바인더, 바인딩, 바인드 그 의미가 뭘까?"

에서 시작되어 진짜로 그 의미를 파악하고 왔다.

독자도 궁금하다면 대충 스-윽 보고 오면 좋을 것 같다 !

2022.08.04 - [웹개발/JAVA] - [바인딩(Binding)] 바인딩 이란

혹시...

Controller 메소드에 @ModelAttribute UserForm userForm 을 파라미터로 선언하고, jsp 단에 <form:spring modelAttribute="userForm"/> 스프링 폼태그를 사용해 본적이 있는가?

이때 컨트롤러단으로 요청이 들어오면 jsp에서 입력한 값이 modelAttribute 로 지정된 객체의 필드값에 매핑 저장되어 파라미터로 넘어오는 것을 볼 수 있다. 바로 여기서 들어온 요청에 대해 modelAttribute 로 선언된 객체의 필드값이 어떻게 매핑되는지 그 과정을 생각해 본 적이 있는 분!

.

.

.

.

.

메소드에 @ModelAttribute 를 선언했을 때 처리되는 과정은 다음과 같다.

1. 파라미터 타입의 객체를 하나 생성한다.

2. HTTP 요청에서 가져온 객체의 프로퍼티에 바인딩(값을 매긴다) 해준다.
   이 과정에서 각 프로퍼티에 맞게 타입을 변환해준다.
   만약 타입 변환 오류가 생기면 BindingResult 객체에 error 를 저장하여 컨트롤러로 넘겨준다.

 

(아~~ 너무 추상적이다..)

파라미터로 넘어온 친구들에 대해 프로퍼티 바인딩을 해준다.
즉, 각 프로퍼티에 맞게 타입을 변환해준다는 거군.

(그럼 '프로퍼티에 바인딩'한다는 건 머선 말이지..)

.

.

.

.

.

프로퍼티에 바인딩한다? - 2가지 방법

프로퍼티 바인딩이란 오브젝트의 프로퍼티(필드값)에 값을 넣는 행위를 말하는 것이다.

각각의 필드에 맞게 타입을 적절히 변환하고 프로퍼티의 커스텀메소드를 호출하는 것이다.

 

스프링에선 크게 두가지 프로퍼티 바인딩을 지원한다.

1. applicationContext.xml 의 설정파일로 Bean 을 정의할 때 사용한 <property> 태그이다.

해당 태그를 통해 빈의 프로퍼티에 값을 주입했었다.

예시

<!-- Hadler Mapping -->
<bean
   class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
   <property name="order" value="1" />
</bean>

<bean
   class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
   <property name="order" value="0" />
</bean>

2. Http request 파라미터를 모델 객체로 변환하는 경우이다.

.

.

여기서 잘 생각해보면, 저 value 에는 문자열이 아닌 Class 타입이 들어가는 경우도 있다.

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
   <property name="annotationClass" value="org.mybatis.spring.annotation.MapperScan"/>
   <property name="sqlSessionTemplateBeanName" value="sqlSession"/>
</bean>

이런 경우 value 에 문자열로 클래스명을 전달한다. 하지만 잘 바인딩 된다.

이는 스프링에서 제공되는 프로퍼티 바인딩 기능을 사용했기 때문이다.

스프링은 프로퍼티 바인딩을 위해 두 가지 API 를 제공한다고 한다.

 

PropertyEditor

스프링에서 제공하는 기본적인 프로퍼티 바인딩 API 이다. 

PropertyEditor 는 스프링 API가 아니라 자바빈 표준에 정의된 API이다.
GUI 환경에서 비주얼 컴포넌트를 만들 때 사용하도록 설계되었고, 기본적인 기능은 문자열과 자바빈 프로퍼티 사이의 타입 변환이다.
스프링은 이 PropertyEditor 를 문자열-오브젝트 상호변환이 필요한 XML 설정이나 HTTP 파라미터 변환에 유용하게 사용할 수 있다고 판단하여 이를 일찍부터 사용해왔다.

핵심은 이 디폴트 PropertyEditor들은 바인딩 과정에서 파라미터 타입에 맞게 자동으로 선정되어 사용된다는 것이다.

디폴트 프로퍼티 데이터에 등록되지 않은 타입을 파라미터로 사용하고 싶다면 직접 PropertyEditor 를 구현하여 등록하고 사용할 수 있다.

 

WebDataBinder

직접 구현한 에디터를 구현하기 전에 Controller 에서 메소드 바인딩하는 과정을 먼저 살펴보자.

AnnotationMethodHandlerAdapter 는 @RequestParam, @PathVariable, @ModelAttribute 와 같이 HTTP 요청을 변수에 바인딩하는 어노테이션을 만나면 먼저 WebDataBinder 라는 것을 만든다.

WebDataBinder 는 여기서 HTTP 요청 문자열을 파라미터로 변환하는 기능을 한다.

-> WebDataBinder (이 친구) 때문에 바로 이 글을 시작할 때 언급했던 ModelAttribute 를 선언한 메소드의 객체 필드에 매핑이 되는 것이다.

이때, 직접 구현한 PropertyEditor 를 사용하려면 이 WebDataBinder 에 직접 등록해줘야 한다.

근데 WebDataBinder 의 변환 과정이 외부로 노출되지 않으므로, 직접 등록해줄 방법은 없다.

그래서 스프링이 제공하는 WebDataBinder 초기화 메서드를 사용해야 한다.

.

.

.

.

@InitBinder

Controller 클래스에 아래와 같이 @InitBinder 어노테이션이 부여되고, WebDataBinder 를 매개변수로 받는 메소드를 하나 생성해 봅니다.

@Controller
public class Controller {
    // 모든 요청이 들어올때마다 해당 method 를 거친다.
    // 모든 컨트롤러 내에서 변환 하려면 ConfigurableWebBindingInitializer 를 설정해서 사용해야 한다.
    // 특정 컨트롤러 내에서만 변환 하려면 컨트롤러에 @InitBinder가 붙은 메서드를 작성하여 사용하면 된다.
    @InitBinder
    private void initBinder(WebDataBinder binder) {
        // , 구분자로 배열화하는 것을 방지한다.
        binder.registerCustomEditor(String[].class , new StringArrayPropertyEditor(null));
    }
    .
    .
    @RequestMapping("json/binder.do")
    public String binder(@RequestParam(value = "param", required = false) String[] param) {
     //...    
    }
 }

initBinder 메서드는 클래스내의 모든 메서드에 대해서 파라미터를 바인딩하기 전에 자동으로 호출된다.
바인딩 적용 대상은 @RequestParam, @PathVariable, @CookieValue, @RequestHeader, @ModelAttribute의 프로퍼티 이다.

기본적으로 PropertyEditor는 지정한 타입과 일치하면 항상 적용된다.
여기에 프로퍼티 이름을 추가 조건으로 주고, 프로퍼티 이름까지 일치해야만 적용되게 할 수 있다.
이러한 타입의 PropertyEditor는 이미 PropertyEditor가 존재할 경우 사용한다.
WebDataBinder는 바인딩 시 커스텀 PropertyEditor가 있을 경우 이를 선적용하고, 없을 경우 디폴트 PropertyEditor를 적용하기 때문이다.

.

.

.

.

.

 

+) 추가 예시 설명

binder.registerCustomEditor(String[].class , new StringArrayPropertyEditor(null));

해당 customEditor 를 등록해주지 않은 경우

@RequestParam 에 배열타입으로(String[ ]) 들어오는 파라미터에 쉼표(,) 구분자가 있는 경우 디폴트(PropertyEditor)가 적용되어 무조건  쉼표(,) 구분자(쉼표가 default인 듯)에 의해 배열화 된다.

 

registerCustomEditor 인자로 String[].class , new StringArrayPropertyEditor(null) 값을 넣으면,

String [] 배열 타입에 대해 어떤 구분자로도 배열화하지 않겠다고 선언하는 것이다. (null 자리의 인자값은 seperator임)

 

필자가 원하는 값은 파라미터에 쉼표가 존재하더라도 하나의 문자열로 보고 구분하지 않도록 하는 것이었기 때문에 위와 같이 적용해주었다.

 

[registerCustomEditor 사용전]

@InitBinder
private void initBinder(WebDataBinder binder) {
    //binder.registerCustomEditor(String[].class , new StringArrayPropertyEditor(null));
}

@RequestParam(value="to") String[] to

입력 값 "테, 스트"<test01@ttestt.kr>
기대 값 to[0] = ""테, 스트"<test01@ttestt.kr>"
결과 to[0]=""테"
to[1]="스트"<test01@ttestt.kr>"

 

, 쉼표 구분값으로 배열화된다.

 

[registerCustomEditor 사용후]

@InitBinder
private void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(String[].class , new StringArrayPropertyEditor(null));
}
입력 값 "테, 스트"<test01@ttestt.kr>
기대 값 to[0] = ""테, 스트"<test01@ttestt.kr>"
결과 to[0]=""테, 스트"<test01@ttestt.kr>"

, 로 배열화 하지 않음

, 쉼표로 배열화하지 않고 한 덩어리로 넘어온 것을 확인 할 수 있다.

 

 

참고 출처 : https://joont92.github.io/spring/%EB%AA%A8%EB%8D%B8-%EB%B0%94%EC%9D%B8%EB%94%A9%EA%B3%BC-%EA%B2%80%EC%A6%9D/

'웹개발 > Spring' 카테고리의 다른 글

[Mybatis] resultMap  (0) 2022.09.04
[Mybatis] Mybatis 기술의 탄생! (feat.ORM)  (1) 2022.07.30

도대체 개발에서 말하는 바인딩이란 뭘까?

"WebDataBinder, BindingResult, initBinder ...... 바인드... 그게 머선 말이고???????"

1. 본디 태초부터 돌아가 그 뜻을 알라!

영어 bind 의 사전적 의미

- (기본적으로) 묶다.

- (정보통신) 두 개의 정보를 서로 연결하는 작업.

- (오픈사전) 지정된 시간 동안 두 개 이상의 프로그래밍 개체 간에 연결을 만드는 것.

 

그러니까 본디 그 뜻을 보아하니.. 뭔가 연결시키고, 연관시키고, 하나로 꽉 묶고, 결합시키고 하는 건가보네?

 

개발적 언어의 의미

속성과 개체 사이 또는 연산과 기호 사이와 같은 연관이다. ( -_- 무슨 말? )
즉, 바인딩(binding) 이란 프로그램의 어떤 기본 단위가 가질 수 있는 구성요소의 구체적인 값, 속성을 확정하는 것(줄로 꽉 묶는다는 뜻 연상)을 말한다. ( 오케이, 앞에거 모르겠고. 일단 느낌은 "값을 매긴다, 확정한다"  요거 구먼. )

2. 예를 들어보자면~

프로그램의 기본 단위인 변수를 예로 들면,

int num = 123;

여기서 int 는 변수의 자료형, num 은 변수 이름, 123 은 변수의 자료값이다.

즉, 데이터 타입이 int 라는 것으로 바인딩되고,
a 라는 변수명에 바인딩 되고,
1 이라는 값이 바인딩 되는 것이다.

(아하 ~~ 너낌 와쒀. 한마디로 정리해서)

이름, 자료형, 자료값에 각각 num, int, 123 이라는 구체적인 타입, 이름, 값이 정해지고
메모리 할당하는 것 각각의 과정을 바. 인. 딩. 이라고 한다.

3. 두가지 바인딩

조금 더 깊이 들어가자면, 일반적으로 바인딩은 일어나는 시간에 따라 크게 정적 바인딩, 동적 바인딩으로 분류한다.

 

정적 바인딩(Static Binding)

컴파일 시간에 일어나며, 실행 중 변하지 않고 유지된다.

- 함수의 정적 바인딩은 컴파일 시간에 호출될 해당 함수의 주소가 결정되어 바인딩 된다.
 즉, 실행 파일에 호출할 함수가 위치한 메모리 주소가 이미 확정 기록된 것이다.

 

동적 바인딩(Dynamic Binding)

- 실행시간 (run time) 중에 일어나며, 프로그램 실행 도중에 변경이 가능하다.

- 말그대로, 실행 파일을 만들때 호출할 함수의 메모리 주소가 확정되지 않고, 이후 실제로 실행되는 그 시간에 호출할 함수의 주소가 결정된다. 

- 그렇기 때문에 이 주소를 저장할 공간을 미리 확보해둔다.

- 실행될지 안될지 확정되지 않았기에 일단, 해당 함수를 위해 저장공간을 할당해야한다는 점에서 메모리 관리에 비효율적이다.

(쏼라~쏼라~~ 메모리 관리에 효율적이냐 비효율적이냐를 말한것이군.. 아무튼 한줄 요약하자면~~)

실행 이전(컴파일될때)에 값이 확정되면 정적 바인딩 ,
 이후(진짜 코드가 실행될때)에 값이 확정되면 동적 바인딩인 것이다.

4. 결론

아주 쉽고 간단하게 말해서, 개발에서 말하는 바인딩은 값이 확정되어 최종적으로 값이 매겨지는 것을 말하는 것이다.

 

 

출처 : https://medium.com/pocs/%EB%B0%94%EC%9D%B8%EB%94%A9-binding-4a4a2f641b27

 

+ Recent posts