본문 바로가기
Backend/Spring

Spring : JSON 데이터 처리에 대해서

by 코딩쥐 2024. 11. 3.

@JsonIgnore 

Jackson 라이브러리에서 제공하는 어노테이션으로, 특정 필드를 JSON 직렬화 및 역직렬화 과정(객체를 JSON,XML 등으로 변환하는 것을 직렬화라고하고, 저장된 데이터를 다시 객체로 변환하는 과정을 역직렬화라고 한다.) 에서 무시하도록 지정하는 기능을 한다. 주로 클라이언트에게 전달하고 싶지 않은 데이터나 불필요한 데이터 전송을 줄이기 위해 사용된다. 접근자/변경자(필드, getter/setter, 생성자 매개변수) 중 하나에만 추가하면 전체 속성에 영향을 끼친다.

 

@JsonIgnore 사용하기

1. DTO 생성 및 @ JsonIgnore 적용

// UserDTO
package com.example.project1.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;

public class UserDTO {
    private String name;
    private Integer age;
    private String id;

    @JsonIgnore
    private String pw; // 비밀번호는 JSON 응답에서 제외

    // 기본 생성자 추가
    public UserDTO() {}

    public UserDTO(String name, Integer age, String id, String pw) {
        this.name = name;
        this.age = age;
        this.id = id;
        this.pw = pw; 
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    @JsonIgnore
    public String getId() {
        return id; // ID는 JSON 응답에서 제외
    }

    public String getPw() {
        return pw; // PW는 JSON 응답에서 제외
    }
}

 

2. Controller에 해당 DTO를 사용

package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @PostMapping("/user")
    public ResponseEntity<String> userEx(@RequestBody UserDTO user) {
        System.out.println(user.getName() + user.getAge() + user.getId() + user.getPw());
        String responseMessage =
                String.format("이름 : %s, 나이 : %d, 아이디 : %s, 비밀번호 : %s",
                        user.getName(), user.getAge(), user.getId(), user.getPw());
        return new ResponseEntity<>(responseMessage, HttpStatus.OK);
    }
}

 

클라이언트가 JSON데이터에서 id와 pw를 포함해 요청을 보내도, @JsonIgnore이 적용된 필드는 JSON 직렬화 과정에서 무시되어 null 값을 출력한다. 또한 System.out.println으로 UserDTO 객체를 출력할 때, id와 pw 필드는 JSON 직렬화에서 무시되어 이 두 필드는 null 값을 출력한다. 

 


@JsonProperty

Jackson 라이브러리에서 제공하는 어노테이션으로 JSON 직렬화 및 역직렬화 과정에서 특정 속성의 이름을 변경하거나, 특정 메서드가 JSON 속성으로 인식되도록 지정할 수 있다.

 

@JsonProperty 사용하기

1. DTO 생성 및 @JsonProperty를 적용

package com.example.project1.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public record UserDTO(
        @JsonProperty("이름")
        String firstname,
        @JsonProperty("성")
        String lastname,
        @JsonProperty("나이")
        Integer age
) {
}

 

 

2. Controller에 해당 DTO를 사용

package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @PostMapping("/user")
    public ResponseEntity<String> userEx(@RequestBody UserDTO user){
        String requestMessage = String.format(
                "이름 : %s, 성 : %s, 나이 : %d",
                user.lastname(),user.firstname(),user.age()
        );
    return new ResponseEntity<>(requestMessage, HttpStatus.OK);
    }
    }

 

서버는 UserDTO 객체의 firstname, lastname, age 필드에 각각 "버거", "햄", 30을 저장하고, 응답 메시지는 "이름 : 버거, 성 : 햄, 나이 : 30"과 같이 출력한다.


@JsonView

Jackson 라이브러리에서 제공하는 어노테이션으로, JSON 직렬화 및 역직렬화 시에 특정 뷰를 정의하여 어떤 필드를 포함할지 또는 제외할지 설정할 수 있다. 주로 데이터 노출을 제어할 때 사용된다. JsonIgnore과는 달리 JsonView의 경우에는 선택적으로 필드를 포함하거나 제외할 수 있다. 뷰를 정의할 때는 인터페이스로 정의한다. 

 

@JsonView 사용하기

1. DTO 생성 및 @JsonView 적용

package com.example.project1.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;

public record UserDTO(
        String name,
        Integer age,
        //PublicView와 nickname 연결
        @JsonView(PublicView.class)
        String nickname,
        String id,
        String pw
) {
//뷰 정의
    public interface PublicView{}
}

 

2. Controller에서 @JsonView 사용

package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @PostMapping("/user")
    //UserDTO의 PublicView를 적용한다.
    @JsonView(UserDTO.PublicView.class)
    public ResponseEntity<UserDTO> userEx(@RequestBody UserDTO user){
        System.out.println(user);
    return new ResponseEntity<>(user, HttpStatus.OK);
    }
}

 

@JsonView가 적용된 필드는 요청 바디에 포함된 데이터를 받아온 후, 응답 시에 활성화 된 뷰에 따라 어떤 필드가 JSON에 포함될지 결정된다. 따라서 @JsonIgnore처럼 특정 필드를 아예 무시하여 항상 직렬화에서 제외되는 것과는 달리, @JsonView는 System.out.println으로 객체를 출력할 때 모든 필드를 확인할 수 있다.

 

만약 다른 메서드에서는 name을 보여줘야 한다고 했을 때, 아래와 같이 뷰를 생성하고 상속받아서 사용하면 필요에 따라 보여지는 것을 변경할 수 있다.

 

1. @JsonView를 정의하고, PublicView를 상속받음

//UserDTO
package com.example.project1.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;

public record UserDTO(
        @JsonView(NameView.class)
        String name,
        Integer age,
        @JsonView(PublicView.class)
        String nickname,
        String id,
        String pw
) {
    public interface PublicView{}
    //PublicView를 상속받음
    public interface NameView extends PublicView{}
}

 

2. PublicView를 상속받은 NameView를 활성화 함

//UserController
package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @PostMapping("/user")
    //UserDTO의 PublicView를 상속받은 NameView를 뷰로 정의한다.
    @JsonView(UserDTO.NameView.class)
    public ResponseEntity<UserDTO> userEx(@RequestBody UserDTO user){
        System.out.println(user);
    return new ResponseEntity<>(user, HttpStatus.OK);
    }
}

 

아래처럼 뷰를 상속받아 name과 nickname 필드가 보여지는 것을 확인할 수 있다. 


@JsonFilter

Jackson 라이브러리에서 제공하는 어노테이션으로, 특정 필드를 동적으로 포함하거나 제외할 수 있는 기능을 제공한다. @JsonFilter를 사용하면 특정 필드를 직렬화할 때 포함하거나 제외할 수있다.

 

@JsonFilter 사용하기

1. DTO에 @JsonFilter 적용

@JsonFilter 어노테이션을 적용하여 필터 이름을 지정한다. 

//UserDTO
package com.example.project1.dto;

import com.fasterxml.jackson.annotation.JsonFilter;

@JsonFilter("userFilter")
public record UserDTO(
        String name,
        Integer age,
        String nickname,
        String id,
        String pw
) {}

 

2. MappingJacksonValue 클래스 사용

응답DTO를 HTTP 응답 본문에 직렬화하기 전에 추가적인 직렬화 지침을 설정할 수 있는 홀더 역할을 한다. 

@RestController
@RequestMapping("/codingji")
public class UserController {
    @GetMapping("/user")
    //MappingJacksonValue 클래스로 설정
    public MappingJacksonValue user(){
    }

 

3. SimpleBeanPropertyFilter 클래스 사용 

JSON 직렬화 과정에서 특정 속성을 포함하거나 제외하는 필터를 생성하는 클래스이다. filterOutAllExcept 메서드를 사용하여 지정된 필드만 JSON으로 변환하도록 설정할 수 있다. 

@RestController
@RequestMapping("/codingji")
public class UserController {
    @GetMapping("/user")
    //MappingJacksonValue 클래스로 설정
    public MappingJacksonValue user(){
        // UserDTO 인스턴스 배열생성
        List<UserDTO> list = new ArrayList<>();
        list.add(new UserDTO("보름달", 22, "달달", "fullmoon", "120dlj"));
        list.add(new UserDTO("김서방", 49, "김김", "kim", "lkd29jf"));
        list.add(new UserDTO("햄버거", 19, "버거", "burgerdelicious", "12kcn0"));

        //이름과 닉네임을 제외한 나머지 데이터를 필터링하기 위해서 SimpleBeanPropertyFilter 사용
        SimpleBeanPropertyFilter filter =
                SimpleBeanPropertyFilter.filterOutAllExcept("name", "nickname");
    }

 

4. SimpleFilterProvider 클래스 사용 

JSON 직렬화 과정에서 적용되는 필터를 관리하는 클래스로, addFilter 메서드를 사용하여 필터를 추가할 수 있다. SimpleFilterProvider의 필터를 설정할 때, 이름은 사용할 DTO에서 설정해놓은 이름으로 설정한다.

package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @GetMapping("/user")
    //MappingJacksonValue 클래스로 설정
    public MappingJacksonValue user(){
        // UserDTO 인스턴스 배열생성
        List<UserDTO> list = new ArrayList<>();
        list.add(new UserDTO("보름달", 22, "달달", "fullmoon", "120dlj"));
        list.add(new UserDTO("김서방", 49, "김김", "kim", "lkd29jf"));
        list.add(new UserDTO("햄버거", 19, "버거", "burgerdelicious", "12kcn0"));

        //이름과 닉네임을 제외한 나머지 데이터를 필터링하기 위해서 SimpleBeanPropertyFilter 사용
        SimpleBeanPropertyFilter filter =
                SimpleBeanPropertyFilter.filterOutAllExcept("name", "nickname");

        //선언한 filter를 userFilter란 이름으로 저장한다. (UserDTO에서 설정해놓은 이름)
        SimpleFilterProvider filters =
                new SimpleFilterProvider().addFilter("userFilter", filter);
    }
    }

 

5. MappingJacksonValue 인스턴스를 생성하여 데이터를 삽입하고 필터를 지정한다. 

package com.example.project1.controller;

import com.example.project1.dto.UserDTO;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/codingji")
public class UserController {
    @GetMapping("/user")
    //MappingJacksonValue 클래스로 설정
    public MappingJacksonValue user(){
        // UserDTO 인스턴스 배열생성
        List<UserDTO> list = new ArrayList<>();
        list.add(new UserDTO("보름달", 22, "달달", "fullmoon", "120dlj"));
        list.add(new UserDTO("김서방", 49, "김김", "kim", "lkd29jf"));
        list.add(new UserDTO("햄버거", 19, "버거", "burgerdelicious", "12kcn0"));

        //이름과 닉네임을 제외한 나머지 데이터를 필터링하기 위해서 SimpleBeanPropertyFilter 사용
        SimpleBeanPropertyFilter filter =
                SimpleBeanPropertyFilter.filterOutAllExcept("name", "nickname");

        //선언한 filter를 userFilter란 이름으로 저장한다. (UserDTO에서 설정해놓은 이름)
        SimpleFilterProvider filters =
                new SimpleFilterProvider().addFilter("userFilter", filter);

        // MappingJacksonValue 인스턴스를 생성하여 데이터를 삽입한 후에 필터를 설정한다.
        MappingJacksonValue mapping = new MappingJacksonValue(list);
        mapping.setFilters(filters);

        return mapping;
    }
    }