Spring 중복 데이터 예외처리 트러블 슈팅(SQLIntegrityConstraintViolationException)

CHOI YUN HO·2022년 10월 7일
1

SW Maestro

목록 보기
9/13

소프트웨어 마에스트로에서 "온라인 강의 큐레이션 서비스 - curady"를 개발하며 생긴 일

Unique column에 대한 중복 입력 핸들링


다음과 같은 데이터로 유저의 일부 정보를 입력하거나 수정하는 상황이다.

{
  "requestTendencies": [
    {
      "tendencyType": "language",
      "tendencyName": "java"
    },
    {
      "tendencyType": "language",
      "tendencyName": "python"
    }
  ],
  "nickname": "string",
  "gitUrl": "string",
  "blogUrl": "string",
  "description": "string"
}

500 server error

로직에 문제가 없고 테스트 해봤을 때 잘 동작했기 때문에 머지 리퀘스트를 올렸고,
팀원도 approve해줘서 배포에 반영이 되었다.

근데 프론트엔드 팀원이 500 server erroor가 뜨니 확인해달라고 한다...

도대체 뭐가 문제일지 모르겠어서 머리가 아팠는데..

이마를 탁치며 닉네임 중복이라는 것을 깨달았다. (ㅠㅠ)

팀원이 나와 같은 테스트데이터로 요청을 날리고 있었고

저 데이터 중에 nickname은 unique column이다........

진짜 바보같고 내가 한심했지만, 이것 또한 경험이지 않을까....라고 생각하며
unique column에 대한 예외를 핸들링해주기로 했다 !!

Unique Column에 대해 같은 값이 들어왔을 때 발생하는 에러

java.sql.SQLIntegrityConstraintViolationException: (conn=591) Duplicate entry 'string' for key 'UK_s0axwawn9qd2tpt1v8alx9o0v'
	at org.mariadb.jdbc.export.ExceptionFactory.createException(ExceptionFactory.java:288) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.export.ExceptionFactory.create(ExceptionFactory.java:368) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.message.ClientMessage.readPacket(ClientMessage.java:137) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.client.impl.StandardClient.readPacket(StandardClient.java:824) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.client.impl.StandardClient.readResults(StandardClient.java:763) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.client.impl.StandardClient.readResponse(StandardClient.java:682) ~[mariadb-java-client-3.0.6.jar:na]
	at org.mariadb.jdbc.client.impl.StandardClient.execute(StandardClient.java:625) ~[mariadb-java-client-3.0.6.jar:na]

SQLIntegrityConstraintViolationException이 발생했다.

그래서 tryCatch 구문으로 구현했다.

???????????? 로그에 에러가 찍혀있는데, never thrown??

나에게 왜 이런 시련을.....

하지만 곧 바로 SODD(Stack Overflow Driven Development)를 통해 해결할 수 있었다.

DataIntegrityViolationException 이라는 생전 처음보는 Exception으로 처리하니까 잘 catch되었다..

그래서 다시 한 번 잘 살펴보니, 위에 에러 구문 바로 위에 이런게 있었다.

2022-10-07 02:48:29.120 ERROR 91412[o-auto-1-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_n4swgcf30j6bmtb4l4cjryuym]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

dispatcherServlet....ㅎ...ㅎ

Spring 초보로서 내린 결론은 다음과 같다.

스프링 내부의 dispatcherServlet에서 내가 잡고싶은 예외인 SQLIntegrityConstraintViolationException 를 먼저 잡은 뒤DataIntegrityViolationException으로 던져주는게 아닐까?

dispatcherServlet을 뜯어보자

spring bean으로 관리되고 있는 Servlet 인터페이스를 통해

dispatcherServlet을 찾을 수 있었다.


이렇게 handlerExceptionResolver를 리스트로 관리하고 있었고,

initHandlerExceptionResolvers() 에서 빈으로 등록된 HandlerExceptionResolver를 리스트에 추가한다.

그리고 processHandlerException() 에서 리스트를 돌면서 resolve한다.

이런 흐름이라면

리스트에 등록된 ExceptionResolver중에 exception을 미리 catch해서 던져주는게 있지 않을까?

리스트를 직접 확인하기 위해 디버깅을 시도했지만, 이걸 계속 하다가는 눈알이 빠질 것 같아서 잠시 보류한다. 좀 더 공부를해서 돌아오겠다.

별거 아니라고 생각했던 unique key로부터 시작해서 spring의 dispatcher servlet도 까보고 아주 조금이지만 동작도 이해해볼 수 있었다.

profile
가재같은 사람

0개의 댓글