[레벨2 - 미션1] 1단계 계산기 피드백

Nine·2022년 5월 9일
0

죽지도 않고 또 돌아온 계산기 미션입니다.

레벨2 미션1 1단계 계산기 PR

이번 미션은 우테코에서 React class Component를 구현하는 처음이자 마지막 미션이예요.

그리고 React를 본격적으로 시작하기 전에 맛보기 느낌의 온보딩 미션이였어요. 온보딩 미션 맞나.. 나만 진심이였지 또ㅠㅠ

그럼 계산기 미션 1단계에서 받은 피드백들을 살펴보러 가시죠!


들어가기 전에...

HOC

HOC(High Order Component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다.

  • render는 재활용이 안되는데 다른 건 재활용이 할 수 있어요.
    • 순수 Javascript는 extends를 많이 썼죠?
    • React에서는 HOC를 많이 사용합니다. (이제는 Context API로 대체되는 추세)
const AComponent = higherOrderComponent(BComponenet);

화살표 함수

  • 화살표함수는 this 바인딩을 못해요.

🤔 그런데 화살표 함수를 많이들 원해요. 왜 일까요?

불필요한 기능은 필요가 없으니까! (불필요한 기능들을 덜어내고 덜어냈기 때문에 화살표 함수를 사용하는 거예요.)


함수형 컴포넌트의 등장

  • 😢아니 클래스 컴포넌트는 너무 많은 코드를 쓰지 않나?
    👉 그냥 함수로 짜보자
const StatelessComponent = props => <div> {props.name} </div>

Hooks

  • Eslint를 제공해요.
  • React 생태계에 따라 써야합니다.

👇 대표적인 useState를 볼까요?

const [count,setCount] = useState(0);
  • 이건 리액트 문법이 아니라 Javascript 구조분해할당입니다.
  • 객체로 받는 것이 아니라 배열로 받는 이유는 네이밍을 지정하기 위함이겠죠?

Stateful vs Stateless Components

리액트에서 가장 흔한 프로그래밍 패턴은 state를 가진 부모 컴포넌트가 있고 그 자식들이 props만 받고 state는 없는 컴포넌트들이 존재하는 경우입니다.

  • 상태가 있는 컴포넌트와 상태가 없는 컴포넌트를 구분합시다.

컴포넌트 고찰하기

(포코의 생각 유도 질문!)

신호등을 예로 들어볼까요?

  • 처음에는 신호등이 뭐 복잡할 이유가 없었죠.

  • 💦 그런데 이제 보세요. 신호등도 복잡해졌죠.

    • 자전거 그려져있고 버스 그려져 있고 사람전용 있고 시간 나오고....
  • 신호등을 컴포넌트라고 생각하면 어떻게 확장될 수 있는지 생각해봅시다.


💪 계산기 미션 1단계 공통 피드백

HTML

꼼꼼한 HTML 확인

너무 javascript만 하는거 아니예요?
html 언어가 en으로 되어있는데... ko로 바꿔줍시다.
👉 프로젝트 내에서 내가 다루는 파일들을 이해하고 넘어갑시다.

React

setState 잘 활용하기

setState({
  ...state,
  prevNumber: selectedDigit,
})

---------vs---------
  
setState((prevState)=>({
  firstNumber: prevState.firstNumber + value,
}));
  • 위의 방식보다 아래의 방식이 선호되는 이유는 무엇일까요?
    👉 Batch update, 의존성 관점에서 생각해봅시다.

컴포넌트는 얼마나 알아야할까⭐⭐⭐

이제 렌더링은 리액트가해요. 우린 권한이 없어😢😢
그러니 우리는 리액트의 렌더링 방식을 알아야해요.

// 1. 렌더링에 관련없이 사용됨
class Digits extends React.Component {
  constructor(props) {
    super(props);

	  // 2. 컴포넌트를 만들 때 내부 상태 정의
  }

  // 3. render에 필요한 정보, 함수, 라이프사이클 등을 정의
  render() {
  // 4. 렌더링될때마다 수행됨
    return (
      <div className="digits flex">
        {[9, 8, 7, 6, 5, 4, 3, 2, 1, 0].map((digit) => (
          <button
            className="digit"
            key={digit}
            onClick={this.props.handleClickDigit}
          >
            {digit}
          </button>
        ))} // list는 계속 반복되겠죠. 1번으로 옮기면 더 좋겠네요.
      </div>
    );
  }
}

class Operators extends React.Component {
  render() {
    return (
      <div className="operations subgrid">
        {['/', 'X', '-', '+', '='].map((operator) => (
          <button
            className="operation"
            key={operator}
            onClick={this.props.handleClickOperator}
          >
            {operator}
          </button>
        ))} // 마찬가지로 1번으로 옮길 것 같아요.
      </div>
    );
  }
}

render() {
	// 이걸로 해! 명령형 좋네요.
  const result =
  this.state.nextNumber === null ? this.state.prevNumber : this.state.nextNumber;
	
	// 변수에 할당해서 사용 vs  return 내부에서 사용
	// 렌더링 순간마다 고유한 렌더링 값을 가진다 -> computedValue라고 합니다.
	return <SomeCode>{this.state.isError
            ? '오류'
            : `${this.state.firstOperand}
            ${this.state.operation ?? ''}
            ${this.state.secondOperand}`}</SomeCode>
}

render() {
  // bad case... : render 메서드 안에서 해주고 있군요.
  const { current } = this.state;
  
  // 변경되지 않는데 이게 여기있네요. 바깥으로 보내죠?
  const digits = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
  const operators = ['/', 'X', '-', '+', '='];

  return <SomeCode>{current === Infinity ? INFINITY_MESSAGE : current}</SomeCode>
} 

Fragment를 사용하자

왜 굳이 <> </> 를 써서라도 최상위 태그를 하나로 유지할까요?

Virtual DOM은 부모가 하나로 구성되면 효율적으로 찾을 수 있도록 구현되어 있거든요!


dataSet 그만

<button className="digit" data-number="9">
  9
</button>
className: 리액트가 저렇게 하라해서 사실 웹 표준을 어긴거죠.
data-set: 웹표준을 지킨거죠.

⚠️ 하지만 dataSet은 비추한답니다!

❓ 왜 attribute를 props라 부를까요?
👇 attribute는 아래처럼 생성하죠.

<a id="mylink" href=""/>
document.getElementById("mylink").setAttribute("href", "")

👇 property는 아래처럼 생성하죠.

document.getElementById("mylink").href = ""

사실 attribute와 property는 모두 HTML의 일부입니다.
거기에 React는 추가 지원과 추상화를 제공합니다.


props 유의미하게 받기

export default class Digit extends Component {
  constructor(props) {
    super(props);

    this.state = { ...props };
  }
}
  • 인자가 변할때 props로 받을 수 있지만 무엇을 받는지 알 수 있나요? NoNo
  • 구조 분해 할당 사용합시다!

Prop Types 사용

class Keypad extends Component {
  render() {
    const { className, keyClassName, keypad, onClick } = this.props;

    return (
      <div className={className}>
        {keypad.map((key) => (
          <Button
            key={key}
            className={keyClassName}
            onClick={onClick}
            text={key.toString()} // react의 children을 쓰는게 나아요.
          />
        ))}
      </div>
    );
  }
}

Keypad.propTypes = {
  className: PropTypes.string,
  keyClassName: PropTypes.string,
  keypad: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.string),
    PropTypes.arrayOf(PropTypes.number),
  ]),
  onClick: PropTypes.func,
};

리뷰어님의 피드백

이번 리뷰어는 Vallista님!

🤔 이번 React를 CRA로 만들면서 기본적으로 생성되어지는 파일에 대해서 크게 생각 안하고 바로 이어서 코딩을 진행했는데 그런 안일한 행동을 하지 말아야겠다 라는 생각을 하게 되었어요.

😢 리뷰어님께서 왜 이렇게 했는지에 대해서 질문을 많이 주셨는데 제가 제대로 알고 적용한 부분들이 많이 없더라구요. (심지어 eslint도...!)

키워드들입니다.

eslint
prettier
devDependencies vs dependencies
package.json의 각 설정들
manifest.json
robots.txt
픽셀 밀도
Number() vs parseInt
strict mode
화살표 함수를 쓰는 이유

profile
함께 웃어야 행복한 개발자 장호영입니다😃

0개의 댓글