실전프로젝트를 하면서 여러 기술들을 해볼 수 있었다. 비록 이전에 배운 내용의 복습이 대부분이긴 하지만 새로, 혹은 깊게 해본 기술에 대해 적어 보자면 다음과 같다.
현재까지 한 깃허브 : 깃허브(프론트엔드)
(깃허브 hook > UseImageUpload.js)
(비록 내가 만든 훅은 아니고 동기분의 허락하에 가져다 쓴 훅이지만..) 잘 만들어진 이미지 훅을 활용하여 프로필 이미지를 변경할 수 있는 코드를 구성하였다.
(myPageUpdate.jsx)
const MypageUpdate = () => {
const uploadedImage = React.useRef(null);
const imageUploader = React.useRef(null);
const [photo, setPhoto] = useState(null);
// 사진을 저장하는 로직이 없었다. => 프로필 미리보기 사진 저장 useState로 활용
const navigate = useNavigate();
const dispatch = useDispatch();
const onClickHandler = () => {
navigate("/mypage");
};
const handleImageUpload = (e) => {
const [file] = e.target.files;
if (file) {
const reader = new FileReader();
const { current } = uploadedImage;
current.file = file;
reader.onload = (e) => {
current.src = e.target.result;
};
reader.readAsDataURL(file);
setPhoto(file);
}
};
// Hook 사용하여 이미지 업로드
const { user } = useSelector((state) => state.Login);
const [write, setWrite, writeHandle] = useInput({
nickname: user.nickname,
});
//get 해오기
useEffect(() => {
dispatch(__UserProfileEdit);
}, [dispatch]);
const onSubmitHandler = () => {
imageUploader.current.click();
};
const nicknameEdit = () => {
const formData = new FormData();
formData.append("image", photo);
const obj = {
nickname: write.nickname,
};
formData.append(
"myInfoRequestDto",
new Blob([JSON.stringify(obj)], { type: "application/json" })
);
dispatch(__UserProfileEdit(formData));
window.location.replace("/mypage");
};
// 백엔드에서 데이터 어떻게 받아오냐 따라 다르지만 이렇게 로직 구현
}}
전체적으로 스타일드 컴포넌트를 사용해서 CSS를 구현했다. 스타일드 컴포넌트를 사용한 이유는 내 생각에는 코드를 경량화하고 직관적으로 CSS를 먹일 수 있으며 props등 확장성을 고려해서 했는데 실제로 멘토링을 받다보니 굳이 그럴 필요가 없이 sytle.css 이렇게 파일을 뺴서 className을 줬어도 똑같았을 것 같다...
그래도 이번 기회에 기본 CSS를 많이 다뤄볼 수 있어서 좋았다.
백엔드에서 페이지네이션 한 것을 순차적으로 페이지가 올라갈 때마다 보여주는 형식으로 로직을 구현하였다.
(대표 코드)
(PostList.jsx)
const PostList = ({ posts, detail, __getDetail, state, setState }) => {
const navigate = useNavigate();
const dispatch = useDispatch();
const params = useParams();
const onClickHandler = (data) => {
dispatch(__getDetail(data));
navigate(`${detail}/${data}`);
};
const [page, setPage] = useState(0); //페이지수
const [loading, setLoading] = useState(false);
const [ref, inView] = useInView();
/** 서버에서 아이템을 가지고 오는 함수 */
const obj = {
page : page,
state: state,
}
const getItems = useCallback(async () => {
//의존하는 값(deps)들이 바뀌지 않는 한 기존 함수를 재사용할 수 있습니다.
dispatch(__getAddPost(obj));
}, [page, params]);
// `getItems` 가 바뀔 때 마다 함수 실행
useEffect(() => {
getItems();
}, [getItems]);
useEffect(() => {
setState({ ...state, pageNumber: page });
}, [page]);
useEffect(() => {
// 사용자가 마지막 요소를 보고 있고, 로딩 중이 아니라면
if (inView && !loading) {
setPage((prevState) => prevState + 1);
}
}, [inView, loading]);
useEffect(() => {
setPage(0);
}, [params]);
...
...
<div ref={ref}></div>
<Div></Div>
// 바뀌는 파트 찾아서 ref 걸기. Div는 공간 만들기용
(PostsSlice.jsx)
export const __getAddPost = createAsyncThunk(
"posts/__getAddPost",
async (payload, thunkAPI) => {
console.log(payload)
try {
const data = await axios.get(
`${process.env.REACT_APP_SERVER}/api/post/${payload.state.paramObj}?page=${payload.page}&size=${payload.state.pageSize}&sort=${payload.state.postSort},DESC`,
//백엔드에서 받아온 uri 맞춰주기
{
headers: {
"Content-Type": `application/json`,
Access_Token: accessToken,
Refresh_Token: refreshToken,
"Cache-Control": "no-cache",
},
}
);
const obj = {
payload: payload.page,
data: data.data.content
}
// obj로 받아서 fulfill 시켜주기
return thunkAPI.fulfillWithValue(obj);
} catch (error) {
return thunkAPI.rejectWithValue(error);
}
}
);
...
...
[__getAddPost.fulfilled]: (state, action) => {
state.isLoading = false;
if(action.payload.payload === 0) {
state.posts.splice(0)
state.posts.push(...action.payload.data)
}else{
state.posts.push(...action.payload.data)
}
// 페이지 값이 0이 되게 하고 계속 반복되도록 한 로직