기술 로그 - Dumboz/yalp GitHub Wiki

공통

1. git push 방지

  • [문제 상황] git repository 의 수정 권한을 팀원에게 부여한 상황(kanban board 수정을 위해)에서 push 만을 막아야 함
  • [해결 방법] 팀원 로컬 repository 내에서 git remote set-url --push <REMOTE_NAME> no_push

2. git revert 방법 (일반적인 상황)

  • [문제 상황] 잘못된 PR이 온 경우 이를 revert 해야 함
  • [해결 방법] git revert --no-commit HEAD~<NUM>.. 에 현재 최신 Commit 이력에서 되돌아갈 commit 개수를 입력하면 HEAD가 이동함

3. git revert 방법 (merge commit 상황)

  • [문제 상황] 되돌리려는 commit 중 merge commit이 포함된 경우
  • [해결 방법]

(가정: 최신 commit이 merge commit 인 상황)

step 1) git log를 확인하여 현재 commit hash를 확인한다

step 2) 현재 merge commit에서 merge 된 commit 을 확인한다

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: `8989ee0` `7c6b236`
Author: Ben James <[email protected]>
Date:   Wed Aug 17 22:49:41 2011 +0100

Merge branch 'gh-pages'

Conflicts:
    README

현재 merge에서는 8989ee0(1번)와 7c6b2362번) 두 commit이 merge 되었음

step 3) git revert <현재 Hash> -m <되돌아갈 hash의 번호>

ex) git revert 8f937c68 -m 1 (1번 commit으로 되돌아감)

참고 자료 1

참고자료 2

BrowserRouter : history 를 관리할 수 있게 만들어 주는 react router Oulet : swap children NavLink : 함수로 인수를 전달하면 isActive 인자가 전달되어 선택 여부를 반환한다 useNavigate :

4. Proxy 서버 설정 방법

[문제 상황] yelp API 에서 client side로 request 요청을 하는데 필요한 CORS header를 제공하지 않는다. (참고 1)

[해결 방법]

  1. 'cors-anywhere.herokuapp.com' 을 get 요청 시 url 앞에 붙여주면, API에 우회하여 요청 가능하다. 하지만, 1일 요청 제한이 있어 근본적인 방법이 될 수 없다
  2. 따라서 server로 Yelp API 요청을 해야 하는 상황이므로, client 에서 api 요청을 했을 때 server가 요청하는 것처럼 동작하기 위해 proxy 중계 서버를 두었다
  • 중계 서버 설정 방법 : client package.json 에 "proxy" 프로퍼티에 back server url을 작성한다.

proxy 오해와 깨달은 점

[깨달은 점]

  1. server를 만들지 않고도 package.json에 proxy를 yelp api url로 설정하면, 자체적으로 proxy 서버가 구동되어 origin을 바꿔 api요청을 우회할 수 있을줄 알았다.
  • [답변] package.json에 proxy는 origin을 바꾸는 역할이 아니고, 중계 서버가 무엇인지 지정하는 설정이다.
  • [답변] proxy 서버는 중계 서버인데, yelp api 주소를 중계 서버로 설정한 것이 문제였다. (yelp api가 중계 서버가 될 수 없는 이유: yelp는 중계 서버가 아니라, 목적지가 되어야 한다. 중계 서버로 설정할 이유가 없다.) 따라서 중계(proxy)서버를 직접 구축하여, 해당 서버에서 API 요청을 보내도록 우회 및 설정하였다.
  1. 왜 클라이언트에서 보낸 요청은 CORS에러가 나고, proxy 서버로 우회했을 때는 CORS에러가 나지 않는가?
  • CORS에러는 브라우저가 발생시키는 것이다.
  • 브라우저는 SOP 정책에 의해 origin이 다르면 통신을 할 수 없지만 CORS 예외를 두면 통신이 가능하다.
  • CORS 예외 처리 방식은 응답 header 내 'Access-Control-Allow-Origin' 속성이 포함되어 있어야 한다. 이 속성이 없거나, 해당 속성 값에 클라이언트 origin이 포함되어 있지 않으면 CORS 에러가 발생한다.
  • 따라서 중계 서버를 두면, 통신할 수 있는 이유는 다음 2가지이다.
    1. 중계 서버와 API 서버간 통신은 origin이 달라도 통신을 할 수 있다.
    2. 중계 서버와 브라우저 간 통신은 중계 서버의 응답 header 내 'Access-Control-Allow-Origin' 속성이 와일드카드(*)로 설정되어 있어, CORS 에러가 발생하지 않는다.

5. Router 설정

6. Redux 기술 부채

  • dispatch에 비동기처리를 담당하는 action을 전달할 경우 일어나는 일에 대해 조금 더 정리가 필요하다.

7. Warning: Cannot update a component (searchForm) while rendering a different component (searchPage) ...

해결법

8. useCallback 사용 시, dependency array 설정의 중요성

  • [문제 상황] filter Section의 click event handler를 useCallback 함수로 설정하였음. 이 때 dependency array를 빈 배열로 설정할 경우, useLocation으로 불러온 search(query)가 변경 될 경우 해당 search 를 반영하지 않아 filter 적용 시 에러 발생
  • [해결 방법] 이벤트 핸들러 내에 자신이 의존하는 search 를 dependency array에 추가함
 const handleClick = useCallback(
    e => {
      ...
      const query = QueryString.parse(`search`.replace(/^\?/, ''));
      ...
    [`search`, setSearchParams],
  );

9. Code Splitting

  • [문제 상황] React.lazy, React.Suspense 를 사용하여 코드 스플릿팅을 진행하던 도중 아래와 같은 에러가 발생함.
Error: The above error occurred in one of React components
  • [이슈 분석] lazy하게 불러올 컴포넌트 파일들을 default로 내보내지 않았기 때문에 발생한 문제였다.

  • 참고 페이지: CRA- Code splitting

강희

컴포넌트

1. 컴포넌트 안에서 비동기 요청을 사용할 때 무한 리렌더링이 발생

  • 문제 코드
async function call(query, setState) {
  const { data } = await axios.get('/api' + query);
  setState(data);
  return { data };
}

const Exam = () => {
  const [state, setState] = useState({});
  const { pathname, search } = useLocation();
  call(pathname + search, setState);

  return <div>{JSON.stringify(state)}</div>;
};
  • [문제 상황] Exam 컴포넌트 안에서 setState를 비동기 요청을 수행하는 Call 함수에게 넘겨주어 state를 변경하도록 하였더니, 계속해서 리렌더링이 발생함.

  • [원인 파악] 컴포넌트 안에서 비동기 함수를 호출해 state를 변경하면, 다시 컴포넌트를 렌더링 하며 비동기함수를 호출되어 state가 변경되고, state가 바뀌었기 때문에 다시 컴포넌트를 렌더링하는 무한 루피가 발생한다.

  • [해결 방안]

  1. react-async라이브러리에서 제공하는 useAsync() hooks을 사용
  2. useEffect() 를 사용해서 Mount시에만 비동기 함수를 호출하도록 구현
  • 이 방법은 state가 초기 값일 때, 비동기 함수가 state를 update해줬을 때, 즉 2번 렌더링이 발생함.

스토리북

1. 컴포넌트 불러올 때 발생한 에러

  • [문제 상황] cannot read properties of undefined (reading 'displayName') 이라는 에러가 발생함
  • [해결책] export default 로 꺼내온 컴포넌트를 일반 export 방식으로 내보낸 컴포넌트를 가져오는 방식을 사용해서 발생한 에러였음. default 로 내보낸 컴포넌트를 가져오는 방식을 사용하여 해결함.

2. 전체 컴포넌트들에게 Global style 적용하는 방법

  • [문제 상황] 전체 컴포넌트에게 styled-component로 작성된 GlobalStyle 컴포넌트를 적용해주어야했음.
  • [해결책] Global decorators 를 사용하여 해결함. storybook - decorators

박지영

  1. 상태 관리를 하면서 중복을 최소화 시키는 방법
  • [문제 상황] Price filter button을 만들 때, 여러 개의 버튼을 한 번에 상태관리를 하게 되면서 이슈가 됨. 하나의 컴포넌트 안에 다양한 상태가 존재하고 이를 useState로 관리하다 보니 코드의 중복으로 가독성이 떨어짐.

  • [해결책] useReducer을 사용해 하나의 reducer라는 함수로 관리해주면서 응집도를 높이고 가독성 이슈도 해결함.

  • [이유] useState와 useReducer 둘 다 고려를 해보았지만 응집도, 가독성, 확장성 측면에서 useReducer가 훨씬 장점이 많다고 판단.

  1. StoryBook
  • [문제 상황] Buttons must have discernible text 에러가 남. aria-label이 없어서 접근성 준수를 하지 못함.

  • [해결책] aria-label, aria-labelledby로 접근성 준수시켜 모든 test 통과됨. button은 반드시 role이 필요.

손원재

지도

1. 지도 마커를 세팅할 때 상위에서 관리하는 마커 배열에 set하면 무한 리렌더링 발생

  useEffect(() => {
    const loader = new Loader({
      apiKey: process.env.REACT_APP_MAP_API_KEY,
      version: 'weekly',
    });

    loader.load().then(setMap(...));

  }, [businesses, GEOArr]);
  • 문제 상황 리렌더링할 때 의존할 필요 없는 값을 의존하여 set과 동시에 리렌더링되고 리렌더링 되며 set이 다시 실행되며 무한 루프에 빠짐

  • 해결 방안 의존해야 할 값을 정리하여 의존 배열을 제대로 세팅함

  useEffect(() => {
    ...
  }, [businesses]);

윤석규

  1. 중첩된 컴포넌트의 click 이벤트가 중복 문제
  • [문제 상황] 상위 컴포넌트에 click 이벤트를 등록한 이후, 하위 컴포넌트를 click 할 경우 하위 컴포넌트의 이벤트가 상위와 하위 컴포넌트 모두 중복으로 발생하는 문제

  • [해결 방법] pointer-event(CSS 속성으로 그래픽 요소가 어떤 상황에서 포인터 이벤트의 대상이 될 수 있는지 지정함)를 none으로 설정하면 해당 컴포넌트는 포인터 대상이 될 수 없기 때문에 이벤트 클릭이 발생할 수 없음 (다만 해당 css 속성을 가진 컴포넌트의 하위 컴포넌트에서 다른 pointer-event를 설정할 경우 포인터 대상이 될 수 있음)

  1. 컴포넌트 내 여러 상태 관리
  • [문제 상황] 부모 컴포넌트 내부에 자식 컴포넌트 별로 useState를 통해 상태를 관리할 경우 코드의 중복이 발생하여 코드 가독성이 떨어짐. 또 이럴 경우, 코드의 응집도가 떨어지는 문제가 발생할 수 있고, 향후 관리해야 하는 상태가 증가할 경우 중복된 코드를 작성해야 함.
  • [해결 방법] 각 컴포넌트의 초기 상태를 배열에 저장하고, useReducer를 활용해 상태 관리 로직을 일원화하여 코드의 중복을 방지하고 응집도를 높임
  1. 리스트 렌더링 시, key 값을 컴포넌트에 prop으로 전달할 경우
  • [문제 상황] 리스트 렌더링 시, 상위 컴포넌트 내 하위 컴포넌트에 key 프로퍼티 이름으로 프로토타입을 전달할 경우 해당 컴포넌트에 전달되는 값은 undefined가 됨
  • [해결 방법] 단순히 key라는 이름을 제외한 다른 이름으로 프로퍼티를 전달하면 해결 됨
  1. mouseOver 대신 CSS Hover 사용하여 모달 구성

  2. keyPress 대시 keyDown

  • arrow key event 의 경우 keyPress 대신 keyDown을 사용할 경우 인식 됨
  1. List rendering 시, 랜더링 되는 각 컴포넌트 참조
  • [문제 상황] 키보드 트랩을 구현하기 위해서, 배열을 통해 랜더링된 컴포넌트별로 useRef를 통해 해당 컴포넌트를 참조하려고 하였으나 ref가 항상 마지막 컴포넌트 참조값만 가지게 됨

    문제 코드 const selectionRef = new Array(length).fill(useRef(null))

  • [해결 방법] useRef를 여러 개 만드는 것이 아니라, useRef의 초기값을 배열로 선언하여 ref의 current를 업데이트 시켜 줌

  1. useCallback 사용 시, dependency array 설정의 중요성
  • [문제 상황] filter Section의 click event handler를 useCallback 함수로 설정하였음. 이 때 dependency array를 빈 배열로 설정할 경우, useLocation으로 불러온 search(query)가 변경 될 경우 해당 search 를 반영하지 않아 filter 적용 시 에러 발생
  • [해결 방법] 이벤트 핸들러 내에 자신이 의존하는 search 를 dependency array에 추가함
 const handleClick = useCallback(
    e => {
      ...
      const query = QueryString.parse(`search`.replace(/^\?/, ''));
      ...
    [`search`, setSearchParams],
  );
⚠️ **GitHub.com Fallback** ⚠️