일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- useEffect
- codesandbox
- 코딩테스트 고득점 Kit 완전탐색
- 자바
- react hook
- 프로그래머스
- 프로그래밍 언어론
- vanillaJS
- 프로그래머스 완전탐색
- 디자인 패턴
- NextJS
- 데이터모델링과마이닝
- websocket
- 백준
- design pattern
- 자바스크립트
- 컴퓨터 네트워크
- useState
- Java
- react firebase
- 리액트
- 코틀린
- 자바 공부
- 코딩테스트 고득점 Kit
- 리액트 훅
- 장고
- JavaScript
- React JS
- 프로그래머스 자바
- react
- Today
- Total
기록하는 개발자
[vanillaJs] 프레임워크없이 SinglePageApplication 만들기-3.동적라우팅 본문
↓이전 글
https://mingmeng030.tistory.com/244
https://mingmeng030.tistory.com/245
이전 글에서는 라우팅까지 적용하였다.
이제 동적라우팅으로 /post/:id로 이동할 수 있게 만들어 보자.
//index.js
import Home from "./views/Home.js";
import Post from "./views/Post.js";
import ViewPost from "./views/ViewPost.js";
import Settings from "./views/Settings.js";
const pathToRegex = (path) =>
new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
const getParams = (match) => {
const values = match.result.slice(1);
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(
(result) => result[1]
);
return Object.fromEntries(
keys.map((key, i) => {
return [key, values[i]];
})
);
};
const navigateTo = (url) => {
history.pushState(null, null, url);
router();
};
const router = async () => {
const routes = [
{ path: "/", view: Home },
{ path: "/posts", view: Post },
{ path: "/posts/:id", view: ViewPost },
{ path: "/settings", view: Settings },
];
const potentialMatches = routes.map((route) => {
return {
route: route,
result: location.pathname.match(pathToRegex(route.path)),
};
});
// potentialMatches의 결과 중 null이 아닌 것을 뽑아 match 변수에 저장
let match = potentialMatches.find(
(potentialMatch) => potentialMatch.result !== null
);
// match가 true인 것이 존재하지 않는 경우(유효하지 않은 경로)
if (!match) {
match = {
route: routes[0],
result: [location.pathname],
};
}
const view = new match.route.view(getParams(match));
document.querySelector("#app").innerHTML = await view.getHtml();
};
window.addEventListener("popstate", router);
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", (e) => {
if (e.target.matches("[data-link]")) {
e.preventDefault();
navigateTo(e.target.href);
}
});
router();
});
1. path 정규표현식 생성
const pathToRegex = (path) =>
new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
1) "^" → ^로 시작한다.
2) path.replace(/\//g, "\\/") → /\//g를 "\\/"로 대체한다.
\ / → /가 온다.
\ g → 매칭되는 것을 모두 다 찾는다.
→ path에서 "/"를 모두 \/로 대체한다.
3) replace(/:\w+/g, "(.+)") + "$") → /:\w+/g를 "(.+)")로 대체한다.
: → :로 시작한다.
\w+ → \w(영문자, 언더스코어)로 이루어진 문자열이 한개 이상(+) 있다.
\ g → 매칭되는걸 모두 다 찾는다.
→ path에서 ":"로 시작하는 문자열을 모두 (.+)로 대체한다.
4) $
$ → 문자열의 끝을 의미
정규식표현 정리 글 참고( https://mingmeng030.tistory.com/246 )
console.log("/posts/:id");
console.log(pathToRegex("/posts/:id"));
↓위 코드 실행 결과이다.
2. potentialMatches 배열
//기존 방식
const potentialMatches = routes.map((route) => {
return {
route: route,
isMatch: location.pathname === route.path,
};
//정규 표현식 사용
const potentialMatches = routes.map((route) => {
return {
route: route,
result: location.pathname.match(pathToRegex(route.path)),
};
});
지난 글까지는 isMatch에 현재 location.pathname과 일치하면 true나 false를 넣어주는 방식이었다.
이번 글부터는 위에서 작성한 정규표현식을 사용하여 검사한다.
console.log(potentialMatches);
↑ 위 코드 실행 결과
→ route 배열 중 현재의 location.pathname과 일치하는 요소는 result에 match 함수의 결과인 정보가 들어있지만
그렇지 않은 요소들은 null이 들어가있다.
3. match 변수
//기존 방식
// potentialMatches의 결과 중 true인 것을 뽑아 match 변수에 저장
let match = potentialMatches.find(
(potentialMatches) => potentialMatches.isMatch
);
// 정규표현식 사용 후
// potentialMatches의 결과 중 null이 아닌 것을 뽑아 match 변수에 저장
let match = potentialMatches.find(
(potentialMatch) => potentialMatch.result !== null
);
console.log(match);
↑ 위 코드 실행 결과
→ 변수 match는배열 potentialMatches의 요소 중 현재의 location.pathname에 해당하는 요소를 저장한다.
4. 예외 처리 조건문
//기존 방식
if (!match) {
match = {
route: routes[0],
isMatch: true,
};
}
// 정규표현식 사용 후
if (!match) {
match = {
route: routes[0],
result: [location.pathname],
};
}
route : route 배열의 첫 번째 요소를 현재 route로 지정한다 → "/"가 location.pathname에 해당된다.
result : 현재 location.pathname을 result에 저장 → 실제 사용자가 입력한 pathname을 result에 저장한다.
5. getParams 함수
const getParams = (match) => {
const values = match.result.slice(1);
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(
(result) => result[1]
);
return Object.fromEntries(
keys.map((key, i) => {
return [key, values[i]];
})
);
};
console.log(match);
console.log(values);
console.log(keys);
↑ 위 코드 실행 결과
1) values : 변수 match 내의 result 객체에 저장된 것 중 index가 1인 요소를 가져온다. → "5"
2) keys
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(
(result) => result[1]
);
- Array.from(match. route.path.matchAll(/:(\w+)/g))
: match 변수 내 result 객체에 저장된 path 문자열에서 ":"로 시작하고 한 글자 이상의 문자로 이루어진 것을 모두 찾아 배열로 만든다.
Array.from(match. route.path.matchAll(/:(\w+)/g))
↑ 위 코드 실행 결과
- Array.from(match. route.path.matchAll(/:(\w+)/g)).map((result)=>result[1]);
: 위 정규표현식을 통한 결과를 반환한 배열 중 index가 1인 요소를 가져온다. → "id"
3) return문
return Object.fromEntries(
keys.map((key, i) => {
return [key, values[i]];
})
);
- [ key, values[i]를 반환한다. → [ "id", "5" ]
→ 현재 동적라우팅에 대해 "/posts/:id"의 id 값이 5임을 저장하는 배열이다.
6. 변수 view
const view = new match.route.view(getParams(match));
- 현재 경로에 해당되는 view에 함수 getParams의 반환값을 인자로 전달한다.
7. AbstractView 및 기존에 작성한 view에 매개변수 추가
//AbstractView.js
export default class {
constructor(params) {
this.params = params;
}
setTitle(title) {
document.title = title;
}
async getHtml() {
return "";
}
}
// Home.js
// Settings.js, Posts.js도 동일하게 적용
import AbstractView from "./AbstractView.js";
export default class extends AbstractView {
constructor(params) {
super(params);
this.setTitle("Home");
}
async getHtml() {
return `
<h1>This is home page</h1>
<p>express yourself</p>
<p>
<a href="/posts" data-link>View recent posts</a>
</p>
`;
}
}
8. ViewPost.js 작성
import AbstractView from "./AbstractView.js";
export default class extends AbstractView {
constructor(params) {
super(params);
this.setTitle("ViewPosts");
}
async getHtml() {
return `
<h1>ViewPosts</h1>
<p>this is view page</p>
`;
}
}
위 과정을 그대로 따라가면 동적라우팅은 잘 적용된다.
그러나 주소창에 "post/숫자" 형태로 페이지를 이동하면 css가 적용되지 않는 이슈가 발생하고 있다.
서치해봐도 잘 나오지 않아 무엇이 문제인지 모르겠다..😢
그럼에도 늘 React로만 라우터 코드를 작성해왔어서 javascript로만 라우팅 코드를 작성해본 것은 좋은 경험이었다.
꼭 발판으로 삼아 javascript를 정복하고 말거야..
참고 영상 : https://www.youtube.com/watch?v=OstALBk-jTc
'Web > Javascript' 카테고리의 다른 글
[vanillaJs] javascript와 html로 영화 card component 만들기 (0) | 2022.11.03 |
---|---|
[vanillaJs] Fetch와 Axios를 비교해보자! (0) | 2022.09.03 |
[vanillaJs] 정규표현식을 예시와 함께 알아보자! (0) | 2022.09.02 |
[vanillaJs] 프레임워크없이 SPA 만들기-2. 라우팅 페이지 구성 (0) | 2022.09.01 |
[vanillaJs] 프레임워크없이 SPA 만들기-1. 라우팅 설정하기 (0) | 2022.08.31 |