[JPA] Jpa에서 자료형을 왜 래퍼클래스로 해야할까?

728x90

안녕하세요🖐

 

제가 Springboot + JPA 공부를 하다가 궁금한 점을 포스팅 해보겠습니다.

 


 

 

저는 Dto를 생성할 때 항상 기본 자료형으로 선언을 했습니다

public class DtoA {
    private long id;
    private String name;
    private int age;
    private String addr;
}

 

이런식으로 Dto를 구성하여도 크게 문제는 없었습니다.

왜냐하면 평소에 ORM은 Mybatis를 위주로 프로젝트를 진행하였고, 대부분 Mybatis를 활용했기 때문 입니다.

실제로 위 필드를 기본자료형으로 해서 생긴 문제 또한 없었습니다..

 

그러나 현재 트렌드인 JPA를 공부 중에 모든 Dto 구성 자료형을 기본 타입이 아닌 래퍼 타입으로 구성을 합니다.

왜 그런지 궁금해졌고, 여러가지 자료를 찾아보았습니다. 

 

Spring Data JPA 공식문서를 찾아보았는데 관련된 자료는 아쉽게도 없었습니다.

구글에서 서치를 하다가 여러자료를 조합해본 결과 스프링에서 정해둔 규칙은 없으며, 그냥 관례같은 것이라고 합니다.

 

무조건 래퍼클래스를 사용해야 한다가 아닌, 상황에 따라 다양하게 사용할 수 있어야 한다고 합니다. 

 

그래도 Spring Data JPAHibernate와 같은 JPA 구현체는 대부분 래퍼클래스선호한다고 합니다.

 

스프링 Data JPA나 Hibernate와 같은 JPA 구현체는 기본적으로 프리미티브 타입보다 래퍼 클래스를 선호합니다.

그 이유 중 하나는 값이 없는 경우(null 값)를 나타내기 위해 래퍼 클래스가 사용되기 때문입니다.

기본형 타입은 null값을 처리할 수 없습니다. 하지만 래퍼 타입은 처리할 수 있기 때문에 권장된다고 생각하시면 됩니다.

 

public class Ex기본타입 {
    private long value;
    private int value2;
    
    public Example(long value, int value2) {
        this.value = value;
        this.value = value2;
    }
}

위 코드는 기본타입으로 Dto를 구성한 코드 입니다.

위 코드를 리팩토링 해보면은

public class Ex래퍼타입 {
    private Long value;
    private Integer value2;
    
    public Example(Long value, Integer value2) {
        this.value = value;
        this.value = value2;
    }
}

이렇게 코드를 수정할 수 있습니다.

 

 

두번째로 래퍼타입을 권장하는 이유는 JPA와 연동시의 편의성 때문 입니다.

보통 JPA에서 Entity를 구성하여

 

JPA와 연동 시의 편의성: 일반적으로 JPA(Java Persistence API)에서는 Long과 같은 래퍼 클래스를 사용하는 것이 유용합니다. JPA에서 Entity의 식별자(identifier)를 나타내는 필드는 기본적으로 래퍼 클래스로 사용하는 것이 좋습니다.

@Entity
public class ExEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Columns
    private Integer age;
}

 

어떠한 유용성과 편의성이 있는지 궁금할 것 입니다. 그냥 간단하게 위에서 설명했던 말에 덧 붙이자면

  • Null 값을 표현할 수 있음
    • Null값 처리를 통해, 값이 없는 경우 로직 처리가 비교적 쉬워 집니다.

기본적으로, 원시타입과 래퍼클래스의 차이를 알고 있다면은 이 문제를 바로 이해할 수 있을 것입니다.

  • JPA에서 기본 값 설정
    • ex) Integer 타입의 필드는 기본적으로 null이 아닌 '0'으로 초기화 됩니다

 

 

세번째는 메소드의 매개변수로 전달할때의 편의성 때문입니다.

 

이해를 돕기 위해 코드를 한번 보며 설명해보겠습니다.

public void method1(long value) {
    // value가 null인 경우에 대한 처리
    if (value == null) {
        // 처리 로직
    } else {
        // 다른 처리 로직
    }
}

위 코드는 틀린 코드입니다.

자료형을 long 으로 할 경우 null값 처리를 할 때 == null이 아닌, ==0으로 처리를 해야할 것 입니다.

 

DB를 기준으로 예시를 들면은 column에 값이 없으면 0 일까요? null 일까요? 

모든 DB는 null처리를 합니다. 0 또한 엄연한 값이기 때문이죠

 

그러므로 위 메소드에서 파라미터 처리를 할 때도 당연히 래퍼클래스를 사용해야 합니다.

public void method1(Long value) {
    // value가 null인 경우에 대한 처리
    if (value == null) {
        // 처리 로직
    } else {
        // 다른 처리 로직
    }
}

 

위 코드가 null값 처리하기에 적합한 코드 입니다.

 

 

실제 프로젝트에서도 DB에서 null 값을 처리해야 할 상황이 있을까? 라는 고민을 잠깐 해보았습니다.

 

제 경험을 되살려보니 당연하게도 null 값 처리를 해주는 경우가 많다고 생각합니다. 

 

간단하게 예시를 들면은 회원가입을 할 때, 사용자가 필수로 입력해야하는 필드 말고,

공백으로 남겨둬두 되는 필드가 있습니다. 그러면 회원가입이 완료되면은, 공백 필드는 어떻게 처리가 될까요?

 

DB에는 당연하게도 null값으로 처리가 될 것입니다. 

위 사진 처럼 null로 처리가 됩니다. 

 

 

또 다른 상황을 생각해 보았습니다. 

 

API 요청 시 파라미터의 값을 객체로 지정해서 보낼 떄

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
        String email = request.getEmail() 
        String name = request.getUsername() 
        //phoneNumber는 보내지 않는다.
        //아래는 API처리 로직 작성 해야함
    }
}

public class CreateUserRequest {
    private String username;
    private String email;
    private String phoneNumber; // 선택적인 파라미터
}

 

클라이언트가 API요청을 보낼 때 모든 필드를 보낼 필요는 없습니다.

그러므로 선택적인 파라미터의 값이 전송되지 않는 경우 해당 필드는 null이 될 수 있다. 

 

 

추가적인 상황은 제가 구글링을 해보았더니 Optional한 값을 표현하는 경우 사용한다고 합니다.

 

자바 8 이후에는 Optional 클래스를 이용하여 값을 감싸서 표현할 수 있습니다.

Optional은 값이 존재하지 않을 수 있는 경우에 사용되며, 값이 없는 경우에는 Optional.empty()를 반환할 수 있습니다.

public class ExampleUsingOptional {
    private Optional<String> optionalValue;

    public ExampleUsingOptional(Optional<String> optionalValue) {
        this.optionalValue = optionalValue;
    }

    public Optional<String> getOptionalValue() {
        return optionalValue;
    }

    public static void main(String[] args) {
        ExampleUsingOptional example1 = new ExampleUsingOptional(Optional.of("Hello"));
        ExampleUsingOptional example2 = new ExampleUsingOptional(Optional.empty());

        System.out.println("Value of example1: " + example1.getOptionalValue().orElse("Default")); // 출력: Value of example1: Hello
        System.out.println("Value of example2: " + example2.getOptionalValue().orElse("Default")); // 출력: Value of example2: Default
    }
}

 

이러한 상황에서 null을 사용하거나 허용하는 것은 상황에 따라 다르며, 예시에서는 null을 표현하는 상황을 보여주었지만, 어떤 상황에서는 Optional 클래스나 다른 방식을 사용하는 것이 더 적절할 수 있습니다.

 

📢 결론은 위와 같은 이유로 JPA에서 entity 구성 및 dto를 구성시 자료형을 무조건은 아니지만, 래퍼클래스로 하는 것을 권장합니다.

 

이상 포스팅 마치겠습니다.

728x90