일시적 버그 방지

이 페이지에서는 개발자가 일시적 버그를 방지하기 위해 따라야 할 아키텍처 패턴 및 팁을 다룹니다.

공통 원인

일시적 버그를 해결할 때 자주 나타나는 몇 가지 원인을 알아보았습니다.

  • 백엔드 또는 프론트엔드에서 상태 관리가 더 필요합니다.
  • 프론트엔드 코드 개선이 필요합니다.
  • 테스트 커버리지 부족
  • 레이스 컨디션

프론트엔드

응답 순서에 의존하지 마세요

여러 요청을 처리할 때 응답의 순서가 트리거된 순서와 일치한다고 가정하기 쉽습니다.

그러나 항상 그렇지는 않고, 응답 순서가 바뀌면 발생하는 버그가 있을 수 있습니다.

예시:

  • diffs_metadata.json (가벼움)
  • diffs_batch.json (무겁음)

요청 사항이 두 요청의 데이터를 필요로 하는 경우, 작업하기 전에 두 요청이 모두 로드되었음을 확인하세요.

수동 테스트 시 느린 연결 모의

브라우저의 개발자 도구에 네트워크 조건 템플릿을 추가하여 느린 연결과 빠른 연결 사이를 전환할 수 있도록 합니다.

예시:

  • 거북이:
    • 다운로드: 50kb/초
    • 업로드: 20kb/초
    • 지연 시간: 10000ms

축소된 요소

이벤트 리스너를 설정할 때 이벤트 위임을 사용할 수 없다면, 확장된 콘텐츠에 대해 모든 관련 이벤트 리스너가 설정되었는지 확인하세요.

그 확장된 콘텐츠가 다음과 같은 경우에도 해당됩니다:

  • 보이지 않는 (display: none;). 일부 JavaScript는 측정이 필요할 때와 같이 요소가 보이는 것을 요구하여 제대로 작동합니다.
  • 동적 콘텐츠 (AJAX/DOM 조작).

충족되지 않은 조건에 의한 일시적 버그 검출을 위한 어서션 사용

일시적 버그는 응용 프로그램의 상태가 하나 이상의 조건을 충족하고 있다는 가정 하에 실행되는 코드의 맥락에서 발생합니다. 때로는 서버 측 API 응답이 항상 여러 속성 그룹을 포함한다고 가정하는 기능을 작성할 수 있으며, 어떤 작업은 응용 프로그램이 새로운 상태로 성공적으로 전환된 경우에만 실행된다고 가정할 수 있습니다.

일시적 버그는 보통 코드에서 명시적으로 나타나지 않는 이러한 조건들로 인해 디버깅하기 어렵습니다. 이러한 상황에 유용한 디버깅 기술은 가정을 명시적으로 만들기 위해 어서션을 배치하는 것입니다. 이러한 어서션은 충족되지 않은 조건으로 인해 발생한 원인을 검출하는 데 도움이 될 수 있습니다.

상태 변이에 대한 사전 조건 어서션

일시적 버그로 이어지는 일반적인 시나리오는 사용자 작업이 완료된 경우에만 상태를 변이해야 하는 폴링 서비스가 있는 경우입니다. 다음과 같이 어서션을 사용하여 사전 조건을 명시적으로 만들 수 있습니다:

// 이 작업은 폴링 서비스에 의해 호출됩니다. 이 작업이 디스패치된 시점에 모든 사전 조건이 충족되었다고 가정합니다.
export const updateMergeableStatus = ({ commit }, payload) => {
  commit(types.SET_MERGEABLE_STATUS, payload);
};

// 다음과 같이 어서션을 추가하여 모든 사전 조건을 명시적으로 만들 수 있습니다.
export const updateMergeableStatus = ({ state, commit }, payload) => {
  console.assert(
    state.isResolvingDiscussion === true,
    'Resolve discussion request must be completed before updating mergeable status'
  );
  commit(types.SET_MERGEABLE_STATUS, payload);
};

API 계약 어서션

어서션을 사용한 또 다른 유용한 방법은 서버 측 엔드포인트에서 반환된 응답 페이로드가 API 계약을 충족하는지를 검출하는 것입니다.

관련 글 읽기

Debug it!는 결정론적이지 않은 버그를 진단하고 수정하고 디버깅하기 쉬운 소프트웨어를 작성하는 기술을 탐구합니다.

백엔드

잠금을 사용한 Sidekiq 작업

Sidekiq을 통해 비동기 작업을 처리할 때, 동일한 인수를 가진 2개의 작업이 동시에 처리될 수 있습니다. 이를 올바르게 처리하지 않으면 오래된 또는 부정확한 상태가 될 수 있습니다.

예를 들어, 객체의 상태를 업데이트하는 워커가 있을 때, 워커가 객체의 상태를 업데이트하기 전에 적절한 상태인지 확인해야 합니다.

동시에 2개의 작업이 처리될 때 다음과 같이 작업이 이루어질 수 있습니다:

  1. (워커 A) #check_state 호출
  2. (워커 B) #check_state 호출
  3. (워커 B) #update_state 호출
  4. (워커 A) #update_state 호출

이 예에서 워커 B가 업데이트된 상태를 설정해야 합니다. 그러나 워커 A#update_state를 너무 늦게 호출합니다.

이러한 상황을 피하기 위해 데이터베이스 잠금 또는 Gitlab::ExclusiveLease를 활용할 수 있습니다. 이렇게 하면 작업이 한 번에 하나씩만 처리됩니다. 이는 또한 [멱등성] (../sidekiq/idempotent_jobs.md)으로 표시될 수 있습니다.

재시도 메커니즘 처리

때로는 개체/레코드가 실패한 상태에 있을 수 있으며 다시 확인할 수 있습니다.

개체가 다시 확인 가능한 상태에 있는 경우, 사용자가 무엇을 해야 하는지 알 수 있도록 적절한 메시지를 표시하고, 다시 시도 기능이 상태를 올바르게 재설정할 수 있도록 해야 합니다.

오류 로깅

오류 로깅은 일시적 버그가 발생하는 것을 직접 방지하는 것은 아니지만, 디버깅에 도움이 될 수 있습니다.

코딩할 때 가끔 예외가 발생할 것으로 예상하고 그것들을 rescue하는 경우가 있습니다.

오류가 발생했을 때 항상 로그를 기록하는 것은 사용자가 볼 수 있는 일시적 버그를 유발할 수 있는 원인일 수 있습니다. 버그 보고서를 조사하는 동안 그 때 로그를 살펴봐야 할 수 있습니다. 로깅된 오류를 보는 것은 다르게 처리할 수 있는 어떤 일이 잘못되었다는 신호일 수 있습니다.