안녕하세요, 여러분의 든든한 IT 길잡이 데브프리입니다.
스프링 부트(Spring Boot)로 게시판이나 API를 만들다 보면, 데이터베이스에서 값을 완벽하게 잘 가져왔는데도 불구하고 화면에 데이터를 뿌려주지 못하고 500 Internal Server Error를 뱉어내는 황당한 상황을 마주하게 됩니다.
콘솔창을 열어보면 엄청나게 긴 에러 메시지가 우리를 반겨줍니다. com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class ... and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
DB 쿼리도 맞고 로직도 완벽한데 도대체 왜 데이터를 보여주지 못하는 걸까요? 오늘은 프론트엔드로 데이터를 넘길 때 빈번하게 발생하는 'Jackson 직렬화(Serialization) 에러'의 원인을 아주 직관적으로 알아보고, 단 1초 만에 해결하는 2가지 방법을 완벽하게 정리해 드리겠습니다!
1. No serializer found 에러, 도대체 넌 누구냐? (블랙박스 포장 비유)
스프링 부트는 우리가 자바 객체(Object)를 반환하면, 프론트엔드가 알아먹을 수 있도록 이를 JSON 형태({"이름": "데브프리"})로 예쁘게 포장해서 보내줍니다. 이때 포장을 전담하는 기본 라이브러리가 바로 Jackson(잭슨)입니다.

Jackson 직원은 우리가 넘겨준 자바 객체의 내부 데이터를 하나씩 꺼내서 JSON 상자에 옮겨 담습니다. 그런데 만약 우리가 객체에 접근할 수 있는 '창문'을 만들어두지 않았다면 어떻게 될까요?
자바에서 클래스의 변수들은 보통 private으로 꽁꽁 숨겨져 있습니다. Jackson 직원은 이 까만 블랙박스 안을 들여다볼 권한이 없으니, "안에 뭐가 들었는지 안 보여서 포장을 못 하겠어!!"라며 파업을 선언해 버립니다. 이것이 바로 No serializer found for class (이 클래스를 변환할 직렬화 도구를 찾을 수 없다) 에러의 진짜 의미입니다.
2. 스프링 부트에서 이 에러를 해결하는 2가지 방법
해결 원리는 너무나도 간단합니다. 까만 블랙박스에 Jackson 직원이 안을 들여다보고 데이터를 꺼내갈 수 있도록 투명한 창문(Getter)을 뚫어주기만 하면 됩니다.

스프링 부트에서는 이 창문을 만들어주는 2가지 확실한 방법이 있습니다.
방법 ①: 롬복(Lombok) @Getter 어노테이션 사용 (가장 추천)
현업에서 가장 많이, 그리고 가장 쉽게 사용하는 방법입니다. 프론트엔드로 반환하려는 DTO(Data Transfer Object)나 Entity 클래스 상단에 @Getter 어노테이션 딱 한 줄만 추가해 줍니다.
import lombok.Getter;
// 💡 @Getter를 붙이면 보이지 않던 private 변수들에 접근할 수 있는 창문이 열립니다!
@Getter
public class BoardResponseDto {
private Long id;
private String title;
private String content;
public BoardResponseDto(Long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
}
@Getter를 붙이는 순간 롬복이 내부적으로 getId(), getTitle() 등의 메서드를 자동으로 만들어주며, Jackson은 이를 통해 데이터를 쏙쏙 뽑아 JSON으로 변환합니다.
방법 ②: 수동으로 Getter 메서드 작성하기
만약 프로젝트에 롬복(Lombok)이 설치되어 있지 않거나, 특정 변수만 선택적으로 노출하고 싶다면 직접 public 접근 제어자를 가진 Getter 메서드를 작성해 주면 됩니다. IDE의 단축키(Alt + Insert 또는 Cmd + N)를 사용하면 1초 만에 만들 수 있습니다.
public class BoardResponseDto {
private Long id;
private String title;
public BoardResponseDto(Long id, String title) {
this.id = id;
this.title = title;
}
// 💡 수동으로 창문(Getter)을 만들어 Jackson이 접근할 수 있게 허용합니다.
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
}
이렇게 Getter가 존재하는 클래스 객체를 컨트롤러에서 반환하면, 500 에러 대신 아름답게 파싱된 JSON 데이터를 브라우저에서 만날 수 있습니다.
3. 요약: 트러블슈팅 체크리스트
No serializer found for class 에러가 발생했다면 당황하지 말고 아래 3가지를 체크하세요.
- 반환(Return) 객체 확인: 컨트롤러에서 프론트엔드나 클라이언트로 Return 하고 있는 객체(DTO, Entity 등)가 무엇인지 확인합니다.
- Getter 존재 여부: 해당 객체의 클래스 파일을 열어 상단에 @Getter 어노테이션이 붙어 있는지, 혹은 하단에 get...() 메서드가 구현되어 있는지 확인합니다.
- 접근 제어자 확인: Getter 메서드를 직접 만들었다면 실수로 private으로 만들지 않았는지 확인합니다. 반드시 외부에서 접근 가능한 public이어야 합니다.
마무리
컨트롤러와 서비스 로직을 완벽하게 짰는데 마지막 출력 단계에서 에러가 나면 정말 허탈하죠.
하지만 이 에러는 컴퓨터가 고장 난 것이 아니라, 자바의 훌륭한 객체지향 보안 원칙(은닉화) 때문에 외부 라이브러리인 Jackson이 여러분의 소중한 데이터에 함부로 접근하지 못해서 생긴 일종의 '보안 출입 통제' 현상입니다. 접근 권한(Getter)만 부여해주면 모든 문제가 눈 녹듯 사라집니다.
오늘 준비한 트러블슈팅 가이드가 여러분의 붉은 에러 창을 깨끗하게 지워주었기를 바랍니다. 오류를 고친 경험은 여러분의 가장 큰 자산이 됩니다. 막히는 에러가 있다면 언제든 댓글로 남겨주세요.
지금까지 데브프리였습니다. 감사합니다!
'💻 트러블슈팅 (에러 해결)' 카테고리의 다른 글
| 데이터 형식 불량! MethodArgumentNotValidException 원인과 해결법 완벽 정리 (0) | 2026.06.29 |
|---|---|
| 분명 데이터를 보냈다는데? HttpMessageNotReadableException 원인과 해결법 완벽 정리 (0) | 2026.06.28 |
| 공포의 하얀 화면! 스프링 부트 Whitelabel Error Page (404) 원인과 해결법 완벽 정리 (0) | 2026.06.26 |
| 자바 개발자의 영원한 숙적! NullPointerException (NPE) 원인과 스프링 부트 해결법 완벽 정리 (0) | 2026.06.25 |
| 브라우저의 붉은 악마! CORS 에러 원인과 스프링 부트 해결법 완벽 정리 (0) | 2026.06.24 |