문제 발단

백엔드에서 닉네임 중복 오류를 고치던 중 갑자기 잘 동작하던 UserRepository 에 있는 점수를 내림차순으로 정렬하여 조회하는 findAllByOrderByScoreDesc() 메소드에서 오류발생

No default constructor for entity: : backend.csquiz.entity.User; nested exception is org.hibernate.InstantiationException

package backend.csquiz.repository;

import backend.csquiz.entity.User;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByNickname(String nickname); // 닉네임으로 사용자 조회

    // 점수 내림차순으로 정렬하여 모든 사용자 조회
    List<User> findAllByOrderByScoreDesc();
}

문제 원인 분석

findAllByOrderByScoreDesc() 메소드 역할

엔티티 생성자와의 연관성

<aside> 💡

“기본 생성자가 없으면 왜 안될까?”

JPA는 엔티티 객체를 데이터베이스에서 조회할 때 내부적으로 리플렉션(Reflection) 을 사용하여 객체를 생성한다. 이 과정에서는 new 키워드와 같은 일반적인 생성자 호출 방식이 아니라, 기본 생성자(no-args constructor)를 통해 객체를 먼저 생성한 뒤, 필드에 값을 주입하는 방식으로 동작하게 된다.

즉, JPA는 다음과 같은 순서를 따르게 된다:

  1. 기본 생성자로 객체 생성
  2. 필드 값 주입

따라서, 기본 생성자가 없으면 객체를 생성할 수 없어 오류가 발생하게된다.

이는 Hibernate나 Spring Data JPA에서 다음과 같은 예외로 나타날 수 있다:

No default constructor for entity: : backend.csquiz.entity.User;
nested exception is org.hibernate.InstantiationException

</aside>

🔍 오류 발생 원인

기존 UserService에서 사용자 점수를 저장하는 방식은 다음과 같았다.

// 사용자 점수 저장
@Transactional
public void saveScore(String nickname, int score) {
    User user = userRepository.findByNickname(nickname)
            .orElseGet(() -> {
                User newUser = new User(nickname, score);
                return userRepository.save(newUser);
            });

    // 점수 누적
    user.setScore(user.getScore() + score);
    userRepository.save(user);
}

만약 사용자가 존재하지 않을 경우, 생성자를 이용해 새로운 사용자를 생성한 뒤 저장하는 방식이었다.