BackEnd/Java

[Java] 역직렬화(Deserialize)

짱호 2020. 7. 16. 11:11
반응형

어떤 요청에 대한 응답으로 JSON을 많이 이용하고 있다. 이때 필요한 Data만 전달받는 DTO를 만들고 적용해보자.

(카카오톡 챗봇 예제를 이용해 진행했습니다.)

 

1. 역직렬화(Deserialize)란?

  • byte로 변환된 Data를 원래대로 변환하는 기술을 역직렬화(Deserialize)라고 부른다.
  • Json → Java Object 변환

 

2. 예제

카카오 챗봇에서는 요청과 응답을 JSON Data로 주고 받는다. 전달된 JSON Data를 통해 사용자를 식별하거나, 사용자가 원하는 동작을 파악할 수 있고 데이터를 컨트롤할 수 있게 된다. 하지만 내가 사용하고자 하는 정보가 명확히 정해져 있고 한정적이라면 보내지는 JSON Data를 모두 받아 처리할 필요가 없다. 

 

예를 들면,

{
    "intent": {
        "id": "5e4381918192ac000135e4fd",
        "name": "start_chatbot",
        "extra": {
            "reason": {
                "code": 101,
                "message": "DIRECT_ID"
            }
        }
    },
    "userRequest": {
        "timezone": "Asia/Seoul",
        "params": {
            "surface": "Kakaotalk.plusfriend"
        },
        "block": {
            "id": "5e4381918192ac000135e4fd",
            "name": "start_chatbot"
        },
        "utterance": "?? ????",
        "lang": "ko",
        "user": {
            "id": "3800646c7318e785e6f9e60b94368e5d0029f4b38ab347bc44e08",
            "type": "botUserKey",
            "properties": {
                "botUserKey": "3800646c7318e785e6f9e60b94368e5d0029f4b38ab347bc44e08",
                "appUserStatus": "REGISTERED",
                "app_user_status": "REGISTERED",
                "app_user_id": "1290980424",
                "plusfriendUserKey": "HfzparVPNY_s",
                "appUserId": "1290980424",
                "bot_user_key": "3800646c7318e785e6f9e60b94368e5d0029f4b38ab347bc44e08",
                "plusfriend_user_key": "HfzparVPNY_s"
            }
        }
    },
    "contexts": [
        {
            "name": "Test_info",
            "lifespan": 5,
            "ttl": 600,
            "params": {
                "Test_Date": {
                    "value": "??? ??? ????",
                    "resolvedValue": "{\"from\": {\"date\": \"2020-03-01\"}, \"to\": {\"date\": \"2020-02-29\"}}"
                }
            }
        }
    ],
    "bot": {
        "id": "5e40a08db617ea000",
        "name": "??? RND?? ??"
    },
    "action" : {
    	"name" : "otp_url",
    	"clientExtra" : { },
    	"params" : {
        	"member_profile" : "{\"otp\":\"https://talk-plugin.kakao.com/otp/5e57269e4b1b3c/profile\",\"app_user_id\":129094}"
    	},
    	"id" : "5e54b8b38192ac0001377a46",
    	"detailParams" : {
      		"member_profile" : {
        		"origin" : "https://talk-plugin.kakao.com/otp/5e57269e4b1b3c/profile",
        		"value" : "{\"otp\":\"https://talk-plugin.kakao.com/otp/5e57269e4b1b3c/profile\",\"app_user_id\":129094}",
        		"groupName" : ""
      		}
    	}
  	}
}

 

위와 같은 요청에 대한 응답이 왔다고 가정해보자.

 

꽤나 복잡하고 어떤 정보를 가공해서 사용할지 한눈에 알아보기 힘들다. 많은 데이터들 중 내가 사용하고 싶은 데이터는 userRequestaction, 사용자가 요청한 날짜 정보를 담고 있는 contexts라고 한다면 아래와 같이 데이터를 표현할 수 있다.

 

  • userRequest - user - properties - appUserId

 

  • contexts - params - Test_Date - resolvedValue

 

  • action - detailParams - member_profile - origin

 

위 내용을 간략히 정리해보면 다음과 같은 Json 형태가 된다.

 

userRequest & contexts

{
    "userRequest": {
        "user": {
            "properties": {
                "appUserId": "1290980424",
            }
        }
    },
    "contexts": [
        {
            "params": {
                "Test_Date": {
                    "resolvedValue": "{\"from\": {\"date\": \"2020-03-01\"}, \"to\": {\"date\": \"2020-02-29\"}}"
                }
            }
        }
    ]
}

 

action

{

 "action" : {
    "detailParams" : {
      "member_profile" : {
        "origin" : "https://talk-plugin.kakao.com/otp/5e57b5e4f842692e7e4b1b3c/profile",
      }
    }
  }
  
}

3. DTO 만들기

이제 내가 사용하고자 하는 Json 데이터의 형태가 정해졌고, 이를 DTO로 만들 수 있다.

DTO Generator를 이용하여 위에서 정리한 JSON 형태를 만족하는 DTO를 만든다. 이번에는 직렬화와 다르게 Inner Class로 구성하여 만들었다.

 

1. RequestDTO라는 Package를 만들고 아래에 Class파일을 구성

 

2. ActionDTO

action을 받는 DTO

  • @JsonIgnoreProperties → 필드와 Mapping 되지 않는 값은 무시한다.

@JsonIgnoreProperties를 사용하지 않으면 Mapping 오류가 나서 Json DataType과 동일한 구조를 가지는 DTO를 만들어 줘야 한다.

 

@JsonIgnoreProperties(ignoreUnknown = true)
public class ActionDTO {

    @JsonProperty("action")
    private Action action;

    public Action getAction() {
        return action;
    }

    public void setAction(Action action) {
        this.action = action;
    }

    public static class Action {
        @JsonProperty("detailParams")
        private DetailParams detailParams;

        public DetailParams getDetailParams() {
            return detailParams;
        }

        public void setDetailParams(DetailParams detailParams) {
            this.detailParams = detailParams;
        }
    }

    public static class DetailParams {
        @JsonProperty("member_profile")
        private MemberProfile memberProfile;

        public MemberProfile getMemberProfile() {
            return memberProfile;
        }

        public void setMemberProfile(MemberProfile memberProfile) {
            this.memberProfile = memberProfile;
        }
    }

    public static class MemberProfile {
        @JsonProperty("origin")
        private String origin;

        public String getOrigin() {
            return origin;
        }

        public void setOrigin(String origin) {
            this.origin = origin;
        }
    }

 

3. ContextDTO

contexts와 userRequest를 받는 DTO

 

@JsonIgnoreProperties(ignoreUnknown = true)
public class ContextDTO {

    @JsonProperty("contexts")
    private List<Contexts> contexts;
    @JsonProperty("userRequest")
    private UserRequest userRequest;

    public List<Contexts> getContexts() {
        return contexts;
    }

    public void setContexts(List<Contexts> contexts) {
        this.contexts = contexts;
    }

    public UserRequest getUserRequest() {
        return userRequest;
    }

    public void setUserRequest(UserRequest userRequest) {
        this.userRequest = userRequest;
    }

    public static class Contexts {
        @JsonProperty("params")
        private Params params;

        public Params getParams() {
            return params;
        }

        public void setParams(Params params) {
            this.params = params;
        }
    }

    public static class Params {
        @JsonProperty("Test_Date")
        private TestDate testDate;

        public TestDate getTestDate() {
            return testDate;
        }

        public void setTestDate(TestDate testDate) {
            this.testDate = testDate;
        }
    }

    public static class TestDate {
        @JsonProperty("resolvedValue")
        private String resolvedValue;

        public String getResolvedValue() {
            return resolvedValue;
        }

        public void setResolvedValue(String resolvedValue) {
            this.resolvedValue = resolvedValue;
        }
    }

    public static class UserRequest {
        @JsonProperty("user")
        private User user;

        public User getUser() {
            return user;
        }

        public void setUser(User user) {
            this.user = user;
        }
    }

    public static class User {
        @JsonProperty("properties")
        @JsonAlias({ "property", "properties" })
        private Properties properties;

        public Properties getProperty() {
            return properties;
        }

        public void setProperty(Properties properties) {
            this.properties = properties;
        }
    }

    public static class Properties {
        @JsonProperty("appUserId")
        private String appUserId;

        public String getAppUserId() {
            return appUserId;
        }

        public void setAppUserId(String appUserId) {
            this.appUserId = appUserId;
        }
    }
}

 

4. Request Data 전달받기

Controller에서 @RequestBody 애노테이션과 함께 Mapping 시킬 DTO를 명시하면 요청의 body 내용(Json)을 자바 객체로 매핑해준다.

 

public MessageDTO createKey(@RequestBody ContextDTO contextDTO) throws Exception {
	.........
	.........
}

 

HTTP의 Header를 먼저 확인해서 DataType에 맞는 형태로 자동 매핑되는 것이다.

 

내가 사용하고 싶은 데이터 형태를 DTO로 만들고 이를 @RequestBody의 파라미터로 설정하면 JSON으로 들어오는 데이터 형태를 간단하게 컨트롤할 수 있게 된다.

 

이 방법 외에 JsonDeserializer라는 추상 클래스를 상속받아 Jackson Custom Deserializer를 만들 수 있다. Deserializer를 먼저 거치고 형태에 맞는 DTO에 값을 넣어주는 형태로 사용된다. 이 방법에 대한 내용은 추후 기회가 되면 올릴 예정이다.

반응형