Computer >> 컴퓨터 >  >> 네트워킹 >> 인터넷

Async JavaScript로 인터넷 연결 상태 확인하기: navigator.onLine의 한계와 Fetch API 대안

JavaScript만으로 앱이 지금 인터넷에 연결되어 있는지 확인할 수 있을까요? 답은 "가능하다"입니다. 이 글에서는 브라우저의 인터넷 연결 감지 문제에 대한 최신 해법을 소개합니다. 핵심 도구는 Fetch API와 Async & Await 기반의 비동기 코드입니다. 그 전에, 많은 개발자들이 가장 먼저 접하게 되는 전통적인 해법과 그 한계부터 짚고 넘어가겠습니다.

navigator.onLine 속성이란?

Navigator 인터페이스의 online 속성인 navigator.onLine은 브라우저의 온라인·오프라인 여부를 감지하는 데 널리 사용됩니다. online 및 offline 이벤트 리스너와 함께 사용하면 구현이 매우 간단해서 처음에는 매력적인 해법으로 보입니다.

navigator.onLine 구현 예제

먼저 load 이벤트 리스너를 등록합니다. 페이지가 로드되면 이 리스너가 navigator 인터페이스의 online 속성 값을 확인하고 상태를 화면에 표시합니다. online 속성은 불리언(true/false) 값을 반환하므로, 삼항 연산자로 상태 표시값을 설정하면 됩니다.

window.addEventListener("load", (event) => {
  const statusDisplay = document.getElementById("status");
  statusDisplay.textContent = navigator.onLine ? "Online" : "OFFline";
});

참고로 'navigator'라는 이름은 90년대의 Netscape Navigator 브라우저에서 유래했습니다.

HTML 페이지 중앙에 id가 "status"인 h1 요소를 배치하고 위 코드를 적용하면 "Online"이라는 텍스트가 표시될 것입니다. 하지만 이 코드는 페이지 로드 시점에만 h1을 갱신합니다. offline 및 online 이벤트 리스너를 추가해 두 이벤트가 발생할 때마다 상태 표시가 업데이트되도록 만들어 보겠습니다.

window.addEventListener("offline", (event) => {
  const statusDisplay = document.getElementById("status");
  statusDisplay.textContent = "OFFline";
});

window.addEventListener("online", (event) => {
  const statusDisplay = document.getElementById("status");
  statusDisplay.textContent = "Online";
});

Chrome 개발자 도구의 Application 탭에서 ServiceWorker 항목을 열면 브라우저가 오프라인인 것처럼 동작하도록 설정할 수 있습니다. Offline 체크박스를 몇 번 켜고 꺼보면, 상태 표시가 offline/online 이벤트에 즉시 반응하는 것을 확인할 수 있습니다.

Async JavaScript로 인터넷 연결 상태 확인하기: navigator.onLine의 한계와 Fetch API 대안
Chrome Dev Tools > Application 탭 > Service Workers > Offline 체크박스

조금 더 깊이 파보기

겉보기에는 위 방법이 간단하고 훌륭한 해법처럼 느껴집니다. 하지만 navigator의 online 속성과 관련 이벤트에 대해 조금 더 조사해 보면 문제점이 드러납니다.

CanIUse.com에서 navigator.onLine을 검색하면 대부분의 브라우저가 이 속성을 폭넓게 지원하는 것으로 나옵니다. 그런데 지원 표 아래의 주석을 보면 이런 문구가 있습니다.

"Online은 반드시 인터넷 연결을 의미하지 않습니다. 단순히 어떤 네트워크에 연결되었다는 의미일 수도 있습니다."

흠, 이것만으로도 사정이 달라집니다. 브라우저의 진짜 온라인 여부를 판단하려면 별도의 검증 수단을 마련해야 한다는 뜻입니다.

MDN 문서의 navigator.onLine 항목 역시 CanIUse.com의 내용을 뒷받침하며 다음과 같은 주의 사항을 덧붙입니다.

"브라우저마다 이 속성을 서로 다르게 구현합니다... 값이 true라고 해서 반드시 브라우저가 인터넷에 접근할 수 있다고 가정해서는 안 됩니다. 거짓 양성(false positive)이 나올 수 있습니다..."

이는 navigator.onLine을 인터넷 연결 감지 수단으로 사용하는 것에 대한 우려를 확실히 해줍니다. 외부 데이터 소스의 가용 여부가 중요한 애플리케이션에서는 이 속성이 큰 혼란을 일으킬 수 있습니다. 대표적인 예가 PWA(Progressive Web App)의 온라인 여부 판단입니다. MDN은 심지어 이렇게 권장합니다.

"브라우저의 실제 온라인 상태를 판단하려면 별도의 검증 수단을 개발해야 합니다."

실제로 "navigator online not working"으로 웹 검색을 해보면, 이 속성에 의존했다가 문제를 겪은 개발자들의 포럼 게시글을 쉽게 찾을 수 있습니다.

그렇다면 해결책은 무엇일까?

우리가 알아야 하는 것은 단순히 공유기나 로컬 네트워크에 연결된 상태가 아니라, 애플리케이션이 진짜 인터넷에 연결되었는지 여부입니다. JavaScript 파일로 돌아가 처음부터 다시 시작해 보겠습니다.

핵심 아이디어는 실제로 요청을 하나 보내고, 실패할 경우 에러 처리로 우아하게 대응하는 것입니다. 요청이 성공하면 온라인, 실패하면 오프라인인 것입니다.

여기서는 작은 이미지 파일을 주기적으로 요청해 온라인 상태를 판별하겠습니다. 모던 JavaScript는 Fetch API와 Async & Await 비동기 코드를 제공하므로, 이 도구들을 활용해 목표를 달성할 수 있습니다.

checkOnlineStatus() 함수 만들기

먼저 checkOnlineStatus라는 이름의 async 화살표 함수를 만듭니다. 이 함수는 navigator의 online 속성처럼 true 또는 false를 반환합니다.

함수 내부에는 try 블록을 두고, 1픽셀짜리 이미지에 대한 fetch 요청을 await 합니다. 이때 서비스 워커가 해당 이미지를 캐싱하지 않도록 주의하세요.

HTTP 응답 코드가 200~299 범위면 성공을 의미하며, 상태 코드 비교 결과를 그대로 반환합니다. 즉, 응답 상태가 200~299 사이면 true, 그렇지 않으면 false가 됩니다.

요청이 실패했을 때 에러를 잡아낼 catch 블록도 필요합니다. catch 블록에서는 false를 반환해 명백히 오프라인 상태임을 나타냅니다.

const checkOnlineStatus = async () => {
  try {
    const online = await fetch("/1pixel.png");
    return online.status >= 200 && online.status < 300; // true 또는 false 반환
  } catch (err) {
    return false; // 확실한 오프라인 상태
  }
};

다음으로 setInterval 메서드에 익명 async 함수를 전달합니다. 이 함수는 checkOnlineStatus 함수의 결과를 await 하고, 그 결과를 삼항 연산자로 평가해 현재 온라인 상태를 화면에 표시합니다.

예제 테스트를 위해서는 간격을 3초(3000밀리초)로 설정하세요. 물론 실제 환경에서는 너무 잦은 편입니다. 실무에서는 30초(30000밀리초) 간격이면 충분할 것입니다.

setInterval(async () => {
  const result = await checkOnlineStatus();
  const statusDisplay = document.getElementById("status");
  statusDisplay.textContent = result ? "Online" : "OFFline";
}, 3000); // 테스트용 간격, 실제 서비스에서는 30000(30초) 권장

새 코드를 저장한 뒤 Chrome 개발자 도구의 Application 탭으로 돌아가 오프라인 응답을 테스트해 보겠습니다.

Async JavaScript로 인터넷 연결 상태 확인하기: navigator.onLine의 한계와 Fetch API 대안
Chrome Dev Tools > Application 탭 > Service Workers > Offline 체크박스

async 기능이 들어간 load 이벤트 리스너도 잊지 말고 추가해야 합니다! load 이벤트 감지는 오프라인 가용성을 위해 서비스 워커를 활용하는 PWA에서만 중요할 가능성이 높습니다. 일반 웹 페이지나 앱은 어차피 인터넷 연결 없이는 로드되지 않기 때문입니다.

window.addEventListener("load", async (event) => {
  const statusDisplay = document.getElementById("status");
  statusDisplay.textContent = (await checkOnlineStatus())
    ? "Online"
    : "OFFline";
});

마지막으로 한 가지 팁

위의 interval 코드는 앱에서 연결 상태를 표시하기에는 좋습니다. 하지만 중요한 데이터 요청 직전의 상태가 아니라 20~30초 전에 확인된 연결 상태에 의존하는 것은 권장하지 않습니다.

따라서 실제 데이터를 요청하기 직전에 checkOnlineStatus 함수를 직접 호출하고, 그 응답을 평가한 뒤 데이터 요청을 진행하는 것이 안전합니다.

const yourDataRequestFunction = async () => {
    const online = await checkOnlineStatus();
    if (online) {
        // 데이터 요청 실행
    }
}

마무리

navigator.onLine은 폭넓게 지원되지만, 애플리케이션이 실제로 인터넷에 연결되어 있는지 판단하는 데는 신뢰할 수 없는 결과를 내놓습니다. Fetch API와 비동기 JavaScript를 활용하면 훨씬 더 안정적인 해법을 빠르게 구현할 수 있습니다.

전체 코드 gist는 GitHub에서 확인할 수 있으며, 아래는 직접 제작한 영상 튜토리얼입니다.