
FastCampus FE Course, JS Part 에서 배운 컴포넌트와 라우팅개념에 대해 기록합니다.
➡ Single Page Application 의 약자로, 한 페이지에서 여러가지 요소를 보여주는 것.
SPA 방식 도입전에는 각 콘텐츠마다 페이지 하나를 할당하여, 사용자가 요청할때마다 그 페이지를 불러와야 했다.
하지만, SPA 방식이 도입되면서 최초 요청시 모든 콘텐츠를 불러와서, URL 경로 이름따라 해당하는 콘텐츠를 동적으로 표현하는 것이 가능해졌다.
셰션 기록이라고도 불리는 히스토리 객체는 이름처럼,사용자가 어떤 페이지를 다녀왔는지 살펴볼 수 있는 Web API이다.
우리가 뒤로가기, 앞으로가기를 사용할 수 있는 이유가 바로 요 History 객체 덕분이다.
자세한 History 의 속성을 알고싶다면? TIL(github)
이를 SPA 에 활용하는 방법은 window 객체의 popstate 이벤트와 replaceState()를 이용한다
replaceState(state, title, address): 현재 히스토리의 상태 및 주소를 교체
popstate: history의 변화가 생겼을때 발생하는 이벤트
if(!location.hash) {
// history 에 기록 남기지 않고 페이지 이동( 메인페이지 해쉬값 붙여주기)
history.replaceState(null,'','/#/')
}
만약 주소에 hash 값이 없이(null) 접속이 된다면 메인 콘텐츠를 보여줄 수 없을때,
history 객체의 hash 속성을 /#/ 로 바꿔줌을 통해서 Main 페이지를 렌더링하는 기능을 구현했다.
예를 들어 http://localhost:1234/#/about?a=123&b=456&c=789 라는 주소가 있을때,
#/about 처럼 # 앵커를 통해 페이지를 변경할때 쓰이는 개념이다.
(사전적 개념으로의 Hash는 임의의 데이터를 고정된 데이터로 바꾸어주는 단방향 함수)
location 객체의 hash 속성을 통해 확인할 수 있다.

routes
L About.js
L Home.js
L index.js
// routes = pages,
// route를 제어하는 용도
// 홈, 어바웃 가져오기
import Home from "./Home";
import About from "./About";
// heropy.js 에서 createRotuer 함수 가져오기
import { createRouter } from "../core/heropy";
export default createRouter([
// 함수의 파라미터로 객체를 가진 배열을 넘겨줌, 배열 안 객체 하나마다 한 페이지의 정보를 담음
// 해쉬와 일치하는 path 값 할당, 일치시 보여줄 컴포넌트를 같이 할당
{path:'#/', component: Home},
{path:'#/about', component: About}
])
index.js 에 라우팅할 페이지의 해쉬경로와 각 해쉬 경로에 해당하는 컴포넌트를 import 하여,
createRouter 함수로 넘겨준다.
page 변경을 감지하여 routes 정보를 routeRende()로 넘겨주는 기능을 담당
// 라우터 정보를 매개변수로 사용, index.js 에서 내보낸 그 라우터 정보 기반
// 라우터 정보를 받으면 routeRender() 함수를 실행
export function createRouter(routes){
// 함수 반환
return function(){
window.addEventListener('popstate', ()=> {
routeRender(routes)
})
// popstate는 최초실행 안되기에 선언하여 1회 실행
routeRender(routes)
}
addEventListener 를 활용하여 popstate를 감지, routeRender() 함수를 routes 를 넘겨주며 호출한다. 코드가 길지만 요약을 해보자면
hash 값을 얻어내서 이에 해당하는 컴포넌트를 append 하는 기능이다.
function routeRender(routes) {
// 해쉬 값 없다면 에러발생 -> 처리 필요
if(!location.hash) {
// history 에 기록 남기지 않고 페이지 이동( 메인페이지 해쉬값 붙여주기)
history.replaceState(null,'','/#/')
}
// 내용이 출력될 위치인 router-view 태그 찾기
const routerView = document.querySelector('router-view')
// 주소 부분의 hash 값을 얻어낼 수 있음, 다만 쿼리스트링 정보 구별이 필요(#about?name=heropy)
// split 통해 hash 와 쿼리스트링 분리, 0번째 Index 가 hash, 1번째가 쿼리스트링
// 구조분해할당 - split은 배열을 리턴하기 때문에 [] 로 해야함!, {}은 object(객체)
const [ hash, queryString = ''] = location.hash.split('?')
// http://localhost:1234/#/?a=123&b=456&c=789
// split = ['a=123', 'b=456'...]
// reduce 메소드,배열 개수 만큼 콜백 사용 가능,2번째 인수로 초기 데이터 설정 가능
const query = queryString
.split('&')
.reduce((acc, cur)=> {
// 'a=123' 을 = 기준으로 할당 -> cur = { key: a, value: 123 }
const [key, value] = cur.split('=')
acc[key] = value
return acc
}, {})
// query 값이 history 객체의 state 값으로 할당 -> state: {a: '123', b: '456', c: '789'}
history.replaceState(query,'')
// 넘어오는 라우터 정보에서 hash 값 추출
// 정규표현식 이용 -> 리터럴 방식(/#\/about\/?$/.)은 중간에 변수사용 불가하기에 생성자 방식 사용
const currentRoute = routes.find(route => new RegExp(`${route.path}/?$`).test(hash))
// 각 객체의 실제 출력할 것은 속에 있는 component 속성 안에 있음
// 그 component 속성은 Home, About 에서 가져온 것
routerView.innerHTML = ``
// 라우터 뷰의 내용을 컴포넌트의 인스턴스의 el을 밀어넣기
routerView.append(new currentRoute.component().el)
}
조금 자세히 설명하자면,
1. 해쉬가 없는 경우에 대해 Home에 해당하는 해쉬를 붙여주어 Home컴포넌트를 출력시킨다(예외처리).
2. 받아온 routes에서 hash 값과 쿼리스트링을 분리한다.
3. .find() 메서드를 통해서 사용자가 보고 있는 화면의 hash 값을 추출한다.
4. 추출한 hash와 동일한 hash(path)를 가진 컴포넌트의 요소를 HTML로 보여준다.
의 순서로 진행된다.

한줄 요약: 해쉬값 받아와서 그 해쉬값에 맞는 컴포넌트 출력
SPA : Link

SPA 를 JS 만으로 어떻게 구현하는지를 알 수 있었던 강의였다.
사실 이 라우팅 구현을 이해하는데 진짜 오랜 시간이 걸렸다.
강의도 3,4번씩 다시 들어보고(중간에 급격히 늘어난 강의 수강시간...), 코드 하나하나 주석을 달아보고,
그림도 그려보니 100% 이해...는 아니지만 70%는 이해가 간다.
라우팅이라는 개념이 추후 배울 React, Vue 의 핵심 원리이기 때문에 이를 배우기 전에 이해하고 싶은 내 욕심이 조금 있었다.
그리고 강의 코드에서 Store 개념도 있는데, 이 부분도 추후에 작성해보겠다.