현재 개인 프로젝트 작업 중... 포스트를 올리지 못하고 있다..
작업 중에 정말 막히고, 막히고 또 막힌 부분들이 있었다. 물론 해결하면서 진행했지만!
그 해결했던 것 중 하나만 정리하고 다시 작업 진행하려고 한다.
현재 벨로그 st.. 프로젝트를 만들고 있다! 물론 ㅋㅋ 비교할 수는 없겠지만 그런 느낌의 프로젝트이다. 포트폴리오에 넣을 프로젝트!
자세한 사항은 생략하려고 한다. (너무 길어져...😇)
여러 포스트카드가 노출되는 페이지에서 하나의 포스트카드를 클릭 시 해당되는 포스트 페이지가 나오는 방법을 구현하고 싶었다.
우선 Next를 사용하고 있었기 때문에 Next에서 제공되는 Link
를 사용했다.
import Link from 'next/link';
// item은 각 포스트의 데이터를 갖고 있음
const PostCardLayout = ({ item }) => {
// ...
return (
// ...
<Link
href={{
pathname: '/[pagename]/[id]',
query: { postId: item.id },
}}
as={`/${item.User.nickname}/${item.title}`}
>
<a className="title">{item.title}</a>
</Link>
)
}
위 처럼 href
에는 해당 경로에 맞는 pathname
을 설정해주고, query
로 필요한 값을
전달할거다. as
는 실제 주소창에 나오는 부분이기도 하고, 데이터를 갖고 있다.
하지만 이대로 보내게 될 경우 item.title
에는 실제 제목을 그대로 갖고 있다. 이게 무슨 뜻이냐~
포스트 제목 - Next.js 배워볼까?!
위 제목을 주소로 보내면 어떻게 될까?
공백에는 %20
이 생기게되고, 특수문자도 그대로 나타난다. 너무 보기 안좋다!
그래서 정규표현식으로 공백과 특수문자를 모두 -
로 바꾸기로 했다.
import Link from 'next/link';
// item은 각 포스트의 데이터를 갖고 있음
const PostCardLayout = ({ item }) => {
// ...
// 정규표현식 - url 타이틀 특수문자 및 공백 '-'변환 후 마지막 '-'은 제거
const regex = /[\s\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]+/g;
const regex2 = item.title.replace(regex, '-');
const title = regex2.replace(/-$/, '');
return (
// ...
<Link
href={{
pathname: '/[pagename]/[id]',
query: { postId: item.id },
}}
as={`/${item.User.nickname}/${title}`}
>
<a className="title">{item.title}</a>
</Link>
)
}
사실 아직 정규표현식에 대해서 많은 지식이 없다... 여기저기 정리된 글을 보며 만들었다.
그래서 코드가 더럽다😊 두번을 변환해서 최종 title
을 as에 넣어준다.
그렇게 바뀐 주소?
너무 깔끔하게 변해서 주소에 출력이 된다. 그럼 저 주소로 서버에 요청을 하면 된다.
이번 작업에서는 Next를 사용한 만큼 SSR로 작업을 했다.
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
// ...
await context.store.dispatch(
// 하나의 포스트 불러오기
singlePostLoad({
nickname: context.params.pagename, // pathname 1
title: context.params.id, // pathname 2
}),
);
});
SSR로 처리 할 시 프론트서버에서 디스패치를 실행 후, 백 서버에 요청이 들어간다.
그리고 다시 백에서 응답을 받고, 데이터가 채워진 채로 브라우저에 나타나게 된다.
저 singlePostLoad
action이 어떻게 요청되는지는 생략... 시간이 없어...
간단하게 저 context.params
에서 주소가 갖고 있는 값을 읽어온다.
읽어온 값을 디스패치해서 백 서버에 요청을 보낸다. 이렇게만 해도 너무 잘되는걸~?
새로고침 및 새 페이지에서 주소를 치면 Not Found
나 백 서버에서 응답처리를 못해주는 경험을 맞이한다. 주소만으로 공유도 하는 상황도 필요할텐데 그게 안되면 이건 완전히 잘못된 기획이다.
그래서 추가적으로 백에도 작업을 해줬다.
그럼 백에서 해당 라우터를 확인해보자.
// 게시글 하나만 불러오기 - GET /post/유저닉네임/포스트타이틀
router.get('/:nickname/:title', async (req, res, next) => {
try {
const user = await User.findOne({
where: { nickname: req.params.nickname },
});
if (!user) {
return res.status(400).send('존재하지 않는 계정입니다.');
}
// 닉네임으로 유저의 포스트 모두 조회
const posts = await Post.findAll({
where: { UserId: user.id },
attributes: ['title', 'id'],
});
// 조회한 포스트 모두 정규표현식으로 변환
const regex = /[\s\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]+/g;
const a = posts.map((item) => {
return {title: item.title.replace(regex, '-'), id: item.id}
});
const b = a.map((item) => {
return {title: item.title.replace(/-$/, ''), id: item.id}
});
// 변환을 거친 포스트 제목과 req.params.title과 비교
const find = b.filter((item) => item.title === req.params.title);
// 비교해서 나온 결과물의 id로 DB 조회
const post = await Post.findOne({
where: { id: find[0].id },
include: [{
model: User,
attributes: ['id', 'nickname'],
}, {
model: Comment,
include: [{
model: User,
attributes: ['id', 'nickname'],
}],
}, {
model: User,
as: 'Likers',
attributes: ['id'],
}],
});
res.status(200).json(post);
} catch(error) {
console.error(error);
next(error);
}
});
뭔가 굉장해 엄청나 급으로 더러운 코드를 보여준다 ㅋㅋ... (좋은 방법있으면 피드백 좀 ㅜ)
백에서는 저장된 DB의 user
테이블에서 nickname
을 조회한다. nickname이 존재한다면
그 nickname의 id
를 가져온다.
가져온 id로 post
테이블에서 where
로 조건 조회를 통해 해당 id가 작성한 포스트를 모두
가져온다.
그 포스트들을 여러 정규표현식 변환을 통해서 최초에 보내준 주소의 포스트 제목을 비교하여,
최종 검색 된 포스트를 응답해주는 방식이다.
사실 이 부분을 작업하면서 백에서 이렇게까지 해당 유저의 포스트를 전부 불러와서 정규표현식을 하고 뭐하고~ 솔직히 이 방법은 정말 아닌 생각이 들긴하다... 하지만 어찌해야할까... 이거 때매 어제 하루를 날리고, 금일 오전도 날려서 일단 이렇게 진행을 하려고 한다.
이렇게 올리는 이유는 더 좋은 방법이나 피드백을 받을 수 있을까? 싶어서도 있긴합니다...