실무 수준으로 프로젝트를 진행했으며, 학습 목적으로 개발했습니다.
⬆️ 이미지를 클릭하면, 시뮬레이션 영상이 담긴 유튜브로 이동합니다.
은진(나) : Main 페이지, Search 페이지, Category 페이지
동훈 : Login 페이지, Signup 페이지, Payment 페이지
하늘 : BookDetails 페이지, E-book viewer
효정 : Nav bar, MyBooks, MyBooksManager 페이지
내가 맡은 부분: ☘️
{covers?.map(({ book_img }) => {
return (
<img
data-aos="fade-up-left"
src={book_img}
alt="Book cover"
></img>
);
})}
나는 css, sass를 사용해 style을 구현하는 것보다 javascript를 이용해 기능을 구현하는 것을 더 즐거워한다. 그러다 보니 style 기능을 많이 익히지 못한 것 같아서 2차 프로젝트 때에는 이 부분을 집중 공부하려고 했다.
밀리의서재 메인에서는 스펙타클한 애니메이션 효과가 아주 많다. 처음에는 scroll의 값을 구해서 scroll event를 통해 애니메이션을 구현하려고 했는데, 공부가 잘 되지 않고 작업에 진전이 더디다 보니 스스로 좀 괴로운 마음이 들었다. 1주 차 목요일까지 이런 마음이 계속되던 중, 한 동기분께서 AOS 라는 스크롤 애니메이션 기능을 가진 라이브러리를 추천해주셨다.
data-aos="fade-up-left"
코드가 바로 아래에서 위쪽과 왼쪽으로 올라가는 애니메이션 효과를 주는 코드이다.
라이브러리를 사용하니 정말 간편하게 아름다운 애니메이션을 구현할 수 있었다.
적절한 라이브러리를 잘 사용하는 것도 실력이라는 것을 느낀 점이 기억에 남는 코드이다.
<TitleBox data-aos="fade-down">
<Title>
<p>{bigStr && bigStr[0]}</p>
<p>{bigStr && bigStr[1]}</p>
<p>{bigStr && bigStr[2]}</p>
<p>{bigStr && bigStr[3]}</p>
</Title>
<SubTitle>
<p>{smallStr && smallStr[0]}</p>
<span>{smallStr && smallStr[1]}</span>
<span>{smallStr && smallStr[2]}</span>
<p>{smallStr && smallStr[3]}</p>
</SubTitle>
</TitleBox>
5번이나 재사용한 컴포넌트이다. 각기 다른 데이터를 주면서 컴포넌트를 사용하니까 화면을 보기에는 모두 다른 코드인 것 같은데 실제 코드는 위처럼 짧았다.
컴포넌트 재활용의 경제성을 크게 느낀 코드이다.
<PriceCard
firstLineBold={priceCardString[1]?.firstLineBold}
firstLineNormal={priceCardString[1]?.firstLineNormal}
secondLineNormal={priceCardString[1]?.secondLineNormal}
thirdLineBold={priceCardString[1]?.thirdLineBold}
monthPrice={priceCardString[1]?.monthPrice}
yearPrice={priceCardString[1]?.yearPrice}
originalPrice={priceCardString[1]?.originalPrice}
description1={priceCardString[1]?.description1}
color="rgb(255, 255, 255)"
backgroundColor="rgb(106,49,164)"
month="rgb(168,125,223)"
year="rgb(106,49,164)"
monthlyPlan={() => {
alert("로그인 후 결제 신청이 가능합니다.");
history.push(`/login`);
}}
annualPlan={() => {
alert("로그인 후 결제 신청이 가능합니다.");
history.push(`/login`);
}}
/>
</PriceWrap>
컴포넌트를 재활용하면서 props로 color, backgroundColor 등을 넘겨주었다.
사실 위의 코드가 가독성과 효율성 면에서 좋은 코드는 아니라고 느끼지만, props를 통해 한 개의 styled-컴포넌트가 서로 다른 색을 내는 점이 신기하게 다가와서 기록해둔다.
const changeType = (e) => {
dispatch(setType(e.target.value));
dispatch(setOffset(0));
};
const updateInputValue = (e) => {
setInputValue(e.target.value);
};
const onSubmitHandler = async (e) => {
await e.preventDefault();
dispatch(setSearchValue(inputValue));
history.push(
`/search_result/${inputValue}?type=${type}&sort=${sort}&offset=${offset}&limit=${14}`
);
};
이 프로젝트를 진행하면서 처음으로 리덕스 라이브러리를 사용해봤다.
검색페이지와 검색결과 페이지에서 각기 type(제목, 저자 등)과 sort(인기순, 발행일순)를 선택하면 그에 맞게 fetch가 이루어져야 하는데, 검색 페이지와 검색결과 페이지가 sibling 관계이다 보니 이 둘을 함께 자식으로 두고 있는 부모 페이지에서 상태 관리를 하려면 Routes 파일에 상태관리를 했어야 했다.
Routes 파일에 상태관리를 할 수는 없었기 때문에(기능적으로는 가능하나, convention에도 맞지 않고 파일이 지저분해지니..) 리덕스를 시작했는데 정말...! 정말...!! 상태 관리를 하기에 편안하고 간결하고 깔끔했다.
더이상 component끼리 연결해서 데이터를 부분적으로 넘겨주지 않아도 되고, 모든 상태는 store라는 하나의 폴더 안에서 관리되다 보니 데이터를 다루는 부분과 사용하는 부분 모든 곳에서 효율적으로 작업이 가능했다.
그치만 리덕스를 사용하기 전 state&props로 상태관리를 하면서 느낀 불편함이 없었다면, 이 시원한 감정도 느끼기 어려웠을 것 같다.
역시 기술은 불편함을 느끼고 왜 써야 하는 지 인식한 후에 적용하는 것이 좋은 것 같다.
useEffect(() => {
fetch(`${BEAPIROOT}/book/search/${searchValue}?type=${type}&sort=${sort}`)
.then((res) => res.json())
.then((res) => {
if (typeof res.MESSAGE == "object") {
dispatch(setBooks(res.MESSAGE));
} else {
dispatch(setBooks(null));
}
})
.catch((err) => console.log("Catched errors!! >>>", err));
}, [searchValue, type, sort]);
const goToBookDetail = (id) => {
history.push(`/book_details/${id}`);
};
const changeSort = (e) => {
dispatch(setSort(e.target.value));
dispatch(setOffset(0));
};
useEffect는 react-lifecycle의 componentDidMount와 componentDidUpdate 기능을 모두 사용할 수 있는 메소드이다.
특히 useEffect의 두 번째 인자는 의존성 배열을 인자로 받는 곳으로, 배열 내의 값을 추적하여 해당 값이 변경될 때마다 첫 번째 인자를 실행한다.
위의 코드는 사용자가 도서를 검색할 때 type, sort, input value가 바뀔 때마다 새로 fetch를 받아와야 하는 상황에서 의존성 배열에 이들을 입력해 원하는 기능을 구현한 코드이다.
사실 input value가 바뀔 때마다 fetch를 받아오면 타자를 칠 때마다 fetch가 진행되어 메모리 누수도 너무 심하다. 따라서 input value가 입력되고 enter 키가 눌리거나 검색 버튼이 눌릴 때, 해당 input value를 search value라는 상태로 저장하고 useEffect가 search value 값을 추적하도록 했다.
위코드에서 2차 프로젝트로 밀리의서재 클로닝 프로젝트를 진행했다. 한 달 전 프로젝트 아이디어로 발표한 밀리의서재가 감사하게도 프로젝트 사이트로 선정되어, PM으로서 프로젝트를 진행할 수 있었다.
프론트엔드 팀원은 나를 포함하여 동훈님, 하늘님, 효정님으로 구성되었고, 백엔드 팀원은 성태님 홀로 구성되었다. 방대한 양의 도서 정보를 저장해야 해서 성태님이 너무 힘들어하실까 걱정이 많았는데, 빠른 속도로 백엔드 작업을 해주셔서 한 번도 딜레이나 어려움 없이 서버 연결을 진행할 수 있었다. 이에 더해 팀 회의 때 의견 조율에도 현명한 도움을 주셔서 정말 감사했다. (역시 백엔드 원탑 에이스...)
매일 아침부터 밤 늦게까지 도움 주셨던 멘토님에게도 감사하고, 막히는 부분이 있을 때 함께 고민 해주셨던 동기분들에게도 진짜 감사하다.
첫 주 목요일까지 애니메이션 공부에 너무 힘들어했는데 aos library 존재랑 사용 방법에 대해 자세히 알려준 정현님과, 마지막 날에 상태관리가 원하는대로 되지 않아 검색 페이지 발표를 포기해야 하나 걱정하고 있을 때 Redux에 대해 상세히 알려줘서 원하는 기능을 구현할 수 있게 도와주셨던 제형님에게도 감사하다.
특히 많이 부족한 PM 믿어주고 계속 힘 내면서 끝까지 함께 해주신 팀원들에게도 감사하고 죄송스런 마음이 크다. 팀원 모두가 각자 맡은 페이지를 세세하고 깊게 구현해주셔서 전체적으로 더 짜임새있는 결과가 나올 수 있었다.
퍼가요 ~~