1.1.1 자바스크립트의 데이터 타입
undefined
: 선언됐지만 할당되지 않은 값null
: 명시적으로 비어 있음을 나타내는 값값 | 타입 | 설명 |
---|---|---|
false | Boolean | false는 대표적인 falsy한 값이다. |
0, -0, 0n, 0x0n | Number, BigInt | 0은 부호나 소수점 유뮤에 상관없이 falsy한 값이다. |
NaN | Number | Number가 아니라는 것을 뜻하는 NaN(Not a Number)은 falsy한 값이다. |
'', "", `` | String | 문자열이 falsy하기 위해서는 반드시 공백이 없는 빈 문자열이어야 한다. |
null | null | null은 falsy한 값이다. |
undefined | undefined | undefined는 falsy한 값이다. |
1.1.2 값을 저장하는 방식의 차이
객체는 값을 저장하는 게 아니라 참조를 저장하기 때문에 앞서 동일하게 선언했던 객체라 하더라도 저장하는 순간 다른 참조를 바라보기 때문에 false를 반환하게 된다.
따라서 자바스크립트 개발자는 항상 객체 간에 비교가 발생하면, 이 객체 간의 비교는 우리가 이해하는 내부의 값이 같다 하더라도 결과는 대부분 true가 아닐 수 있다는 것을 인지해야 한다.
1.2.2 함수를 정의하는 4가지 방법
1.2.3 다양한 함수 살펴보기
(function (a, b) {
return a + b
})(10, 24); // 34
((a, b) => {
return a + b
})(10, 24) // 34
1.2.4 함수를 만들 때 주의해야 할 사항
함수의 부수 효과(side-effect)란 함수 내의 작동으로 인해 함수가 아닌 함수 외부에 영향을 끼치는 것을 의미한다. 이러한 부수 효과가 없는 함수를 순수 함수라 하고, 부수 효과가 존재하는 함수를 비순수 함수라고 한다.
그렇다면 어떻게서든 항상 순수 함수로만 작성해야 할까? 그렇지 않다. 웹 애플리케이션을 만드는 과정에서 부수 효과는 어떻게 보면 피할 수 없는 요소다. 컴포넌트 내부에서 API를 호출한다면 어떨까? 외부에 어떠한 영향(HTTP request)을 끼쳤으므로 부수 효과다. console.log 또한 브라우저의 콘솔 창이라는 외부에 영향을 미쳤으므로 부수 효과다. HTML 문서의 title을 바꿨다면 이 또한 외부에 영향을 미쳤으므로 부수 효과다. 이러한 부수 효과는 웹 애플리케이션 개발에 있어 피할 수 없는 요소 중 하나다. 부수 효과를 만드는 것은 애플리케이션을 만들면서 피할 수 없는 요소지만 이러한 부수 효과를 최대한 억제할 수 있는 방향으로 함수를 설계해야 한다. 리액트의 관점에서 본다면 부수 효과를 처리하는 훅인 useEffect의 작동을 최소화하는 것이 그 일환이라 할 수 있다. useEffect의 사용은 피할 수 없지만 최소한으로 줄임으로써 함수의 역할을 좁히고, 버그를 줄이며, 컴포넌트의 안정성을 높일 수 있다.
자바스크립트 개발자들이 프로젝트를 만들 때 사용하는 ESLint에는 max-lines-per-function이라는 규칙이 있다. 여기에 있는 표현을 빌리자면, 함수당 코드의 길이가 길어질수록 코드 냄새(문제를 일으킬 여지가 있는 코드)가 날 확률이 커지고, 내부에서 무슨 일이 일어나는지 추적하기 어려워진다. 이 규칙에서는 기본값으로 50줄 이상이 넘어가면 과도하게 커진 함수로 분류하고 경고 메시지를 출력한다. 그 외에도 중첩이 얼마나 많이 있고 콜백은 얼마나 많은지도 이 규칙에서 확인이 가능하다. 이 규칙의 요점은 간단하다. 하나의 함수에서 너무나 많은 일을 하지 않게 하는 것이다. 유닉스의 선구자인 더글러스 매킬로이(Malcolm Douglas McIlroy)가 말한 것처럼, 함수는 하나의 일을, 그 하나만 잘 하면 된다(Do One Thing and Do It Well). 그것이 함수의 원래 목적인 재사용성을 높일 수 있는 방법이다.
또한 리액트에서 사용하는 useEffect나 useCallback 등의 훅에 넘겨주는 콜백 함수에 네이밍을 붙여준다면 가독성에 도움이 된다.
useEffect(function apiRequest() {
// ... do something
}, [])
1.4.2 변수의 유효 범위, 스코프
if (true) {
var global = 'global scope'
}
console.log(global) // 'global scope'
console.log(global === window.global) // true
function hello() {
var local = 'local variable'
console.log(local) // local variable
}
hello()
console.log(local) // Uncaught RefferenceError: local is not defined
1.4.4 주의할 점
1.5.2 이벤트 루프란?
이벤트 루프의 역할은 호출 스택에 실행 중인 코드가 있는지, 그리고 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인하는 역할을 한다. 호출 스택이 비었다면 태스크 큐에 대기 중인 작업이 있는지 확인하고, 이 작업을 실행 가능한 오래된 것부터 순차적으로 꺼내와서 실행하게 된다. 이 작업 또한 마찬가지로 태스크 큐가 빌 때까지 이루어진다.
그렇다면 마지막으로 궁금해지는 것은 저 비동기 함수는 누가 수행하느냐다. n초 뒤에 setTimeout을 요청하는 작업은 누가 처리할까? fetch를 기반으로 실행되는 네트워크 요청은 누가 보내고 응답을 받을 것인가? 이러한 작업들은 모두 자바스크립트 코드가 동기식으로 실행되는 메인 스레드가 아닌 태스크 큐가 할당되는 별도의 스레드에서 수행된다. 이 별도의 스레드에서 태스크 큐에 작업을 할당해 처리하는 것은 브라우저나 Node.js의 역할이다. 즉, 자바스크립트 코드 실행은 싱글 스레드에서 이루어지지만 이러한 외부 Web API 등은 모두 자바스크립트 코드 외부에서 실행되고 콜백이 태스크 큐로 들어가는 것이다. 이벤트 루프는 호출 스택이 비고, 콜백이 실행 가능한 때가 오면 이것을 꺼내서 수행하는 역할을 하는 것이다. 만약 이러한 작업들도 모두 자바스크립트 코드가 실행되는 메인 스레드에서만 이루어진다면 절대로 비동기 작업을 수행할 수 없을 것이다.
다른 언어와 마찬가지로 자바스크립트도 매년 새로운 버전과 함께 새로운 기능이 나온다. 이러한 자바스크립트 표준을 ECMAScript라고 하는데, 작성하고자 하는 자바스크립트 문법이 어느 ECMAScript 버전에서 만들어졌는지도 파악해야 한다. 왜냐하면 모든 브라우저와 자바스크립틑 런타임이 항상 새로운 자바스크립트 문법을 지원하는 것이 아니기 때문이다. 자바스크립트 개발자를 오랜 시간 괴롭혀 온 인터넷 익스플로러를 예로 들어보자. 인터넷 익스플로러 11은 ECMAScript 5(ES5)까지만 지원하기 때문에 최신 자바스크립트 문법을 사용할 수 없다. 만약 서비스하는 웹페이지가 인터넷 익스플로러 11도 지원해야 한다면 코드에서 최신 문법을 제공할 수 없다는 점을 고려해야 한다. 그리고 웹페이지에 접근하는 사용자의 브러우저와 버전은 개발자와 다르게 항상 최신 버전이 아니고, 크롬, 사파리, 파이어폭스 등 다양하기 때문에 이러한 다양한 브로우저에서의 문법 지원 또한 염두에 두어야 한다.
이러한 사용자의 다양한 브라우저 환경, 그리고 최신 문법을 작성하고 싶은 개발자의 요구를 해결하기 위해 탄생한 것이 바로 바벨이다. 바벨은 자바스크립트의 최신 문법을 다양한 브라우저에서도 일관적으로 지원할 수 있도록 코드를 트랜스파일한다. 바벨이 어떻게 최신 코드를 트랜스파일하는지, 그리고 그 결과 어떤 코드가 생성되는지 이해하면 향후 애플리케이션을 디버깅하는 데 도움이 된다.
1.6.4 Array 프로토타입의 메서드: map, filter, reduce, forEach
Array.prototype.forEach
콜백 함수를 받아 배열을 순회하면서 단순히 그 콜백 함수를 실행하기만 하는 메서드다.
forEach
는 아무런 반환값이 없다. 단순히 콜백 함수를 실행할 뿐, map
과 같이 결과를 반환하는 작업은 수행하지 않는다. 즉, 콜백 함수 내부에서 아무리 반환해도 모두 의미없는 값이 된다. forEach
의 반환값은 undefined
로 의미 없다는 것을 알아두어야 한다.
또 한 가지 주의할 점은 forEach
는 실행되는 순간 에러를 던지거나 프로세스를 종료하지 않는 이상 이를 멈출 수 없다는 것이다. break
, return
그 무엇을 이용해도 배열 순회를 멈출 수 없다.
중간에 return
이 존재해 함수 실행이 끝났음에도 불구하고 계속해서 forEach
콜백이 실행되는 것을 볼 수 있다. 이는 return
이 함수의 return
이 아닌 콜백 함수의 return
으로 간주되기 때문이다. 따라서 forEach
를 사용할 때는 절대로 중간에 순회를 멈출 수 없다는 사실을 인지하고 있어야 한다. forEach
내부의 콜백함수는 무조건 0(n)만큼 실행되므로 코드 작성과 실행 시에 반드시 최적화할 가능성이 있는지 검토해 보자.
1.6.5 삼항 조건 연산자
const value = useMemo(
() => (condition1 ? '1' : conditiom2 ? '2' : condition3 ? '3' : 'else'),
[condition1, condition2, condition3],
)
1.7.1 타입스크립트란?
1.7.2 리액트 코드를 효과적으로 작성하기 위한 타입스크립트 활용법
unknown
과 반대되는 bottom type으로 never
가 있다. 이 never
타입은 unknown
과 반대로, 어떠한 타입도 들어올 수 없음을 의미한다.never
가 사용된다.function getFirstAndLast<T>(list: T[]): [T, T] {
return [list[0], list[list.length - 1]]
}
const [first, last] = getFirstAndLast([1, 2, 3, 4, 5])
first // number
last // number
const [first, last] = getFirstAndLast(['a', 'b', 'c', 'd', 'e'])
first // string
last // string