[삽질기] 첫 번째 Item을 삭제할 때 "가끔" 터지네요?

햄스터아저씨·2021년 7월 10일
0

아마 누구나 이런 말을 들으면, 에이 설마 싶을꺼다.

언제는 되고, 언제는 안되고...
이런류의 문제는 복잡할 것이 분명하기 때문에 보고 싶지도 않다.

하지만 재현되는 걸 보면 어쩌겠어.. 봐야지..


이슈 현상 분석

  1. 페이지에 들어있는 첫번째 아이템 삭제시 문제 발생
  2. 삭제에 성공할 때도 있고, 실패할 때도 있음.
  3. 로그를 통해 아이템이 삭제시 redux 내에서 특정 변수의 예상치 못한 undefined로 인해 동작이 멈춤을 확인
    아니 이건 undefined가 나올 수 없는 부분인데?

재현

  • 새로운 페이지가 생성된 뒤, 그 페이지에 아이템을 2개 만들 경우
    ②번은 삭제가 되지만, ①은 삭제가 안됨.

  • 그런데 앱을 다시 실행한 뒤
    다시 삭제를 시도하면 삭제된다!


1차 분석 - 로직/변수 검토

  1. 삭제대상 확인
    삭제될 아이템 구조는 다음과 같다.
interface IChat {
  id: number; //this is created at.
  userId: number;
  content: string;
  d: boolean; //is having date
  p: number; //page
}
  1. undefinded 가 되버린 변수 생성 로직을 검토
    로직 자체의 이상은 없음.
    그랬다면 삭제 자체가 불가능 했어야 함.

  2. 생성에 필요한 변수 검토
    로직 앞 뒤로 생성에 필요한 모든 변수를 출력해 확인

확인 결과

  • 삭제 대상인 아이템이 가지고 있는 p값이 예상과 다름
    원래 값이 1이여야 한다면
    • 언제는 0이고
    • 언제는 1이다
      다른 아이템도 아니고 id랑 다른 들은 모두 정상인데, p만 다르다.

2차 분석 - 가설과 확인

  • 잘못 생성했기 때문에 잘못된 데이터가 들어오겠지(추측)

    대상 아이템은 redux의 add() 리듀서에서 저장이 된다.
    그런데 생성된 원본 데이터는 1로, 잘 생성이 된다.
    생성하고 저장하는 부분은 문제가 없으므로, add() 함수는 잘못이 없는 것 같다.🤔

  • 그렇다면 중간에 누군가 데이터를 변형하는가?

    그렇다면 remove() 리듀서가 문제일 것이다.
    그러나 실패 후에 다시 삭제를 요청했는데 성공했다는 것은
    원본 item은 바뀌지는 않는다는 것이다.
    그러면 뭐가 문제지?? p값이 이상한 데이터가 요청되는건 확실한데?
    원본 데이터와, 요청할 때 값이 다르다고?🤔

  • remove() 리듀서가 아닌, item 자체가 문제인가?
    (이 부분을 생각하기까지 상당히 시간이 걸렸다.)

    그러면 remove() 함수를 호출하는 곳에서 로그를 찍어보자.
    호출 직전 p 값에 원래 문제가 있었다.
    호출을 하는 부분은 View 컴포넌트다.
    아!

    item이 2개였다.

    1. redux가 들고있는 원본 item
    2. 그리고 이번에 문제가 된 사용자가 보고있는 View에 노출된 item
  • 사용자가 보고있는 item이 문제인가?

    원래 사용자가 보는 item도 add() reducer를 통해 생성이 된다.
    p값은 add()함수 내에서 결정지어진다는 것.
    그리고 문제의 원인을 발견했다.

    add() 리듀서에서 p값을 보정한 뒤 저장하는데,
    View가 사용해야 할 item을 지정해준다.
    이때 p값이 보정이 안된, 생성 과정 중에 생긴 item을 보내준 것.

  • View를 닫을 때 잘못된 값을 가진 item은 사라지고
    다시 열릴 때 정상 item으로 채워지므로 버그 현상이 들쑥날쑥 했었던 것이다.

결국 문제를 해결했다.


피드백

  • 2차 분석시 처음부터 생성 이 문제일 것으로 예측했음에도, 첫 분석에서 바로 잡아내지 못하고 시간을 소모했다.

  • 잡지 못한 이유는 이 부분이다

    생성하고 저장하는 부분은 문제가 없으므로, add() 함수는 잘못이 없는 것 같다.🤔

    저 생각은 add() 생성/저장 부분까지만 봤기 때문에 한 생각이지만
    add() 리듀서가 하는 역할은 마치 트랜젝션 같았다.

    • 검증
    • 변형
    • 페이지 생성
    • 저장
    • 영향을 받는 아이템들 처리
    • 메타정보 변경
    • View 가 사용할 수 있도록 따로 준비해두기

    하는일이 너무 많다보니, 저기서 4번째 저장부분 까지만 보고,
    마지막 부분인 따로 저장하는 부분에서 실수를 했던 것을 놓쳤던 것.

  • 그래도 놓칠 수 밖에 없었다.
    View가 호출하는 아이템이 원본과 다르며,
    문제 있을 것이라고는 이때까진 생각하지 못했기 때문.


교훈

  1. 코드 작성시, 의미가 생긴 객체에게는 변수를 할당하자.
    Add() 에서 생성된 데이터를 바로 state에 넣어버리고 따로 변수로 저장해두지 않으니,
    나중에 다시 쓰기 귀찮아서 action.payload 를 그대로 사용했던 것이 핵심실수.
    로직 진행중에 다시 사용하지 않을지언정,
    가독성 측면에서도 변수를 할당하는게 좋다.
  1. 버그 분석시, 추론보다 데이터 확인을 먼저 하자
    초반에 문제의 범위를 잘못된 추론으로 문제의 원인을 찾기 어려웠다.
    또 View에서 사용중인 ①임시 Item②원본 Item을 혼동해 분석에 시간이 더 걸렸다.
    이 부분은 View와 Data를 따로 관리하는 모든 경우에 혼동될 수 있으므로,
    항상 Data를 저장하는 주인이 누구인지 먼저 명확히 하고 문제범위를 줄인다면,
    버그 확인에 시간을 줄일 수 있을 것이다.

  2. 하나의 함수는 하나의 역할을 수행해야 한다
    할 수 없다면 분리할 수 있는 역할이라도 분리해야 한다
    현재 각 리듀서가 함수의 역할보다 트랜젝션의 역할을 수행하게 된 것은 근본적으로

    Saga 패턴 혹은 Redux-Saga 라이브러리를 쓰지 않아서

    Saga가 있었다면 각 리듀서를 몇개로 구분하고,
    그 역할을 모은 작업을 할 수 있었을 것이다.

profile
서버도 하고 웹도 하고 시스템이나 인프라나 네트워크나 그냥 다 함.

0개의 댓글