210202 TIL #React#Typescript

이예주·2021년 2월 2일
0

Today I Learned

목록 보기
7/10

React

웹 게임을 만들며 배우는 React(7)

  • 컴포넌트는 주로 반복문을 기점으로 분리한다.
  • componentDidUpdate는 항상 실행되기 때문에 꼭 조건문으로 감싸주는 것이 좋다.
  • useEffectinput 인자가 빈 배열이면 componentDidMount와 동일하다. 배열에 요소가 있으면 componentDidMountcomponentDidUpdate를 둘 다 수행한다.

const [winNumbers, setWinNumbers] = useState(getWinNumbers());

훅스에서 위와 같이 state에 함수가 들어갈 경우, setWinNumbers를 수행할 때마다 getWinNumbers 함수도 매번 수행된다. getWinNumbers가 수행 시간이 오래 걸리는 함수일 경우, 성능 최적화에 문제가 발생한다. 이 때 사용하는 훅스 메소드가 useMemo이다.

const lottoNumbers = useMemo(() => getWinNumbers(), []);
const [winNumbers, setWinNumbers] = useState(lottoNumbers);

useMemo를 사용하면 이전의 데이터가 임시 저장소에 캐싱된다. 두번째 인자의 배열 요소가 갱신될 때마다 useMemo가 수행되므로 불필요한 렌더링을 줄일 수 있다.

useMemouseRef의 차이점

  • useMemo: 복잡한 함수 결과 값을 기억한다.
  • useRef: 일반 값을 기억한다.

useMemouseCallback의 차이점

  • useMemo: 함수의 리턴 값을 기억한다.
  • useCallback: 함수 자체를 기억한다.

그렇다면 모든 함수를 useCallback으로 감싸주는 게 이득인가? 웬만한 함수는 useCallback으로 감싸주어도 별 문제없이 실행된다. 다만 useCallback은 가장 처음에 실행한 함수를 기억하기 때문에 반복 수행해도 값이 바뀌지 않는다. 두번째 인자의 배열 요소가 바뀔 때마다 useCallback이 수행되고, 그 바뀐 값도 함께 저장되어야 하므로 input 인자에 바뀌는 요소를 넣어주어야 한다.

const onClickRedo = useCallback(() => {
        console.log(winNumbers);
        setWinNumbers(getWinNumbers());
        setWinBalls([]);
        setBonus(null);
        setRedo(false);
        timeouts.current = [];
    }, [winNumbers]);	//배열에 winNumbers를 넣지 않으면 값이 갱신되지 않음

useCallback을 필수로 적용해야하는 경우도 있다. 자식 컴포넌트에 props로 함수를 넘길 때에는 꼭 useCallback으로 그 함수를 감싸주어야 한다. 감싸주지 않을 경우 매번 새로운 함수가 수행되기 때문에 자식 컴포넌트에도 매번 새로운 함수가 전달된다. 이런 경우, 자식 컴포넌트는 매번 새로운 함수가 전달되는 것으로 인식하기 때문에 렌더링 또한 매번 일어나는 불상사가 생긴다.

Hooks 관련 추가 팁

훅스에서 state를 생성할 때에는 순서가 매우 중요하다. 조건문이나 함수, 반복문에도 넣지 않고 무조건 최상위에서 선언해야 한다.

간혹 componentDidMount는 수행하지 않고 componentDidUpdate만 수행해야 하는 때가 있는데, 보통 다음과 같은 코드 패턴을 사용한다.

const mounted = useRef(false);
useEffect(() => {
  if(!mounted.current) {
    mounted.current = true;
  } else {
    //실행 코드(componentDidUpdate)
  }
}, [/* 바뀌는 값 */]);



Typescript

타입스크립트 입문 - 기초부터 실전까지(4)

Union Type

유니온 타입은 하나의 변수에 여러가지 타입을 지정할 수 있다. 단순히 Any로 타입을 지정하면 타입스크립트의 장점을 극대화할 수 없기에 유니온 타입을 사용하면 수월하다.

var seho: string | number | boolean	// | 연산자를 이용해 계속해서 추가 가능

function logMessage(value: string | number) {
  console.log(value);
}
logMessage('hello');
logMessage(100);

Type Guard
유니온 타입을 사용하면 한 함수 내에서도 타입 별로 코드 관리가 가능하다.

function logMessage(value: string | number) {
  if (typeof value === 'number') {
    value.toLocaleString();
  }
  if (typeof value === 'string') {
    value.toString();
  }
  throw new TypeError('value must be string or number');
}

타입을 interface로 사용하는 경우에는 두 interface의 공통된 속성만 바로 사용이 가능한데, 즉 인자로 어떤 값이 들어올지 확신할 수 없기 때문에 보장된 속성만 할당을 해줄 수 있다.

interface Developer {
  name: string;
  skill: string;
}
interface Person {
  name: stirng;
  age: number;
}

function askSumeone(someone: Developer | Person) {
  someone.name;		// 접근 가능
  someone.skill;	// 에러
  someone.age;		// 에러
}

공통되지 않은 속성에 접근하고 싶은 경우에는 타입 가드를 사용해서 먼저 인자의 타입이 보장된 후에 접근이 가능하다.

Intersection Type

인터섹션 타입은 & 연산자를 이용하고, 사용된 타입의 모든 속성을 만족해야 한다.

interface Developer {
  name: string;
  skill: string;
}
interface Person {
  name: stirng;
  age: number;
}

function askSomeone(someone: Developer & Person) {
  someone.name;		// 접근 가능
  someone.skill;	// 접근 가능
  someone.age;		// 접근 가능
}

유니온 타입과 달리 someonename, skill, age를 모두 가지고 있어야하기 때문에 skillage에도 접근이 가능하다.

Union type과 Intersection type

실무에서는 보통 유니온 타입을 더 많이 사용한다. 함수에서 사용한 유니온 타입은 함수 내부에서 타입 가드를 통한 타입 추론을 한 번 더 거쳐야하는 대신에 함수를 사용할 때 넣을 수 있는 인자 값의 폭이 넓어진다. 반면에 인터섹션 타입은 함수 내부에서 각 타입의 속성에 쉽게 접근할 수 있는 대신 함수를 사용할 때 전달되는 인자 값이 모든 타입의 속성을 포함하고 있어야 한다.

/* 유니온 타입 */
function askSomeone(someone: Developer | Person) {}
askSomeone({ name: '디벨로퍼', skill: '웹 개발' });	// 가능
askSomeone({ name: '캡틴', age: 100 });		// 가능

/* 인터섹션 타입 */
function askSomeone(someone: Developer & Person) {}
askSomeone({ name: '디벨로퍼', skill: '웹 개발', age: 100 });	// 가능
askSomeone({ name: '캡틴', age: 100 });		// 에러

Enum

이넘은 특정 값들의 집합을 의미하는 자료형이다. 타입스크립트는 숫자형 이넘과 문자형 이넘을 지원한다.


숫자형 이넘
이넘은 별도의 값을 지정하지 않으면 숫자형 이넘으로 취급한다. 이넘에 값들을 계속 추가하면 순서에 맞게 1씩 숫자가 증가한다.

enum Shoes {
  Nike,
  Adidas,
}

var myShoes = Shoes.Nike;
console.log(myShoes)	// 0

문자형 이넘
이넘의 각 값에 별도의 값을 지정해주면 해당 이넘에 접근할 때 지정된 값을 불러온다.

enum Shoes {
  Nike = '나이키',
  Adidas = '아디다스'
}

var myShoes = Shoes.Nike;
console.log(myShoes);   // '나이키'

Class

/* 자바스크립트 */
class Person {
  // 클래스 로직
  constructor(name, age) {
    console.log('생성되었습니다.');
    this.name = name;
    this.age = age;
  }
}

var seho = new Person('세호', 30);   //생성되었습니다.
console.log(seho);


/* 타입스크립트 */
class Person {
  private name: string;
  public age: number;
  readonly log: string;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// 리액트 예전 문법 - 클래스 기반 코드
class App extends React.Component {}

// 리액트 최신 문법 - 훅 기반의 함수형 코드
function App() {
  return <div>Hello world</div>
}
profile
🏫Chung-Ang Univ. 👩‍💻Computer Science

0개의 댓글