Post

404의 함정 - 사라진 커리큘럼을 찾아서

404의 함정: 사라진 커리큘럼을 찾아서


오늘은 DOORE 프로젝트 QA 중 발생한 커리큘럼 문제와 해결 과정을 작성하려 한다. 원인을 찾느라 이틀을 꼬박 썼다. 멋있게 해결하고 트러블슈팅 기록을 남기고 싶었지만, 생각보다 허무하게 해결돼 삽질일기로 기록한다. 하지만 얻은 깨달음이 많아 남기기로 했다.

1. 문제 발단


QA 과정에서 커리큘럼에 문제가 발견됐다. 의도한 동작이 전혀 안되는 심각한 문제였다.

  • 최초 생성만 되고, 수정이 전혀 안 되었다.
    • 추가 생성, 순서 변경, 내용 변경, 삭제 모두 불가
  • 커리큘럼 체크가 동작하지 않았다.
    • 체크는 가능하지만 저장되지 않아 작물이 자라지 않음
    • 새로고침 시 체크가 사라짐

초기 QA에선 정상 작동했는데, 갑자기 문제가 생겼다. 또한 프론트와 백엔드 모두 코드를 고치지 않았다고 했다.

2. 문제 분석


image5-1

image5-2

해당 예외 메시지를 보고 어디서 예외를 던지는 지 찾아보았다. “커리큘럼이 존재하지 않습니다.” 라는 예외는 커리큘럼을 생성 & 수정할 때만 발생한다.

해당 로직은 다음과 같다.

image5-3

curriculumIdnull이면 커리큘럼을 생성하고, 아니라면 수정한다. (RESTful 하지 않지만, 프론트 요청과 백엔드 회의를 통해 커리큘럼만 이렇게 진행하기로 했었다.) 여기서 해당 예외는 커리큘럼 수정 시 커리큘럼을 찾을 수 없을 때 발생하고, 이 의미는 커리큘럼을 생성해야 하는데 생성 분기를 타지 않고 수정 분기를 탔다는 것이다.

3. 문제 찾기 1 - 로컬에서 요청해보며 로컬 DB 동작을 확인해보자.


가장 먼저 생각했던 것은 ‘로직이 잘못되었나?’ 였다. 왜냐하면 생성, 삭제, 수정 그리고 커리큘럼 순서 매기기 까지 모두 하나의 api에 있기 때문에 커리큘럼 로직이 꽤 복잡하기 때문이다.

하지만 로컬에서는 의도했던 동작대로 돌아갔다. 그렇다면 로직 문제는 아니라고 생각했다.

4. 문제 찾기 2 - 요청이 들어오면 제대로 분기를 타는 지 확인해보자.


다음으로 id 값이 null일 때, 정상적으로 생성 분기를 타는 지 확인하고 싶었다. 혹시 nullnull로 인식하지 못해서 잘못된 분기를 타는 것은 아닐까 생각했다. 그래서 코드를 아래와 같이 수정했다.

image5-4

하지만 이것도 정상적으로 동작했다. null이어도, 아무 값이 없어도 동작했다. 당연히 null을 받아야 하기 때문에 Wrapper class을 사용했고, 값이 없을 경우 기본 값인 null로 받기 때문이다. 결론적으로 코드에는 문제가 없다는 의미였다.

5. 문제 찾기 3 - 그렇다면 요청이 잘못되지 않았을까?


다음으로 했던 생각은 ‘요청에 문제가 있다!’ 였다. 하지만 실제 서버 요청을 확인했을 때, null로 잘 들어오고 있었다.

image5-5

6. 문제 찾기 4 - DB를 초기화하면 잠시 정상적으로 동작했었다. → DB 초기화를 해보자.


과거에 DB를 초기화했을 때, 잠시 정상 작동한 기억이 났다. 그래서 DB를 다시 초기화하고 테스트했다. 첫 번째 스터디만 정상 동작했고, 이후 다시 문제가 발생했다.

7. 문제 찾기 5 - id가 이상하다.


이제 느낌이 왔다. 이건 DB를 확인해보면 알 수 있을 것 같았다. 마침 Infra 담당자로부터 이전 데이터베이스가 남아있으니 오류 분석이 필요하면 포트를 개방해주겠다고 연락이 왔다.

8. 찾아내다.


접속 정보를 받은 후 확인을 해보니 정말 id가 이상했다. 요청 id가 90번대 인데, 실제 id는 70번대였다.

  • 요청

image5-6

  • 실제

image5-7

그래서 운영 서버에 커리큘럼을 생성한 뒤 id 확인을 부탁했고, 역시 id가 이상한 것을 확인했다. 예시로 만든 구운치킨, 삶은치킨, 튀긴치킨 커리큘럼의 id와 실제 저장된 id가 달랐다.

  • 요청

image5-8

  • 실제 - Curriculum

image5-9

id가 원래 저장된 것보다 크다는 것을 확인했고, 혹시 참여자 커리큘럼 id가 오는 것은 아닐까 생각했다.

그리고 다시 확인해봤을 때, 참여자 커리큘럼 id와 동일하다는 것을 알게 되었다.

  • 실제 - ParticipantCurriculum

image5-10

정상적인 저장 및 조회 흐름은 다음과 같다.

  1. 팀장이 커리큘럼을 생성한다. (커리큘럼 id = 1)
  2. 스터디 참여자 각각 커리큘럼이 생성된다. (참여자 커리큘럼)
    • 참여자가 2명이라면, 참여자 커리큘럼은 2개 생성

커리큘럼 조회 시 커리큘럼 id로 조회해야 한다. 하지만 참여자 커리큘럼 id로 조회하여 “커리큘럼이 존재하지 않습니다.” 오류가 떴던 것이다. 첫 번째 요소가 id가 null이 아니기 때문에 수정 분기로 들어갔고, id를 찾을 수 없으니 예외를 던진 것이다.

9. 왜 헤맸을까?


당시 생각의 흐름

오 404 에러네? → 내가 404 띄우도록 해뒀지 → (+)내 문제구나! → 디버깅 해봐야지

그 때 당시 서버 문제를 Service Layer에서 비즈니스 예외로 감싸서 404 에러로 던졌다고 생각했다. 404 에러에도 요청 값 자체를 먼저 의심하지 않고 서버 내부 로직만 계속 확인했다.

간과한 부분

  • 커스텀 예외를 명확히 설정했기 때문에 내부 로직 문제가 발생할 가능성은 낮았다.
  • @Transactional@GeneratedValue 때문에 저장은 항상 되었을 것이다. 단지 원하는 값이 아닐 수는 있어도 저장 자체가 안 되는 문제는 없다.

정상적인 문제 분석 흐름은 다음과 같아야 했다.

404 발생 → id가 잘못 전달됐나? → DB id와 요청 id를 비교 → 프론트로 문제 전달

DB 접근이 제한된 환경이라 빠르게 확인하지 못했다. 근본적으로 코드와 로직에 대한 확신 부족이 문제였다.

그리고 백과 프론트의 코드의 변화가 없었는데, 문제가 생겼던 이유는 해당 파트 프론트 담당자의 변경 때문인 것 같다. 제대로 인수인계가 되지 않아서 코드가 꼬인 것 같다.

10. 결론


총 삽질 시간 : 2일

  • 오류 발생 시 가장 먼저 데이터와 id값 정확성부터 확인한다.
  • 예외 메시지를 명확히 정의했으니 로직을 무조건 의심하기보다 요청 데이터를 빠르게 검증한다.
  • 코드에 대한 확신을 갖자.
This post is licensed under CC BY 4.0 by the author.