지금까지는 단순히 메뉴트리를 보여주는것, 포스트 하나를 정적으로 보여주는작업을 했는데 실제 블로그에서 이런식으로 당연히 표현할 수 없기때문에 메뉴 클릭시 해당되는 포스트를 보여주는 작업을 할 것이다.
해당 작업에 대한 구조를 어떤식으로 짜야지 잘짰다고 생각이 들까 고민을 해봤다.
1. 각 포스트에 아이디를 줘서 해당 포스트로 이동
2. 동적 라우팅 사용
기존에 만들어 뒀던 MenuTree.js
에서 onClick 이벤트를 추가해주자
// MenuTree.js
<Link
passHref
key={index}
href={{
pathname: onClickFile(router, absFilePath, file, value[v]),
}}
>
<MenuTreeFiles key={index} menuDepth={menuDepth}>
{fileNameConverter(file)}
</MenuTreeFiles>
</Link>
const onClickFile = (router, absFilePath, file, v) => {
const filePath = "/" + findPath(absFilePath, "files", v) + "/" + file;
router.push(filePath);
};
onClickFile
에서 findPath
function 을 사용하고있는것을 볼 수 있는데 해당 로직은 아래와같이 생겼다.
const findPath = (ob, key, value) => {
const path = [];
const keyExists = (obj) => {
if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
return false;
} else if (obj.hasOwnProperty(key) && obj[key] === value) {
return true;
} else if (Array.isArray(obj)) {
let parentKey = path.length ? path.pop() : "";
for (let i = 0; i < obj.length; i++) {
path.push(`${parentKey}[${i}]`);
const result = keyExists(obj[i], key);
if (result) {
return result;
}
path.pop();
}
} else {
for (const k in obj) {
path.push(k);
const result = keyExists(obj[k], key);
if (result) {
return result;
}
path.pop();
}
}
return false;
};
keyExists(ob);
return path.join("/");
}
전체 디렉토리 구조에서 파라미터로 받아온 value값이 있는곳까지의 경로를 path
배열에 집어넣고 path
를 리턴해준다, 구분자는 "/"로 표현했다.
예시를 들어보자면 아래와 같은 구조가 있다고 가정할때
{
"posts": {
"back": {
"express": {},
"node": {}
},
"devops": {
"aws": {},
"docker": {}
},
"front": {
"next": {},
"react": {
"files": [
"React_Hook_이란?.md",
"test.md"
],
"tt": {}
},
"files": [
"ts.md"
]
},
"temp": {
"files": [
"nonblocking.md"
]
}
}
}
해당 메뉴 클릭시 아래와 같은 경로가 나오게 된다
/posts/front/react/React\_Hook\_이란?.md
// [...page].js
export default function Home() {
const router = useRouter();
const {page} = router.query;
console.log(page);
return (
<Root>
</Root>
)
};
위와같이 파일을 만들어 준 후 메뉴를 클릭해보면 아래와 같이 정상적으로 이동이 되는것을 볼 수 있다.
[ "front", "react", "React_Hook_이란" ]
로그도 정상적으로 찍혀져 나온다. 이제 위의 경로를 가지고 해당 페이지에 포스팅 된 것을 보여줄 차례이다.
// [...page].js
const router = useRouter();
const {page} = router.query;
const [htmlContent, setHtmlContent] = useState();
useEffect(() => {
(async () => {
try {
const response = await axios.get("/api/markdown", {params: page});
setHtmlContent(response?.data?.result);
} catch (e) {
console.log(e);
return;
}
})();
}, [router]);
return (
<Root>
<div dangerouslySetInnerHTML={{ __html: htmlContent }}/>
</Root>
)
기존 devlog.js
페이지에서 사용하던 코드를 그대로 가져왔다. router.query로 받아온 값을 markdown.js
api 에 넘겨주면. 아래와 같이 받아서 각 파일을 불러올 수 있다.
// markdown.js
const params = req.query;
const fileContent = getContent(params);
// markdown.js (func getContent)
const getContent = (params) => {
let filePath = process.cwd() + '/posts';
const paramKeys = Object.keys(params);
paramKeys.map(v => {
filePath += "/" + params[v];
});
const fileContents = fs.lstatSync(filePath).isDirectory() ? "" : fs.readFileSync(filePath, 'utf8');
return fileContents;
};
그럼 아래와같이 메뉴 클릭시 동적으로 페이지 이동이 되는것을 볼 수 있다.
결과 :::
이렇게 해서 [...page].js
페이지 하나에서 모든 마크다운 파일들을 처리할 수 있게 되었다.