리액트도 관심사를 분리하지만, 관심사를 컴포넌트 기반으로 나눈다. 같은 기능을 하는 컴포넌트에 대해 HTML과 JavaScript가 함께 작성된다.
컴포넌트 기반 관심사 분리, 단방향 바인딩, 선언적 UI 변경 등 리액트 생태계가 혁신적으로 성장할 수 있던 몇 가지 주요 키워드들이 있다. 앞으로 공부하면서 하나씩 깊게 잘 알아가봐야겠다.
Props, 리액트 훅에서 사용되는 의존성 배열, Virtual DOM의 변경 여부 등 비교 연산이 필요한 경우가 많이 있다. 그래서, 리액트가 비교 연산을 어떤 방식으로 처리하는지를 아는 것이 중요하다.
리액트에서의 비교
Object.is를 사용하며, 비교 연산자 ==나 ===를 사용하지 않는다.
참조형 데이터를 비교할 때 얕은 비교를 사용한다. 이는 JSX에서 props는 객체이고, props 객체에 있는 값만 일차적으로 비교하면 되기 때문이다.
Props가 깊어지는 경우에는 비교가 제대로 이뤄지지 않아 컴포넌트 리렌더링 동작이 의도하지 않은 방식으로 될 수 있다. 주의하자.
애플리케이션을 구성하는 함수가 100% 순수 함수이도록 하는 건 사실상 불가능하다. 함수 내에서 request 요청을 보내는 것, console을 찍는 것 등도 넓게 보면 부수 효과(Side Effect)일 수 있다.
하지만, 많은 경우에서 함수는 순수 함수로 작성되도록 최대한 노력해야 한다. 순수 함수는 같은 입력(input)에 대해 언제나 같은 출력(output)이 발생해야 하며, 함수의 동작으로 외부 값이 변경되지 않아야 한다.
자바스크립트의 함수는 일급 객체이기 때문에 함수의 매개변수로 전달할 수 있고, 반환 값으로 가질 수 있다. 이러한 특징을 사용하면 고차 함수(Higher Order Function)를 생성할 수 있다. 배열 메서드(map, forEach, reduce 등)는 매개변수로 콜백 함수를 전달하는 고차 함수이고, 클로저를 생성하기 위해 함수를 리턴하는 함수도 고차 함수이다. 고차 함수는 다양한 방식으로 활용될 수 있다.
리액트 16.8 버전 이전까지는 함수형 컴포넌트가 없었고, 클래스형 컴포넌트만 존재했다. 과거에 작성된 코드를 읽거나 리팩토링 행햐나느 경우가 존재하기 때문에, 클래스에 대한 정확한 이해와 활용은 중요하다.
자바스크립트의 클래스는 프로토타입 체인을 통한 상속을 활용하며, 생성자 함수로 클래스의 모든 기능이 대체가 가능하다. 클래스는 생성자 함수의 활용을 더 수월하게 하기 위해 추가적인 몇 가지 기능을 선별적으로 추가한 문법적 설탕(Syntactic Sugar)이다.
리액트의 함수형 컴포넌트 구조와 작동 방식, 훅의 원리, 의존성 배열 활용 등에 있어서 클로저는 핵심적인 개념이다. 클로저를 사용해 변수의 생명 주기를 늘리면서, 동시에 변수에 쉽게 접근해 값을 수정하지 못하도록 방어할 수 있다.
useState는 setState 함수를 반환하고, setState를 통해 useState 함수 실행의 결과로 반환된 state를 변경하면서 리렌더링 작업 수행을 반복적으로 할 수 있다(useState의 실행 컨텍스트가 끝난 다음에도 접근이 가능핟). 이는 클로저를 사용해 구현된 방식이다.
이벤트 루프에는 태스크 큐와 마이크로 태스크 큐가 있다. 태스크 큐에는 setTimeout, setInterval 등의 콜백 함수가 추가되고, 마이크로 태스크 큐에는 Promise 등이 추가된다.
실행 순서는 콜 스택, 마이크로 태스크 큐, 태스크 큐 순이며, Promise로 처리한 비동기 작업은 setTimeout이나 setInterval보다 먼저 실행된다.
객체 구조 분해 할당과 객체 전개 구문은 Babel로 트랜스파일링 할 때 코드의 양이 많아진다(Babel은 ES5 이전까지의 문법만 지원하는 곳에서 ES6 이후의 문법이 동작 가능하도록 변환해주는 역할을 한다. 객체 구조 분해 할당과 객체 전개 구문은 둘 다 ES6 이후에 추가된 문법이다). 따라서, 객체 구조 분해 할동과 객체 전개 구문은 꼭 필요한 상황에서 사용하는 게 유리하다.
타입스크립트는 선택이 아니라 필수다. 아직 자바스크립트랑 리액트도 익숙치 않아 미뤄두긴 했지만, 조만간 공부해봐야겠다.
감정 일기장 만들어보기
React Router를 이용한 페이지 기본 라우팅
npm install react-router-dom 실행해서 react-router-dom 설치
Main.js에 App.js를 <BrowserRouter> 컴포넌트로 감싸주기
App.js에 react-router-dom의 Routes와 Route 컴포넌트 불러오기
return 문에 <Route> 컴포넌트들을 <Routes> 컴포넌트로 감싸서 선언
<Route> 컴포넌트의 Props로 path와 element 설정
Date 객체 다루기
new Date().getTime()으로 실행하면 타임스탬프를 찍을 수 있다.
new Date().getTime()으로 얻은 타임 스탬프를 new Date()의 인자로 전달해주면 타임 스탬프에 해당하는 날짜를 확인할 수 있다.
new Date에 차례대로 7개의 인자를 넘기면 연, 월, 일, 시, 분, 초, ms의 값을 임의로 지정할 수 있다. 이때, 월은 1월이 0이다(배열의 인덱스 개념이 적용된다).
초, 분, 시, 일, 월이 각각 단위를 가득 채우면 자동으로 다음 단위의 값이 조정된다. 시-분-초는 60진법, 월은 12진법, 일은 30-31진법이 사용되는 셈이다.
만약에 특정 달의 말일을 선택하고 싶으면 월을 그 담음 달로 지정하고 세 번째 인자(일을 나타냄)에 0을 넣으면 초일 하루 전날이 선택됨. 그 상태에서 23시 59분 59초를 선택하면 전날 날짜가 바뀌기 직전 시점을 선택할 수 있다.
리팩토링 하고 싶은 부분
오늘 많은 개발을 하지 않았는데도 고치고 싶은 것들 투성이었다.
먼저, Date 객체로 쓰는 값을 어떤 곳에선 getTime으로 해서 타임스탬프로 받고, 어디는 날짜로 받고 있다. 좀 더 코드가 간결해질 수 있는 하나의 방향으로 통일하자.
함수 이름을 짓는 방법을 잘 모르겠다. 이벤트 핸들러 함수 앞에는 보통 on을 붙이는 것 같긴 한데, 그 뒤에 오는 이름이 '이벤트 중심'에 관심이 있는지, 아니면 '이벤트 이후에 오는 액션'에 관심있는지에 따라서 onClick이 될 수도 있고, onToggleItem이 될 수도 있다. on을 안 붙일수도 있고. 아무튼, 이 부분에 대해 현업에서는 어떤 식으로 하는지 좀 찾아보고 나서 개선해봐야겠다.
컴포넌트를 쪼갠 방식이 마음에 안든다. 예를 들어, contents 영역에 menu와 lists가 있다면 둘을 다른 컴포넌트로 나누는 게 좋지 않나 싶은데(꼭 그러라는 법은 없고, 내 상황에서), 그렇게 설계가 안돼있어서, 이것도 바꿔봐야겠다.
Props로 들어오는 값들 중에 값이 없어서 undefined가 내려오는 값들이 분명 생길 수밖에 없다. 그런 값들에 대한 처리를 꼼꼼하게 잘 해주자.