Wecode 2차 프로젝트 후기_Pinterest_Frontend

Yunji·2020년 6월 22일
2

project

목록 보기
1/2

프로젝트 소개

  • 2주 프로젝트로 프론트 3명, 백앤드 1명이서 Pinterest 페이지를 만들었다
    팀명은 Pinterrorist! (우리팀 백앤드 nick의 작명센스) trello 와 매일 아침 stand up 미팅으로 서로 일정을 공유하고 매일 할 일을 계획했다
  • 데모영상 https://youtu.be/axZ_IJpYmWQ

사용한 기술

1차 프로젝트(Wecode 1차 프로젝트 후기_Beats_Frontend) 때는 React class component 와 Sass를 사용했는데 2차 프로젝트 때는 함수형 componentstyle component 를 사용했고 리액트 Hooksredux 를 사용하였다 백앤드 API 를 확인할 때는 Postman 을 사용하였다

  • React
  • CRA
  • function componenet, style component
  • React Hooks
  • React Router
  • Redux
  • Google Login API
  • Git
  • Postman
  • fetch, map, Query parameter, media query

내가 맡은 역할/부분

핀터레스트 자체가 페이지 UI 가 많지 않아서 모든 페이지를 다 만들수 있었고 최대한 기능에 집중하기 좋았다
다른 프론트 팀원 두명 다 1차 프로젝트때 로그인과 회원가입을 해봐서 1차때 로그인, 회원가입을 안 해본 내가 로그인과 회원가입을 맡기로 했다. 하지만 소셜 로그인으로 구글 로그인을 연동하게 되서 만들 UI도 거의 없고 코드도 간단해서 생각보다 일찍 끝나게 되었다 그래서 추가로 1주차에 네비게이션 바도 맡아서 만들었다

  • 최종적으로 내가 맡은 부분은 로그인과, 회원가입, 회원가입시 관심사 선택 부분, 상단 네비게이션 바 (검색, 장바구니, 로그아웃), 메인 페이지 이다

로그인/ 회원가입

- 구글 소셜 로그인 연동

실제로 핀터레스트가 구글 로그인으로 연동 할 수 있어서 우리는 구글 로그인으로 소셜 로그인을 사용하기로 했다 덕분에 제작할 UI 가 줄어 기능에 집중 할 수 있었다
아래 사진은 처음 핀터레스트를 켰을 때 화면이다 뒤에 이미지 콘텐츠들은 mock data 로 만들어 놓은 데이터를 뿌려놓은 모습이다 로그인/회원가입은 모달로 만들었고 뒷부분 스크롤은 막아놓았다 실제 핀터레스트 로그인 모달과는 조금 다른데 우리는 구글 로그인만 가능하게 하려고 디자인을 조금 수정해 보았다

  • 디자인을 수정한 로그인 모달(맨 오른쪽 위에는 우리팀원의 강아지인 복실씨가 있다)

  • 아래는 실제 페이지의 로그인 모달이다

  • 구글 로그인을 누르면 뜨는 구글 로그인 창

access_token...

구글 소셜 로그인은 프론트에서 구글로부터 받은 access token 을 백앤드로 넘겨주고 백에서는 그 access token을 다시 구글로 넘겨 승인을 받아 다시 프론트로 Authorization token 을 넘겨주는 방식이다
처음에는 밑에 보이는 accessToken 을 넘겨주었는데 너무 불안정했다 5번 시도하면 3번은 undefined 가 떴다 그래서 결국 중간 발표때도 로그인에 실패했다🤯 그러다 우리팀 백앤드님이 다른 팀 프론트분과 wc 안에 access_token 을 발견해(둘 다 값은 같았다) 그 token 으로 시도하니 허무하게도 너무 잘됐다... 이렇게 소셜 로그인 문제는 wc 만 바꿈으로 해결...

  • 아래 사진은 우리를 괴롭혔던 구글 로그인 token.... 지금도 undefined😂

- 회원가입 시 관심사 체크

  • 구글 소셜 로그인 바로 다음에 나타나는 모달이다 회원이 아니면 나타나는 관심사 체크 모달로 최소 5개를 체크해야 넘어갈 수 있다
    서버에서 넘겨주는 77개의 주제를 map을 돌려서 나열했고 각 주제별로 고유의 id 를 가지고 있어 사용자가 선택한 id를 리스트화해서 서버로 넘기면 그 관심사를 가지고 메인 화면에 띄울 콘텐츠를 보내준다 map을 돌리니 각각의 관심사를 체크할 때 id로 접근해서 이벤트를 줘야했다 그리고 밑에 버튼에도 선택한 만큼 카운트를 줘야해서 까다로운 부분이었다 그만큼 재미도 있었다

네비게이션 바

- 검색

  • 원래 핀터레스트는 단어를 검색하면 그 단어와 관련있는 핀들을 보여준다 하지만 우리는 관련있는 핀을 보여주는 대신 주제를 검색하면 그 주제의 핀들을 보여주는 방법으로 검색을 구현하였다 밑에 추천 아이디어를 클릭해도 해당 주제의 핀들을 보여준다

- 장바구니

  • 장바구니는 원래 핀터레스트에 없는 기능인데 리덕스를 활용해보고 싶어서 추가한 기능 이다 핀터레스트는 한개씩만 저장이 되는데 우리는 장바구니 (일명 핀바구니) 를 만들어서 여러개의 핀을 한번에 한 보드에 저장할 수 있게 만들어 보았다

- 메인, 팔로잉, 마이페이지 이동

  • 각 버튼을 누르면 해당 페이지로 이동한다 리액트 라우터를 이용하였다

- 로그아웃

  • 로그아웃 버튼을 누르면 로컬 스토리지에 저장되어있던 토큰이 사라지고 로그인 화면으로 돌아가게 구현하였다

반응형

  • 작은 부분이지만 프로젝트 발표 당일에 구현해본 모바일 반응형
    특정 크기보다 작아지면 검색 창이 사라지고 검색 아이콘으로 바뀌게 만들어보았다🥳

메인 페이지

- 핀터레이스 레이아웃, 이미지 호버 이벤트

  • 핀터레스트 레이아웃은 masonry layout 이라고도 하는데 벽돌을 쌓은 느낌의 레이아웃이다 핀터레스트가 나온 이후 유명해진 레이아웃이다 처음에는 이 레이아웃을 어떻게 구현할 지 막막했는데 검색해보니 figure 를 사용하면 쉽게 만들 수 있었다 그렇게 메인화면이 완성되었고 여기도 마찬가지로 서버에서 전달해주는 데이터를 map 돌려서 구현했다 그래서 개별적으로 이미지 호버 이벤트를 주는 부분이 까다로웠는데 역시 고유 id값으로 구별해서 넣어주었다
  • 이미지에 마우스가 올라갔을 때 총 세가지 레이어가 나오는데 오버레이 되는 레이어와 위에 보드 선택 메뉴, 오른쪽 하단의 핀 저장 하는 버튼이다
  • 오버레이 되는 레이어는 클릭했을 때 해당 이미지의 상세 페이지로 넘어가게 해야했고 위에 보드 메뉴는 드롭다운으로 그 안에서 버튼을 클릭할 요소가 많았다 마지막으로 오른쪽 하단의 핀 저장 버튼은 눌렀을 때 장바구니에 들어가는 이벤트가 있었다 이렇게 세가지도 다 다른 이벤트가 있어서 그부분을 구별하는 것이 어려웠다

- 장바구니

  • 발표 직전에 했지만 재밌었던 장바구니이다 프로젝트 중간에 배운 리덕스를 활용해서 만든 부분인데 만들면서 하나의 state로 모든 부분에 적용할 수 있는 점이 매우 편리하게 느껴졌다 각 이미지의 오른쪽 아래 핀 저장 버튼은 누르면 해당 이미지의 id 값과 이미지 url 이 state 에 저장되고 그것을 장바구니 탭에서 map 을 돌려 보여준다
  • 그리고나서 하단에 보드 선택하는 메뉴에서 핀들을 저장할 보드를 선택하고 저장 버튼을 누를 때 핀들의 id 값과 보드 title 을 서버로 보낸다

기억하고 싶은 부분

구글 로그인

// 구글 로그인 성공했을 때 실행할 함수
const onLogin = (result) => {
    fetch(`${url}/account/google`, {
      method: "POST",
      headers: {
        // 서버로 access token 보내기
        Authorization: result.wc.access_token,
      },
    })
      .then((res) => res.json())
      .then((res) => {
        // 들어온 데이터 확인
        // console.log("res", res);
        // authorization token 받아서 local storage 에 저장
        localStorage.setItem("Authorization", res.access_token);
        // 구글 로그인한 사용자가 이미 회원일 경우 모달 창 닫기
        if (res.message === "ALREADY SIGNED-UP") {
          window.location.reload();
        // 회원이 아닐경우 다음단계로 넘어가는 함수 goNext 실행
        } else {
          goNext();
        }
      });
};
//구글 로그인 버튼
<GoogleLogin
      clientId="클라이언트Id"
      render={(renderProps) => (
  	// 버튼 커스터마이징 한 부분 
        <button
          onClick={renderProps.onClick}
          disabled={renderProps.disabled}

          Google로 계속하기
        </button>
      )}
      // 로그인 성공 시 실행할 함수 result 안에 구글에서 보내준 데이터가 들어있다
      onSuccess={
        // result 를 인자로 받아 onLogin 함수 실행
        (result) => onLogin(result)
        // 들어온 데이터 확인 뭐든 확실하게 하려면 console.log 로 찍어보는게 좋다는 것을 다시 한번 느꼈다
        // (result) => console.log(result.accessToken)
      }
      onFailure={(result) => console.log(result)}
      cookiePolicy={"single_host_origin"}
/>

관심사 선택 부분

  • 회원가입 시 관심사 선택 부분은 서버에서 보내주는 데이터를 map 돌려서 제작된 레이아웃에 넣고 사용자가 선택한 리스트를 서버로 보내 메인에 뿌려주는 기능이다 이것 역시 모달이라 삼항 연산자를 써서 로그인 다음에 조건에 따라 나타나는 식으로 구현했다
// 각 관심사 선택 시 state 에 추가하거나 있다면 뺀다
const checkBox = (id) => {
    // 선택한 관심사가 리스트에 없을 때
    if (!check.includes(id)) {
      setCheck(check.concat(id));
    // 이미 선택한 것을 또 눌렀을 때
    } else {
      // 필터를 이용해서 id 와 일치하는 것을 빼고 새로 리스트를 만든다
      const filtered = check.filter((el) => el !== id);
      setCheck(filtered);
    }
  };

스타일컴포넌트에서 직접 props 를 받아와서 사용하는 부분이 편해서 기억에 남는다

const Check = styled.button`
    ${(props) =>
    	props.active >= 5 &&
    	css`
      	background-color: #e60023;
      	color: #fff;
    `}
`
//버튼 카운트 부분
<Button active={check.length} onClick={() => is(check)}>
	// 최소 5개를 선택해야 완료 버튼이 활성화 되게 length 로 카운트함
	{check.length >= 5 ? `완료` : `${5 - check.length}개 더 선택`}
</Button>

검색 부분

검색 부분은 1차 프로젝트 때부터 해보고 싶었다 물론 완벽하게 핀터레스트의 검색 기능을 구현한 것은 아니지만 그래도 실제로 검색이 되는 모습을 보니 신기했다
input 창에 입력한 내용을 state 에 담아 서버로 보내면 그 해당 주제에 대한 핀을 보내준다

 const onChange = (e) => {
    // input 에 들어오는 값 state에 저장
    setSearchValue(e.target.value);
    // 엔터 쳤을 때 함수 실행
    if (e.keyCode === 13) {
      // 타겟의 value 값을 넣어서 search 함수 실행
      search(e.target.value);
      e.target.blur();
      // search 드롭 닫기
      setActive(false);
    }
  };
  // 검색한 값 서버로 보내서 해당 검색어에 해당하는 핀 받아오기
  const searchHandler = (text) => {
    setSearch(true);
    fetch(`${url}/search/?search=${text}`, {
      headers: {
        Authorization: localStorage.getItem("Authorization"),
      },
    })
      .then((res) => res.json())
      .then((res) => setContentsList(res.search_term));
  };

장바구니

  • 프로젝트 마지막날에 한 부분이라 가장 기억에 많이 남는다 리덕스를 이용해서 하나의 state로 여러 부분에 데이터를 줄 수 있는 부분이 너무 편했다 핀터레스트에서는 딱히 적용 할 수 있는 부분이 없어서 기능을 새로 만드느라 시간을 조금 쓰긴 했지만 그래도 해보길 잘했다는 생각이 든다 리덕스를 보기만 했을 때는 너무 어렵게 느껴졌는데 실제로 적용해보니 너무 편했다
  • 새로 추가한 기능은 핀터레스트에는 없는 다중 저장 기능이다 기획해보니 실제로 있어도 좋을 것 같다 예를들어 특정 프로젝트를 위해 핀들을 저장하고 싶을 때 보드를 하나 만들어 해당 보드에 들어갈 핀들을 저장하는 상황에서 하나하나 저장하기보다는 여러개를 클릭해서 동시에 저장하면 편할 것 같다
  • 기능은 이렇다 메인에서 저장하고 싶은 핀들 오른쪽 하단의 핀 버튼을 눌러 장바구니(핀바구니) 로 보낸다 -> 선택이 끝나고 장바구니를 열어 보드를 선택하고 저장을 누른다 -> 서버로 해당 보드와 핀 리스트를 보낸다 -> 저장완료!
// actions
export const addPin = (pin) => {
  return {
    type: "ADD_PIN",
    payload: pin,
  };
};
// reducers
export const cartList = (state = [], action) => {
  switch (action.type) {
    case "ADD_PIN":
      // 선택한 핀을 state 에 추가
      return [...state, action.payload];
    default:
      return state;
  }
};

아쉬운 부분

  • 아쉬운 부분은 메인 페이지에 레이지로딩을 적용해보지 못했다는 것? 하지만 이 부분도 추가적으로 수정해서 넣을 예정이다 장바구니도 마지막에 시간이 부족해서 서버로 넘기는 부분을 마무리하지 못했는데 이 부분도 코드 리팩토링 하면서 보완할 예정✨

후기

  • 이번 프로젝트에서 백앤드와 커뮤니케이션이 너무 잘 되서 좋았다 1차때 부족하다고 생각했던 부분이라 신경쓰고 있었는데 역시 한번 프로젝트를 하고 나서 그런지 데이터를 어떻게 받을지 백앤드분과 대화하는데 더 수월해진 느낌이었다🙆‍♀️
  • 우리 프론트팀도 정말 소통이 잘 됐다 최고🙌 너무 재밌고 좋은 능력자들ㅋㅋㅋ 그리고 무엇보다 우리팀 백앤드 nick... 진짜 어마어마한 실력자 뚝딱뚝딱 코드를 짜고 데이터를 넘겨주고 장난아니었다... 시간만 좀 더 있었으면 DM 기능도 공부하면서 구현해봤어도 좋았을 것 같다
  • 이번 프로젝트는 사실 처음 주제로 받았을 때는 걱정이 많았다 레이아웃도 그렇고 어렵다고 생각했다 하지만 막상 시작하고 나니 UI 가 간단한 핀터레스트가 너무 고맙게 느껴졌다 덕분에 기능에 온전히 집중할 수 있었다 기능에 맞게 디자인을 살짝살짝 변경하는 부분도 재밌었다

0개의 댓글