▸Spring MVC/기본 문법

스프링 Restful 서비스(API)_API 구현(XML) [3/3]

코데방 2020. 3. 21.
728x90
- Develop OS : Windows10 Ent, 64bit
- WEB/WAS Server : Tomcat v9.0
- DBMS : MySQL 5.7.29 for Linux (Docker)
- Language : JAVA 1.8 (JDK 1.8)
- Framwork : Spring 3.1.1 Release
- Build Tool : Maven 3.6.3
- ORM : Mybatis 3.2.8

 

Restful API에 대한 기본 개념과, JSON을 이용한 구현 방법은 이전글을 참조하시면 됩니다.

 

[Spring MVC/- 기본 문법] - 스프링 Restful 서비스(API)_기본 개념 [1/3]

[Spring MVC/- 기본 문법] - 스프링 Restful 서비스(API)_API 구현(JSON) [2/3]

 

 

 


 

 

스프링에서 REST 방식을 지원할 때 가장 기본이 되는 데이터 형식은 JSON입니다. 그래서 JSON을 사용해 데이터를 교환할 때는 데이터 타입이 JSON이라는 것을 굳이 명시할 필요가 없습니다. 아무 설정이 없으면 요청 내용이 Content-Type이 JSON인지 아닌지 확인하거나, 응답할 때 Content-Type을 JSON으로 설정해서 보내줍니다. 이 과정은 서블릿의 핸들러와 메세지 컨버터가 자동으로 진행합니다. 복잡한 과정은 다른 글에서 추가로 정리하겠습니다.

 

하지만 XML을 사용하기 위해서는 해당 데이터가 XML 형식이라는 것을 명시해줘야 합니다. XML 데이터를 받거나 전송할 VO 클래스에 어노테이션을 통해 이 객체의 변환 형식은 XML이라는 것을 표시해주면 스프링이 이를 인지하고 처리해줍니다. VO 객체에 어노테이션을 추가하는 것 외에는 JSON을 사용할 때와 동일한 방법으로 사용하면 됩니다.

 

참고로 자바 객체를 XML로 변환하는 작업을 마샬링(Marshalling), 그 반대를 언마샬링(UnMarshalling)이라고 부릅니다. 

 

 

 


 

 

[ XML 변환용 VO(DTO) 클래스 만들기 ]

 

1. XML의 루트 태그를 의미하는 @XmlRootElement(name="이름") 어노테이션 적용 (클래스 선언)

 

XML의 태그는 계층별로 묶여있으므로, 클래스에다가 루트 계층의 태그를 설정해줍니다. 만약 name을 명시하지 않으면 클래스명이 디폴트로 적용됩니다.

@XmlRootElement(name ="user")

 

 

2. XML의 직렬화 범위를 결정하는 @XmlAccessorType(Type) 어노테이션 적용 (클래스 선언)

 

직렬화에 대한 내용은 아래 링크를 참조하시면 됩니다.

2019/12/20 - [JAVA/- 기본 문법] - 직렬화와 역직렬화 (Serializable)

 

@XmlAccessorType(XmlAccessType.NONE)

 

Serializable 인터페이스를 상속받아 자바 객체를 직렬화/역직렬화 한 것처럼, XML을 사용해서 VO객체를 직렬화/역직렬화 할 수 있습니다. 간단히 직렬화란 자바 객체가 가지고 있는 데이터를 외부에 그대로 옮기는 것이고, 역직렬화는 그렇게 옮겨진 데이터를 가지고 와서 다시 자바 객체로 만들어 사용하는 것입니다.

 

@XmlAccessorType 설정은 이 VO 객체가 가진 데이터를 어느 범위까지 직렬화시킬지 결정하는 항목입니다. 그냥 XML로 변환할 데이터 범위를 정하는 방식이라고 보면 됩니다. Jaxb 어노테이션이라고 지칭한 것은 밑에서 나올 @XmlAttribute와 @XmlElement 입니다. 길어서 그냥 아래와 같이 기입했습니다.

 

JAXB는 자바 SE에 내장되어 있는 XML 관련 라이브러리입니다. Javax.xml.bind 패키지를 줄여서 JAXB로 부르는거 아닐까 생각합니다. ㅎㅎ

 

직렬화 대상 필드가 적을 경우와 많을 경우 등을 잘 고려해서 사용하면 될 것 같습니다. 

 

상수 설명
XmlAccessType.NONE - Jaxb 어노테이션을 설정한 필드만 직렬화
XmlAccessType.FIELD

- 모든 필드를 직렬화

- static / transient 필드는 제외

- @XmlTransient 어노테이션이 설정된 필드 제외

XmlAccessType.PROPERTY

- getter/setter를 모두 가진 필드를 직렬화

- @XmlTransient 어노테이션이 설정된 getter/setter의 필드 제외

- getter/setter가 없는 필드라도 Jaxb 어노테이션이 있을 경우 직렬화

XmlAccessType.PUBLIC_MEMBER

- public 필드 또는 public getter/setter를 가진 필드 직렬화

- @XmlTransient 어노테이션이 설정된 필드 및 getter/setter 제외

- public이 아니어도 Jaxb 어노테이션이 있을 경우 직렬화

 

 


 

 

3. Jaxb 어노테이션 사용 (필드, getter/setter 선언)

 

직렬화 대상을 범위로 잡지 않고 NONE을 사용하거나, 범위로 잡았더라도 추가적으로 대상에 포함시키고 싶은 데이터가 있다면 어노테이션을 사용해 지정해줄 수 있습니다. 주의할 점은 범위에 들어가는데 어노테이션을 중복으로 설정하면 예외가 발생한다는 것입니다.

 

  • @XmlAttribute : 루트 태그의 값 
  • @XmlElement : 하위 태그의 값

아래 코드와 같이 사용했을 때의 결과입니다.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user userID="codevang">
    <userPW>codevang1234</userPW>
    <userLevel>VIP</userLevel>
    <userPower>Strong</userPower>
    <userMoney>10000000won</userMoney>
</user>
package com.hsweb.springweb;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name ="user")
@XmlAccessorType(XmlAccessType.NONE)
public class UserVO {

	@XmlAttribute
	private String userID;
	@XmlElement
	private String userPW;
	@XmlElement
	private String userLevel;
	@XmlElement
	private String userPower;
	@XmlElement
	private String userMoney;
    
// getter, setter 생략

 

 

 

이번에는 루트 태그의 값을 넣지 않고 아래와 같은 코드로 설정했을 때의 결과입니다. 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
    <userID>codevang</userID>
    <userPW>codevang1234</userPW>
    <userLevel>VIP</userLevel>
    <userPower>Strong</userPower>
    <userMoney>10000000won</userMoney>
</user>
package com.hsweb.springweb;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name ="user")
@XmlAccessorType(XmlAccessType.NONE)
public class UserVO {

	@XmlElement
	private String userID;
	@XmlElement
	private String userPW;
	@XmlElement
	private String userLevel;
	@XmlElement
	private String userPower;
	@XmlElement
	private String userMoney;

// getter, setter 생략

 

 

 

만약 하위 태그를 계속 늘려 나가고 싶다면 List<VO> 타입으로 필드를 설정하면 됩니다. 여러 개의 XML VO 객체를 필드멤버로 가지게 되면 계층 구조를 계속 늘려나갈 수 있습니다. 

 

 


 

 

 

[ 컨트롤러 작성 및 테스트]

 

컨트롤러는 JSON을 사용했을 때와 동일합니다. 클라이언트가 XML 데이터를 Body에 넣어서 줄 때는 HTTP Header에 Content-Type이 application/xml로 되어 있으면서 @RequestBody로 설정한 VO 객체가 XML을 사용하는 것을 명시해 뒀으므로 서블릿과 핸들러가 캐치할 수 있습니다. 

 

또한 데이터를 응답해줄 때도 리턴하는 객체에 XML 변환을 명시해뒀기 때문에 서블릿과 핸들러가 XML로 데이터 변환 후 클라이언트에게 전달하게 됩니다. 간단한 컨트롤러 코드는 아래와 같고, Postman을 통해 테스트해보면 됩니다. 테스트 방법은 이전글에 나와있습니다.

 

[Spring MVC/- 기본 문법] - 스프링 Restful 서비스(API)_API 구현(JSON) [2/3]

 

package com.hsweb.springweb;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/board")
public class HomeController {

	// XML 데이터 만들어 주기
	@RequestMapping(value = "/sendxml", method = RequestMethod.GET)
	@ResponseBody
	public UserVO sendxml() {

		UserVO user = new UserVO();
		user.setUserID("codevang");
		user.setUserPW("codevang1234");
		user.setUserLevel("VIP");
		user.setUserPower("Strong");
		user.setUserMoney("10000000won");
		
		return user;
	}
	
	// XML 데이터 받아서 확인하기
	@RequestMapping(value = "/getxml", method = RequestMethod.POST)
	public void getxml(@RequestBody UserVO user) {

		System.out.println(user.getUserID());
		System.out.println(user.getUserPW());
		System.out.println(user.getUserLevel());
		System.out.println(user.getUserLevel());
		System.out.println(user.getUserPower());
		System.out.println(user.getUserMoney());
	}
}
728x90

댓글

💲 추천 글