[DAY 18] VanillaJS를 통한 자바스크립트 기본 역량 강화 I (7)

송히·2023년 10월 12일
post-thumbnail

Today I Learn📖

  • history API (강의)
  • 히스토리 모드에서 서버가 항상 index.html을 반환해야하는 이유 (추가 학습)

history API

브라우저에서 페이지 로딩을 하면 세션 히스토리를 갖게 됨

세션 히스토리: 페이지를 이동할 때마다 쌓이는 페이지 이동 기록
-> 이를 통해 뒤로 가기 / 앞으로 가기 등의 이동 가능

  • history API를 사용하면 실제 화면 이동 없이 히스토리에 쌓이거나 url만 업데이트 하기 가능
    -> 화면 이동이 없다 = 서버 요청 / 페이지 새로고침 없다
    -> 근데 사용자 입장에서는 화면이 바뀐 것처럼 보이는 이유가 url에 따라 컴포넌트는 교체되기 때문
    => 싱글 페이지 어플리케이션(SPA) 가능해짐
  • history API 장점
    1. hashbang으로 썼던 url을 명시적으로 적을 수 있음
      • / -> HomePage
      • /list -> ListPage
      • /detail/1 - DetailPage
    2. 일반 url 형식을 따르기 때문에 querystring도 자유롭게 붙일 수 있음
      -> ex) /list?page=2&limit=10

사용되는 history API 메서드

  • history.pushState(state, title, url): 세션 히스토리에 새 url 상태를 쌓음 (뒤로가기 & 앞으로가기 가능, history 속 length에 1개씩 추가됨)

    • state: history.state에서 꺼내쓸 수 있는 상태 값 (null 넣어도 됨)
    • title: 변경될 페이지의 title을 바꿔주는 값, 근데 대부분의 브라우저가 지원하지 않음
      -> 빈문자열("") 넣으면 됨
    • url: 세션 히스토리에 새로 넣을 url(실제로 바꿀 url), 이 url이 변경되도 화면이 리로드되진 않음
  • history.replaceState(state, title, url): 세션 히스토리의 현재 url을 대체함 (뒤로가기를 제한해야할 때 사용, history 속 length 값 변화 없음)

    • state: history.state에서 꺼내쓸 수 있는 상태 값 (null 넣어도 됨)
    • title: 변경될 페이지의 title을 바꿔주는 값, 근데 대부분의 브라우저가 지원하지 않음
      -> 빈문자열("") 넣으면 됨
    • url: 세션 히스토리에서의 현재 url과 대체할 url, 이 url이 변경되도 화면이 리로드되진 않음

  • url에 맞게 페이지를 전환하는 함수

    function route() {
      const { pathname } = location; // location 속 pathname으로 주소에서 바뀔 부분만 가져오기
      const $container = document.querySelector('#container'); // 바뀔 페이지 선택
        if (pathname === '/') $container.innerHTML = '<h1>Home</h1>'
        else if (pathname === '/product-list') $container.innerHTML = '<h1> 상품 목록</h1>'
        else if (pathname === '/article-list') $container.innerHTML = '<h1>게시글 목록</h1>'
      }
    route();
  • <a>태그에서 이동하는 기능 막는 방법

    window.addEventListener ('click', e => {
      if (e.target.className === 'LinkItem') { // a 태그의 class를 'LinkItem'으로 지정했을 경우
        e.preventDefault(); // 기본 기능인 이동 막기
        const { href } = e.target; // href로 url 꺼내오기
        const path = href.replace(window.location.origin, '')
        // 속성 중 location.origin에 해당하는 http~~ 하는 앞부분을 빈문자열로 대체 -> 바뀌는 부분만 남음
        history.pushState(null, null, path) // 히스토리에 url 쌓기
        route() // 바뀐 url로 화면 전환
    })
  • 뒤로가기 / 앞으로 가기 할 때 그에 맞게 페이지 전환 시키기

    window.addEventListener('popstate', () => route()) 
    // 뒤로 가기나 앞으로 가기 했을 때 route 실행됨 (popstate는 뒤로가기나 앞으로 가기 발생시 동작)

히스토리 모드에서 서버가 항상 index.html을 반환해야하는 이유

  • 브라우저는 원래 url에 따라 서버에게 해당 url에 맞는 파일을 요청하고, 서버는 그에 맞는 화면으로 전환함
  • SPA 라우팅은 서버가 페이지를 전환하지 않고 브라우저 & 자바스크립트가 처리함
    -> SPA에서 서버는 항상 모든 요청에 대해 루트에 있는 index.html을 반환해야함
    => index.html이 로드되면 자바스크립트가 실행되고, 그때 자바스크립트 모듈 중 라우터가 url을 읽어 적절한 페이지 컴포넌트를 렌더링하기 때문

즉, SPA에서는 페이지 렌더링시 서버에 새로운 요청을 보내지 않고 자바스크립트가 내부적으로 적절한 페이지를 렌더링 하는 것 !

이때 히스토리 모드에서 직접 url을 입력하거나, url 이동을 한 뒤 새로고침을 하면 브라우저는 서버에게 해당 url에 대한 요청을 보내게 됨
-> 서버는 해당 파일이 없어서 404 에러 반환
=> 그렇기 때문에 서버는 항상 index.html을 반환하고, SPA의 라우터가 url 읽어서 화면 렌더링 해야함

  • 이때 index.html의 <script>태그 속 src="경로"는 무조건 절대경로 !!

  • 대부분의 서비스는 이걸 지원하지만, 만약 지원하지 않으면 hasgbang 사용하면 됨 !

  • npx serve -s: 404 에러 발생하면 루트에 있는 index.html로 돌려주는 모드

위처럼 구조가 설계됐을 때 각 컴포넌트 별 역할 설명

  • index.html: 브라우저 렌더링의 시작 ! main.js 모듈 불러옴
  • main.js: index.html 속 #id와 연결, App 컴포넌트 호출 !
  • App.js:
    • 필요한 하위 페이지 컴포넌트 호출(homePage, productPage)
    • route() 함수 생성 (경로에 맞는 페이지 로드 역할)
    • 히스토리 모드로 url 변경하고 그에 맞게 route 호출하기 (EventListener 사용)
    • popstate 사용해서 뒤로가기 / 앞으로가기 에도 route 적용
    • init() 함수 생성 (App 컴포넌트 진입하자마자 route도 호출하기 위해) & 호출
  • HomePage.js:
    • render 함수 생성 (홈페이지에 맞게 화면 UI 구현)
    • 이 페이지는 App에서 호출할 때만 나타나야 하니까 this.render()도 없고, $target.appendChild()도 render 함수 속에 있음
  • ProductPage.js & 그 하위 컴포넌트: HomePage와 마찬가지

😊오늘의 느낀점😊

히스토리 API라는 새로운 개념을 배웠다.
리액트는 SPA라는 이야기를 들었었지만 그게 뭔지는 몰랐는데, 이번에 SPA에 대해서도 공부하며 서버에서 화면을 렌더링하는 서버 사이드 렌더링과, 브라우저 & 자바스크립트가 관리하는 클라이언트 사이드 렌더링에 대해 이해하게 됐다.

profile
데브코스 프론트엔드 5기

0개의 댓글