본문 바로가기
💻 트러블슈팅 (에러 해결)

DB가 데이터를 뱉어내요! DataIntegrityViolationException 원인과 해결법 (영화관 지정좌석 비유)

by 데브프리 2026. 7. 25.

안녕하세요, 여러분의 든든한 IT 길잡이 데브프리입니다.

 

회원가입 기능이나 게시글 작성 기능을 만들고 나서 "이제 DB에 데이터가 잘 들어가겠지?"라며 테스트를 돌려봅니다. 그런데 스프링 부트(Spring Boot) 콘솔 창에 시뻘건 글씨와 함께 다음과 같은 무시무시한 에러가 터집니다.

 

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [...]

 

"데이터 무결성 위반? 내가 무슨 법이라도 어긴 건가?"

초보 백엔드 개발자들이 JPA나 Mybatis를 이용해 데이터베이스(DB)에 값을 넣을 때(INSERT/UPDATE) 정말 자주 만나는 단골 에러입니다.

 

오늘은 이름부터 압도적인 '데이터 무결성 위반 에러'의 원인을 영화관 지정좌석에 비유하여 알아보고, 실무에서 가장 흔하게 발생하는 3가지 케이스와 해결법을 완벽하게 정리해 드리겠습니다!

 

1. DataIntegrityViolationException, 도대체 넌 누구냐? (영화관 지정좌석 비유)

이 에러를 직역하면 "데이터베이스가 정해놓은 엄격한 규칙(제약 조건)을 네가 어겼다!"라는 뜻입니다. DB 입장에서는 자신의 질서를 망가뜨리는 불량 데이터를 단호하게 거부(거절)하는 것이죠.

DataIntegrityViolationException 중복 에러 영화관 비유
이 에러는 이미 예매가 끝난 영화관 'A1' 좌석표를 들고 와서 억지로 앉으려고 할 때, 영화관(DB) 시스템이 "중복 예매는 안 됩니다!"라며 쫓아내는 상황과 똑같습니다.

우리가 DB 테이블을 설계할 때는 여러 가지 규칙을 정합니다.

"이메일은 중복되면 안 돼(Unique)", "이름은 절대 비워두면 안 돼(Not Null)", "전화번호는 20글자를 넘으면 안 돼(Length)" 같은 규칙들이죠.

 

스프링 부트(백엔드)가 이 규칙을 깜빡 잊고, 이미 가입된 이메일로 또 회원을 넣으려고 하거나 필수 값을 텅 비운 채로 DB에 던져버리면, 깐깐한 문지기인 DB가 이 에러를 뱉으며 데이터를 튕겨냅니다.

 

2. 이 에러가 발생하는 3가지 흔한 실수와 해결법

에러 로그를 조금만 더 아래로 내려서 Cause by: 부분을 읽어보면, 정확히 어떤 규칙을 어겼는지 친절하게 알려줍니다. 가장 대표적인 3가지 원인을 살펴볼까요?

실수 ①: Duplicate entry (중복 데이터 밀어 넣기)

가장 흔하게 발생하는 케이스입니다. DB 컬럼에 UNIQUE 제약 조건(예: 회원 아이디, 이메일 등)이 걸려 있는데, 이미 테이블에 존재하는 똑같은 값을 또 저장하려고 할 때 발생합니다.

  • 로그 특징: Duplicate entry 'devfree@mail.com' for key 'users.UK_email'
  • ✅ 해결법: 데이터를 DB에 저장(Save)하기 전에, 이미 존재하는 값인지 먼저 검사하는 로직을 추가해야 합니다.
Java

// 🚨 나쁜 예: 검사 없이 무작정 저장
userRepository.save(newUser); 

// 💡 좋은 예: DB에 이미 이메일이 있는지 먼저 확인
if (userRepository.existsByEmail(newUser.getEmail())) {
    throw new IllegalArgumentException("이미 사용 중인 이메일입니다.");
}
userRepository.save(newUser);

실수 ②: Column cannot be null (필수 값 누락)

DB 설계 시 NOT NULL 조건이 걸려 있는 컬럼에 아무 값도 넣지 않고 null 상태로 저장하려 할 때 발생합니다. 프론트엔드에서 데이터를 안 보냈거나, DTO에서 Entity로 값을 옮겨 담을 때 개발자가 실수로 특정 필드를 빼먹은 경우가 대다수입니다.

  • 로그 특징: Column 'password' cannot be null

DB NOT NULL 제약조건 위반 에러
반드시 내용물이 있어야 하는 상자(Not Null 컬럼)를 빈 깡통(Null) 채로 DB에 밀어 넣으려 하면 즉시 튕겨 나옵니다.

  • ✅ 해결법:
    1. 프론트엔드에서 password 값을 제대로 보냈는지 (네트워크 탭 확인)
    2. 컨트롤러 로직에서 Entity를 생성할 때 해당 필드에 값을 정상적으로 set 했는지 꼼꼼히 확인하세요.

실수 ③: Data too long for column (데이터 길이 초과)

DB 컬럼은 VARCHAR(20)으로 최대 20글자까지만 받도록 설계해 두었는데, 사용자가 게시글 제목이나 이름 란에 50글자를 꽉꽉 채워서 억지로 쑤셔 넣으려고 할 때 터집니다.

  • 로그 특징: Data too long for column 'title' at row 1
  • ✅ 해결법: 프론트엔드와 백엔드 양쪽에서 길이 제한 유효성 검사(Validation)를 꼼꼼하게 걸어주어야 합니다. 백엔드 DTO에서는 @Size나 @Length 어노테이션으로 1차 방어를 해주는 것이 좋습니다.
Java

public class PostCreateDto {
    // 💡 DB에 가기 전에 스프링 부트 단에서 미리 길이를 차단합니다.
    @Size(max = 20, message = "제목은 20자를 넘을 수 없습니다.")
    private String title;
}

 

💡 [데브프리의 심층 면접 꿀팁: exists 검사만 믿었다간 '동시성(Race Condition)'에 당합니다!]

면접관이 "이메일 중복을 막기 위해 자바 코드에서 existsByEmail로 검사하면 완벽할까요?"라고 묻는다면 함정 질문입니다!

수천 명의 유저가 몰리는 서비스에서, 두 명의 유저가 '완벽하게 동일한 0.001초'에 같은 이메일로 가입 버튼을 누르면 어떻게 될까요? 둘 다 existsByEmail 검사를 통과해버리고, 결국 DB에 무작정 INSERT를 시도하다가 쾅! 하고 DataIntegrityViolationException이 터지게 됩니다. (이를 Check-Then-Act 동시성 문제라고 합니다.)

따라서 자바 코드에서의 검사뿐만 아니라, DB 테이블 자체에 무조건 UNIQUE 제약 조건을 걸어두는 것이 최후의 안전장치입니다. 그리고 이 동시성 충돌로 인해 발생하는 DataIntegrityViolationException을 스프링의 @RestControllerAdvice로 우아하게 낚아채서, 프론트엔드에게 "이미 가입된 이메일입니다(409 Conflict)"라고 예쁘게 응답해 주는 것이 진짜 시니어 백엔드 개발자의 클린 코드랍니다!

 

3. 요약: 트러블슈팅 체크리스트

DataIntegrityViolationException이 떴다면 쫄지 말고 로그 아래쪽의 Caused by: 문장을 읽은 뒤 아래 3가지를 대조해 보세요.

  1. Duplicate entry 인가? ➡️ 저장하려는 데이터 중 유니크(Unique) 설정이 된 값(이메일, 아이디 등)이 이미 DB에 존재하는지 확인.
  2. cannot be null 인가? ➡️ Entity 객체를 save() 하기 직전에 System.out.println이나 디버거로 해당 필드가 정말 비어있는지(null) 확인.
  3. Data too long 인가? ➡️ 사용자가 입력한 데이터 길이가 DB 테이블 컬럼의 최대 길이를 초과하지 않았는지 확인.

 

마무리

데이터베이스는 우리의 소중한 자산을 지켜주는 아주 깐깐하고 믿음직한 금고입니다.

이 금고가 붉은 에러를 내뿜으며 화를 낸다는 것은, "내가 지키고 있는 데이터의 청결함(무결성)이 깨질 뻔했잖아!"라고 알려주는 아주 고마운 경고입니다.

 

에러 메시지가 길다고 당황하지 마세요. 에러 로그 끝부분에 적힌 원인(Duplicate, Null, Too long)만 정확히 짚어낸다면 여러분도 단 3분 만에 우아하게 문제를 해결할 수 있습니다.

 

오늘 준비한 트러블슈팅 가이드가 여러분의 답답함을 뻥 뚫어주었기를 바랍니다. 오류를 고치다 막히는 부분이 있다면 언제든 댓글로 남겨주세요.

 

지금까지 데브프리였습니다. 감사합니다!

 

👉 실서버 뻗기 전에 꼭 확인하세요! 내 서버 스펙에 맞는 최적의 DB 커넥션 풀 개수 계산기 [링크]