티스토리 뷰

일급 컬렉션이란 Collection을 Wrapping하여 다른 멤버 변수는 없는 특징을 갖고 있습니다.

저는 DTO를 일급 컬렉션으로 만들었는데, request, response 할 때 직렬화, 역직렬화를 설정하는 방법을 설명하겠습니다.

 

일단 예제를 위해 게시글을 등록하기 위한 request dto부터 소개하겠습니다.

게시글 제목, 내용, 이미지에 대한 정보들을 가지고 있습니다.

이미지에 대한 정보들이 ImageCreateRequest이며 우리가 앞으로 중점으로 봐야할 일급 컬렉션입니다.

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PostCreateRequest {

    private String title;
    private String content;
    private ImageCreateRequest imageCreateRequest;

    @Builder
    public PostCreateRequest(
            String titlee,
            String content, 
            ImageCreateRequest imageCreateRequest) {
        this.title = title;
        this.content = content;
        this.imageCreateRequest = imageCreateRequest;
    }
}

 

사실 이미지 하나의 정보였다면 정말 쉬운 일이었습니다.

아래와 같은 Dto를 이용하고 PostCreateRequest에서는 json으로 받을 key값만 명시해주면 됩니다.

@JsonProperty를 사용해서 말이죠.

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ImageCreate {

    private String url;
    private String fileName;
    private String originalFileName;

    @Builder
    public ImageCreate(String url, String fileName, String originalFileName) {
        this.url = url;
        this.fileName = fileName;
        this.originalFileName = originalFileName;
    }
}

 

 

하지만 다음과 같이 ImageCreate를 여러 개 가진 일급 컬렉션 클래스는 아주 살짝 신경을 써줘야합니다.

PostCreateRequest -> ImageCreateRequest -> ImageCreate 순으로 현재 property를 가지고 있습니다.

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ImageCreateRequest {

    private List<ImageCreate> imageCreate;

    @Builder
    public ImageCreateRequest(List<ImageCreate> imageCreate) {
        this.imageCreate = imageCreate;
    }
}

 

아래와 같은 요청이 왔을 때를 생각해보겠습니다.

ImageCreateRequest는 images라는 키값을 가져야합니다.

images 배열 안 원소의 값들이 ImageCreate의 속성들과 매치되어 생성되어야 합니다.

결국에는 ImageCreateRequest의 List<ImageCreate> 값에 맵핑되어야 합니다.

{
    "title": "제목",
    "content": "내용",
    "images": [
    	{
            "url": "http://localhost:3065/uploads/images/adsfasd312dasfas_기본.png",
            "fileName": "파일 이름",
            "originalFileName": "원본 파일 이름",
        },
        {
            "url": "http://localhost:3065/uploads/images/adsfasd312dasfas_기본2.png",
            "fileName": "파일 이름2",
            "originalFileName": "원본 파일 이름2",
        },
    ]
}

PostCreateRequest에서는 다음과 같이하면 images 키값으로 읽습니다.

@JsonProperty("images")
private ImageCreateRequest imageCreateRequest;

ImageCreateRequest에서 역직렬화를 할 때 ImageCreate의 속성들은 배열의 요소에 매칭하는 방법을 설명하겠습니다.

이럴 때 사용하는 것이 @JsonDeserialize 어노테이션입니다. 사용 방법은 직렬화할 클래스 위에 어노테이션을 붙입니다.

@JsonDeserialize(using = ImageCreateRequestDeserializer.class)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ImageCreateRequest {

 

그러고 역직렬화를 어떻게 할건지 명시하는 클래스를 만들어 JsonDeserializer 클래스를 상속받아 deserialize를 구현합니다.

public class ImageCreateRequestDeserializer extends JsonDeserializer<ImageCreateRequest> {

    @Override
    public ImageCreateRequest deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException {
        ObjectCodec codec = parser.getCodec();
        JsonNode node = codec.readTree(parser);

        List<ImageCreate> imageCreateList = new ArrayList<>();
        if (node.isArray()) {
            ArrayNode imagesNode = (ArrayNode) node;
            for (JsonNode imageNode : imagesNode) {
                String url = imageNode.get("url").asText();
                String originalFileName = imageNode.get("originalFileName").asText();
                String fileName = imageNode.get("fileName").asText();
                ImageCreate imageCreate = ImageCreate.builder()
                        .url(url)
                        .originalFileName(originalFileName)
                        .fileName(fileName)
                        .build();
                imageCreateList.add(imageCreate);
            }
        }

        return ImageCreateRequest.builder()
                .imageCreate(imageCreateList)
                .build();
    }
}

위의 코드를 보면 JsonParser를 이용해서 Json content를 읽습니다.

JsonNode의 값은 다음과 같습니다.

아까 예시로 든 형태의 요청을 하면 다음과 같이 이미지 정보들이 _childern arraylist에 저장되어 있습니다.

arraylist를 순회하며 ImageCreateRequest를 생성한 것을 합한 후 ImageCreateRequest를 생성해 반환하면 PostCreateRequest가 원하는대로 역직렬화가 가능해집니다.

 

직렬화같은 경우에는 신경을 안썼는데, 나중에 api 테스트를 할 때 원하는 형태로 직렬화가 되지 않아서 request 포맷이 다른 문제가 생겼습니다.

 

PostCreateRequest postCreateRequest = PostCreateRequest.builder()
        .content("This is new content")
        .tradeType(TradeType.DIRECT)
        .reward(1_000)
        .itemName("airpods")
        .address(Address.builder()
                .latitude(38.123456)
                .longitude(126.123456)
                .street("서울시 광진구 자양동 123-123")
                .build())
        .imageCreateRequest(imageCreateRequest)
        .build();

String json = objectMapper.writeValueAsString(postCreateRequest);

//api test
mockMvc.perform(post("/api/posts")
                .contentType(APPLICATION_JSON)
                .content(json))
                .andDo(print());

request 요청을 보면 json content가 우리가 원하는 대로 만들어지지 않습니다.

{
    ...,
    "images":{
    	"imageCreate": [{url...}]
    }
}

 

위와 같은 형태가 만들어져서 테스트가 실패하게 됩니다. 

 

저는 @JsonValue 어노테이션을 추가하여 해결하였고 정상적으로 테스트를 통과하는 것을 확인할 수 있었습니다.

@JsonDeserialize(using = ImageCreateRequestDeserializer.class)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ImageCreateRequest {

    @JsonValue
    private List<ImageCreate> imageCreate;
    
    ...
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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 31
글 보관함