티스토리 뷰

반응형

Spring MVC 에서는 REST API 을 지원하기 위해 Jackson 라이브러리를 이용하여 response 을 Json 형태로 변경하여 제공하는 기능을 포함하고 있습니다. 또한 @RequestBody 을 이용해 @RequestMapping 으로 선언된 method 의 매개변수로 Json 으로 된 body 을 POJO 로 받을 수도 있습니다. 이러한 처리를 위해 Spring 은 Json 혹은 XML 등의 여러 형태의 값을 자동으로 처리하기 위해 MessageConverter 을 등록해두는데, HttpMessageConverter 인터페이스를 구현한 것을 개발자가 상속받아 변경하여 사용할 수 있습니다. Jackson 은 MappingJackson2HttpMessageConverter 을 제공하는데, 이 class 을 상속받아 수정하게 되면 개발자가 원하는 동작을 할 수 있도록 수정할 수 있습니다.


기본적으로는 아래와 같이 @Configuration 으로 class 을 등록하여 사용할 수 있습니다.


@Configuration
public class MessageConverterConfig extends MappingJackson2HttpMessageConverter {

	private final HttpServletRequest request;

	@Autowired
	public MessageConvertorConfig(HttpServletRequest request) {
		this.request = request;
	}

}


이 때, 위의 예제와 같이 request 을 주입받아 필요할 경우 사용할 수 있음을 알 수 있습니다.




제가 가장 많이 사용하는 경우 HTTP 요청 시 body 영역에 form 이 아닌 Json 형태로 전송할 데이터를 보내는 것을 처리하는 것입니다. 이를 위해 read() 을 상속 받아서 수정을 해줘야 합니다. MappingJackson2HttpMessageConverter 소스를 구해서 read() 가 어떻게 구현되어 있는지 확인해서 수정할 수 있겠지만, 간단하게 어떤 식으로 수정할 것인지 확인해보겠습니다.


	@Override
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) {

		JavaType javaType = this.getJavaType(type, contextClass);
		
		return this.objectMapper.readValue(inputMessage.getBody(), javaType);

	}


가장 기본적인 형태는 위와 같습니다. JavaType 을 이용해서 Jackson 이 사용자가 요구하는 형태의 POJO 에 body 내용을 변환하도록 구현할 수 있습니다.


여기서, 요청을 보내는 쪽이 웹 브라우져가 아니라 모바일 기기 등일 경우 본문 내용을 암호화해서 보내는 경우를 감안해서 확장해보겠습니다. 모바일 기기가 어떠한 암호화를 한 뒤 본문 내용을 보내고, 암호화 여부를 HTTP header 에 ISCRYPT 라는 key 로 1 이냐 아니냐로 구분해서 보낸다고 한다면 다음과 같이 처리할 수 있습니다.


	@Override
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) {

		JavaType javaType = this.getJavaType(type, contextClass);

		// Request Header 에 ISCRYPT 가 1 일 경우 암호화 전송
		String isCrypt = request.getHeader("ISCRYPT");
		if (!Strings.isNullOrEmpty(isCrypt) && isCrypt.equals("1")) {

			// 암호화된 메세지일 경우 별도 처리
			String message = CharStreams.toString(new InputStreamReader(inputMessage.getBody()));

			byte[] result = cipher.doFinal(Base64Utils.decodeFromString(message));  // 여기에 복호화될 부분이 구현되면 됩니다.

			return this.objectMapper.readValue(result, javaType);

		}

		return this.objectMapper.readValue(inputMessage.getBody(), javaType);

	}


암호화가 되지 않았다면 body 을 그대로 변환하고, 암호화가 된 것이라면 별도의 복호화 처리(여기서는 이해를 돕기 위해 Java 암복화화를 위한 Cipher 로 처리하는 형태를 남겨놨습니다)를 내부에서 구현하면 됩니다. 그리고 그렇게 복호화된 구분을 받아 Jackson 에서 JavaType 에 맞게 다시 변환하는 방식입니다.


response 구현도 마찬가지이지만, 한가지 주의할 점이 있습니다. request 에서는 Json 이 POJO 로 저장되는 동일한 형태로 구현하였지만, response 로 사용자에게 결과를 전송할 때에는 암호화가 되지 않으면 Json 으로 전송되어야 하나, 암호화가 된 경우라면 String 으로 전송이 된다는 것입니다. 그렇기 때문에 역시 HTTP header 에 암호화 여부를 추가해주고 두 경우에 따라 다른 처리가 필요하다는 것입니다.

아래 예제 코드를 통해 차이점을 확인하고 구현하면 됩니다.


	@Override
	protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) {

		// Request Header 에 ISCRYPT 가 1 일 경우 암호화 전송
		String isCrypt = request.getHeader("ISCRYPT");
		if (!Strings.isNullOrEmpty(isCrypt) && isCrypt.equals("1")) {

			// JSON text 생성
			String message = objectMapper.writeValueAsString(object);

			byte[] result = Base64Utils.encode(cipher.doFinal(message.getBytes()));

			HttpHeaders header = outputMessage.getHeaders();

			header.add("ISCRYPT", "1");

			OutputStream output = outputMessage.getBody();

			output.write(result);
			output.flush();

			return;

		}

		outputMessage.getHeaders().add("ISCRYPT", "0");

		super.writeInternal(object, type, outputMessage);

	}


이 외에도 본인이 추가해야할 것이 있다면 그 코드를 위와 같이 추가함으로써 모든 요청과 응답을 제어할 수 있습니다.


반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
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 26 27
28 29 30
글 보관함