Let's Write_ 프론트엔드

코드리뷰 작성 가이드

TIL

## **우리는 왜 코드리뷰를 해야하나요?**

:fire: “소프트웨어를 유지보수하는 조직에서 코드 한 줄을 변경한다고 했을 때, 코드리뷰가 도입되기 전에는 그러한 변경의 55% 정도가 문제를 일으켰다.

그러나 리뷰 과정이 도입된 이후에는 그러한 변경의 2% 정도에서만 문제가 발생했다.”

- **버그의 조기 발견**(불필요한 시간과 비용을 절감)
- **개발 컨벤션 준수**(가독성과 유지보수 편의성 극대화)
- **중복 코드 방지 및 모듈의 재사용성 증대**
- **배움의 기회**(‘아, 이렇게 쉬운 방법이 있었구나?’)

## **우리의 코드 리뷰 규칙**

- 2일 이내에 리뷰를 완료하여 리뷰의 병목을 해소합니다. (피쳐 마감 일자에 따라 우선순위를 결정합니다.)
- 이슈가 있다면 페어프로그밍으로 같이 해결합니다.
- 각자의 개발스타일은 다릅니다. 기존에 자신의 스타일이 정답이라는 생각을 버리고, “나의 의견은 이러한데 당신은 어떻게 생각하시나요?”와 같이 의견을 묻는 것이 좋습니다.
- 리뷰어가 진행해주는 QA 중 이슈가 발생된다면 리뷰이는 코드 변경사항과 개발된 피쳐와 관련된 모든 사항에 대해 전체 테스트 및 확인을 다시 진행합니다. 놓친 부분이 한 군데 발생된다면 다른 곳도 꼼꼼하게 다시 한번 점검하는 것이 좋습니다.
- 공통 컴포넌트를 수정한다면 사용하고 있는 모든 부분을 체크하여 side effect 여부를 확인합니다. 
- 앞으로도 함께 만들어가는 문화이니 어떻게 진행할지 같이 고민해봐요 :slight_smile: 언제든 의견 주세요<span dir="">\~</span>

## 리뷰 체크리스트

:grinning: MR올리기 전 아래 내용을 확인해주세요.

* [ ] **merge할 브랜치의 위치를 확인**하셨나요?
* [ ] **코드의 맥락(CONTEXT)을 이해할 수 있는 설명을 추가하셨습니까?** (ex.로그인 시 발생하는 이메일 인증 오류에 대한 버그 수정입니다)
* [ ] **기능이 정상적으로 동작**하나요?
* [ ] 변수명 등을 **통일성 있게, 축약 또는 생략하지 않고** 작성했습니까?
* [ ] **개발 컨벤션에 준수**하여 작성했습니까?

:grin: MR 올린후

* [ ] **기존 코드와 충돌이 발생**하면 머지 버튼이 비활성화 되기도 합니다! 그 부분까지 체크하여 충돌을 해결해야 리뷰를 진행할 수 있습니다.

:robot: **프로젝트를 진행하면서 체크리스트는 더효율적인 코드 리뷰를 위해 수정될 수 있습니다.**

## 리뷰어 체크리스트

:laughing: 팀원들은 체크리스트를 보며 코드리뷰를 진행해주세요.

* [ ] **왜 개선이 필요한지 이유를 충분한 설명**해주셨습니까?
* [ ] 작은 커밋 단위만을 보지 말고, **전체 코드의 맥락을 살피면서 리뷰**를 해주세요.
* [ ] **이해가 안가는 부분이 있다면 질문**해주세요.
* [ ] 리뷰를 위한 리뷰를 하지 마세요. **피드백 할 게 없으면 칭찬**해 주세요.:heartbeat:

:raising_hand_tone1:**리뷰를 어떻게 해야할지 모르겠다면 이 글을 한번 봐주세요**

https://tech.kakao.com/2022/03/17/2022-newkrew-onboarding-codereview/

https://blog.logi-spot.com/코드리뷰의-진짜-목적은-따로있다/

https://xo.dev/github-collaboration-guide/ 깃헙에 코드리뷰 요청 보내는법

https://www.youtube.com/watch?v=9FZaYz0s8s4 라매개발자 깃헙 코드리뷰 협업

cf. 체크사항

> ### **배울만한 점은 없는지**
>
> 코드리뷰에 많은 사람이 오해하는 부분 중 하나는, 경력이 많거나 실력이 뛰어난 개발자가 후배 개발자의 코드를 검사한다고 생각하는 것입니다. 코드리뷰에서 코드의 작성자와 리뷰어는 누가 더 경력이 높거나 낮을 필요가 없습니다. 또한, 코드리뷰의 목적은 코드 작성자에 게 피드백을 주는 것도 있지만, 해당 코드를 보면서 ‘아, 이런 식으로 코드를 작성하는 것도 가능하구나?’, ‘아, 이렇게 쉬운 방법이 있었구나?’와 같은 학습효과도 함께 가지고 있습니다. 그렇기 때문에 코드를 리뷰할 때는 피드백을 주기 위한 시각과 좋은 점을 배우려는 시각, 이 두 가지 시각의 균형을 맞추며 진행하는 것이 좋습니다.

redux 내부는 context API 로 돌아간다고 하던데 그 작동원리를 알려줄 수 있어?

TIL

Redux가 Context API를 내부적으로 사용한다는 것은 정확한 설명입니다. Redux가 최신 버전(주로 React Redux v7 이상)에서 React의 Context API를 활용하여 전역 상태를 제공하는 방식이기 때문입니다. 이를 이해하려면 Redux와 React Redux의 작동 원리를 간단히 살펴보겠습니다.

1. Redux의 기본 구조

Redux는 세 가지 핵심 개념으로 구성됩니다:

  • 스토어(Store): 애플리케이션의 전역 상태를 보유하는 객체입니다. 이 객체는 createStore()를 통해 생성됩니다.
  • 액션(Action): 상태에 대한 변경을 요청하는 객체입니다. 액션은 반드시 type 속성을 가져야 하며, 추가적으로 데이터를 담을 수 있습니다.
  • 리듀서(Reducer): 상태 변경 로직을 정의하는 순수 함수입니다. 액션을 받아 현재 상태를 기반으로 새 상태를 반환합니다.

2. React Redux의 역할

React Redux는 Redux를 React 애플리케이션에 통합하기 위한 공식 바인딩 라이브러리입니다. React 컴포넌트가 Redux의 스토어에 접근할 수 있게 하고, 상태 변경이 발생하면 자동으로 UI를 업데이트합니다.

3. Context API의 활용

React Redux v7부터는 React의 Context API를 활용하여, 전역 상태를 자식 컴포넌트들에게 전달합니다. 이 과정을 단계별로 살펴보면 다음과 같습니다:

a. 스토어를 Context로 전달

React Redux의 <Provider> 컴포넌트는 Redux 스토어를 React의 Context로 전달합니다. Provider는 다음과 같이 동작합니다:

```
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);

<Provider store={store}>
  <App />
</Provider>
```
 

여기서 Provider 컴포넌트는 React.createContext()를 사용하여 Context를 생성하고, 그 Context를 통해 store 객체를 하위 컴포넌트들에게 전달합니다.

b. useSelector와 useDispatch 훅의 동작

React Redux에서 useSelector와 useDispatch 훅을 사용하여 스토어의 상태를 조회하거나 액션을 디스패치할 수 있습니다.

  • useSelector: 이 훅은 Context에서 스토어를 가져와, 컴포넌트가 스토어의 특정 상태를 구독(subscribe)하게 만듭니다. 상태가 변경되면 해당 컴포넌트는 자동으로 리렌더링됩니다.
  • useDispatch: 이 훅은 Context를 통해 스토어의 dispatch 메서드를 가져옵니다. 이를 통해 액션을 디스패치할 수 있습니다.

c. Context API로 상태 변화 반영

React Redux의 Provider는 Context를 사용하여 하위 컴포넌트들이 상태 변화를 감지할 수 있게 합니다. 예를 들어, useSelector 훅을 사용하는 컴포넌트는 Context에서 현재 스토어 상태를 가져오고, 이 상태가 변경될 때마다 해당 컴포넌트가 자동으로 리렌더링됩니다.

4. 작동 원리 요약

  • Context 생성: Provider 컴포넌트가 Context를 생성하고, 그 안에 Redux 스토어를 담아 하위 컴포넌트들에게 전달합니다.
  • Context 활용: useSelector, useDispatch 훅이 이 Context에 접근하여 상태를 구독하거나 액션을 디스패치합니다.
  • 상태 변경과 리렌더링: Context를 통해 전달된 스토어의 상태가 변경되면, 이 상태를 구독하는 컴포넌트들이 리렌더링됩니다.

이러한 방식으로 React Redux는 Context API를 활용하여 React 컴포넌트들이 전역 상태를 쉽게 사용할 수 있게 하고, 상태 변화에 따라 UI가 자동으로 업데이트되도록 합니다.

 

# Context API를 직접 사용하는 것과 Redux를 사용하는 것의 장단점은 무엇일까요?

장점

  1. 간단함:
    • Context API는 React에 내장된 기능으로, 추가적인 라이브러리 설치가 필요 없습니다. 기본적인 전역 상태 관리에는 충분히 사용할 수 있습니다.
    • 설정과 사용법이 비교적 단순하며, 학습 곡선이 낮습니다.
  2. 작은 프로젝트에 적합:
    • 작은 규모의 애플리케이션이나 상태가 복잡하지 않은 경우 Context API를 사용하여 쉽게 상태를 공유할 수 있습니다.
  3. 유연성:
    • 특정 컴포넌트 계층구조 내에서 전역 상태를 관리하고 싶을 때 적합합니다. Context는 필요한 부분에서만 사용하도록 범위를 제한할 수 있습니다.

단점

  1. 성능 이슈:
    • Context API를 사용할 때, 상태가 변경되면 해당 Context를 구독하는 모든 컴포넌트가 리렌더링됩니다. 상태의 변화가 빈번하거나 구독자가 많은 경우 성능 저하가 발생할 수 있습니다.
    • 특히, 상태가 특정 컴포넌트에만 관련 있는 경우에도, 전체 Context가 재생성되어 불필요한 리렌더링이 발생할 수 있습니다.
  2. 복잡한 상태 관리에 부적합:
    • 복잡한 전역 상태나 많은 상태를 관리해야 하는 경우, Context API는 적합하지 않을 수 있습니다. 상태의 변화 추적이나 비동기 작업 관리 등에서 한계가 있습니다.
    • 미들웨어를 통해 비동기 작업을 효율적으로 처리하기 어려운 경우가 많습니다.
  3. 디버깅 도구 부족:
    • Redux와 같은 강력한 디버깅 도구가 부족하여, 상태 변화 추적이 어려울 수 있습니다.

Redux의 장단점

장점

  1. 예측 가능한 상태 관리:
    • Redux는 상태의 흐름을 명확하게 규정하고, 상태 변화가 언제 어디서 발생하는지 예측할 수 있게 합니다. 이는 특히 대규모 애플리케이션에서 중요한 장점입니다.
  2. 미들웨어 지원:
    • Redux는 redux-thunk, redux-saga와 같은 미들웨어를 사용하여 비동기 작업을 쉽게 처리할 수 있습니다. 이를 통해 복잡한 비동기 로직도 깔끔하게 관리할 수 있습니다.
  3. 강력한 디버깅 도구:
    • Redux DevTools를 사용하면 상태의 변화를 시간순으로 추적하고, 특정 시점으로 되돌리거나 재생(replay)할 수 있습니다. 이는 디버깅과 개발 과정에서 매우 유용합니다.
  4. 모듈화와 확장성:
    • Redux는 상태와 로직을 리듀서로 모듈화하여, 애플리케이션의 규모가 커져도 코드가 체계적으로 관리될 수 있습니다. 다양한 패턴과 구조를 지원해, 대규모 애플리케이션에서도 확장 가능성을 제공합니다.

단점

  1. 복잡성:
    • Redux의 설정은 복잡할 수 있으며, 특히 작은 프로젝트에서는 오히려 오버헤드가 될 수 있습니다. 초기 설정과 보일러플레이트 코드가 많아 간단한 작업에도 불필요한 복잡성이 추가됩니다.
  2. 추가적인 학습 필요:
    • Redux의 개념(액션, 리듀서, 스토어, 미들웨어 등)을 이해하고 활용하려면 추가적인 학습이 필요합니다. 특히 팀원 모두가 이 개념들을 잘 이해해야 원활한 협업이 가능합니다.
  3. 가독성 저하 가능성:
    • 비록 Redux가 코드 구조를 체계적으로 관리할 수 있게 해주지만, 잘못 설계된 Redux 코드베이스는 오히려 코드의 가독성을 저하시킬 수 있습니다. 모든 상태 변화가 중앙 집중식으로 처리되므로, 코드가 길어지고 복잡해질 수 있습니다.

결론

  • Context API는 간단한 전역 상태 관리가 필요한 소규모 프로젝트에서 적합하며, 별도의 라이브러리 설치 없이 React의 내장 기능을 활용할 수 있습니다. 그러나 상태가 복잡해지거나 많은 컴포넌트가 상태에 의존할 경우 성능 이슈가 발생할 수 있습니다.
  • Redux는 대규모 애플리케이션이나 복잡한 상태 관리가 필요한 경우에 적합합니다. 예측 가능하고 체계적인 상태 관리를 제공하며, 미들웨어와 디버깅 도구가 풍부합니다. 하지만 설정과 사용법이 복잡하여 작은 프로젝트에서는 오버헤드가 될 수 있습니다.

 


# Redux 도 내부적으로는 context api 를 사용하고 있는데 redux도 그럼 모든 컴포넌트가 리렌더링이 일어나나?

Redux는 내부적으로 Context API를 사용하고 있지만, 모든 컴포넌트가 리렌더링되지 않도록 최적화되어 있습니다. 이를 이해하기 위해서는 React Redux가 어떻게 작동하는지 살펴볼 필요가 있습니다.

1. React Redux의 최적화 메커니즘

React Redux는 useSelector 훅과 connect 함수로 컴포넌트들이 스토어의 상태를 구독할 수 있게 합니다. 이때 Redux는 상태 변경에 따른 불필요한 리렌더링을 방지하기 위해 몇 가지 중요한 최적화를 적용합니다:

a. useSelector와 리렌더링 최적화

  • useSelector 훅은 상태의 특정 부분만 구독하도록 설계되어 있습니다. 이 훅은 컴포넌트가 관심 있는 상태만 선택해 구독하며, 상태의 해당 부분이 변경될 때만 컴포넌트를 리렌더링합니다.
  • 내부적으로 useSelector 훅은 === 연산자를 사용해 이전 상태와 새 상태를 비교합니다. 만약 상태의 특정 부분이 변경되지 않았다면, 리렌더링을 트리거하지 않습니다.

b. connect 함수와 shouldComponentUpdate

  • connect 함수는 클래스형 컴포넌트에서도 사용되며, 기본적으로 shouldComponentUpdate 메서드를 통해 상태 변화가 해당 컴포넌트에 영향을 미치는지를 판단합니다.
  • connect는 상태의 특정 부분을 mapStateToProps로 매핑하고, 이 부분에 변화가 있는 경우에만 컴포넌트를 리렌더링합니다.

2. Context API와 Redux의 차이

Context API 자체는 구독자가 상태를 구독할 때, 상태가 변경되면 해당 Context를 사용하는 모든 컴포넌트를 리렌더링하게 합니다. 그러나 React Redux는 이러한 문제를 해결하기 위해 훨씬 세밀한 최적화를 적용합니다.

  • 구독 단위: Redux는 Context를 통해 스토어를 전달하지만, 각 컴포넌트는 useSelector나 connect를 통해 특정 상태만 구독합니다. 이로 인해 전체 컴포넌트 트리가 아닌, 관심 있는 컴포넌트만 리렌더링됩니다.
  • 구독 최적화: Redux는 상태의 특정 부분만 구독하고, 이 부분에 변화가 있는지 여부를 효율적으로 비교하여 불필요한 리렌더링을 방지합니다.

3. Redux가 리렌더링을 최소화하는 방법

  • 메모이제이션: useSelector와 같은 훅은 상태 비교를 통해 메모이제이션을 사용합니다. 이전과 동일한 상태라면 컴포넌트는 리렌더링되지 않습니다.
  • 선택적 구독: 각 컴포넌트가 전체 상태가 아닌, 필요한 부분만 선택적으로 구독함으로써 불필요한 리렌더링을 피합니다.
  • 의도적 리렌더링 제어: mapStateToProps와 같은 함수는 개발자가 어떤 상태 변화가 컴포넌트에 영향을 미치는지를 명확하게 정의할 수 있게 해줍니다.

결론

Redux는 내부적으로 Context API를 사용하지만, 모든 컴포넌트가 리렌더링되지 않도록 철저히 최적화되어 있습니다. 컴포넌트는 Redux 스토어의 특정 부분만 구독하며, 해당 상태에 변화가 있을 때만 리렌더링됩니다. 이러한 최적화 덕분에 Redux는 대규모 애플리케이션에서도 효율적인 상태 관리를 제공할 수 있습니다.

 

 

# zustand는 작동원리에 context API 가 들어가? 안들어간다면 zustnad의 작동원리를 알려줘

Zustand는 React 애플리케이션에서 전역 상태를 관리하기 위한 상태 관리 라이브러리입니다. 다른 상태 관리 라이브러리와는 달리, Zustand는 Context API를 사용하지 않습니다. 대신, 더 간단하고 효율적인 상태 관리 접근 방식을 제공합니다. 이제 Zustand의 작동 원리를 살펴보겠습니다.

1. Zustand의 핵심 개념

Zustand는 상태 관리를 위해 몇 가지 주요 개념을 사용합니다:

  • Store: Zustand의 스토어는 전역 상태를 보유하는 객체입니다. 이 스토어는 create 함수를 통해 생성됩니다.
  • Selectors: 상태의 특정 부분을 선택하고 구독할 수 있게 하는 함수입니다.
  • Actions: 상태를 변경하는 함수를 정의하는 곳입니다.

2. Zustand의 작동 원리

Zustand는 React의 훅(Hooks) 시스템을 기반으로 상태를 관리하며, 주요 작동 원리는 다음과 같습니다:

a. 스토어 생성

Zustand에서 상태를 관리하려면 먼저 스토어를 생성해야 합니다. 이는 create 함수를 사용하여 이루어집니다.

이 코드에서 useStore는 React 컴포넌트에서 사용할 수 있는 커스텀 훅입니다. set 함수는 상태를 변경하는 데 사용됩니다.

b. 상태 구독

Zustand에서는 상태를 구독(subscribe)하기 위해 간단히 useStore 훅을 사용합니다

 

c. 리렌더링 최적화

Zustand는 상태의 특정 부분만 구독할 수 있게 해주며, 이로 인해 상태의 일부가 변경될 때만 해당 부분을 구독하는 컴포넌트가 리렌더링됩니다. 이는 useSelector 훅을 사용하는 것과 유사하지만, Context API를 사용하지 않기 때문에 불필요한 리렌더링이 발생하지 않습니다.

d. 미들웨어 및 플러그인

Zustand는 미들웨어를 통해 상태 관리를 확장할 수 있습니다. 예를 들어, 상태를 로컬 스토리지에 저장하거나 Redux DevTools와 통합할 수 있습니다. 이를 통해 복잡한 상태 관리 로직을 구현할 수 있습니다.

 

Zustand 스토어를 로컬 스토리지에 저장하며, Redux DevTools와 통합하여 상태를 디버깅할 수 있습니다.

3. Zustand의 주요 특징

  • 간결함: Zustand는 상태 관리 로직을 간단하게 유지하며, 최소한의 API로 동작합니다.
  • 컨텍스트가 필요 없음: Context API를 사용하지 않으므로 상태를 사용하는 컴포넌트의 리렌더링 성능이 뛰어납니다.
  • 선택적 구독: 상태의 특정 부분만 구독함으로써 불필요한 리렌더링을 방지합니다.
  • 확장성: 미들웨어와 플러그인을 통해 기능을 확장할 수 있습니다.

결론

Zustand는 Context API를 사용하지 않고, React 훅과 간단한 API를 통해 상태를 관리합니다. 이를 통해 효율적인 리렌더링을 제공하며, 상태 관리의 복잡성을 줄입니다. Zustand는 작은 프로젝트부터 중간 규모의 애플리케이션까지 간편하게 사용할 수 있는 상태 관리 도구로, 성능과 간결함을 중시하는 개발자들에게 인기가 많습니다.

 


# Zustand에서 상태 구독 시 성능 최적화를 위해 사용할 수 있는 다른 기법은 무엇이 있을까요?

 
ChatGPT

Zustand에서 상태 구독 시 성능을 최적화하기 위해 사용할 수 있는 몇 가지 추가적인 기법이 있습니다. 이들 기법은 상태 변경이 애플리케이션 성능에 미치는 영향을 최소화하고, 불필요한 리렌더링을 방지하는 데 도움을 줍니다.

1. 상태 슬라이싱 (State Slicing)

Zustand에서 상태를 구독할 때, 상태의 특정 부분만 선택하여 구독하는 것이 좋습니다. 이를 "상태 슬라이싱"이라고 합니다. 예를 들어, 상태가 다음과 같이 정의되어 있다고 가정합니다:

```
const useStore = create((set) => ({
  user: { name: 'Alice', age: 25 },
  posts: [],
  addPost: (post) => set((state) => ({ posts: [...state.posts, post] })),
}));
```
 

user와 posts 상태가 둘 다 존재하지만, 특정 컴포넌트에서 user.name만 필요하다면, 전체 user 객체를 구독하는 대신, 특정 값만 구독하도록 코드를 작성할 수 있습니다:

```

const userName = useStore((state) => state.user.name);

```

이렇게 하면 user 객체의 다른 속성들이 변경되더라도 name을 구독하는 컴포넌트는 리렌더링되지 않습니다.

2. 상태 비교 함수 사용

Zustand의 useStore 훅은 상태의 특정 부분을 구독할 때, 상태가 변경되었는지 확인하는 비교 함수를 사용할 수 있습니다. 이를 통해 보다 정밀하게 리렌더링을 제어할 수 있습니다. 예를 들어:

```

const selectedPosts = useStore(
  (state) => state.posts,
  (oldPosts, newPosts) => oldPosts.length === newPosts.length
);
```

이 비교 함수는 posts 배열의 길이가 동일한지 비교하여, 길이가 변경되지 않으면 리렌더링을 방지합니다.

3. Shallow Equality Comparison

Zustand는 기본적으로 구독한 상태가 변경되었는지 확인하기 위해 얕은 비교(shallow comparison)를 사용합니다. 이를 통해 객체의 속성이 변경되지 않은 경우 리렌더링을 방지할 수 있습니다. 예를 들어:

 
```
const useStore = create((set) => ({
  user: { name: 'Alice', age: 25 },
  updateName: (name) => set((state) => ({ user: { ...state.user, name } })),
}));
```
 

여기서 useStore((state) => state.user)로 전체 user 객체를 구독할 때, 객체 자체가 새로 생성되었지만 속성값이 동일하다면 얕은 비교를 통해 리렌더링을 방지할 수 있습니다. 그러나 상태 슬라이싱을 통해 더 세밀한 구독을 하는 것이 이상적입니다.

4. 배치 업데이트 (Batch Updates)

Zustand는 상태 업데이트가 발생할 때 배치 처리(batch processing)를 지원합니다. 여러 상태 업데이트를 한 번에 처리하여 불필요한 리렌더링을 줄일 수 있습니다. 예를 들어, React의 startTransition 함수를 사용하여 배치 업데이트를 수행할 수 있습니다:j

```
import { startTransition } from 'react';

function handleMultipleUpdates() {
  startTransition(() => {
    useStore.setState({ user: { name: 'Bob' } });
    useStore.setState({ posts: ['Post 1', 'Post 2'] });
  });
}
```

이 방식은 여러 상태 변경을 한 번에 처리하여, 리렌더링 횟수를 최소화합니다.

5. React.memo 사용

상태 구독에 관련된 컴포넌트를 React.memo로 래핑하면, 상태 변경 시 해당 컴포넌트가 불필요하게 리렌더링되지 않도록 할 수 있습니다. React.memo는 props가 변경되지 않은 경우 리렌더링을 방지합니다:

```

const MyComponent = React.memo(() => {
  const userName = useStore((state) => state.user.name);
  return <div>{userName}</div>;
});
```

6. 컴포넌트 트리 분리

상태 구독의 영향을 최소화하기 위해, Zustand에서 상태를 구독하는 컴포넌트와 그렇지 않은 컴포넌트를 트리 구조에서 분리하는 것도 성능 최적화에 도움이 됩니다. 이렇게 하면 상태 변경이 컴포넌트 트리 전체에 불필요한 리렌더링을 유발하지 않게 됩니다.

 

노드 버전 관리자들 중 Volta는 여러 버전의 프로젝트를 유지해야하는 환경에서 유리합니다.

TIL

volta 라는 노드버전관리자가 있다. 

https://velog.io/@sehyunny/5-node-manager-compared?utm_source=substack&utm_medium=email

 

(번역) 5가지 노드 버전 관리자 비교 - 어떤 것이 적합할까요?

이 글에서는 서로 다른 5가지의 Node.js 버전 관리자를 비교해보겠습니다. 이들이 서로 어떻게 다른지 그리고 프로젝트의 상황에 따라 어떤 것을 고려해야하는 지 확인하실 수 있습니다.

velog.io

 이 글을 통해 알게되었고

`Volta는 버전 관리자 세계에서 떠오르는 샛별입니다(10k개 GitHub 스타).

Rust로 작성되었으며 크로스 플랫폼을 지원합니다.`

이에 안해볼 수가 없었다. 

 

현재 회사에서 진행하고 있는 프로젝트는 아주 아주 구버전과 최근 구축한 버전이 있다.

그래서 구버전을 유지보수해야할 때에 기존에는 n버전관리를 사용하고 있었기 때문에 할때마다 노드 버전 변경작업과 약간의 변경시간이 필요했는데, 볼타를 사용한다면, package.json 에 이 개발환경을 명시적으로 작성하여 모두에게 공유하여 협업이 용이해질 아니라

자동으로 해당 버전으로 실행시켜준다는 점이 정말 편리하고 마음에 든다. 

 

 업데이트 시키지 못한 나머지 여러버전을 관리해야하는 상황이라면 볼타를 추천한다.

 

1. 일단 설치한다. 

curl https://get.volta.sh | bash

 

2. 볼타를 설치하고 나면 package.json 에 이렇게 작성하기만 하면 된다. 

우리 구 버전은 다음과 같다. 

 

```
  "volta": {
    "node": "14.19.0",
    "yarn": "1.22.0"
  },

````

 

또 다른건 node 버전 20임...^^

 

3. 그리고 실행하면 알아서 찾아서 맞는 환경으로 실행시켜준다. 

React - event handler 이벤트 핸들러의 e 는 어떻게 전달되는 걸까?

TIL

처음 공부를 시작할 때 이해가 잘 안 되는 부분이 있었는데 바로 이벤트 핸들러 함수를 정의할 때의 e 였다.

 

예를 들면 아래에서의 handleSubmit(e) 함수 정의가 있다.

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

 

onSubmit에서 e는 등장한 적이 없는데 handleSubmit 은 대체 어떻게 e를 받는 거지?

 

이런 의문을 나만 가졌을 리 없다.

 

누군가에게 도움이 되길 바라며 적는 글이다.

 

[TOC]
# 1 . 결론
# 2. 자바스크립트
## 2.1 addEventListener
## 2.2 DOM Spec
## 2.3 listener callback
# 3. React
## 3.1 React listener
## 3.2 Synthetic Event

 

 

결과적으로 기억할 내용은 다음이다.

# 1. 결론

React 에서 JSX 에 이벤트핸들러로 넘기는 이벤트리스너함수(콜백)는 return/render 전에 정의하고, 이벤트에 걸리는 이벤트 리스너는 인자로  해당synthetic event 를 받을 수 있다.

 

이유에 대한 결론은 만들어진 곳에서 넘겨받은 함수는 첫번째 인자로 e(이벤트) 를 받아 호출할 수 있게 정의해 뒀기 때문이다.

호출되는 곳에서는 콜백함수에 인자로 이벤트 객체를 넘기고 넘겨질 콜백함수는 개발자가 정의, 선언하는데
그 때 정의 하는 인자의 네이밍은 e, event,evt 등등 쓰는 사람 마음대로 적혀지곤 한다.
그러나 최고의 네이밍은 누가 봐도, 코딩을 모르는 사람이 봐도 읽을 수는 있는 event 로 적는 것이 가독성과 협업성에 좋으니까 event로 는게 좋다.

 

라고 경험 + 공식문서 내용으로 알 수 있는데 좀 더 파헤쳐 보자.

 

React는  javascript 라이브러리 라는 걸 먼저 기억하자.

 

# 2. 자바스크립트

## 2.1 addEventListener

 

javascript 에서는

EventTarget.addEventListener()

를 이용해서 돔에서 발생하는 이벤트를 잡아 콜백 함수를 설정할 수 있다.

(EventTarget의 addEventListener() 메서드는 지정한 이벤트가 대상에 전달될 때마다 호출할 함수를 설정합니다.)

 

참고로 콜백 함수란, 함수의 인자로 보낼 수 있는 함수로 생각하면 이해하기 쉽다.

 

사용 구문은 다음과 같다.

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);

 

1. 사용할 때 필수적으로 와야 하는 첫 번째 인자 type이란

이벤트의 유형으로 'click', 'submit', 'focus'  등 w3c에서 정의한  이벤트 종류이다.

 

2.  필수적으로 와야 하는 두 번째 인자 listner

앞에서 정의한 이벤트 type의 이벤트가 발생했을 때 실행될 콜백 함수로,

('click' -> 'click' event)를 첫 번째 인자로 받는 함수이다. => 요 리스너가 리액트의 리스너와 같은 역할이다.

 

그리고 관련하여 돔 명세를 봐보자


## 2.2 DOM Spec

obj.addEventListener("load", imgFetched)

function imgFetched(ev) {
  // great success
  …
}
 In the example above ev is the event. ev is passed as an argument to the event listener’s callback (typically a JavaScript Function as shown above). Event listeners key off the event’s type attribute value ("load" in the above example). The event’s target attribute value returns the object to which the event was dispatched (obj above).

 

돔명세에 의하면 이벤트 리스너의 콜백에는 이벤트가 소괄호 안에 argument 로 전달된다.

e든 ev든 evt, event 든 identifier 일 뿐 그 자리에는 이벤트가 들어온다.

 


## 2.3 listener callback

addEventListener 에 오는  콜백함수 형태를 보면

1. function(e) { ...}

2. function(){...}

이런 익명함수들인데,

인자가 없는 건, 함수 내에서 e 를 참조할 필요가 없을 때 굳이 넘겨받지 않아도 되기때문에 인자로도 안받는 것이다.

이 부분이 포인트다.

 

이 부분의 이해를 돕는 예시를 만들어 봤다.

function log(a) {
    console.log(a)
}
log('test') // test
log('test', 2) // test

function alarm(msg){
  alert("!");
}
alarm(msg) // !
alarm() // !

function alarm2(){
  alert("!");
}
function alarm2(123) // !

 

그리고 event.target 은 이벤트가 발생한 객체이다.

 

 이벤트 타겟의 명세는 다음과같다

 

2.7. Interface EventTarget

[Exposed=(Window,Worker,AudioWorklet)]
interface EventTarget {
  constructor();

  undefined addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options = {});
  undefined removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options = {});
  boolean dispatchEvent(Event event);
};

callback interface EventListener {
  undefined handleEvent(Event event);
};

dictionary EventListenerOptions {
  boolean capture = false;
};

dictionary AddEventListenerOptions : EventListenerOptions {
  boolean passive = false;
  boolean once = false;
  AbortSignal signal;
};

 

 

# 3. React

자바스크립트에서는 addEventListener를 통해서 이벤트 핸들러를 구현하지만, 리엑트에서는 다르다. 


## 3.1 React listener

When using React, you generally don’t need to call  'addEventListener' to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.

React에서는  DOM element가 생성된 "후"에 리스너를 추가하기 위해 addEventListener를 호출할 필요가 없습니다.  처음 렌더링 될 때 리스너를 제공합니다.

이는 return 위에서 정의한 handleSubmit 함수의 위치를 의미하는 듯하다.

다시 말하자면 리스너는 이벤트가 발생할 때 실행될 콜백 함수이다.

이 콜백 함수를 return 위에서 작성해 주는 것이 react의 방식이다.


## 3.2 Synthetic Event

리액트에서는 합성이벤트 (Synthetic Event) 라는 리액트 이벤트를 사용하는데 이건 브라우저에서 기본적으로 제공하는 

native event 를 참고해서 리액트에서 재가공한 이벤트 객체이다.

이벤트 핸들러는 모든 브라우저에서 이벤트를 동일하게 처리하기 위한 이벤트 래퍼 SyntheticEvent 객체를 전달받습니다. 
Although events are typically dispatched by the user agent as the result of user interaction or the completion of some task, applications can dispatch events themselves by using what are commonly known as synthetic events:
이벤트는 일반적으로 사용자 상호 작용 또는 일부 작업 완료의 결과로 사용자 에이전트에 의해 전달되지만 어플리케이션은 일반적으로 합성 이벤트로 알려진 것을 사용하여 이벤트를 전달할 수 있습니다.

// add an appropriate event listener
obj.addEventListener("cat", function(e) { process(e.detail) })
// create and dispatch the event  [ synthetic event ]  <- 요걸 리액트에서 쓴다
var event = new CustomEvent("cat", {"detail":{"hazcheeseburger":true}}) obj.dispatchEvent(event)

 

따라서 JS DOM 에 정의된 이벤트와는 정확히 일치하지는 않는다. 

 

 

 

 

https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener

https://developer.mozilla.org/ko/docs/Web/API/EventListener

https://developer.mozilla.org/en-US/docs/Web/API/EventListener/handleEvent

https://ko.reactjs.org/docs/events.html 

 

https://dom.spec.whatwg.org/#introduction-to-dom-events

https://dom.spec.whatwg.org/#interface-eventtarget

 

 

 

시니어 프론트엔드 개발자처럼 크롬 개발자도구 사용하기

TIL

1. Run command
2. [console] 마지막 실행 결과 참조 $_
3. [network] XHR 재요청
4. [network] 화면 로딩상태 추적하기
5. [console] 변수복사 : copy
6. [network] 이미지를 data URI 로 복사하기
7. [console] 테이블로 객체배열 보기
8. [Elements] 드래그앤 드랍
9. [Console] 현재 선택된 요소 참조 : $0
10.[Elements] css 가상 클래스 작동
11. [Elements] 요소 숨기는 단축키 : H/h
12. [Elements → Console] DOM 요소를 글로벌 임시 변수에 저장하기

 

junwoo45.github.io/2020-07-28-chrome_devtools/

 

<번역>시니어 프론트엔드 개발자처럼 크롬 개발자도구 사용하기

시니어 프론트엔드 개발자처럼 크롬 개발자도구 사용하기 이 글은 bitfish의 Use Chrome DevTools Like a Senior Frontend Developer을 번역한 글입니다. 원문은 Use Chrome DevTools Like a Senior Frontend Developer 에서 확인

junwoo45.github.io

 

React- hooks

TIL

드디어 Hooks 가 뭔지 감이 온다.

 

공식 문서는 ㅠㅠ 개념만 익혀서 뭔가 사용례가 너무 궁금해서 찾아보니 역시 벨로퍼트님이다.

 

https://velog.io/@velopert/react-hooks

리액트의 Hooks 완벽 정복하기

Hooks 는 리액트 v16.8 에 새로 도입된 기능으로서, 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해줍니다. 이번 튜토리얼을 진행하기 위하여 create-react-app 을 사용하여 ...

velog.io

 

이 글을 참고해서 한땀한땀 작성해가면서 공부해보았다.

 

빠른 시일내에 복습하면 이제 뭔지 제대로 알 듯한데..

 

커스텀 훅스 너무 유용하고 재밌다!

 

context 부분만 좀 더 찾아서 공부해봐야지.

 

 ※ push 말고 concat 으로 새로운 배열을 만들어서 변수에 할당한 이유 : push는 추가된 요소를 포함한 새로운 리스트의 length를 리턴한다.

 

 

 

const onInsert = useCallback(e => {

const nextList = list.concat(parseInt(number));

setList(nextList);

setNumber('');

inputEl.current.focus();

}, [number, list]);

//number 혹은 list 가 바뀌었을 때만 함수 생성 - 기존의 number 와 list 를 조회해서 nextList 를 생성하기 때문에 배열 안에 number 와 list 를 꼭 넣어주어야 합니다.

 

 

// function reducer(state, action){

// return {

// ...state,

// [action.name] : action.value

// }

// }

 

const Info2 = () => {

// const [state, dispatch] = useReducer(reducer, {

// name: '',

// nickname: ''

// })

const [state, onChange] = useInput({

name: '',

nickname:''

})

 

const {name, nickname} = state;

// const onChange = (e) => {

// dispatch(e.target)

// }

react - class, JSX 콜백함수에서 this

TIL

React를 사용할 때 일반적으로 DOM 요소가 생성된 후에 리스너를 추가하기 위해 addEventListener 를 호출할 필요가 없습니다.

대신 요소가 처음 렌더링될 때 리스너를 제공합니다.

ES6 class 를 이용해 요소를 정의할 때 이벤트 핸들러의 일반적인 패턴은 클래스의 메서드 형태입니다. 

 

JSX 콜백에서 this 의 의미에 대해 주의해야합니다.

자바스크립트에서 클래스 메서드는 기본적으로 bound 되지 않습니다. 만약 constructor 내부에서 

this.handleClick = this.handleClick.bind(this) 바인드를 잊은채로 onClick 에 전달하면, this 는 함수가 실제로 호출될 때 undefined 로 취급됩니다.

 

이건 React에서 정의한 동작이 아닙니다. 자바스크립트의 함수의 동작 방식 의 일부입니다.

일반적으로 onClick={this.handleClick} 처럼 () 없이 메서드를 참조하면, 그 메서드를 bind 해야합니다.

만약 bind 를 호출하는 게 귀찮은 경우 이 문제를 해결할 수 있는 두가지 방법이 있습니다.

 

1. 만약 실험 기능인 퍼블릭 클래스 필드 문법 을 사용한다면 클래스 필드를 정확히 콜백에 bind할 수 있습니다.

- 함수선언에서 화살표함수를 사용

 

2. JSX에서 이벤트에 거는 콜백함수에서 화살표함수를 사용

이경우는 바인드를 하지 않아도 되지만, 해당 컴포넌트를 렌더링 할때마다 서로 다른 콜백이 만들어진다.

. 대부분의 경우에서 크게 문제는 없습니다. 하지만 만약 콜백에서 하위 컴포넌트에 prop을 전달하는 경우, 이 컴포넌트는 큰 비용으로 다시 렌더링될 수 있습니다. 이런 종류의 성능 문제를 피하기 위해 보통

 

생성자 함수에서 바인딩하거나

클래스 필드 문법을 사용하는 걸 권장합니다.

 

https://reactjs-kr.firebaseapp.com/docs/handling-events.html

react - 이벤트 핸들러에 인수 전달하기

TIL

이벤트 핸들러에 인수 전달하기

반복 안에서 보통 이벤트 핸들러에 추가 파라미터를 전달하고 싶어할 것입니다. 예를 들어, 만약 id 가 원시적인 ID라면, 아래처럼 전달할 수 있습니다.

 

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

 

위 두 라인은 동일하며, arrow functions과and Function.prototype.bind 를 각각 사용하고 있습니다.

두 경우 모두, React 이벤트를 나타내는e 인수는 ID 뒤에 두 번째 인수로 전달됩니다.

arrow function을 사용하여 명시적으로 전달해야하지만,

bind를 사용하면 추가 인수가 자동으로 '마지막 인자'로 전달됩니다.

 

 

[링크] React - event handler 이벤트 핸들러의 e 는 어떻게 전달되는 걸까? 1

 

 

https://reactjs-kr.firebaseapp.com/docs/handling-events.html

 

'TIL' 카테고리의 다른 글

React- hooks  (0) 2020.01.29
react - class, JSX 콜백함수에서 this  (0) 2019.12.03
javascript 화살표함수 특징 6 _191202  (0) 2019.12.02
딥카피 만드는 함수  (0) 2019.12.02
191201 어제의 충격 포인트 indexOf  (0) 2019.12.02

javascript 화살표함수 특징 6 _191202

TIL

arrow Function

this, arguments, super, or new.target 키워드에 바인딩이 없다.

화살표 함수는 항상 익명입니다.

이 함수 표현은 메소드 함수가 아닌 곳에 가장 적합합니다.

그래서 생성자로서 사용할 수 없습니다.

구문
// 객체 리터럴 표현을 반환하기 위해서는 함수 본문(body)을 괄호 속에 넣음:
params => ({foo: bar})

/*구문 예시*/
var materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

// 화살표 함수를 이용해 아래와 같이 표현할 수 있다
materials.map((material) => {
  return material.length;
}); // [8, 6, 7, 9]

// 매개변수 목록 내 비구조화도 지원됨
materials.map(({length}) => length); // [8, 6, 7, 9] 

** this 간단 정리

: 생성자 함수와 객체의 메소드를 제외한 모든 함수(내부 함수, 콜백 함수 포함) 내부의 this는 전역 객체

1. Arrow Function 에서 this

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.

화살표 함수는 자기 this 가 없다.

화살표 함수 내부에서 사용된 this는 상위체인의 범위에서 찾는다(감싸고 있는 렉시컬 스코프).

화살표함수도 일반적인변수 찾는 룰 처럼 this를 찾을때까지 enclosing scope 를 타고 올라간다.

메소드로 사용될 때

var obj = { 
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
}

obj.b(); // prints undefined, Window {...} (or the global object)
obj.c(); // prints 10, Object {...}

화살표 함수는 자신의 this를 가지고("bind" 바인드)있지 않습니다.

그래서 obj.b 의 this 는 window

 

* ES6에서는 메소드를 선언할 때, function 키워드를 생략한 축약 표현을 사용할 수 있다.

const person = {
  name: 'Lee',
  sayHi() { // === sayHi: function() {
    console.log(`Hi ${this.name}`);
  }
};

 

Arrow Function 에서 call, apply 의 this argument 는 무시된다. (this 가 없으니까)

var adder = {
  base: 1,

  add: function(a) {
    var f = v => v + this.base; 
    return f(a);
  },

  addThruCall: function(a) {    //(2) a에 1이 들어간다.
    var f = v => v + this.base; //함수를 화살표함수로 정의
    var b = { //변수 정의
      base: 2
    };

    return f.call(b, a); //(3) call에 this, paramenter 를 담아서 실행한다. f(1) 과 같은 결과임
                                                //-> 1 + 1 =2, 즉 b라는 객체를 this로 넘겨도 무시함 두번째 인자로 받은 변수만 적용한다.
  }
};

console.log(adder.add(1));         // This would log 2 
console.log(adder.addThruCall(1)); // This would log 2 still  (1) 호출  (4) 결과값 2 

Since arrow functions do not have their own this, the methods call() and apply() can only pass in parameters. Any this argument is ignored.

2. 화살표 함수는 생성자로서 사용될 수 없으며 new와 함께 사용하면 오류가 발생합니다.

 

3. 화살표 함수는 prototype 속성이 없습니다.

 

4. yield 키워드는 화살표 함수의 본문(그 안에 더 중첩된 함수 내에서 허용한 경우를 제외하고)에 사용될 수 없습니다. 그 결과, 화살표 함수는 생성기(generator)로서 사용될 수 없습니다. <-  ㅁ

 

5. 바인딩 되지 않는 arguments

화살표 함수는 arguments 객체를 바인드 하지 않습니다. 

function foo(n) {
  var f = () => console.log(arguments);//foo's implicit arguments binding. arguments[0] is n
  return f();
}
foo(4);
//내부 화살표함수 f 에서 arguments는
//foo(4) 가 실행되면서 넘어온 n 값인 4 를 arguments로 받음.(화살표함수 당사자의 인자는 없음에도 불구하고)

화살표 함수는 자신의 arguments 객체가 없지만, 대부분의 경우에 나머지 매개변수가 좋은 대안입니다

function foo(n) {
  var f = (...args) => args[0] + n; ///args = [2]
//:foo 실행시 들어오는 n 과 관계없이 return f(2) 의 2값을 받는다.
  return f(2);
}
foo(1); // 3

6. addEventListener 함수의 콜백 함수

addEventListener 함수의 콜백 함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역 객체 window를 가리킨다.

따라서 addEventListener 함수의 콜백 함수 내에서 this를 사용하는 경우, function 키워드로 정의한 일반 함수를 사용하여야 한다. 일반 함수로 정의된 addEventListener 함수의 콜백 함수 내부의 this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다.

 

 

 

참고 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/%EC%95%A0%EB%A1%9C%EC%9A%B0_%ED%8E%91%EC%85%98    

https://poiemaweb.com/es6-arrow-function

딥카피 만드는 함수

TIL
function copyDeep(obj){
  if(typeof obj !== "object"){
    return obj;
  }

  var result;
  if(obj.constructor === Array){ //타입확인하기
    result =[];
  }else if (obj.constructor === Object){
    result = {};
  }else{
    return obj;
  }

  for(var name in obj){
    result[name] = copyDeep(obj[name]);
  }
  return result;

}