일시적 버그 방지
GitLab v19.1이 페이지에서는 개발자가 일시적 버그를 방지하기 위해 따를 수 있는 아키텍처 패턴과 팁을 다룹니다. 일시적 버그를 해결할 때 자주 등장하는 근본 원인들을 파악했습니다. 백엔드 또는 프론트엔드에서 더 나은 상태 관리가 필요합니다.
이 페이지에서는 개발자가 일시적 버그를 방지하기 위해 따를 수 있는 아키텍처 패턴과 팁을 다룹니다.
주요 근본 원인#
일시적 버그를 해결할 때 자주 등장하는 근본 원인들을 파악했습니다.
-
백엔드 또는 프론트엔드에서 더 나은 상태 관리가 필요합니다.
-
프론트엔드 코드에 개선이 필요합니다.
-
테스트 커버리지가 부족합니다.
-
레이스 컨디션(Race condition)이 발생합니다.
프론트엔드#
응답 순서에 의존하지 않기#
여러 요청을 처리할 때, 응답 순서가 요청을 트리거한 순서와 일치한다고 가정하기 쉽습니다.
하지만 항상 그런 것은 아니며, 순서가 바뀌는 경우에만 발생하는 버그가 생길 수 있습니다.
예시:
-
diffs_metadata.json(가벼운 요청) -
diffs_batch.json(무거운 요청)
두 데이터 모두 필요한 기능이라면, 두 요청이 모두 완료된 이후에 작업을 진행하도록 하세요.
수동 테스트 시 느린 연결 환경 시뮬레이션#
브라우저 개발자 도구에 네트워크 조건 템플릿을 추가하여 느린 연결과 빠른 연결 사이를 전환할 수 있도록 설정하세요.
예시:
- Turtle:
다운로드: 50kb/s
-
업로드: 20kb/s
-
지연: 10000ms
접힌 요소#
이벤트 리스너를 설정할 때, 이벤트 위임을 사용할 수 없는 경우 펼쳐진 콘텐츠에 대한 모든 관련 이벤트 리스너가 설정되어 있는지 확인하세요.
펼쳐진 콘텐츠가 다음에 해당하는 경우도 포함됩니다:
-
보이지 않는 요소 (
display: none;). 측정을 수행할 때와 같이 일부 JavaScript는 요소가 표시되어 있어야 정상적으로 동작합니다. -
동적 콘텐츠 (AJAX/DOM 조작).
어설션을 사용하여 조건 미충족으로 인한 일시적 버그 탐지#
일시적 버그는 애플리케이션의 상태가 하나 이상의 조건을 충족한다는 가정 하에 실행되는 코드 컨텍스트에서 발생합니다. 서버 측 API 응답이 항상 특정 속성 그룹을 포함한다거나, 애플리케이션이 새 상태로 성공적으로 전환된 경우에만 작업이 실행된다고 가정하는 기능을 작성할 수도 있습니다.
일시적 버그는 충족되지 않은 조건에 대해 사용자나 개발자에게 알려주는 메커니즘이 없어서 디버깅하기 어렵습니다. 이러한 조건들은 코드에 명시적으로 표현되지 않는 경우가 많습니다. 이런 상황에서 유용한 디버깅 기법은 어설션(assertion)을 배치하여 모든 가정을 명시적으로 만드는 것입니다. 어설션은 어떤 조건이 충족되지 않아 버그가 발생했는지 파악하는 데 도움이 됩니다.
상태 변경 전 사전 조건 어설션#
일시적 버그로 이어지는 일반적인 시나리오는 사용자 작업이 완료된 경우에만 상태를 변경해야 하는 폴링(polling) 서비스가 있을 때입니다. 어설션을 사용하여 이 사전 조건을 명시적으로 만들 수 있습니다:
// 이 액션은 폴링 서비스에 의해 호출됩니다. 액션이 디스패치될 때 모든 사전 조건이
// 충족되어 있다고 가정합니다.
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 job#
Sidekiq을 통한 비동기 작업을 처리할 때, 동일한 인수를 가진 2개의 job이 동시에 처리될 수 있습니다. 올바르게 처리되지 않으면 오래된 상태나 부정확한 상태가 발생할 수 있습니다.
예를 들어, 객체의 상태를 업데이트하는 워커를 생각해보세요.
워커가 객체의 상태(예: #update_state)를 업데이트하기 전에 적절한 상태가 무엇인지 확인해야 합니다(예: #check_state).
2개의 job이 동시에 처리될 때, 작업 순서가 다음과 같이 진행될 수 있습니다:
-
(Worker A)
#check_state호출 -
(Worker B)
#check_state호출 -
(Worker B)
#update_state호출 -
(Worker A)
#update_state호출
이 예시에서 Worker B가 업데이트된 상태를 설정해야 합니다. 그런데 Worker A가 #update_state를 너무 늦게 호출합니다.
데이터베이스 락 또는 Gitlab::ExclusiveLease를 활용하면 이 문제를 방지할 수 있습니다.
이 방법으로 job이 한 번에 하나씩 처리됩니다.
또한 job을 멱등(idempotent)으로 표시할 수 있습니다.
재시도 메커니즘 처리#
객체/레코드가 재확인할 수 있는 실패 상태에 있는 경우가 있습니다.
객체가 재확인 가능한 상태에 있는 경우, 사용자가 무엇을 해야 하는지 알 수 있도록 적절한 메시지를 표시하세요. 또한 재시도 기능이 트리거될 때 상태를 올바르게 재설정할 수 있는지 확인하세요.
오류 로깅#
오류 로깅이 일시적 버그를 직접적으로 방지하는 것은 아니지만 디버깅에 도움이 될 수 있습니다.
코딩할 때 일부 예외가 발생할 것으로 예상하고 이를 rescue하는 경우가 있습니다.
오류를 rescue할 때마다 로깅을 하면 사용자가 볼 수 있는 일시적 버그를 유발하는 경우에 도움이 됩니다. 버그 보고서를 조사할 때 해당 시점의 로그를 살펴봐야 할 수도 있습니다. 오류가 로깅된 것을 확인하면 다르게 처리할 수 있는 무언가가 잘못되었다는 신호가 될 수 있습니다.