Skip to main content

브라우저 API

Views 4약 11분생성형 AI 활용
Summary

브라우저 API라는 새 도구 상자

지금까지 우리는 JavaScript로 값을 다루고, 조건을 나누고, 반복하고, DOM을 고치고, 이벤트에 반응했습니다. 이제 웹페이지는 꽤 그럴듯하게 움직입니다. 버튼을 누르면 화면이 바뀌고, 입력한 글자가 목록에 추가되고, 모달 창이 열렸다 닫힙니다.

그런데 사용자는 여기서 한 걸음 더 기대합니다.

페이지를 새로고침해도 다크 모드 설정이 남아 있기를 바랍니다. 장바구니에 담은 상품이 갑자기 사라지지 않기를 바랍니다. 검색 결과 주소를 친구에게 보내면 친구도 같은 화면을 볼 수 있기를 바랍니다. 날씨, 뉴스, 상품 가격처럼 서버에 있는 최신 데이터도 가져오고 싶어 합니다.

이때 필요한 것이 브라우저 API입니다. API는 Application Programming Interface의 줄임말입니다. 어렵게 느껴진다면 "프로그램이 다른 기능을 사용할 수 있도록 열어 둔 사용법" 정도로 이해하면 됩니다. 브라우저 API는 브라우저가 JavaScript에게 빌려주는 도구 상자입니다. 이 도구 상자 덕분에 JavaScript는 화면 안쪽의 계산을 넘어 브라우저 저장소, 타이머, 주소창, 네트워크 같은 기능을 다룰 수 있습니다.1

중요한 점이 하나 있습니다. localStorage, setTimeout, fetch, URLSearchParams 같은 이름은 JavaScript 문법 자체라기보다 브라우저 환경이 제공하는 기능입니다. if, for, const는 언어의 기본 문법이고, document.querySelector, localStorage.setItem, fetch는 브라우저가 제공하는 기능을 JavaScript 코드로 사용하는 것입니다.

사실 우리는 이미 브라우저 API를 쓰고 있었습니다. DOM도 브라우저가 제공하는 API이고, 이벤트도 브라우저가 사용자 행동을 JavaScript에 전달해 주는 API입니다. 이번 장에서는 그 범위를 조금 더 넓혀 저장소, 타이머, URL, 네트워크 요청을 차례대로 살펴보겠습니다.

브라우저 API

localStorage: 브라우저의 작은 기억 장치

JavaScript 변수는 새로고침에 약합니다. 페이지가 다시 로드되면 코드가 처음부터 실행되고, 변수에 들어 있던 값은 사라집니다.

let username = '윤서';

이 값은 현재 페이지가 살아 있는 동안만 존재합니다. 사용자가 새로고침하면 username은 다시 처음 상태가 됩니다. 할 일 목록 앱을 만들었는데 새로고침하는 순간 목록이 전부 사라진다면 사용자는 불편함을 느낄 것입니다.

localStorage는 이런 상황에서 사용할 수 있는 브라우저 저장소입니다. 같은 출처, 즉 프로토콜·호스트·포트가 같은 웹사이트 안에서 키와 값 형태로 데이터를 저장합니다. 저장된 데이터는 브라우저를 닫았다가 다시 열어도 유지됩니다.2

localStorage.setItem('username', '윤서');

const savedName = localStorage.getItem('username');
console.log(savedName); // 윤서

localStorage.removeItem('username');
localStorage.clear();

setItem은 저장하고, getItem은 꺼내고, removeItem은 특정 값을 지우고, clear는 해당 출처의 저장소를 모두 비웁니다.

다만 조심해야 할 점이 있습니다. localStorage는 기본적으로 문자열을 저장합니다. 숫자를 넣어도 문자열로 돌아오고, 객체를 그대로 넣으면 기대한 모양으로 저장되지 않습니다.

localStorage.setItem('count', 3);

const count = localStorage.getItem('count');
console.log(count); // 3
console.log(typeof count); // string

콘솔에는 3처럼 보이지만 실제 타입은 문자열입니다. 계산에 사용하려면 숫자로 바꿔야 합니다.

const count = Number(localStorage.getItem('count'));
console.log(count + 1); // 4

객체를 그대로 저장하면 더 큰 문제가 생깁니다.

const user = {
  name: '윤서',
  theme: 'dark'
};

localStorage.setItem('user', user);

console.log(localStorage.getItem('user')); // [object Object]

객체의 내용이 저장된 것이 아니라, 객체를 억지로 문자열로 바꾼 결과가 저장되었습니다. 배열과 객체를 저장하려면 JSON을 사용해야 합니다.

JSON으로 배열과 객체 저장하기

JSON은 데이터를 문자열로 표현하는 형식입니다. JavaScript 객체와 배열을 저장하거나 서버와 데이터를 주고받을 때 자주 사용합니다. 객체를 저장할 때는 JSON.stringify로 문자열로 바꾸고, 다시 꺼낼 때는 JSON.parse로 JavaScript 값으로 되돌립니다.

const user = {
  name: '윤서',
  theme: 'dark'
};

localStorage.setItem('user', JSON.stringify(user));

const savedUserText = localStorage.getItem('user');
const savedUser = JSON.parse(savedUserText);

console.log(savedUser.name); // 윤서
console.log(savedUser.theme); // dark

배열도 같은 방식으로 저장합니다.

const todos = [
  { id: 1, text: 'JavaScript 복습하기', done: false },
  { id: 2, text: '할 일 목록 꾸미기', done: true }
];

localStorage.setItem('todos', JSON.stringify(todos));

const savedTodosText = localStorage.getItem('todos');
const savedTodos = JSON.parse(savedTodosText);

console.log(savedTodos[0].text); // JavaScript 복습하기

실전에서는 저장된 값이 없을 때도 고려해야 합니다. getItem은 해당 키가 없으면 null을 반환합니다. 그러므로 기본값을 준비해 두면 안전합니다.

function loadTodos() {
  const savedTodosText = localStorage.getItem('todos');

  if (savedTodosText === null) {
    return [];
  }

  return JSON.parse(savedTodosText);
}

function saveTodos(todos) {
  localStorage.setItem('todos', JSON.stringify(todos));
}

let todos = loadTodos();

이제 할 일이 추가될 때마다 저장하면 됩니다.

const form = document.querySelector('.todo-form');
const input = document.querySelector('.todo-input');
const list = document.querySelector('.todo-list');

let todos = loadTodos();

function renderTodos() {
  list.innerHTML = '';

  todos.forEach((todo) => {
    const item = document.createElement('li');
    item.textContent = todo.text;
    list.append(item);
  });
}

form.addEventListener('submit', (event) => {
  event.preventDefault();

  const text = input.value.trim();

  if (text === '') {
    return;
  }

  const newTodo = {
    id: Date.now(),
    text,
    done: false
  };

  todos = [...todos, newTodo];
  saveTodos(todos);
  renderTodos();

  input.value = '';
});

renderTodos();

핵심 흐름은 단순합니다.

상태를 바꾼다 → 저장한다 → 다시 렌더링한다

데이터가 바뀌면 저장하고, 화면을 다시 그립니다. 이 패턴은 앞으로 더 큰 웹앱에서도 계속 반복됩니다.

localStorage와 sessionStorage의 차이

브라우저 저장소에는 localStorage만 있는 것이 아닙니다. sessionStorage도 있습니다. 두 저장소는 사용법이 거의 같습니다.

sessionStorage.setItem('currentStep', '2');

const currentStep = sessionStorage.getItem('currentStep');
console.log(currentStep); // 2

sessionStorage.removeItem('currentStep');

차이는 "얼마나 오래 기억하느냐"입니다. localStorage는 브라우저를 닫았다가 다시 열어도 유지됩니다. 반면 sessionStorage는 현재 탭이나 창이 열려 있는 동안만 유지됩니다. 새로고침해도 남아 있지만, 탭을 닫으면 사라집니다. 웹 스토리지는 쿠키와 달리 HTTP 요청마다 서버로 자동 전송되지 않으며, 같은 출처를 기준으로 분리됩니다.3

구분localStoragesessionStorage
유지 기간직접 지우기 전까지 유지현재 탭이나 창이 닫힐 때 삭제
새로고침유지유지
브라우저 재시작대체로 유지삭제
사용 예테마 설정, 장바구니, 임시 캐시다단계 폼의 현재 단계, 탭 안에서만 필요한 임시 값
APIsetItem, getItem, removeItem, clearsetItem, getItem, removeItem, clear

사용자가 오래 유지되기를 기대하는 값에는 localStorage가 어울립니다. 다크 모드 설정, 최근 검색어, 비로그인 장바구니, 간단한 메모 초안이 대표적입니다.

현재 탭에서만 의미가 있는 값에는 sessionStorage가 어울립니다. 예를 들어 회원가입 3단계 중 현재 단계, 결제 페이지에서 잠깐 유지할 임시 선택값, 새 탭에는 공유되면 안 되는 임시 필터가 그렇습니다.

다만 브라우저 저장소는 서버 데이터베이스를 대신하지 않습니다. 사용자가 다른 브라우저나 다른 기기에서 접속하면 같은 localStorage를 볼 수 없습니다. 사용자가 브라우저 데이터를 삭제하면 저장된 값도 사라질 수 있습니다. 또한 JavaScript로 접근할 수 있으므로 비밀번호, 주민등록번호, 민감한 인증 정보 같은 중요한 데이터는 저장하지 않는 것이 안전합니다.

브라우저 저장소는 "서버에 꼭 맡기지 않아도 되는 작은 기억"에 가장 잘 어울립니다.

테마 설정 저장하기

저장소를 처음 배울 때 재미있는 예제는 다크 모드입니다. 사용자가 버튼을 눌러 어두운 테마를 선택하면, 다음 방문 때도 그 선택이 유지되게 만들 수 있습니다.

<button class='theme-button'>테마 바꾸기</button>
body.dark {
  background: #111827;
  color: #f9fafb;
}
const themeButton = document.querySelector('.theme-button');
const savedTheme = localStorage.getItem('theme');

if (savedTheme === 'dark') {
  document.body.classList.add('dark');
}

themeButton.addEventListener('click', () => {
  document.body.classList.toggle('dark');

  const isDark = document.body.classList.contains('dark');

  if (isDark) {
    localStorage.setItem('theme', 'dark');
  } else {
    localStorage.removeItem('theme');
  }
});

페이지가 처음 열릴 때 저장된 테마를 확인합니다. 저장된 값이 dark라면 body에 dark 클래스를 붙입니다. 버튼을 누르면 클래스를 토글하고, 현재 상태에 따라 저장하거나 삭제합니다.

사용자가 한 행동을 기억해 다음 화면에 반영한다는 점에서, 이 예제는 작지만 중요합니다.

타이머: 나중에 한 번, 계속 반복

브라우저 API의 두 번째 도구는 타이머입니다. 타이머는 특정 시간이 지난 뒤 코드를 실행하거나, 일정한 간격으로 코드를 반복 실행할 때 사용합니다.1

setTimeout은 일정 시간이 지난 뒤 함수를 한 번 실행합니다.

setTimeout(() => {
  console.log('3초가 지났습니다.');
}, 3000);

두 번째 인수 3000은 밀리초 단위입니다. 1000밀리초가 1초이므로 3000은 3초입니다.

함수를 따로 만들어 전달할 수도 있습니다.

function showMessage() {
  console.log('잠시 후 도착한 메시지입니다.');
}

setTimeout(showMessage, 3000);

showMessage()처럼 괄호를 붙이면 지금 바로 실행됩니다. "나중에 실행할 함수"를 전달할 때는 함수 이름만 넘긴다는 점을 다시 확인해 주세요.

예약한 타이머는 취소할 수도 있습니다.

const timeoutId = setTimeout(() => {
  console.log('이 메시지는 보이지 않을 수도 있습니다.');
}, 5000);

const cancelButton = document.querySelector('.cancel-button');

cancelButton.addEventListener('click', () => {
  clearTimeout(timeoutId);
});

setInterval은 일정 시간마다 함수를 반복 실행합니다.

let count = 5;

const intervalId = setInterval(() => {
  console.log(count);
  count -= 1;

  if (count === 0) {
    console.log('끝!');
    clearInterval(intervalId);
  }
}, 1000);

setInterval을 사용할 때는 멈추는 조건을 꼭 생각해야 합니다. 필요 없는 반복이 계속 돌면 브라우저는 보이지 않는 곳에서 계속 일을 하게 됩니다.

Date와 함께 현재 시각 표시하기

시간을 다룰 때는 Date 객체를 사용합니다. Date는 현재 시각이나 특정 날짜와 시간을 표현하는 JavaScript의 기본 객체입니다. 브라우저 API만은 아니지만, 타이머와 함께 쓰면 시간 기반 UI를 만들 수 있습니다.

<p class='clock'></p>
const clock = document.querySelector('.clock');

function updateClock() {
  const now = new Date();
  clock.textContent = now.toLocaleTimeString('ko-KR');
}

updateClock();
setInterval(updateClock, 1000);

updateClock()을 먼저 한 번 호출하는 이유는 페이지가 열리자마자 시간을 보여 주기 위해서입니다. 그다음 setInterval로 1초마다 다시 갱신합니다.

새로고침해도 유지되는 카운트다운

10분짜리 할인 타이머가 있다고 해 봅시다. 단순히 변수에 남은 시간을 저장하면 새로고침할 때 다시 10분으로 돌아갑니다. 사용자 입장에서는 "새로고침 무한 할인"처럼 보일 수 있습니다.

이럴 때는 남은 시간이 아니라 종료 시각을 저장하는 편이 좋습니다.

<p class='sale-timer'></p>
const timerElement = document.querySelector('.sale-timer');
const END_TIME_KEY = 'saleEndTime';

function getEndTime() {
  const savedEndTime = localStorage.getItem(END_TIME_KEY);

  if (savedEndTime !== null) {
    return Number(savedEndTime);
  }

  const tenMinutes = 10 * 60 * 1000;
  const endTime = Date.now() + tenMinutes;

  localStorage.setItem(END_TIME_KEY, String(endTime));

  return endTime;
}

const endTime = getEndTime();
let timerId;

function updateTimer() {
  const remaining = endTime - Date.now();

  if (remaining <= 0) {
    timerElement.textContent = '할인이 종료되었습니다.';
    localStorage.removeItem(END_TIME_KEY);

    if (timerId !== undefined) {
      clearInterval(timerId);
    }

    return;
  }

  const totalSeconds = Math.floor(remaining / 1000);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  timerElement.textContent = `${minutes}분 ${seconds}초 남았습니다.`;
}

timerId = setInterval(updateTimer, 1000);
updateTimer();

Date.now()는 1970년 1월 1일 0시 0분 0초 UTC부터 현재까지 지난 시간을 밀리초로 반환합니다. 현재 시각을 숫자로 다루기 때문에 시간 차이를 계산하기 좋습니다.

단, 정말 중요한 만료 검사는 서버에서도 확인해야 합니다. 브라우저 시간은 사용자가 조작할 수 있기 때문입니다.

URL과 쿼리스트링: 상태를 주소에 담기

검색 페이지를 떠올려 보세요. 사용자가 javascript를 검색했고, 카테고리는 book, 페이지는 2페이지입니다. 이 상태를 변수에만 담아 두면 새로고침하거나 링크를 공유할 때 사라집니다.

하지만 주소에 담으면 이야기가 달라집니다.

https://example.com/search?q=javascript&category=book&page=2

물음표 뒤에 붙은 q=javascript&category=book&page=2 부분을 쿼리스트링이라고 합니다. 쿼리스트링은 주소에 추가 정보를 담는 방법입니다. 검색어, 필터, 정렬 기준, 페이지 번호, 상세 항목 ID처럼 화면 상태를 표현할 때 자주 사용합니다.4

JavaScript에서는 window.location.search로 현재 주소의 쿼리스트링을 읽을 수 있습니다.

console.log(window.location.search);

이 문자열을 직접 자를 수도 있지만, 브라우저에는 더 편한 도구가 있습니다. URLSearchParams입니다.

const params = new URLSearchParams(window.location.search);

const keyword = params.get('q') || '';
const page = Number(params.get('page') || '1');

console.log(keyword);
console.log(page);

get은 특정 키의 값을 가져옵니다. 값이 없으면 null을 반환하므로 ||를 이용해 기본값을 줄 수 있습니다. 쿼리스트링에서 가져온 값은 문자열이므로 페이지 번호처럼 숫자로 써야 할 값은 Number로 변환합니다.

주소를 수정할 때는 URL 객체와 history.pushState를 함께 사용할 수 있습니다.

<form class='search-form'>
  <input class='search-input' placeholder='검색어를 입력하세요'>
  <button>검색</button>
</form>
const searchForm = document.querySelector('.search-form');
const searchInput = document.querySelector('.search-input');

const params = new URLSearchParams(window.location.search);
searchInput.value = params.get('q') || '';

searchForm.addEventListener('submit', (event) => {
  event.preventDefault();

  const keyword = searchInput.value.trim();
  const url = new URL(window.location.href);

  if (keyword === '') {
    url.searchParams.delete('q');
  } else {
    url.searchParams.set('q', keyword);
  }

  url.searchParams.set('page', '1');

  window.history.pushState(null, '', url);
});

주소에 상태를 담으면 사용자가 링크를 복사해 다른 사람에게 보낼 수 있고, 새로고침해도 같은 검색어를 복원할 수 있습니다. 다만 주소는 눈에 보이고, 복사되고, 기록에 남습니다. 비밀번호, 개인 메모, 너무 긴 데이터, 숨겨야 하는 값은 쿼리스트링에 넣지 않는 것이 좋습니다.

Fetch API: 서버에게 자료를 부탁하기

fetch는 브라우저에서 서버로 HTTP 요청을 보내고 응답을 받을 때 사용하는 API입니다. 쉽게 말해 "서버야, 이 데이터 좀 보내 줘"라고 부탁하는 기능입니다.

async function loadUsers() {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await response.json();

  console.log(users);
}

loadUsers();

fetch는 바로 데이터를 반환하지 않습니다. 서버 응답을 기다려야 하므로 Promise를 반환합니다. Promise와 async/await는 다음 장에서 더 깊이 다루겠습니다. 지금은 await가 "응답이 올 때까지 기다린다"는 뜻이라고 이해하면 충분합니다.

response는 실제 데이터 배열이 아닙니다. 서버의 응답 상태, 헤더, 본문을 담은 응답 객체입니다. JSON 데이터를 JavaScript 값으로 꺼내려면 response.json()을 호출해야 합니다.

fetch를 사용할 때 반드시 기억할 점이 있습니다. 요청이 404나 500 같은 HTTP 오류 응답을 받아도 fetch 자체가 항상 실패로 처리되는 것은 아닙니다. 네트워크 오류나 CORS 문제처럼 요청 자체를 완료할 수 없는 경우에는 거부되지만, HTTP 상태 코드는 직접 확인해야 합니다. 그래서 response.ok를 확인하는 습관이 중요합니다.5

async function loadUsers() {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');

  if (!response.ok) {
    throw new Error(`요청 실패: ${response.status}`);
  }

  const users = await response.json();
  return users;
}

response.ok는 상태 코드가 200번대일 때 true가 됩니다.

로딩 중, 성공, 실패를 화면에 보여 주기

네트워크 요청은 시간이 걸립니다. 그래서 데이터를 가져오는 UI에는 적어도 세 가지 상태가 필요합니다.

  • 로딩 중: 아직 데이터를 기다리는 상태

  • 성공: 데이터를 받아 화면에 표시한 상태

  • 실패: 데이터를 가져오지 못해 안내가 필요한 상태

<button class='load-button'>사용자 불러오기</button>
<p class='status'></p>
<ul class='user-list'></ul>
const loadButton = document.querySelector('.load-button');
const statusElement = document.querySelector('.status');
const userList = document.querySelector('.user-list');

function renderUsers(users) {
  userList.innerHTML = '';

  users.forEach((user) => {
    const item = document.createElement('li');
    item.textContent = `${user.name} - ${user.email}`;
    userList.append(item);
  });
}

async function loadUsers() {
  statusElement.textContent = '사용자 목록을 불러오는 중입니다...';
  userList.innerHTML = '';
  loadButton.disabled = true;

  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');

    if (!response.ok) {
      throw new Error(`요청 실패: ${response.status}`);
    }

    const users = await response.json();

    renderUsers(users);
    statusElement.textContent = '사용자 목록을 불러왔습니다.';
  } catch (error) {
    console.error(error);
    statusElement.textContent = '데이터를 가져오지 못했습니다. 잠시 후 다시 시도해 주세요.';
  } finally {
    loadButton.disabled = false;
  }
}

loadButton.addEventListener('click', loadUsers);

콘솔에는 개발자를 위한 자세한 에러를 남기고, 화면에는 사용자가 이해할 수 있는 문장을 보여 주는 편이 좋습니다. 웹앱은 성공할 때만 친절해서는 부족합니다. 실패할 때도 친절해야 합니다.

GET 요청과 POST 요청

서버에서 데이터를 가져오는 요청은 보통 GET 요청입니다. fetch는 기본적으로 GET 요청을 보냅니다.

const response = await fetch('/api/products');

반대로 서버에 데이터를 보내 새로 만들거나 저장하려면 POST 요청을 사용합니다. 이때는 두 번째 인수로 옵션 객체를 전달합니다.

async function createMemo(text) {
  const newMemo = {
    text,
    createdAt: Date.now()
  };

  const response = await fetch('/api/memos', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(newMemo)
  });

  if (!response.ok) {
    throw new Error(`저장 실패: ${response.status}`);
  }

  const savedMemo = await response.json();
  return savedMemo;
}

method는 요청 방식입니다. headers는 서버에게 "내가 보내는 데이터는 JSON 형식입니다"라고 알려 줍니다. body에는 실제로 보낼 데이터를 넣습니다. 객체를 바로 넣지 않고 JSON.stringify로 문자열로 바꾸는 점을 기억하세요.

검색어를 주소에 담고 Fetch로 결과 불러오기

URL과 Fetch를 함께 사용하면 검색어는 주소에 담고, 그 검색어로 API 요청을 보내는 구조를 만들 수 있습니다.

<form class='book-search-form'>
  <input class='book-search-input' placeholder='예: javascript'>
  <button>검색</button>
</form>

<p class='book-status'></p>
<ul class='book-list'></ul>
const form = document.querySelector('.book-search-form');
const input = document.querySelector('.book-search-input');
const status = document.querySelector('.book-status');
const list = document.querySelector('.book-list');

const params = new URLSearchParams(window.location.search);
const initialKeyword = params.get('q') || '';

input.value = initialKeyword;

function renderBooks(books) {
  list.innerHTML = '';

  books.forEach((book) => {
    const item = document.createElement('li');
    item.textContent = book.title;
    list.append(item);
  });
}

async function searchBooks(keyword) {
  const encodedKeyword = encodeURIComponent(keyword);
  const response = await fetch(`/api/books?q=${encodedKeyword}`);

  if (!response.ok) {
    throw new Error('검색 결과를 불러오지 못했습니다.');
  }

  return await response.json();
}

async function runSearch(keyword) {
  if (keyword === '') {
    status.textContent = '검색어를 입력해 주세요.';
    list.innerHTML = '';
    return;
  }

  status.textContent = '검색 중입니다...';
  list.innerHTML = '';

  try {
    const books = await searchBooks(keyword);
    renderBooks(books);
    status.textContent = `${books.length}개의 결과를 찾았습니다.`;
  } catch (error) {
    console.error(error);
    status.textContent = '검색 결과를 가져오지 못했습니다. 잠시 후 다시 시도해 주세요.';
  }
}

form.addEventListener('submit', (event) => {
  event.preventDefault();

  const keyword = input.value.trim();
  const url = new URL(window.location.href);

  if (keyword === '') {
    url.searchParams.delete('q');
  } else {
    url.searchParams.set('q', keyword);
  }

  window.history.pushState(null, '', url);
  runSearch(keyword);
});

runSearch(initialKeyword);

이 예제에는 이번 장의 주요 흐름이 함께 들어 있습니다. 주소에서 상태를 읽고, 사용자의 검색으로 주소를 바꾸고, Fetch로 데이터를 가져오고, 로딩·성공·실패 상태를 화면에 보여 줍니다.

이번 장에서 기억할 것

브라우저 API는 JavaScript가 브라우저의 기능을 사용할 수 있게 해 주는 도구입니다. localStorage와 sessionStorage는 작은 데이터를 브라우저에 저장하고, setTimeout과 setInterval은 시간에 맞춰 코드를 실행합니다. Date는 현재 시각과 시간 차이를 다루는 데 도움이 됩니다.

URL과 쿼리스트링은 검색어, 필터, 페이지 번호처럼 공유 가능한 화면 상태를 주소에 담게 해 줍니다. Fetch API는 서버와 데이터를 주고받는 출발점입니다. 이때 response.ok를 확인하고, 로딩·성공·실패 상태를 사용자에게 보여 주는 습관이 중요합니다.

또 하나 기억해야 할 점은 안전한 화면 출력입니다. 사용자가 입력한 값이나 서버에서 온 문자열을 화면에 넣을 때는 가능한 한 textContent를 사용하세요. innerHTML은 HTML 구조를 넣을 수 있어 편리하지만, 신뢰할 수 없는 값을 그대로 넣으면 보안 문제가 생길 수 있습니다.

다음 장에서는 Fetch API 뒤에 숨어 있던 핵심 개념인 비동기 JavaScript를 더 깊이 살펴보겠습니다. 서버 응답을 기다리는 동안 웹페이지가 멈추지 않는 이유, Promise와 async/await가 어떤 흐름으로 동작하는지 차근차근 연결해 보겠습니다.

참고

1MDN Web Docs, "Web APIs" 및 "Window: setTimeout() method", 브라우저가 제공하는 다양한 API와 타이머 함수 설명.

2MDN Web Docs, "Window: localStorage property", 같은 출처의 저장소에 키-값 데이터를 저장하는 Web Storage 설명.

3MDN Web Docs, "Window: sessionStorage property" 및 "Web Storage API", localStorage와 sessionStorage의 보존 범위 차이 설명.

4MDN Web Docs, "URLSearchParams" 및 "URL", 쿼리스트링을 읽고 수정하는 API 설명.

5MDN Web Docs, "Fetch API" 및 "Response: ok property", Fetch 요청과 HTTP 응답 상태 확인 방식 설명.

책 목록

처음 만나는 JavaScript: 웹페이지를 살아 움직이게 만드는 언어

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기