[MyBatis] null 처리
문제는 CreatorDTO에서 User 엔티티가 null일 때 제대로 처리하지 못하는 부분입니다. User 객체가 null인데도 해당 객체의 속성에 접근하려 하기 때문에 문제가 발생합니다. 이로 인해 NullPointerException이 발생하고, 예외 처리 과정에서 의도치 않은 값 (예: name, key)으로 대체되는 것으로 보입니다. 원하는 것은 null 값을 그대로 받는 것입니다.
다음은 수정된 CreatorDTO와 변경 이유에 대한 설명입니다.
package -----.dto;
import -----.entity.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor; // NoArgsConstructor 추가
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor // 중요: User가 null일 경우를 대비하여 추가
public class CreatorDTO {
private String creatorID;
private String creatorName;
private String creatorImageURL;
public static CreatorDTO fromEntity(User entity) {
if (entity == null) {
return null; // User 엔티티가 null이면 null 반환
}
return CreatorDTO.builder() // 빌더 패턴을 사용하여 일관성 유지
.creatorID(entity.getUserID())
.creatorName(entity.getName())
.creatorImageURL(entity.getProfileImageUrl())
.build();
}
}
변경 사항 및 이유:
- @NoArgsConstructor 추가: MyBatis는 데이터를 조회할 때 DTO의 인스턴스를 생성해야 합니다. 특히 관련 엔티티(User와 같은)가 null일 경우, 인자가 없는 생성자가 필요합니다. @NoArgsConstructor를 추가하면 MyBatis가 User가 null일 때도 CreatorDTO 객체를 생성할 수 있습니다.
- 명시적인 null 확인 및 반환: fromEntity 메서드에서 User 엔티티가 null인지 명확하게 확인합니다. null일 경우 null을 반환하여 NullPointerException이 발생하는 것을 방지합니다.
- 빌더 패턴 사용: 빌더 패턴을 사용하여 CreatorDTO 객체를 생성하도록 변경하여 코드의 일관성을 유지합니다.
해결 과정:
- CreatorDTO 수정: 위에서 제시된 코드로 CreatorDTO를 수정합니다.
- MyBatis 설정 확인: MyBatis 설정 파일(mybatis-config.xml)에 mapUnderscoreToCamelCase 설정이 true로 되어 있는지 확인합니다. 이 설정은 데이터베이스의 컬럼명(snake_case)을 자바의 변수명(camelCase)으로 매핑해줍니다.
- 다시 테스트: API를 다시 호출하여 creatorImageURL과 creatorName이 null일 때 JSON 응답에 null 값이 제대로 포함되는지 확인합니다.
이렇게 수정하면 User 정보가 없을 때 creator 객체 자체가 null이 되거나, creator 객체는 존재하지만 내부의 creatorName과 creatorImageURL 속성이 null인 상태로 JSON 응답에 포함될 것입니다. 더 이상 다른 값으로 대체되는 문제는 발생하지 않습니다.
public CreatorDTO() { // @NoArgsConstructor 대체
}
이걸해야 결과값이 NULL이 나올 수 있는거야??
네, 맞아요. public CreatorDTO() {} 와 같이 인자가 없는 생성자를 추가하는 것은 MyBatis와 같은 프레임워크에서 객체를 생성할 때 매우 중요합니다. 특히 데이터베이스에서 값을 읽어와 DTO에 매핑할 때, 특정 필드가 null일 수 있습니다. 이 경우, 다음과 같은 이유로 인자가 없는 생성자가 필요합니다.
MyBatis의 객체 생성 과정:
- 결과 집합 처리: MyBatis는 SQL 쿼리 실행 결과를 ResultSet 형태로 받습니다.
- DTO 객체 생성: ResultSet의 각 행(row)에 해당하는 데이터를 DTO 객체에 매핑해야 합니다. 이때, MyBatis는 DTO 클래스의 인스턴스를 생성합니다.
- 필드 매핑: MyBatis는 ResultSet의 컬럼 값을 DTO 객체의 필드에 매핑합니다. 만약 특정 컬럼 값이 null이라면, 해당 필드에 null 값을 할당합니다.
인자가 없는 생성자의 필요성:
- null 값 처리: 만약 DTO 클래스에 인자가 있는 생성자만 존재하고 인자가 없는 생성자가 없다면, MyBatis는 null 값을 처리하는 방법을 알 수 없습니다. 예를 들어, User 테이블에서 profile_image_url 컬럼 값이 null인 경우, MyBatis는 CreatorDTO 객체를 생성할 때 profile_image_url 필드에 어떤 값을 할당해야 할지 모르게 됩니다.
- 기본 생성자: 인자가 없는 생성자는 클래스의 기본 생성자 역할을 합니다. MyBatis는 DTO 객체를 생성할 때 기본 생성자를 사용합니다. 만약 기본 생성자가 없다면, MyBatis는 DTO 객체를 생성할 수 없게 됩니다.
결론:
public CreatorDTO() {} 와 같이 인자가 없는 생성자를 추가하는 것은 MyBatis와 같은 프레임워크에서 null 값을 제대로 처리하고 DTO 객체를 성공적으로 생성하는 데 필수적입니다. 이 생성자가 없으면 null 값을 가진 필드를 가진 객체를 만들 수 없기 때문에, 데이터베이스에서 null 값을 읽어와 DTO에 매핑하는 과정에서 오류가 발생할 수 있습니다.
추가 정보:
- Lombok의 @NoArgsConstructor: Lombok 라이브러리의 @NoArgsConstructor 어노테이션은 인자가 없는 생성자를 자동으로 생성해주는 편리한 기능입니다. 하지만 Lombok을 사용하지 않는 경우에는 직접 생성자를 작성해야 합니다.
- 명시적인 null 확인: CreatorDTO의 fromEntity 메서드 내에서 entity가 null인지 확인하고 null을 반환하는 것은 NullPointerException을 방지하는 중요한 단계입니다.