본문 바로가기

프로그래밍/Spring

Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (8) - HandlerMethodArgumentResolver 분석(4)

저번 글에서는 HandlerMethodArgumentResolver 인터페이스를 구현한 클래스인 XplatformArgumentResovler 클래스에서 @RequestDataSet 어노테이션이 붙었을 경우에 대한 처리를 설명했다. 이제 3개의 어노테이션 중 마지막인 @RequestVariable 어노테이션에 대한 설명을 하도록 하겠다. 먼저 이 부분에 대한 전체적인 골격에 대한 설명을 하도록 하겠다

 

else if(annotationClass.equals(RequestVariable.class)) {	// @RequestVariable 어노테이션에 대한 처리
	RequestVariable requestVariable = (RequestVariable)annotation;
	String variableName = requestVariable.name();
	Variable variable = variableList.get(variableName);
	if(StringUtils.hasText(variableName)) {					// 특정 변수 이름이 있기 때문에 해당 이름에 대한 값을 낸다
		...
	} else {										// 특정 변수 이름이 없으면 List, Set, Map 또는 VO로 매핑하는 것이기 때문에 오히려 이런 경우 자바의 데이터타입과는 매핑을 할 수 없다
		List<String> keyList = variableList.keyList();

		if(Collection.class.isAssignableFrom(type)) {
			...
		} else if(Map.class.isAssignableFrom(type)) {
			...
		} else {
			// 객체로 변환하는 과정에서 예외가 발생하면 지원하지 않는 타입이기 때문에 그에 대한 처리를 한다
			try {

				Object obj = type.newInstance();
				...
				result = obj;
			} catch(Exception e) {
				result = WebArgumentResolver.UNRESOLVED;
			}
		}
	}
}

 

이 시점에서 Xplatform의 Variable과 VariableList에 대한 구조를 다시 한번 상기시킬 필요가 있어서 한번 상기시키도록 하겠다. Xplatform에서 DataSet이 아닌 1개 이상의 key=value 형태의 값을 넘길때 VariableList 객체로 받게 된다. 이때 VariableList에서 key를 통해 Variable 객체로 변환된 value를 저장하고 있게 된다. 어찌보면 VariableList는 개념적인것만 놓고 보면 Java의 Map과 같은 구조이다. 이러한 VariableList 객체에 있는 값들을 Controller의 메소드에서 Java 클래스 객체로 받아야 하는데 이때 사용하는 것이 @RequestVariable 어노테이션이 된다.

 

이 @RequestVariable 어노테이션을 이용해서 데이터를 가져올때 Xplatform에서 데이터를 전송하기 위해 사용했던 key를 이용해서 데이터를 가져오는 경우와 key를 이용하지 않고 전송된 모든 데이터를 가져오는 경우로 사용 방법이 나뉘게 된다. 이런 구조인것을 알고 이제 이러한 구조를 어떤 구조의 데이터 객체로 변환할 것인지를 요약하자면 다음과 같다.

 

  1. Collection 인터페이스를 상속받은 인터페이스(List, Set) 또는 이를 구현한 클래스 객체
  2. Map 인터페이스 또는 이를 구현한 클래스 객체
  3. VO 형태의 클래스 객체

Collection 인터페이스와 같이 key가 없는 구조에서는 value 값을 Java의 Object 객체로 변환해서 넣게 된다. Map의 경우는 key가 있기 때문에 key를 사용해서 value를 Object 객체로 변환해서 넣게 된다. VO 형태의 클래스 경우엔 key와 같은 이름의 멤버변수에 value를 멤버변수의 해당 타입으로 변환해서 멤버변수에 값을 넣게 된다. 이러한 변환 개념을 이해해두고 구체적인 코드 설명에 들어가도록 하겠다. 먼저 List, Set 인터페이스의 경우에 대해 보도록 하겠다.

 

Collection collectionResult = null;
if(type.isInterface()) {
	if(type == List.class) {
		collectionResult = new ArrayList<Object>();
	} else if(type == Set.class) {
		collectionResult = new HashSet<Object>();
	}
} else {
	collectionResult = (Collection)type.newInstance();
}

for(String key : keyList) {
	collectionResult.add(variableList.getObject(key));
}

result = collectionResult;

 

먼저 Collection 인터페이스를 구현한 클래스 객체를 저장하기 위해 Collection 인터페이스 변수를 정한뒤 Controller 메소드의 파라미터에 설정한 타입이 인터페이스일 경우 List 인터페이스면 ArrayList 객체를, Set 인터페이스이면 HashSet 객체를 Collection 인터페이스 변수에 설정한다. 만약 인터페이스가 아닐 경우엔 파라미터에 설정한 타입이 클래스이기 때문에 newInstance 메소드를 이용해서 해당 클래스 객체를 생성한다. 그리고 VariableList 객체에 있는 key들을 이용해서 값을 꺼내와 Collection 인터페이스 변수에 설정된 객체에 넣고 그 객체를 최종 return 할 객체로 설정한다. 설명이 좀 복잡할 수도 있는데 요약하자면 VariableList 객체에서 key를 이용해 VariableList에 있는 값들을 꺼내와서 Collection 인터페이스 구현 객체에 값들을 저장하게 되는 것이다. 이 경우 주의할 것이 있는데 List일 경우엔 중복을 허용하기 때문에 key가 다르더라도 값이 같은 상황에서는 중복되어서 들어가게 된다. 그러나 Set일 경우엔 중복을 허용하지 않기 때문에 key가 다르더라도 값이 같은 상황에서는 중복을 허용하지 않고 1개만 들어가지게 된다.

 

그러면 Map 인터페이스 또는 이를 구현한 객체에 넣을 경우를 보도록 하자

 

Map<String, Object> mapResult = null;
if(type.isInterface()) {
	mapResult = new HashMap<String, Object>();
} else {
	mapResult = (Map<String, Object>)type.newInstance();
}

for(String key : keyList) {
	mapResult.put(key, variableList.getObject(key));
}

result = mapResult;

 

Map 인터페이스도 위에서 다루었던 Collection 인터페이스의 경우와 같은 방법이다. Controller 메소드 파라미터에 Map 인터페이스로 선언했을 경우엔 HashMap 객체를, 그렇지 않은 경우는 사용자가 지정한 클래스 객체를 생성한다. 그리고 Map은 key를 가지고 있기 때문에 VariableList 객체의 key를 Map의 key로 삼아서 VaraibleList에 저장되어 있는 값을 Object 타입으로 넣게 된다.

 

마지막으로 Collection, Map 인터페이스도 아닌 VO 클래스 객체의 경우를 보자

 

try {

	Object obj = type.newInstance();

	for(String key : keyList) {
		Field keyField = ReflectionUtils.findField(type, key);
		if(keyField == null) continue;
		ReflectionUtils.makeAccessible(keyField);

		Class<?> keyFieldType = keyField.getType();

		if(keyFieldType == int.class) {
			keyField.setInt(obj, variableList.getInt(key));
		} else if(keyFieldType == Integer.class) {
			keyField.set(obj, new Integer(variableList.getInt(key)));
		} else if(keyFieldType == long.class) {
			keyField.setLong(obj, variableList.getLong(key));
		} else if(keyFieldType == Long.class) {
			keyField.set(obj, new Long(variableList.getLong(key)));
		} else if(keyFieldType == float.class) {
			keyField.setFloat(obj, variableList.getFloat(key));
		} else if(keyFieldType == Float.class) {
			keyField.set(obj, new Float(variableList.getFloat(key)));
		} else if(keyFieldType == double.class) {
			keyField.setDouble(obj, variableList.getDouble(key));
		} else if(keyFieldType == Double.class) {
			keyField.set(obj, new Double(variableList.getFloat(key)));
		} else if(keyFieldType == Date.class) {
			keyField.set(obj, variableList.getDateTime(key));
		} else if(keyFieldType == String.class) {
			keyField.set(obj, variableList.getString(key));
		} else {
			keyField.set(obj, variableList.getObject(key));
		}
	}

	result = obj;
} catch(Exception e) {
	result = WebArgumentResolver.UNRESOLVED;
}

 

VO 클래스의 경우엔 VariableList의 key와 같은 이름을 가진 멤버변수를 찾아 여기에 해당 key에 대한 값을 넣는 것이다. 그러나 이 경우도 해당 key에 대한 멤버변수가 발견되지 않았을 경우 그냥 bypass 한뒤 다음 key에 대한 작업을 하도록 했다. 멤버변수를 찾으면 해당 멤버변수에 대한 타입을 알아낸뒤 그 타입에 맞춰서 VariableList에 저장되어 있는 값을 꺼내와 설정하게 된다. 기본적으로 Java에서 제공하는 타입으로 했고 만약 지정된 타입이 아닐 경우 Object 객체 그대로 넣도록 했다. 이러한 과정을 진행하면서 예외가 발생했을 경우 처리할 수 없다는 의미로 WebArgumentResolver.UNRESOLVED를 return 하게끔 설정했다. 

 

지금까지 Xplatform에서 넘어온 DataSet 및 VariableList를 Controller의 메소드 파라미터에서 정의한 타입으로 받아오기 위해 만든 HandlerArgumentResolver 인터페이스 구현에 대해 설명했다. Java Reflection 등 좀 복잡한 내용이 있지만 궁금한 부분은 디버그 모드로 돌려보면서 해당 변수안에 어떤 값이 들어가는지 추적해보면 이해하는데 큰 문제는 없을 것이다. 다음 글부터는 서버쪽 작업이 마쳐지면 이제 Xplatform 으로 데이터를 전송해야 하는데 이때 사용되는 ViewResolver 인터페이스 구현 클래스인 XplatformViewResolver 클래스와 이 클래스에서 사용하는 클래스인 XplatformView 클래스에 대해 알아보도록 하겠다.

 

 1. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (1) - 개요

 2. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (2) - 사전지식

 3. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (3) - HttpServletRequestWrapper

 4. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (4) - Spring Controller에서 하려는 것

 5. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (5) - HandlerMethodArgumentResolver 분석(1)

 6. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (6) - HandlerMethodArgumentResolver 분석(2)

 7. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (7) - HandlerMethodArgumentResolver 분석(3)

 8. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (8) - HandlerMethodArgumentResolver 분석(4)

 9. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (9) - XplatformView 분석

 10. Xplatform과 Spring Framework 연동 템플릿으로 보는 HandlerMethodArgumentResolver와 ViewResolver (10) - 예외처리