본문 바로가기
항해99/프로젝트

[WIL-025] 토이 프로젝트 - WLW : WeLuvWine 와인 사이트 프로젝트

by junvely 2023. 5. 15.

Week목표 : 05/05일~05/11일 토이프로젝트 WLW : WeLuvWine 와인 사이트 프로젝트


WLW : WeLuvWine 와인 사이트 프로젝트

이번 주는 첫 협업 토이 프로젝트 주간이었다. 

백엔드 Spring 4분과 프론트 React 2명, 총 6명이서 진행한 첫 프로젝트였으며 주제는 와인 사이트 정보를 크롤링 하여 만든 우리만의 와인 추천 및 리뷰 사이트이다. 결과물은 다음과 같다.

- SA 보러 가기: https://www.notion.so/S-A-a3f56ee2319a4e50b8a78313e7511ff7

- Github : https://github.com/junvely/WeLuvWine-FE

- Youtube 시연영상 : https://www.youtube.com/watch?v=dqBlUCRxxps&feature=youtu.be

 

GitHub - junvely/WeLuvWine-FE: 항해14기 FE repo입니다.

항해14기 FE repo입니다. Contribute to junvely/WeLuvWine-FE development by creating an account on GitHub.

github.com

 

 

 

프로젝트 기능

- 회원가입, JWT토큰 로그인, 리뷰 작성, 리뷰 수정, 리뷰 삭제, 와인 리스트 조회, 와인 랭킹 조회, 와인 검색 기능, 와인 추천 기능, 와인 정보 상세페이지 동적 라우팅

  1. 와인 검색 API<br>
    - 와인 이름을 키워드로 조회할 수 있습니다.<br>
    - Full name을 검색하지 않아도 됩니다. 일부 키워드로 검색이 가능합니다.
  <br>
  
  2. 리뷰 작성 API<br>
    - 내가 좋아하는 와인에 리뷰를 업로드할 수 있습니다. 
  <br>
    
  3. 리뷰 수정 API<br>
    - 내가 작성한 리뷰에 한하여 수정할 수 있습니다.
  <br>
    
  4. 리뷰 삭제 API<br>
    - 내가 작성한 리뷰에 한하여 삭제할 수 있습니다.
  <br>
    
  5. 와인 좋아요 API<br>
    - 내가 좋아하는 와인에 좋아요 버튼을 눌러 추천할 수 있습니다.<br>
    - 이미 누른 좋아요 버튼을 다시 누른다면 추천은 취소됩니다.
  <br>
  
  6. 추천 와인 조회 API<br>
    - 추천된 와인 중 추천수가 가장 많은 와인은 `와인의 전당`에 올라갑니다.<br>
    - `와인의 전당`에 오른 와인의 추천수가 같다면 고유 식별 코드를 기준으로 정렬되어 올라갑니다.

 

시도한 점

로그인 처리

1. 프론트 협업 세션에서 받은 자료를 참고해서, 백엔드로부터 토큰을 받으면 헤더에 토큰이 존재할 경우 인터셉터로 응답을 가로채 자동으로 세션 스토리지에 저장되게 처리 

=> 세션 스토리지에 저장한 이유 : 브라우저 창이 닫히거나 윈도우 창이 종료될 경우 등 자동으로 토큰이 제거되어 로그아웃 처리하게 하기 위하여 세션 스토리지를 선택했다.

2. 사용자 정보가 필요한 요청에서도 인터셉터를 사용해서 헤더에 토큰이 존재할 경우 자동으로 서버에 전달되게 하여

토큰을 전달하는 작업을 컴포넌트에서 반복 진행하지 않고 API에 오기 전에 수행하도록 했다.

instance.interceptors.response.use(
  function (response) {
    const token = response.headers.get("ACCESS_KEY");
    if (token) {
      sessionStorage.setItem("AccessToken", token);
    }
    return response;
  },
  function (error) {
    return Promise.reject(error);
  }
);

// baseURL
baseURL.interceptors.request.use((config) => {
  if (config.headers === undefined) return;
  const token = sessionStorage.getItem("AccessToken");
  config.headers["ACCESS_KEY"] = token;
  return config;
});

 

검색 기능 : 리액트 쿼리, 지역상태로 관리

같은 페이지 내에서 데이터가 업데이트 되어야 해서 리덕스로 관리하고자 했으나, 다른 페이지에서 재사용되지 않는 데이터기 때문에 굳이 리덕스를 사용할 필요까진 없다고 생각되어 지역상태로 관리 하였다.

=> 실전 프로젝트에서는 recoil 또는 justand, useContext 등을 사용해 볼 예정이다.

 

좋아요 기능 

1. 로그인한 사용자가 해당 와인에 좋아요를 했는지 여부를 상세 페이지 와인 정보 API에서 받아와 UI를 좋아요 여부에 따라 달리 그려주도록 한다.

{isLike ? (
          <AiFillHeart
            style={{
              marginLeft: "1rem",
              fontSize: "2rem",
              lineHeight: "2rem",
              paddingTop: "5px",
              color: "#b92427",
            }}
          ></AiFillHeart>
        ) : (
          <AiOutlineHeart
            style={{
              marginLeft: "1rem",
              fontSize: "2rem",
              lineHeight: "2rem",
              paddingTop: "5px",
              color: "#b92427",
            }}

2. 좋아요 클릭 시 와인의 좋아요 수를 증가시키는 API로 수정 요청을 보낸다. 좋아요가 증가하면 즉시 반영되어 증가되도록 useMutation의  queryClient.invalidateQueries("reviews")하여 관찰하도록 한다.

function WineDetailPost({ wineInfo, isLike }) {
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const mutation = useMutation(increaseLikeAxios, {
    onSuccess: () => {
      queryClient.invalidateQueries("reviews");
    },
  });

 

 

트러블 슈팅, 알게된 점 

- 기본적인 CRUD는 문제 없었지만, 로그인 처리와, 검색 기능, 추천 기능을 구현하는 데에 문제가 발생했다.

1) 리덕스 상태관리 시 URL 직접경로 접근 문제

문제 상황

데이터 통신을 줄이기 위해 리덕스로 와인 데이터를 관리하였을 때 발생한 문제다. 상세 페이지로 이동하는 경우, URL 경로로 직접 진입 시에는 페이지가 새로고침에 의해 리렌더링 되어 전역 상태 데이터가 소실되어 가져오지 못하는 문제가 발생했다.

해결 방법

=> 이 때문에 상세 페이지는 분리되게끔 상세 페이지에서 따로 데이터 통신을 하여 와인 정보를 받아 오게 하였는데,

이 문제를 리덕스 persist를 사용하면 해결할 수 있다는 것을 알게 되어 다음 프로젝트에서 적용해볼 예정이다.



2) 검색 기능, UI 업데이트 문제

문제 상황

검색 기능까지 구현하였으나, 처음 검색을 하면 데이터를 가져오고 있지만, UI는 업데이트 되지 않는 문제가 발생했다. 이 때문에 검색을 두번 실행해야 새로고침에 의해 렌더링 되어 데이터가 나타났다.

해결 방법

  const [searchValue, handleInputChange, reset] = useInput(initialState);
  const { data, refetch } = useQuery(
    "search",
    async () => {
      return await searchWineListAxios({ searchKeyword: searchValue });
    },
    { enabled: false }
  );
  
  useEffect(() => {
    if (data) {
      setWineList(data);
    }
  }, [data]);

=> 버튼 이벤트 발생 시 useQuery로 데이터를 받아올 때, useEffect에서 data가 있을 경우 data를 감시하여

따로 setState에 data가 업데이트 될 때 마다 => 데이터를 바로 업데이트 하도록 설정하여 해결

 

3. 로그인 Origin문제

CORS 문제 => 포트 번호를 맞춰야 한다는 것을 알게되어 3000번에서 요청하여 해결

 

 

매니저님 피드백 

- 프리티어, ESlint를 사용하여 협업에 필요한 셋팅을 해놓았으면 좋았을 것

- axios에서 instance를 너무 많이 사용하면 좋지 않다. 지금 코드에서는 intercepter를 하나로 줄일 수 있을 것.

 (어차피 토큰이 있는지 여부를 조건문으로 걸러주고 있기 때문에)

 

 

느낀 점