목차
반응형
유저명/타이틀 변경, 계정 삭제 기능 추가
수정 버튼 클릭 시 flag State 변경으로 수정모드로 변경한다.
const onEditHandler = useCallback(() => {
setIsEditing((prev) => !prev);
}, []);
<UserForm onSubmit={onEditUserSubmit}>
{isEditing ? (
<>
<Label htmlFor='editName-label'>
<NameInput
autoComplete='off'
type='text'
name='editName'
id='editName-label'
value={editName}
onChange={onChangeEditName}
/>
</Label>
<Label htmlFor='editTitle-label'>
<TitleInput
autoFocus
autoComplete='off'
type='text'
name='editTitle'
id='editTitle-label'
value={editTitle}
onChange={onChangeEditTitle}
/>
</Label>
</>
) : (
<>
<Name>{userName}</Name>
<Title>{userTitle}</Title>
</>
)}
{isEditing ? (
<Edit onClick={onEditUserSubmit}>저장</Edit>
) : (
<Edit onClick={onEditHandler}>수정</Edit>
)}
</UserForm>
포스트 변경과 동일한 구조로 유저명과 유저타이틀도 변경된 내용을 POST로 서버로 보낸다.
const onEditUserSubmit = useCallback(
(e) => {
e.preventDefault();
const regexp = /^[가-힣.,?;^_!%\s]+$/g;
if (editName) {
if (editName.length > 5 || editName.length < 2 || !regexp.test(editName)) {
toast.error('유저명은 최소 2자, 최대 5자의 한글만 가능합니다.');
} else {
axios
.put(
`/api/users/${userId}`,
{
userName: userName,
editName: editName,
editTitle: editTitle,
},
{ withCredentials: true },
)
.then(() => {
refetch();
setIsEditing(false);
})
.catch((error) => {
console.log(error);
toast.error('오류가 발생했습니다.');
});
}
}
},
[userName, editName, editTitle, userId, refetch],
);
router.put('/users/:id', async (req, res) => {
await User.updateOne(
{ _id: req.params.id },
{ $set: { userName: req.body.editName, userTitle: req.body.editTitle } },
)
.then((user) => {
if (!user) return res.status(404).json({ message: 'User not found' });
Post.updateOne({ postUser: req.body.userName }, { $set: { postUser: req.body.editName } })
.then(() => {
return res.status(200).json(user);
})
.catch((error) => {
console.log(error);
return res.status(403).json(error);
});
})
.catch((error) => {
console.log(error);
return res.status(403).json(error);
});
});
계정 삭제 기능은 포스트와 댓글 삭제와 동일하지만, 해당 이름의 모든 포스트를 삭제하는 과정을 추가했다
router.delete('/users/:id', async (req, res) => {
await User.findOne({ _id: req.params.id })
.then((user) => {
// 해당 유저의 모든 포스트 삭제
Post.deleteMany({ postUser: user.userName })
.then(() => {})
.catch((error) => {
console.log(error);
return res.status(403).json(error);
});
// 계정 삭제
User.deleteOne({ _id: req.params.id })
.then(() => {
return res.status(200).json({ WithdrawalSuccess: true });
})
.catch((error) => {
console.log(error);
return res.status(403).json(error);
});
})
.catch((error) => {
console.log(error);
return res.status(403).json(error);
});
});
모바일 화면 메뉴 나누기
기존 모바일크기 홈페이지는 인기포스트와 최근포스트가 일렬로 배치되었고, 최근 포스트를 보기 위해서는 꽤나 많은 스크롤이 필요했다.
벨로그의 홈페이지를 벤치마킹해 홈에서 인기포스트와 최근포스트를 선택해서 바로 볼 수 있도록 나눴다.
디바이스 크기에 따라 CSS(emotion)에서 display:none; 을 설정할 수 있겠지만, 그럼 컴포넌트를 불러오고 스타일에서 보이지 않게 설정하는 것이다.
그래서 애초에 렌더링 되지 않도록 아래와 같이 코드를 적용했다.
<Main>
<LeftWrap>
<Intro />
{innerWidth > 768 ? <TopPosts /> : <PostMenu />}
</LeftWrap>
{innerWidth > 768 ? (
<>
<CenterWrap>
<RecentPosts />
</CenterWrap>
<RightWrap>
<Time />
<UserLists />
</RightWrap>
</>
) : (
''
)}
</Main>
PostMenu는 메뉴를 클릭 할 때마다 해당 메뉴에 맞는 컴포넌트를 렌더링 하도록 설정했다.
const PostMenu = () => {
const ref = useRef();
const [isPostMenu, setIsPostMenu] = useState(true);
const onTopPostClick = useCallback(() => {
ref.current.classList.remove('recent');
setIsPostMenu(true);
}, []);
const onRecentPostClick = useCallback(() => {
ref.current.classList.add('recent');
setIsPostMenu(false);
}, []);
return (
<Container>
<TopPostTitle ref={ref} onClick={onTopPostClick}>
인기 포스트
</TopPostTitle>
<RecentPostTitle onClick={onRecentPostClick}>최신 포스트</RecentPostTitle>
<DotMenu>
<BsThreeDots />
</DotMenu>
<PostWrap>{isPostMenu ? <TopPosts /> : <RecentPosts />}</PostWrap>
</Container>
);
};
기타 스타일 변경
전체적으로 한글 적용
한국 유저들을 대상으로 기획한 프로젝트인데 영어가 많아서 뭐하겠는가.. 하고 왠만한 영어는 한글로 변경했다.
max-width 변경
4K 모니터로 작업하다보니 너무 큰 화면에 맞게 UI구성이 된 것 같아 홈페이지의 max-width:1920px로 지정했고, 기타 페이지는 max-width: 768px로 지정했다.
내용이 과하게 퍼져있지 않고 넘치지 않으니 시각적으로 보기 좋았다.
포스트 유저 아바타 추가
포스트에 내용, 날짜, 이름만 있어서 너무 기본적인 스타일인 것 같아 해당 포스트의 유저 아바타를 불러와 적용했다.
useEffect(() => {
axios
.get(`/api/users/${postUser}`)
.then((res) => {
if (res.data.userAvatar) {
setPostAvatar(res.data.userAvatar);
}
})
.catch((error) => {
console.log(error.response);
});
}, [postUser]);
반응형