새로고침 후에도 결과 화면 유지하기 : Web Storage API

nnm·2020년 3월 29일
12
post-thumbnail

프로그래머스 2020 Dev-Matching : 웹 프론트엔드 과제 복기
https://github.com/woohyeonjo/ilovecat

회원가입 또는 결제 정보를 입력하는 중에 새로고침을 눌러 작성한 정보가 모두 없어지거나 쇼핑몰을 이용하면서 제품 상세 정보로 들어갔다가 뒤로가기를 눌렀을 때 스크롤이 처음으로 돌아가서 불편했던 경험이 있을 것이다. 새로고침이나 뒤로가기를 하게되면 웹 페이지를 다시 불러와 랜더링을 하는데 이전의 입력 정보 또는 스크롤 정보는 유지되지 않기 때문에 발생하는 문제다.

그렇다면 유지하고 싶은 정보를 따로 저장한다면 해결될 일 아닌가? 그런데 어디에 저장할 것인가? 그것이 문제다.

Web Storage API


모든 이용자들의 일시적인 정보를 서버에 저장하는 것은 특별한 경우를 제외하고는 좋은 생각이 아닌 것 같다. 큰 저장 용량이 필요한 것도 아니고 오래 보관해야하는 데이터도 아니다. 이런 필요성에서 클라이언트에도 데이터를 저장할 수 있는 공간(브라우저에 저장한다.)이 생긴게 아닐까 싶다.

과거에는 쿠키가 있었고 HTML5 부터는 Web Storage라는 더 좋은 저장소가 생겼다. Web Storage API는 두 가지 저장소를 제공한다.

  • Local Storage
    • 직접 데이터를 삭제하지 않는 한 계속 유지된다.
  • Session Storage
    • 페이지 세션이 유지되는 동안 데이터가 유지된다.

기본적으로 데이터는 Key-Value 형태로 저장된다. 사용은 굉장히 간단하다. API에서 제공하는 매서드를 사용하기만 하면 된다.

새로고침 후에도 결과 화면 유지하기

이번 프로젝트의 요구사항에는 다음과 같은 사항이 있었다.

  • 페이지를 새로고침해도 마지막 검색 결과 화면이 유지되도록 처리합니다.

우선 저장소는 SessionStorage를 사용하기로 결정했다. 마지막 검색 결과 화면의 유지는 세션 종료시까지만 유지하면 되기 때문이다. LocalStorage를 사용하면 직접 저장소에서 삭제하기 전 까지 데이터가 유지될 것인데 그럴 필요는 없기 때문이다.

  // sessionStorage.js
  function getItem(key) {
      const value = sessionStorage.getItem(key);

      if(key === 'data') return value === null ? null : JSON.parse(value);
      else return value === null ? [] : JSON.parse(value);
  }

  function setItem(key, value) {
      if(value === null || value === undefined) return;

      const toJson = JSON.stringify(value);

      sessionStorage.setItem(key, toJson);
  }

  export { getItem, setItem };
  • 빠르게 기능 구현부터 하다보니 데이터에 맞춰서 매서드를 만들었다. 최근 검색어마지막 검색 결과에 대한 분기가 생겼고... 다시 보니 좀 부끄러운 코드다.
  • 나중에 null과 undefined에 대한 처리를 따로 공부해야겠다.
  • 데이터는 JSON 형태로 저장한다.

다음으로 마지막 검색 결과 화면이 유지되도록 하려면 마지막으로 검색한 결과 데이터를 가지고 있어야한다. 따라서 검색할 때 결과 데이터를 계속 저장하도록 하였다.

export default class App {
    constructor($target) {
        const keywords = getItem('keywords');
        const data = getItem('data');
        
        const searchingSection = new SearchingSection({
            $target,
            keywords,
            onSearch: async keyword => {
                loading.toggleSpinner();

                const response = await api.fetchCats(keyword);
                if(!response.isError){
                    setItem('data', response.data);
                    resultsSection.setState(response.data);
                    loading.toggleSpinner();
                } else {
                    error.setState(response.data);
                }
            },
            onRandom: async () => {
                loading.toggleSpinner();
                
                const response = await api.fetchRandomCats();
                if(!response.isError){
                    setItem('data', response.data);
                    resultsSection.setState(response.data);
                    loading.toggleSpinner();
                } else {
                    error.setState(response.data);
                }
            }
        });
        const resultsSection = new ResultsSection({
              $target,
              data,
              onClick: data => {
                  detailModal.setState(data);
              },
              onScroll: async () => {
                  loading.toggleSpinner();

                  const response = await api.fetchRandomCats();
                  if(!response.isError){
                      const beforeData = getItem('data');
                      const nextData = beforeData.concat(response.data);

                      setItem('data', nextData);
                      resultsSection.setState(nextData);
                      loading.toggleSpinner();
                  } else {
                      error.setState(response.data);
                  }
              }
          });
      	...
    }
}
  • searchingSection에 전달하는 onSearch, onRandom 함수에 fetchAPI 호출에 대한 응답 데이터(검색 결과 데이터)를 저장하는 코드를 작성하였다.
  • App이 생성될 때(새로고침 되었을 때) 생성자에서 가장 먼저 저장소에 저장되어 있는 데이터를 가져온다.
  • 그리고 그 데이터는 resultsSection의 매개변수로 넘겨져 화면에 랜더링 된다.
profile
그냥 개발자

6개의 댓글

comment-user-thumbnail
2020년 4월 22일

잘 보고 갑니다! 그런데 setItem('data', response.data); 이렇게 매번 검색결과를 저장한다고 했을 때, 새로고침해도 페이지를 유지할 수 있는 건 알겠는데, 무한스크롤이 적용된 페이지라면 어떻게 하는 게 좋을까요? 의견 듣고 싶습니다.

1개의 답글