독서 플랫폼 개발 프로젝트 후기

백은진·2020년 11월 14일
13

project

목록 보기
2/2
post-thumbnail

안녕하세요.

국내 최대 독서 플랫폼 밀리의서재를 참조하여 개발한 윌리의서재 팀입니다.

실무 수준으로 프로젝트를 진행했으며, 학습 목적으로 개발했습니다.

📒 프로젝트 소개

  • 개발 기간: 2020년 11월 2일 ~ 2020년 11월 13일 (12일)
  • 개발 인원: Front-end 4명, Back-end 1명
  • GitHub: Front-end / Back-end
  • 프로젝트 목표:
    - Scrum 진행 방식을 체득한다.
    - 팀원들과 건강하게 소통하는 방법을 익히고, 프론트엔드와 백엔드 사이에서 필요한 의사소통 내용을 안다.
    - 매일 아침 진행하는 Stand-up meeting을 통해 나의 업무 현황과 팀원의 업무 현황을 이해한다. 이 업무 현황을 바탕으로 앞으로 진행할 업무의 속도를 조절한다.
    - 새로 공부하는 React functional component, Styled component, Flatform social login, Redux, Git rebase 등을 바로 적용하여 코드를 작성한다.

🎞 프로젝트 결과물

밀리의서재 프로젝트 영상
⬆️ 이미지를 클릭하면, 시뮬레이션 영상이 담긴 유튜브로 이동합니다.

💻 사용한 기술

Front-end

  • HTML
  • CSS
  • JavaScript ES6+
  • React (CRA, Router, Hooks)
  • Redux
  • Styled Component
  • Kakao API (for social login)
  • Twilio API (for SMS verification)
  • I'mport API (for online payment)
  • AOS (library)
  • Slick (library)
  • Git,GitHub

Back-end

  • Python
  • Django
  • CORS Header
  • Bcrypt
  • PyJWT
  • MySQL
  • REST API
  • AqueryTool (데이터베이스 모델링)
  • Endpoint에 대한 UnitTest 구현

📸 구현 사항

각자 맡은 페이지 (혹은 컴포넌트)

은진(나) : Main 페이지, Search 페이지, Category 페이지
동훈 : Login 페이지, Signup 페이지, Payment 페이지
하늘 : BookDetails 페이지, E-book viewer
효정 : Nav bar, MyBooks, MyBooksManager 페이지

구현한 기능

내가 맡은 부분: ☘️

  • AOS library를 이용한 애니메이션 구현☘️
  • offset, limit을 설정하여 백엔드로부터 필요한만큼의 데이터 수신☘️
  • JWT를 이용한 로그인(+카카오), 회원가입, 로그아웃 기능 및 유효성 검사
  • 카카오 API를 이용한 소셜 로그인
  • 카카오 API를 이용한 카카오페이 결제 기능
  • Twilio API를 이용한 문자 인증 기능
  • IMPort API를 이용한 신용카드 온라인 결제 기능
  • Redux store를 이용한 다중 type 상태 관리☘️
  • query string을 통해 filtering된 data 서버로부터 받아오기☘️
  • map 메소드를 통해, 카테고리 페이지 전체의 데이터(텍스트, 이미지 등)를 받아 화면 구성하기☘️
  • Navigation bar: 페이지 이동, 로그아웃 기능
  • 더보기 버튼을 통해 pagination 구현
  • 사용자 댓글 기능 (본인 댓글만 삭제 가능함), 좋아요 기능
  • 도서 상세 페이지에서 책 담으면, 사용자 내서재에 담기는 기능
  • 책 뷰어 기능
  • 사용자 내서재에서 원하는 책만 선택하여 책장 생성하는 기능

👩🏻‍💻 기억에 남는 코드

1. 애니메이션

            {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" 코드가 바로 아래에서 위쪽과 왼쪽으로 올라가는 애니메이션 효과를 주는 코드이다.

라이브러리를 사용하니 정말 간편하게 아름다운 애니메이션을 구현할 수 있었다.
적절한 라이브러리를 잘 사용하는 것도 실력이라는 것을 느낀 점이 기억에 남는 코드이다.

2. 컴포넌트 재사용

    <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번이나 재사용한 컴포넌트이다. 각기 다른 데이터를 주면서 컴포넌트를 사용하니까 화면을 보기에는 모두 다른 코드인 것 같은데 실제 코드는 위처럼 짧았다.

컴포넌트 재활용의 경제성을 크게 느낀 코드이다.

3. 컴포넌트 재사용2

          <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-컴포넌트가 서로 다른 색을 내는 점이 신기하게 다가와서 기록해둔다.

4. Redux

  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로 상태관리를 하면서 느낀 불편함이 없었다면, 이 시원한 감정도 느끼기 어려웠을 것 같다.

역시 기술은 불편함을 느끼고 왜 써야 하는 지 인식한 후에 적용하는 것이 좋은 것 같다.

5. useEffect의 고퀄 사용

  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 값을 추적하도록 했다.


💛 To. 고마운 사람들

위코드에서 2차 프로젝트로 밀리의서재 클로닝 프로젝트를 진행했다. 한 달 전 프로젝트 아이디어로 발표한 밀리의서재가 감사하게도 프로젝트 사이트로 선정되어, PM으로서 프로젝트를 진행할 수 있었다.

프론트엔드 팀원은 나를 포함하여 동훈님, 하늘님, 효정님으로 구성되었고, 백엔드 팀원은 성태님 홀로 구성되었다. 방대한 양의 도서 정보를 저장해야 해서 성태님이 너무 힘들어하실까 걱정이 많았는데, 빠른 속도로 백엔드 작업을 해주셔서 한 번도 딜레이나 어려움 없이 서버 연결을 진행할 수 있었다. 이에 더해 팀 회의 때 의견 조율에도 현명한 도움을 주셔서 정말 감사했다. (역시 백엔드 원탑 에이스...)

매일 아침부터 밤 늦게까지 도움 주셨던 멘토님에게도 감사하고, 막히는 부분이 있을 때 함께 고민 해주셨던 동기분들에게도 진짜 감사하다.
첫 주 목요일까지 애니메이션 공부에 너무 힘들어했는데 aos library 존재랑 사용 방법에 대해 자세히 알려준 정현님과, 마지막 날에 상태관리가 원하는대로 되지 않아 검색 페이지 발표를 포기해야 하나 걱정하고 있을 때 Redux에 대해 상세히 알려줘서 원하는 기능을 구현할 수 있게 도와주셨던 제형님에게도 감사하다.

특히 많이 부족한 PM 믿어주고 계속 힘 내면서 끝까지 함께 해주신 팀원들에게도 감사하고 죄송스런 마음이 크다. 팀원 모두가 각자 맡은 페이지를 세세하고 깊게 구현해주셔서 전체적으로 더 짜임새있는 결과가 나올 수 있었다.

profile
💡 Software Engineer - F.E

4개의 댓글

comment-user-thumbnail
2020년 11월 29일

퍼가요 ~~

답글 달기
comment-user-thumbnail
2020년 11월 29일

짱짱맨

답글 달기
comment-user-thumbnail
2020년 11월 29일

잘 읽었습니다!

답글 달기
comment-user-thumbnail
2020년 12월 1일

와 은진님!!! 너무멋있습니다 엄청 자극받고 가네요

답글 달기