min

[항해 99 미니 프로젝트 5주차] 본문

TIL

[항해 99 미니 프로젝트 5주차]

minprogramming 2023. 7. 19. 15:03

1. 핵심 기술

1-1) 업로드/ 삭제 / 상세페이지

빵집 리뷰와 관련된 글들을 업로드 , 삭제기능 제공

빵집 리뷰 리스트에서 특정 item을 상세하게 볼 수 있는 기능 제공

1-2) 로그인 / 회원가입

빵집 리뷰와 관련된 글을 삭제하기 위한 로그인 / 회원가입 기능제공

2. 기술적 의사 결정

1. vercel를 사용한 이유

vercel를 사용한 이유는 github에 push를 하면 자동적으로 build와 배포 과정이 이루어집니다.

즉 배포 과정이 최적화가 되고 이로 인해서 개발 프로세스가 굉장히 간단해지기에 사용했습니다.

2. styled-components 를 사용한 이유

styled-components를 사용한 이유는 동적으로 스타일링이 가능하기 때문입니다.

styled-components는 CSS In JS이기 때문에 js로 동적인 스타일링 가능하고 이를 통해서 제사용 컴포넌트를 만들때 유리하기 때문에 사용했습니다.

또한 styled-components를 사용하면 각 요소마다 고유의 클래스 이름을 부여할 수 있기 때문에 스타일링에서 요소의 className으로 인해서 스타일이 겹치는 부분이 없기 때문에 사용했습니다.

3. axios를 사용한 이유

fetch 대신 axios를 쓴 이유는 axios 자체에 내장되어 있는 라이브러리들이 많기 때문입니다. 즉 구체적으로 axios에는 instance와 interceptor가 있습니다. 이는 토큰 처리와 에러 핸들링 , 로그 관리 , 등 여러가지 작업들을 진행할 수 있기 때문에 axios를 사용했습니다.

그 뿐만 아니라 instance를 통해서 api 의 baseUrl나 header , withCredtial등을 통해서 유지보수 , 코드의 가독성적인 측면에서 많은 향상을 불러올 수 있기 때문에 사용했습니다.

 

 

3. 트러블 슈팅

1. CORS 에러

일반적으로 브라우저는 SOP를 기준으로 통신을 합니다. 이때 SOP는 같은 도메인에서의 통신을 뜻합니다.

이때 SOP를 기준으로 통신을 하는 이유는 내가 통신을 주고 받을 려고 하는 상대방 사이트가 안전한 사이트 인지 위험한 사이트인지 확인할 수 없다는 점입니다. 이런 점으로 인해서 내가 만약 다른 사이트와 통신을 주고 받는 경우에는 SOP로 인해서 통신이 막히게 되고 이로 인해서 CORS 오류가 뜨는 것입니다. 그래서 이를 허용하기 위해서 CORS 정책을 적용해야 합니다. 그래서 저희는 CORS 정책을 적용하기 위해 백엔드에서는 ~~~ 프론트엔드에서는 기본적인 default withCredtial을 false로 주고 인증 , 인가를 하는 부분에서는 withCredital을 true로 줘서 해당 문제를 해결했다.

2. 게시글 업로드 multipart/form-data 형식 처리

게시글을 업로드 하는 과정에서 file을 보내기 때문에 프론드에서는 formData로 해당 데이터를 보내줘야 하는 상황이었다.

이때 나는 formData를 보내는 코드를 따로 custom hooks로 분리했기 때문에 해당 코드를 바꾸면 업로드 , 로그인 , 회원가입과 관련된 데이터가 전부다 formData로 보내지는 문제가 발생했다. 그래서 나는 서버로 데이터를 보내는 코드는 custom hooks로 대체하고 나머지 보내는 데이터와 관련된 부분은 각각의 컴포넌트마다 작성을 해줘서 해당 문제를 해결했다.

<기존의 코드>

  const initialState = { id: "", pw: "" };
  const [form, onChange, onSubmit] = useForm(initialState, null);

<바뀐 코드>

  const [cookies] = useCookies();
  const [form, setForm] = useState({ name: "", password: "" });
  const [result, fetchData] = usePost(getUser, form);

  const onChange = (e) => {
    const { name, value } = e.target;
    setForm({ ...form, [name]: value });
  };

  const onSubmit = (e) => {
    e.preventDefault();
    fetchData();
  };

커스텀 훅을 너무 많이 사용하거나 너무 많이 제사용 할 시에 각각의 다른 점들을 반영하기가 너무 어렵고 내가 custom hooks에 로직을 바꾸는 순간 다른 로직들도 다 바뀌는 문제가 발생하기 때문에 커스텀 훅을 많이 사용하는 것이 좋지 않다는 것을 알게 되었다.

4. redux thunk의 사용 문제

redux thunk의 경우에는 redux의 미들웨어로  액션을 리듀서에게 바로 보내기 전에 내가 원하는 작업을 중간에 하기 위해서 사용한다.

그래서 처음에 redux thunk를 사용해서 성공 여부 , 실패 여부 , 로딩을 전역적으로 관리하기 위해서 사용을 했다. 하지만 성공 여부 , 실패 여부 , 로딩을 전역적으로 관리할 필요가 없었고 그 데이터는 하나의 컴포넌트 안에서만 사용하기 때문에 결국엔 redux thunk를 사용할 이유가 없었다. 그래서 나는 성공 여부 , 실패 여부 , 로딩을 지역 상태로 관리하면서 이와 관련된 로직을 따로 custom hooks로 분리해서 내가 원하는 경우에 바로바로 사용할 수 있도록 만들어 놓았다.

import { useState } from "react";

const useFetch = (promiseCreater, req) => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(null);
  let result = { loading, error, success };

  const fetchData = async () => {
    try {
      setLoading(true);
      const data = await promiseCreater(req);
      setSuccess(data);
    } catch (e) {
      setError(e);
    } finally {
      setLoading(false);
    }
  };

  return [result, fetchData];
};

export default useFetch;

5. 로그인 토큰 처리 문제

로그인과 관련해서 처음에 응답 해더로 쿠키로 토큰을 보냈을 때 해당 쿠키의 값이 쿠키 스토리지에 저장되지 않는 문제가 발생했다.

그래서 부분적으로 withCreditial을 true로 주고 백엔드에서도 SamSite나 secure를 설정했음에도 불구하고 여전히 문제가 해결되지 않았다. 그래서 저희는 백엔드에서 토큰을 body로 보내주면 그 받은 token을 프론트에서 interceptor를 통해서 응답을 받는 과정에서 response body에 token을 가져와서 쿠키로 그 token을 넣어주었다. 그리고 나서 그 token을 백엔드에서 보낼때도 interceptor를 통해서 header에 token을 담은 채로 요청을 보냈다. 이를 통해서 해당 문제를 해결했지만 해당 token을 response header에 쿠기로 보내지 못한 부분이 아쉬웠다.

    // 토큰 생성 코드
    if (config.data.token) {
      const cookies = new Cookies();
      const expires = moment().add("50", "m").toDate();
      cookies.set("token", config.data.token, {
        path: "/",
        secure: "/",
        expires,
      });
    }

 

  (config) => {
    //토큰 헤더에 담아서 보내는 코드
    const cookies = new Cookies();
    config.headers.userToken = "Bearer " + cookies.get("token");
    return config;
  },