[코드숨 리액트 13기] 1주차 과제 피드백

박세진·2022년 10월 22일
0

해당 게시글에는 풀이가 있습니다. 풀이를 보기 전에...한번 더 혼자 힘으로 해보시는 것을 추천드립니다!

1주차 과제 피드백

1주차 과제

1주차 과제는 매개변수와 인자를 이용하여 값을 관리하는 과제였다.
해당 과제를 하면서 eslint가 설정이 되어 있었기 때문에 꽤나 까다로웠다.
동작은 하는 듯 했으나, eslint에서 에러가 발생했고, 이를 없애기는 생각보다 쉽지 않았다.

remove let

/* eslint-disable react/react-in-jsx-scope, react/jsx-filename-extension */
/* @jsx createElement */

function createElement(tagName, props, ...children) {
  const element = document.createElement(tagName);

  Object.entries(props || {}).forEach(([key, value]) => {
    if (key === 'onClick') {
      element[key.toLowerCase()] = value;
      return;
    }

    element[key] = value;
  });

  children.flat().forEach((child) => {
    if (child instanceof Node) {
      element.appendChild(child);
      return element;
    }
    element.appendChild(document.createTextNode(child));
    return element;
  });

  return element;
}

function render(count = 0) {
  const handleClick = (number) => {
    render(number + 1);
  };

  const handleClickNumber = (value) => {
    render(value);
  };

  const element = (
    <div id="hello" className="greeting">
      <p>CodeSoom first assignment</p>
      <p>{count}</p>
      <p>
        <button type="button" onClick={() => handleClick(count)}>Click</button>
      </p>
      <p>
        {[1, 2, 3].map((i) => <button type="button" onClick={() => { handleClickNumber(i); }}>{i}</button>)}
      </p>
    </div>
  );

  document.querySelector('#app').textContent = '';
  document.querySelector('#app').appendChild(element);
}

render();
  • function 키워드를 이용하면 호이스팅이 되는 것으로 알고 있는데, 계속해서 eslint에서는 'render' was used before it was defined라는 에러를 주는 게 아닌가? 그래서 궁금했다. 호이스팅이 되더라도 사용하기 전에 선언하는 것이 권장되는 방식인지...

    • 사용하기 전에 선언하는 것이 가독성유지보수측면에서는 더 좋다

간단계산기 만들기

/* eslint-disable react/react-in-jsx-scope, react/jsx-filename-extension, no-unused-vars */

/* @jsx createElement */

function createElement(tagName, props, ...children) {
  const element = document.createElement(tagName);

  Object.entries(props || {}).forEach(([key, value]) => {
    element[key.toLowerCase()] = value;
  });

  children.flat().forEach((child) => {
    if (child instanceof Node) {
      element.appendChild(child);
      return;
    }
    element.appendChild(document.createTextNode(child));
  });

  return element;
}

const or = (num1, num2) => (num1 === null ? num2 : num1);

const operationFunctions = {
  '+': (num1, num2) => num1 + num2,
  '-': (num1, num2) => num1 - num2,
  '*': (num1, num2) => num1 * num2,
  '/': (num1, num2) => num1 / num2,
};

const defaultFunctions = (num1, num2) => or(num2, num1);

const initialState = {
  accumulator: 0,
  number: null,
  operator: '',
};

// eslint-disable-next-line max-len
const calculate = (operator, accumulator, number) => (operationFunctions[operator] || defaultFunctions)(accumulator, number);

function render({ accumulator, number, operator }) {
  const handleClickNumber = (value) => {
    render({ accumulator, operator, number: (number || 0) * 10 + value });
  };

  const handleClickOperator = (value) => {
    render({
      accumulator: calculate(operator, accumulator, number),
      operator: value,
      number: null,
    });
  };

  const handleReset = () => {
    render(initialState);
  };

  const element = (
    <div>
      <p>간단 계산기</p>
      <p>{or(number, accumulator)}</p>
      <div>
        <div>
          {
            [1, 2, 3, 4, 5, 6, 7, 8, 9, 0].map((value) => <button type="button" onClick={() => handleClickNumber(value)}>{value}</button>)
          }
        </div>
        <div>
          {
            ['+', '-', '*', '/', '='].map((value) => <button type="button" onClick={() => handleClickOperator(value)}>{value}</button>)
          }
        </div>
        <div>
          <button type="button" onClick={handleReset}>reset</button>
        </div>
      </div>
    </div>
  );

  document.getElementById('app').textContent = '';
  document.getElementById('app').appendChild(element);
}

render(initialState);
  • 변수명은 동사보다는 형용사나 수동태를 사용해야 된다

    • 함수명은 동사를 이용하기 때문에 함수명이랑 변수명이랑 혼동을 줄 수 있다
  • 1주차 과제는 매개변수를 이용한 과제이다 보니, 매개변수가 점점 많아졌다.

    • 이로 인한 불편함으로 RORO 패턴에 대해서 읽어봤다. (공부를 했다고 말할 수는 없을 것 같다...ㅎ)
    • RORO패턴 / 원본
    • operator 객체의 프로퍼티 이름 operator와 매개변수의 이름인 operator가 동일해서 no-shadow 문제도 있었다.

RORO 패턴

RORO 패턴은, 객체를 받아서, 객체를 반환한다는 의미를 줄인 것이다.
RORO 패턴을 공부하기 위해서는 비구조화할당(destructurin)을 알아야 된다.

  • Named Parameters
    모호하지 않고, 읽고 이해하기 훨씬 쉬어질 수 있다는 장점이 있다
    이전 코드를 손상하지 않고 새 파라미터를 추가할 수 있다
    불변성을 장려한다...!?(파라미터 객체의 속성 중 하나가 복잡한 타입(array, object)인 경우 destructuring은 얕은 복사를 하기 때문에 원본이 변경될 수 있음!)
function findUser(id, password, email) {
  console.log(id);
}

findUser('sejinee', 123, 'sejinee@aaa.com');
function findUser({id, password, email}) {
  console.log(id)
}

findUser({id: 'sejinee', password: 123, email: 'sejinee@aaa.com'})

→ 두 코드를 보았을 때, 매개변수에 객체를 받은 쪽이 더 읽고 이해하기 쉽다

  • default parameter
    더 명확하게 디폴트 파라미터의 값을 보존할 수 있다
function findUser(id, student= false, password, email, ) {
  console.log(student)
}

findUser('sejinee', undefined, 123, 'sejinee@aaa.com')

// 인자에 undefined로 전달하지 않으면, student에 부여했던 default 값이 변함
function findUser({id, student= false, password, email} = {}) {
  console.log(student)
}

findUser({id: 'sejinee', password: 123, email: 'sejinee@aaa.com'})

→ 아래와 같이 객체를 전달하면 undefined 값을 쓰지 않고, 디폴트 값을 보존할 수 있다.

(아직 RORO 패턴을 읽어보면서, 이해한 것은 이 두 가지 장점이었다! 해당 게시글에 가면 더 다양한 장점이 있다...)

no-shadow

shadowing은 로컬 변수가 포함된 스코프의 변수와 동일한 이름을 공유하는 프로세스이다. 이런 경우, 코드를 읽을 때도 혼동이 발생할 수 있고, 원하는 값에 접근할 수 없는 일이 발생한다.

간단계산기 과제를 하면서 operator 값을 가져오는데 이러한 문제가 발생했었다. eslint에서 no-shadow라고 알려줬음에도 알지 못했었다. 그래서 원하는 값을 가져오지 못한 채로 2시간 넘게 끙끙거렸다.

매개변수의 이름을 바꿔주니 금방 해결된 걸 보고, no-shadow의 중요성을 몸소 느꼈다.

no-shadow


1주차 과제를 하면서 받은 피드백들을 정리해봤습니다.
remove let은 매개변수를 이용하라는 힌트를 얻어, 금방 완료했다.
하지만 간단 계산기는 말과는 다르게 간단하지 않았다.
처음에는 직접 구현한 코드를 유지해서 보완해 나가고 싶었다...😭
하지만 계속 0과 먼저 계산이 된 채로 연산이 되어, 더하기는 정상적으로 작동하는 모습을 보였고, 나머지 연산을 해본 결과 제대로 작동이 안되길래, 결국 풀이 영상을 보고 완료했다.
시간이 있을 때, 직접 구현했던 코드를 가지고 간단 계산기 작업을 완료하고 싶다. (마무리 되지 않아서 그런지 머리속에 더 아른아른거림)


❗️틀린 내용이 있거나 문제가 있는 경우 댓글로 말씀해주시면 수정하도록 하겠습니다!!

profile
경험한 것을 기록

0개의 댓글