치춘짱베리굿나이스

[프리온보딩] 220508 강의 메모 01 (그룹과제 코드리뷰) 본문

프로젝트/원티드 프리온보딩

[프리온보딩] 220508 강의 메모 01 (그룹과제 코드리뷰)

치춘 2022. 5. 10. 01:35

서론

항상 일은 더 생기기 때문에 이것을 염두하고 미리미리 작업해야 한다

일주일이면 걸릴 과제라면 한달은 잡아놓고 해야 넉넉함

수정사항은 계속 생기기 때문

코드리뷰

1팀

handleChange 이벤트 받아오기

const handleChange = ({currentTarget}) => {}
  • e에서 currentTarget 받아올 때 구조분해 할당으로 받아오면 알아보기 힘들다

return값 내보낼 때 객체로 묶어 내보내기

return [date, {handlePrevClick, handleNextClick, handleCalChange}];

return {
    date, 
    handlePrevClick, 
    handleNextClick, 
    handleCalChange
};
  • 위의 반환문을 아래의 반환문으로 작성할 수 있다

라이브러리

  • 특정 요소 바깥을 클릭할 때 이벤트를 발생시키고 싶다면 useRef와 커스텀 훅 라이브러리 (react-use) 를 사용하면 쉽다
  • 날짜 포맷 변경은 dayjs를 사용하면 쉽다

기타 내용

  • 코드가 200줄이 넘어간다 → 좋지 않음
  • 클릭 등의 이벤트가 걸려있지 않을 경우 굳이 button 태그를 사용할 필요가 없다
  • CSS로 짧게 구현할 수 있는 내용이라면 굳이 라이브러리로 작성할 필요가 없음
  • 투두 리스트를 보여줄 때 filter를 사용하면 필터링된 데이터를 바로 넣어줄 수 있을 텐데
  • 배열 map 메서드 안에서 함수 선언하지 마세요 (바깥에다 선언해서 불러오기)

2팀

key에 인덱스 (i) 넣지 말라는 말 무시하기

{
    arr.map((v, i) => {
        const key = `test-${i}`;
        return (
            <div key={key}>{v}</div>
        );});
}
  • 린터가 key값에 인덱스 넣지 말라고 경고 띄우면 key를 map 내부 함수 최상단에 선언 및 초기화해주고 그 key를 불러와서 쓰면 된다

린터 잘 지키기

  • 노란줄, 빨간줄 나오지 않게 하세요 (다 지워주기)
  • 노란 줄 (warning) 도 안 나오도록 해주는 것이 좋다
  • 어지간한 에러는 경고문구만 잘 봐도 해결된다

라이브러리

  • localStorage는 변수를 string으로 저장하기 때문에 storejs 라이브러리를 사용하면 JSON.stringifyJSON.parse 등의 함수를 일일이 사용할 필요 없이 쉽게 로컬 스토리지의 값을 가져오거나 저장할 수 있다

기타 내용

  • prettier 씁시다..
  • 함수 내에서만 사용하는 변수는 함수 내에 선언하기 (컴포넌트 상단에 선언하면 컴포넌트 렌더링될때마다 계속 재선언된다)
  • input 요소에 값을 입력하고 엔터로 제출되게 할 때 addEventListener를 사용하는 사람은 나쁜 사람 ⇒ form 태그 사용

3팀

라이브러리 쓰기 vs low level 에서 구현하기

  • 원리를 알고 공부하는 용도로는 직접 구현하는것도 좋은데 라이브러리를 사용하는게 훨씬 직관적이고 간단하긴 함
  • 다만 라이브러리가 작동하는 원리를 알고 있는 게 좋겠다
  • 너무 사소한 것까지 (간단한 사칙연산 같은...) 라이브러리를 갖다 쓰는 행위만 지양하면 된다

기타 내용

  • 에러 하나도 안뜨는거 너무 좋아요
  • json 잘라서 반환하는 함수 예외처리는 배열 반환 말고 객체 반환이 나을것같다
  • 자바스크립트에서 기본 제공되는 alert는 안 쓰는 것이 좋다 ⇒ 경고창이 뜨면 페이지 전체 코드가 멈춰버리기 때문
  • 이벤트가 발생하면 이벤트 핸들러 내에 useRef를 이용하여 특정 input에 포커스를 주는 것 좋은듯

4팀 (우리팀임)

setState만 하는 핸들러는 굳이 핸들러로 뺄 필요가 없다

const handleButtonClick = (v) => {
    setValue(v);
}

<button ... onClick={() => handleButtonClick(v)} >
    버튼
</button>
<button ... onClick={() => setValue(v)} >
    버튼
</button>
  • 어차피 onClick 내에 화살표 함수를 작성해야 하는 상황이니 그냥 setState를 가져와서 작성해도 상관없음

컴포넌트를 너무 썰어도 문제다

  • 너무 잘게 썰면 오히려 어떤 컴포넌트가 무슨 역할을 하는지 알아보기 어렵게 된다
  • 간단한 투두리스트 제작이 너무 복잡해졌다

html 요소 안쪽을 너무 복잡하게 만들지 말고 밖으로 빼자

<div>
    { arr
        .filter(foo)
        .filter(foo2)
        .map(
            ...
        )
    }
</div>
const filtered = arr.filter(foo).filter(foo2);

<div>
 { filtered.map(...) }
</div>
  • 컴포넌트가 렌더링될 때마다 저 arr.filter.filter 부분이 계속 재작동한다
  • 차라리 jsx 구문 안쪽에 중괄호로 감싸서 넣지 말고 로직 부분으로 빼버리자

useMemo, useCallback

const filtered = arr.filter(foo).filter(foo2);
const filtered = useMemo(() => {
    return arr.filter(foo).filter(foo2)
}, [arr, foo, foo2]);
  • 수식이 복잡해질 경우 useMemo를 사용하면 리렌더링마다 불필요하게 계산되는 것을 줄이고 의존성을 갖는 특정 상태값이 변화할 때만 재계산되도록 캐싱을 할 수 있다
  • 복잡한 수식은 연산속도도 오래 걸리는데, 이를 사용하여 반복적인 연산을 줄이고 속도를 개선할 수 있음
  • 렌더링될 때마다 무조건 함수나 변수를 새로 만들기보다는, 의존성을 갖는 값이 변화할 때만 새로 계산하거나 함수를 생성하는 식으로 속도를 개선하지만, 등가교환으로 메모리를 더 잡아먹으므로 너무 남용하는 건 또 좋지 않다 (굉장한 성능 개선이 있는 건 아니므로 너무 습관적으로 감싸지 말자)
  • useMemo는 변수를 저장하고, useCallback은 함수를 저장한다 ⇒ 자세히 알아봅시다

라이브러리

  • slick.js ⇒ 드래그로 좌우 스크롤 구현할 수 있게 도와주는 라이브러리 (carousel)

기타 내용

  • warning 다 없애버리세요
  • 와 세상에 키보드를 눌러야 움직인다니!
  • (메뉴) 와 비주얼 쥑이네 여기 근데 아무짝에도 쓸모없어 보기좋은떡이 먹기에도 좋습니다
  • 초기값같은건 상수화해서 컴포넌트나 로직 바깥으로 빼놔야 가독성이 올라감 (setState 함수 내에 박아놓지 말고 상수화해서 넣자)
  • 전역 상태관리 라이브러리를 안 써서 고생하고 계시는군요
  • 메뉴 부분 등에 있는 컴포넌트들이 장식이 아니라 실제로 의미있는 동작을 했더라면 더 좋은 투두앱이 되었을 것 같다

5팀

리액트 가족관계

  • 부모자식간엔 사이가 좋다 (서로 props를 주고받을 수 있다)
  • 형제간엔 사이가 좋지 않다 (부모를 통해야만 props를 전달받을 수 있다)
  • 할아버지와 손주간에도 사이가 좋지 않다 (부모를 통해야만 props를 전달받을 수 있다)

드래그 동작

  • 컴포넌트를 누르고 있는 상태에서 (onMouseDown) 좌표값을 얻고, 컨테이너의 좌우 너비 등에 비례하여 마우스를 뗄 때까지 (onMouceUp) 드래그되도록 (onMouseMove) 제작
  • documentaddEventListener를 거는 것보단 useRef를 쓰는게 좋지 않았을까
  • 컴포넌트가 마운트되었을 때 이벤트리스너를 붙이고, 언마운트될 때 이벤트를 제거하는 게 좋을 것 같다 (마우스를 떼고 붙일 때 이벤트 리스너를 걸고 제거하면 문제발생 여지 높음)
  • ⇒ 컴포넌트 바깥으로 마우스가 빠져나가면 이벤트가 동작하지 않아서

setTimeOut과 clearTimeOut

  • setTimeOut()을 사용할 땐 clearTimeOut도 같이 써줘야한다 (쌍으로 움직여야 함)
  • 같이 써주지 않으면 설정한 시간이 흐르고 있는 도중 다른 동작을 수행했을 때 동작이 꼬여버림
  • 예시: 라우터를 이용한 페이지 이동으로 현재 setTimeOut이 걸려있는 컴포넌트가 시간이 흐르던 도중 언마운트 될 때 오류발생

컴포넌트 옆으로 밀었을 때 동작 나오게 구현

  • overflow-x 속성으로 스크롤을 만든 다음 스크롤을 숨기고, 스크롤 offset을 주면 구현이 조금 더 쉬웠을지도

기타 내용

  • useRefcurrent.style을 써서 스타일을 다르게 먹일 필요가 없다 클래스를 지정하면 되었을텐데..
  • props를 건네줄 때 {... {INT_TODO, CATEGORIES, openMenu, ...}} 이렇게 할 이유가 없다
  • prevState 쓰세요..

6팀

useContext 단점

  • 컨텍스트 개수가 늘어나면 html 태그의 깊이가 무한정으로 깊어진다
  • 컨텍스트를 배열로 만들어서 태그를 평평하게 만드는.. 방법도 있긴 하지만 복잡하다
  • 리액트에서 기본적으로 제공하긴 하지만 구리다... 퍼포먼스 좋지않음
  • Redux도 내부적으론 useContext를 썼었지만 성능문제로 롤백함
  • 다크모드나 테마 등을 만들 때 쓰면 좋겠지만 대규모 데이터에는 부적절함

useState 안에 객체를 넣는 건 좋지 않다

  • 리액트에선 객체의 참조값이 바뀌지 않으면 같은 값으로 생각함 ⇒ set할 때 좀 귀찮아짐
  • 객체만 넣지 마세요

props 하나로 뭉쳐서 보내기

<testComponent
    propA={propA}
    propB={propB}
    propC={propC}
    propD={propD}
    propE={propE}
    ...
/>
const testProp = {propA, propB, propC, propD, propE};
<testComponent propObj={testprop}/>

...
const testComponent = ({propObj}) => {
    const {propA, propB, propC, propD, propE} = propObj
    // 받아올 때
...
  • 이렇게 받아오면 조금 더 간단해진다

useEffect는 useCallback을 대신하지 못하는가?

  • useEffect는 의존성을 갖는 변수에 변화가 있으면 실행되는 훅
    • useCallback은 캐싱에 관련된 훅

객체와 물음표

(testObj?.name)
(testObj.name ? testObj.name : '')

객체의 . 앞에 물음표를 붙이면 오른쪽에 적은 키 값이 존재할 경우키에 대응하는 값을 반환하고, 존재하지 않을 경우 빈 문자열을 반환하는 코드

(testObj.name ?? 'nothing');
(testObj.name ? testObj.name : 'nothing');

비슷한 기능을 하되 키 값이 존재하지 않을 때 빈 문자열 말고 정의한 값을 반환하도록 할 수 있다

기타 내용

  • localStorage에서 값 들고오는 코드를 jsx 안에 넣지 말고 함수로 빼는 것이 적합할듯 (리렌더링 방지)
  • 한 파일에는 한 컴포넌트만 넣자 (2개 이상 넣는 것은 안티패턴)
  • 컴포넌트를 선언했다면 해당 컴포넌트를 끌어다 쓰는 코드가 컴포넌트 선언부보다 위에 있는건 좋지 않다 (린터에서 잡아줄듯)
  • localStorage 안에 값이 없을 땐 initialState를 이용해서 기본값을 적용시켜줍시다
  • 이러나저러나 store.js를 쓰면 훨씬 편함
  • 날짜 다루는 함수 같은건 utils 같은 파일로 분리하면 가독성에 좋다
  • props를 잔뜩 보내는 것보단 하나로 뭉쳐서 전달하는 게 좋아보임
  • useEffect 내의 어마어마하게 긴 내용들 (setState함수들 잔뜩) ⇒ useCallback 각이 보인다
  • 또한 setState 함수를 카테고리별로 useEffect에 전부 때려넣는 코드는 카테고리 개수가 늘어날 경우 대응하기 매우 힘들어지므로 지양하는 게 좋겠음
  • toISOString은 문자열이 깨질 위험이 있다
  • 컴포넌트 사이즈 최적화가 필요할 듯

7팀

삼항연산 길이 줄이기

isTrue ? {hidden: false} : {hidden: true}

{hidden: !isTrue}

삼항연산자 제일 앞에 있는 조건문이 boolean을 뱉음을 기억하자..

Recoil 팁

export const testRecoil = atom({
    key: '#testRecoil',
    default: []
});
  • Recoil 변수의 키값에 #을 붙이면 다른 변수들과 구분되는 소소한 팁
  • Recoil 참 깔끔해서 좋아

기타 내용

  • 컴포넌트 너무 잘게 쪼개지 않기
  • localStorage.clear() 은 로컬스토리지의 저장된 값을 다 날리는 기능을 한다
  • 버튼의 이름 (버튼 위에 적힌 텍스트) 는 굳이 ::before, ::after를 쓰기보단 버튼 태그 내에 적어넣는게 낫다

8팀

useNavigate에 state 넘겨주기 지양

const navigate = useNavigate();
navigate('/test', {'state': state});
  • 이런식으론 잘 사용하지 않음
  • useNavigate를 사용하는 페이지를 거쳐서 해당 페이지로 넘어가면 문제가 없겠지만, 사용자가 임의로 페이지의 url을 입력해서 넘어가면 넘겨지는 state가 없으므로 오류가 날 확률이 높다
  • 주소에 인덱스를 넘겨주는 방식 등으로 주소만으로도 페이지가 어떤 값인지 판단하여 컴포넌트 내에서 받아오도록 해야 안전함

태그 attribute를 통해 색상변수 결정하기

$LIGHT_TITLE: #222222;
$LIGHT_BACKGROUND: #bbbbbb;
$DARK_TITLE: #FFFFFF;
$DARK_BACKGROUND: #333333;

:root[color-theme='light'] {
    --color-title: #{LIGHT_TITLE};
    --color-background: #{LIGHT_BACKGROUND};
}

:root[color-theme='dark'] {
    --color-title: #{DARK_TITLE};
    --color-background: #{DARK_BACKGROUND};
}
  • css의 :root 의사 클래스 (Pseudo class) 는 문서 트리의 가장 루트 요소를 선택한다
  • 색상을 지정할 때 위와 같이 설정하면 루트 하위의 모든 요소에 대해 색상 변수에 저 값이 들어가므로 전역적으로 색을 바꿔줄 수 있음
  • html 상에 렌더링된 모든 요소가 접근가능함

기타 내용

  • display: block은 부모 영역 길이까지 꽉 차지하므로 길이에 제한을 두고 싶다면 display: inline-block 사용하기
Comments