JPA 엔티티의 UUID를 MySQL BINARY(255)에 저장한 뒤 같은 UUID로 조회하지 못한 문제를 재현하고 원인을 좁혀 간다. H2와 개발용 MySQL의 테스트 결과가 다르다는 단서에서 출발해, UUID의 실제 크기와 MySQL BINARY 타입의 오른쪽 패딩 규칙을 확인한다. BINARY(16)으로 스키마를 맞추는 것이 단순한 우회가 아니라 데이터 표현과 조회 조건을 일치시키는 해결책임을 SQL로 검증한다.
핵심 포인트- UUID 기본 키를 저장했지만 findById가 값을 찾지 못했고, H2 기반 테스트와 MySQL 기반 테스트가 서로 다른 결과를 냈다.
- UUID는 16바이트인데 컬럼이 BINARY(255)여서 MySQL이 나머지 공간을 오른쪽 패딩 값으로 채웠다.
- Hibernate는 UUID를 기본적으로 binary로 저장하므로 엔티티 매핑과 실제 컬럼 길이의 불일치가 조회 실패로 이어졌다.
- @Column(columnDefinition = "BINARY(16)")으로 길이를 명시하자 애플리케이션 조회가 정상화됐다.
- 직접 만든 테이블과 SQL로 패딩 포함 여부, 16바이트 조회 실패, RPAD를 넣은 조회 성공을 차례로 재현했다.
상세 정리- 증상: UUID를 ID로 쓰는 엔티티를 먼저 저장하고 다른 기능에서 같은 UUID로 조회했지만 Repository가 null을 반환해 예외가 발생했다.
- 재현 전략: 기능별 테스트 컨텍스트를 비교했다. 저장 기능은 H2 인메모리 DB에 의존했고 조회 기능은 개발 MySQL에 의존한다는 차이를 발견했다.
- 초기 가설: UUID 하나는 16바이트라는 자료와 실제 스키마를 대조하니 컬럼이 BINARY(255)로 생성돼 있었다.
- 첫 수정: 엔티티 ID 필드에 BINARY(16)을 명시했다. 동일한 조회 테스트가 통과해 컬럼 길이가 문제와 연결돼 있음을 확인했다.
- 추가 의문: H2와 MySQL의 binary 처리 차이로만 결론내리지 않고, MySQL 공식 문서에서 BINARY 값이 지정 길이까지 오른쪽 패딩된다는 규칙을 찾았다.
- 매핑 근거: RFC 4122에서 UUID 크기가 16바이트임을 확인했고, Hibernate가 UUID의 기본 표현으로 binary를 사용한다는 가이드도 대조했다.
- 독립 재현: BINARY(255) 기본 키를 가진 temp 테이블을 만들고 MySQL uuid()로 테스트 값을 생성했다.
- 바이트 확인: UUID 문자열에서 하이픈을 제거하고 UNHEX한 값의 length를 조회해 실제 길이가 16임을 확인했다.
- 저장 확인: 16바이트 값을 BINARY(255)에 넣은 뒤 length(id)와 hex(id)를 조회하자 남은 영역에 패딩 값이 들어갔다.
- 실패 검증: 원래의 16바이트 UNHEX 값만 조건으로 사용하면 저장된 행을 찾지 못했다. 애플리케이션에서 나타난 증상과 동일하다.
- 성공 검증: 조회 조건도 RPAD로 255바이트까지 채우자 행을 찾았다. UUID 자체가 잘못된 것이 아니라 고정 길이 컬럼의 저장 표현이 달랐던 것이다.
- 결론: UUID binary 컬럼은 실제 16바이트 크기에 맞춰 선언해야 한다. 테스트 DB가 운영 DBMS와 다른 경우 타입·패딩 의미 차이가 결함을 숨길 수 있다.
왜 읽나JPA와 MySQL에서 UUID 기본 키를 쓰거나 H2 테스트와 운영 DB의 동작 차이를 추적하는 백엔드 개발자에게 재현 가능한 디버깅 사례가 된다.