Tech/mysql | DB모델링 | JPA

JPA 에서는 int 를 써야하나요? integer 를 써야하나요?

glqdlt 2025. 7. 9. 17:05

제목에 대한 대답은 JPA 에서는 integer 를 써야한다가 나의 대답이다, 그것도 확고하게. JPA 에서는, 더 나아가서 Java 에서는 int 를 사용하면 안된다.

Java에서 정수를 표현하는 방법은 두 가지가 있다. int와 Integer. 전자는 primitive type이고, 후자는 wrapper type 이라 부른다. 이 정도는 JPA를 쓰는 Java 개발자라면 당연히 알고 있는 내용일테니 더 길게 설명하진 않겠다. 중요한 건 이 차이가 JPA에서 매우 중요한 문제가 된다는 거다.

RDBMS에서는 null이란 개념이 존재한다. RDBMS에서 null 은 값이 아직 할당 되지 않은 상태를 나타내는 표현이며, 이는 RDBMS 에서 매우 중요하다. 그런데 java 세상에서는 int, long, boolean 와 같은 primitive type 에서는 null이라는 개념을 담을 수가 없다. 여기에는 null이라는 개념 자체가 없기 때문이다. 그래서 JPA에서 int 타입 필드에 DB의 null 값이 들어오게 되면, JPA는 강제로 기본값(예: int는 0)을 넣어버린다.  

그런데 재밌는 점은 JPA 에서 컬럼을 맵핑하는 필드값을 primitive type 으로 선언하더라도 별 문제 없이 동작을 한다는 것이다. 앞서 말한 것처럼 기본값이 할당되기 떄문에 문제 없는 것처럼 보일 뿐이다. 그래서 int 를 써도 되는가보다 라는 생각이 들 수 있다. 하지만 컬럼의 값이 null 임에도 기본값으로 치환되어 있어 오염된 상태가 된 것이기 때문에 문제다. int 였다면 기본값이 0임으로, 0으로 할당되어 있다. 마치 RDBMS 에도 0으로 저장 되어 있는 것처럼 보이는 오해를 가지게 한다. 반대로 보아도 골칫덩이이다, java 어플리케이션 에서 rdbms 에 삽입 쿼리를 날릴 때에도 null 을 넣어야할 필드에 0이 들어가게 된다는 얘기가 되니 이것은 꽤나 큰 문제다.

이야기를 듣고 보면 매우 단순하고 당연하게 느껴지지만, 의외로 많은 개발자들이 wrapper type 대신 primitive type을 사용하는 모습을 자주 보게 된다. 여담이지만, JPA뿐 아니라 객체지향 프로그래밍(OOP)을 하는 개발자라면 wrapper type을 사용하는 습관을 들이는 것이 바람직하다.

개인적으로는 primitive type을 사용하지 않는다. 그 이유는 간단하다. primitive type은 OOP의 핵심인 Object가 아니기 때문이다. 반면 wrapper type은 Object를 상속한 클래스다. Integer.class 라는 클래스는 본적이 있을테지만, int.class 란 클래스를 본 적이 있을까? 객체지향 프로그래밍을 한다면서 객체(Object)가 아닌 primitive type을 사용한다는 건 어딘가 어색하다. OOP를 공부하면 공부할수록 Integer 를 쓸 수 밖에 없다는 생각은 확고해질 뿐이다. 그래서 나는 primitive type을 사용하지 않고, 가능한 한 wrapper type을 사용한다. 

그렇다면 왜 Java에는 primitive type이 존재할까? 이유는 크게 두 가지다. 첫째는 일부 성능 마니아들을 위한 선택지,  둘째는 하위 호환성이다. 분명 성능 측면에서는 primitive type이 훨씬 좋은 게 사실이다. wrapper type은 결국 primitive type을 한 번 감싼 구조이기 때문에, 접근 속도나 연산 속도 면에서 손해가 발생할 수밖에 없다. 여기서 말하는 성능 차이는 메모리 번지에 있는 이진 데이터를 직접 접근하느냐, 아니면 객체를 통해 primitive type 을 찾아가는 reference를 거치느냐의 차이다.

하지만 흥미로운 점도 있다. 메모리 사용 관점에서는 wrapper type이 오히려 더 효율적이다. 이는 wrapper type이 내부적으로 캐시 전략을 활용하기 때문이다. 예를 들어, Integer.valueOf() 같은 정적 팩토리 메서드는 자주 사용하는 숫자 값들을 캐시해두고, 동일한 값을 요청하면 기존 객체를 재활용한다. 물론 이런 캐시 전략은 구조적으로 primitive type 에 비해 wrapper type 이 메모리 낭비를 한다고 주장하는 이도 있다. 캐싱을 행하기 위한 비용이 분명히 생기기 때문이다. 하지만 숫자 1의 값을 가지는 int 변수가 100개가 있다면 100개의 정수 1이 만들어지겠지만, Integer 변수라면 1개의 정수 1이 있을 뿐이다. 100개가 아닌 1억개로 늘어난다면 그 차이는 기하급수적으로 늘어난다. 디자인패턴으로 비유 하자면 Flyweight 패턴과 동일하다. 이런 전략 덕분에 메모리 사용이 최적화될 수 있다. 여유가 된다면 JVM의 constant pool(상수 풀)이나 내부 캐시 메커니즘에 대해 공부해보면 도움이 될 거다.

성능 관점으로 파고들면 Integer 를 써야할 이유에 대해서 공감대가 적을 수도 있다. 굳이 성능에서 접근 할 필요 없이 OOP 패러다임으로 프로그래밍 하기 위해서라는 측면으로만 보아도 wrapper type 을 써야할 이유는 충분하다. 

이야기가 조금 샜지만, 결론은 분명하다. JPA에서는 반드시 Integer와 같은 wrapper type을 써야 하고, 객체지향 개발자라면  wrapper type을 사용하는 할 수 밖에 없다는 것이 결론이다.

끝.