2월 15일 UMC에서 demo day 행사가 있었다.
선발된 36팀이 여섯 달 동안 진행한 프로젝트를 다른 사람들에게 보여주는 행사였다.

버그 발견

“vibe를 하나 삭제해도 삭제되었다고 메세지만 출력되고 실제로 지워지지 않아요”

앱 시연까지 한 시간정도 남은 상태였다.
PM과 해당 도메인 담당자가 매우 조급해했다. 못 고칠거같으면 안 고쳐도 된다고 했다.
버그를 발견했다는 말을 들었을 때에는 당황했지만 삭제가 되지 않는다는 말에 안심했다. 단순한 기능이라 복잡할거같지 않았기 때문이다. (사실 앱 전체가 단순하다)

문제 파악

버그가 발생하는 조건은 다음과 같았다.

  • album 메뉴에 들어가면 자신이 생성한 vibe가 전체 조회되고 사진이 출력된다.
  • 화면에 보여지는 사진 하나를 선택하여 삭제하기를 누르면 삭제되었다는 메세지가 출력된다.
  • 하지만 album에는 여전히 해당 사진이 보여진다.

삭제되었다는 메세지가 출력되는 건 프론트에서 삭제 버튼을 누르는 경우 이 메세지가 출력되도록 프로그래밍했기 때문이였다.
서버의 문제 원인을 파악하기 위해 일단 로그를 살펴보았다.

DB에 접속해 테이블에 저장된 데이터를 직접 확인했는데 post는 지워졌는데 vibetag 테이블의 데이터가 그대로 남아있었다.

이 사실로 보아 테이블의 데이터 삭제 과정은
vibe 튜플 삭제 요청
-> vibe의 pk를 참조하는 테이블(post) 확인
-> post 튜플 삭제 시도
-> 성공(onDeleteAction.CASCADE)
-> tag 튜플 삭제 시도
-> 실패(onDeleteAction이 설정되지 않았기 때문) 순으로 실행되는 것 같았다.
이 일련의 과정을 transaction으로 묶지 않아 중간에 실패했기 때문에 post만 지워지고 나머지는 살아 있었다.

log file

문제의 원인은 외래 키 조건 때문에 특정 테이블을 삭제할 수 없는 것이였다.

tag 테이블의 외래 키 조건으로 인해 해당 vibe의 데이터(튜플)을 삭제할 수 없는 상황이였다.

테이블들의 외래 키 참조 관계를 쭉 훑어보았다.

외래 키 참조 관계

post 테이블의 데이터는 참조하는 vibe 테이블의 데이터가 삭제되면 같이 삭제되도록 설정되어있었지만 tag 테이블에는 그런 코드를 볼 수 없었다.
대신 tag 테이블과 post 테이블이 @SecondaryTable이라는 관계로 묶여 있다는 것은 알 수 있었다.

결론은 vibe 테이블 데이터를 삭제하기 전에 tag를 먼저 삭제되도록 하면 해결될 문제였다.

일단 해결

@SecondaryTable에도 onDelete를 설정해주고 싶었지만 방법을 찾지 못해 일단 내가 확실히 아는 방법을 적용하기로 했다. 1시에 밥을 먹지 못하면 점심을 못 먹을 것 같았기 때문에 빨리 해결하고 싶었다.

album 도메인의 DAO 인터페이스에 두 개의 메서드를 추가하고 AlbumService.deleteVibe()' 메서드에 @Transactional` 어노테이션을 붙여 트랜잭션으로 묶어줬다.

deleteVibe(Long vibeId) 메서드 실행 과정

  • 삭제하려는 Vibe 인스턴스 획득
  • Vibe에 대응되는 Post 인스턴스 획득 - AlbumRepository.getPostIdByVibeId(vibeId)
  • Post 인스턴스의 post_id값을 사용해 Tag 삭제 - AlbumRepository.deleteTagByPostId(targetPostId)

앞으로 해결해야 할 문제

버그를 해결하면서 DAO 객체들을 봤는데 전체적으로 SQL query에 대한 의존도가 높다.
JPA를 사용하긴 했지만 테이블을 생성하는데 조금 사용했을 뿐 나머지 DB 접근 방식은 객체지향적 사고와는 거리가 멀다. (대부분 Query를 직접 작성해 접근한다.)
JPA를 공부해서 객체지향적으로 DB에 접근하는 코드를 작성하자.

버그 해결에 대한 사후 평가

문제 해결 과정을 정리하다 보니 지금까지는 왜 이 문제가 발견되지 않았고 해결 방법의 문제점을 찾았다.
항상 앱이 사용되는 흐름을 파악해야지 하면서 그러지 못했는데 앱의 전체적인 흐름을 알았다면 이런 생각은 진작에 할수 있었을 것이다.
어제 데모 데이에서 앱을 직접 사용해보니 api에 허점이 있었음을 알게 되었다.

vibe까지만 생성하고 post를 작성하지 않을 경우 tag를 생성하지 않는다. 그렇기 때문에 게시글을 작성하지 않은 vibe는 문제없이 잘 삭제되었던 것이다. 그리고 삭제를 해보지 않았다. 그런데 어제 문제를 해결한 방식은 다시 이 이유 때문에 오류가 발생할 것이다.
수정된 코드는 vibe에 대한 posttag가 반드시 존재할 거라 예상하고 두 데이터를 조회하고 삭제하고 있기 때문이다. 위와 같은 가설을 확인해보기 위해 deleteVibe() 메서드에 아래와 같은 코드를 추가 한 뒤 postman에서 vibe 삭제 요청을 보내 보았다.

LOGGER.warn("targetPostId: " + targetPostId);

예상이 반만 맞았다. post가 없는 vibe에 대해서는 post_id값을 찾지 못했지만 오류가 발생하지는 않았다.
Spring Data JPA

결과

UMC 활동을 마무리하며

지금까지 해본 팀플레이 중에 가장 재미 있었다. 😁

Vibecap 팀

Comments