[Spring] RestController에서 객체 반환

·2025년 5월 16일
1

Spring Boot

목록 보기
5/6
post-thumbnail

서론

스프링을 통해 REST API를 구축하다보면 객체(DTO)를 반환하는 일은 매우 자주있다.

// UserResponse.java
public class UserResponse {
	private String name;
	private int age;

	public UserResponse() {
		name = "Chan";
		age = 27;
	}
}


// MainController.java
@RestController
public class MainController {

	@GetMapping("/api/v1/users")
	public UserResponse getUsers() {
		UserResponse response = new UserResponse();
		return response;
	}
}

다음의 코드를 보고 잘못된 부분을 찾아 보자.
분명 틀린 부분이 없어보이지만, 심지어 실행에도 문제가 없지만 실행을 하면, 다음과 같은 문제가 발생한다.

🔴 HTTP Status 406 - Not Acceptable ??

이 상태 코드는 클라이언트가 요청할 때 지정한 Accept 헤더에 서버가 응답 가능한 타입이 없다는 뜻입니다.
즉, 서버가 JSON을 제공하지 못했다는 것!
무엇이 문제일까? 좀 더 알아보자.

단계별로 알아보기

@RestController ??

@Controller + @ResponseBody

해당 클래스의 모든 메서드는 반환값을 View가 아닌 HTTP 응답 본문(body)에 직접 쓴다.
즉, 객체를 반환에 직접 사용!

객체반환? - 어떻게??

기본적으로 Spring은 반환 시 HttpMessageConverter를 사용.
객체를 반환하면 Spring은 자동으로 객체를 JSON으로 변환한다.
이때, MappingJackson2HttpMessageConverter를 사용!
객체를 JSON으로 변환을 한다!

JSON 변환은 누가??

MappingJackson2HttpMessageConverter은 Jackson 라이브러리를 사용한다.
Jackson은 객체를 JSON으로 직렬화함!

왜 문제가 발생하는가?

Jackson의 직렬화에 대한 문제

Jackson은 직렬화 시, 다음 중 하나를 필요로 함

  • public 필드
  • getter 메서드

결국 private 필드인데 getter가 없으면 JSON 직렬화 실패

올바른 코드

// UserResponse.java
public class UserResponse {
	private String name;
	private int age;

	public UserResponse() {
		name = "Chan";
		age = 27;
	}

	public String getName() {
		return "HaeChan";
	}
}

다음과 같이 getter가 있어야 작동한다.

다음을 통해 Spring에서 RestController에서 객체 반환 시, Getter의 내용을 사용한다는 것을 알 수 있다.

심화학습

Jackson을 이용하기 때문에

@JsonIgnore
@JsonProperty
@JsonManagedReference
@JsonBackReference

다음과 같은 어노테이션을 사용할 수 있음

  • @JsonIgnore: 해당 필드를 JSON 직렬화/역직렬화에서 제외

  • @JsonProperty: JSON의 필드 이름을 Java 필드와 다르게 지정

  • @JsonManagedReference & @JsonBackReference: 두 어노테이션은 양방향 관계에서 순환 참조로 인해 StackOverflow가 발생하는 것을 방지
    @JsonManagedReference → 직렬화에 포함됨 (보통 부모 → 자식 방향)
    @JsonBackReference → 직렬화에서 제외됨 (보통 자식 → 부모 방향)

  • @JsonIdentityInfo: Jackson이 객체의 ID 기반으로 참조를 추적하게 하여, 순환 참조 구조에서도 무한 루프 없이 직렬화가 가능

ObjectMapper를 사용하기

ObjectMapper는 Jackson 라이브러리의 핵심 클래스

직접 사용하기

ObjectMapper objectMapper = new ObjectMapper();

// 직렬화: Java 객체 → JSON
User user = new User("haechan", 25);
String json = objectMapper.writeValueAsString(user);

// 역직렬화: JSON → Java 객체
String jsonString = "{\"name\":\"haechan\",\"age\":25}";
User user2 = objectMapper.readValue(jsonString, User.class);

커스텀 마이징

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // 예: snake_case → camelCase 자동 매핑
        mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

        // null 값 무시
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        return mapper;
    }
}

다음과 같이 커스텀해서 사용할 수 있다.

정리

  • Spring은 객체 반환 시, Jackson을 사용한다.
  • 따라서, public 혹은 getter가 반드시 필요하다.

0개의 댓글