읽은 책들/모던 리액트 DeepDive

[모던 리액트 Deep Dive] 1장. 리액트 개발을 위해 꼭 알아야 할 자바스크립트

today is ? 2024. 10. 30. 15:53

1.2 함수

1.2.1 함수란 무엇인가?

  • 자바스크립트에서 함수란 작업을 수행하거나 값을 계산하는 등의 과정을 표현하고, 이를 하나의 블록으로 감싸서 실행 단위로 만들어 놓은 것
function sum(a, b) {
	return a + b
}

sum(10, 24)
  • 리액트에서 컴포넌트를 만드는 함수도 기초적인 형태를 따름
    • 함수를 선언하고 매개변수로는 일반적으로 props라고 부르는 단일 객체를 받으며 return 문으로 JSX를 반환
    • Component(props) 형태로 호출하지만, 리액트에서의 함수 컴포넌트는 <Component hello={props.hello} ... />와 같이 JSX 문법으로 단일 props별로 받거나, <Component {...props} /> 같은 형태로 모든 props를 전개 연산자로 받는다는 차이가 있음

1.2.2 함수를 정의하는 4가지 방법

함수 선언문

  • 함수 선언문은 표현식이 아닌 일반 문(statement)으로 분류
  • 표현식이란 무언가 값을 산출하는 구문을 의미
const sum = function sum(a, b) {
	return a + b
}

sum(10, 24)
  • 마치 sum이라는 변수에 함수 sum을 할당하는, 표현식과 같은 작동
    • 자바스크립트 엔진이 코드의 문맥에 따라 동일한 함수를 문이 아닌 표현식으로 해석하는 경우가 있기 때문
  • 위와 같이 이름을 가진 형태의 함수 리터럴은 코드 문맥에 따라 전자와 같은 선언문으로도, 후자와 같은 표현식으로도 사용될 수 있음

함수 표현식

더보기
더보기

일급 객체 : 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체

 

자바스크립트에서 함수는 일급 객체로 함수는 다른 함수의 매개변수가 될 수도 있고, 반환값이 될 수도 있으며, 할당도 가능

  • 함수 표현식에서는 할당하려는 함수의 이름을 생략하는 것이 일반적
    • 코드를 봤을 때 혼란을 방지하기 위함
const sum = function add(a, b) {
	// 함수 몸통에서 현재 실행 중인 함수를 참조하는 데 사용할 수 있다.
    // 이는 단순히 코드에 대한 이해를 돕기 위한 예제 코드고,
    // 실제 프로덕션 코드에서는 절대로 사용해서는 안 된다.
    console.log(arguments.callee.name)
    return a + b
}

sum(10, 24)
// add
add(10, 24)
  • add는 실제 함수 내부에서만 유효한 식별자일 뿐, 함수를 외부에서 호출하는 데에는 사용할 수 없는 식별자

함수 표현식과 선언 식의 차이

호이스팅(hosting) 여부

함수의 호이스팅?

  • 함수 선언문이 마치 코드 맨 앞단에 작성된 것처럼 작동하는 자바스크립트의 특징
hello()

function hello() {
	console.log('hello')
}

hello()

 

함수를 선언한 hello는 코드 중간에 있음에도 불구하고, 맨 앞에서 호출한 hello()는 어떠한 에러도 없이, 그리고 마치 함수가 미리 만들어지기라도 한 것처럼 정상적인 hello 함수의 작동을 수행하는 것

  • 함수의 호이스팅은 함수에 대한 선언을 실행 전에 미리 메모리에 등록하는 작업을 의미
  • 함수 선언문이 미리 메모리에 등록됐고, 코드의 순서에 상관없이 정상적으로 함수를 호출할 수 있게 된 것
  • 함수 표현식은 함수를 변수에 할당
    • 변수도 마찬가지로 호이스팅이 발생
    • 함수의 호이스팅과는 다르게, 호이스팅 되는 시점에서 var 경우에는 undefined로 초기화한다는 차이가 있음
console.log(typeof hello === 'undefined') // true

hello() // Uncaught TypeError: hello is not a function

var hello() = function () {
	console.lg('hello')
}

hello()

함수 선언문과 다르게 정상적으로 호출되지 않고, undefined로 남아있는 것을 알 수 있음

함수와 다르게 변수는, 런타임 이전에 undefined로 초기화되고, 할당문이 실행되는 시점, 즉 런타임 시점에 함수가 할당되어 작동한다는 것

 

둘 중에 어떤 것이 좋을까?
  • 함수를 자유롭게 선언하고 어디서든 자유롭게 호출하고 싶거나, 변수 선언과 다르게 명시적으로 함수를 구별하고 싶을 때는 함수 선언문이 더 좋을 수 있음
    • 함수가 선언된 위치에 상관없이 함수 호이스팅의 특징을 살리면 어디서든 호출할 수 있고, 또 변수 선언과 뚜렷하게 구별되는 차이점이 있음
  • 함수가 선언되기 전에 함수가 호출되는 것이 이상하게 느껴지는 사람도 있음
    • 함수 호출은 제일 먼저 보이고, 실제 함수를 어디서 어떻게 선언했는지를 해당 스코프를 확인하지 않으면 개발자가 찾기 어려움
      • 관리해야할 스코프가 길어질 경우 더 나쁘게 작용

둘 중에 어떤 것이 더 낫다거나 기능적으로 우위에 있다고 구별지을만한 점은 없기 때문에 본인이나, 프로젝트의 상황에 맞는 작성법을 사용

 

Function 생성자 (권장되지 않음)

const add = new Function('a', 'b', 'return a + b')

add(10, 24)

메모장에서 코드를 작성하는 것만큼이나 어려운 방법이며, 코드의 양이 길어진다면 혼란스러워짐

생성자 방식으로 함수를 만들게 되면 함수의 클로저 또한 생성되지 않음

 

화살표 함수

  • ES6에서 새롭게 추가된 함수 생성 방식
  • function이라는 키워드 대신 =>라는 화살표를 활용해서 함수를 만듦
const add = (a, b) => {
	return a + b
}

const add = (a, b) => a + b
  • 화살표 함수에서는 constructor를 사용할 수 없음
  • arguments가 존재하지 않음
  • 화살표 함수와 일반 함수의 가장 큰 차이점 = this 바인딩
더보기
더보기

this는 화살표 함수 이전까지는 함수를 정의할 때 결정되는 것이 아니라, 함수가 어떻게 호출되느냐에 따라 동적으로 결정

함수가 일반 함수로서 호출된다면, 그 내부에 this는 전역 객체를 가리키게 됨

  • 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 따르게 됨
  • 클래스 컴포넌트에서 일반 함수와 화살표 함수로 state를 갱신하는 예제
class Component extends React.Component {
	constructor(props) {
    	super(props)
        this.state = {
        	counter: 1,
        }
    }

    functionCountUp() {
        console.log(this)
        this.setState((prev) => ({ counter: prev.counter + 1 }))
    }

    ArrowFunctionCountUp = () => {
        console.log(this)
        this.setState((prev) => ({ counter: prev.counter + 1 }))
    }

    render() {
        return (
    		<div>
                    <button onClick={this.functionCountUp}>일반 함수</button>
                    <button onClick={this.ArrowFunctionCountUp}>화살표 함수</button>
		</div>
		)
	}
}

functionCountUp과 ArrowFunctionCountUp는 모두 state를 하나씩 올리는 작업을 동일하게 하고 있음

그러나 일반 함수에서의 this는 undefined를 화살표 함수에서의 this는 우리가 원하는 대로 클래스의 인스턴스인 this를 가리키는 것

별도의 작업을 추가로 하지 않고 this에 접근할 수 있는 방법이 화살표 함수(이러한 차이점은 바벨에서도 확인 가능)

 

화살표 함수의 this는 선언 시점에 결정된다는 일반 함수와 대비되는 큰 차이점이 있기 때문에 this를 사용할 수밖에 없는 클래스 컴포넌트 내부에서는 주의가 필요

 

1.2.3 다양한 함수 살펴보기

즉시 실행 함수

(function (a, b) {
	return a * b
})(10, 24);

((a, b) => {
		return a + b
    },
)(10, 24)
  • 함수를 정의하고 그 순간 즉시 실행되는 함수
  • 단 한 번만 호출되고, 다시금 호출할 수 없는 함수
    • 일반적으로 이름을 붙이지 않음
  • 장점
    • 글로벌 스코프를 오염시키지 않는 독립적인 함수 스코프를 운용할 수 있음
    • 리팩터링에 도움이 됨
      • 선언돼 있으면 어디서 쓸지 모르는 일반 함수와 다르게, 선언만으로도 실행이 거기서 끝난다는 것을 각인시킬 수 있음
  • 재사용되지 않는 함수이고, 단 한번만 실행되고 끝난다면 즉시 실행 함수의 사용을 검토

고차 함수

const doubledArray = [1, 2, 3].map((item) => item * 2)

doubledArray

const add = function (a) {
	return function (b) {
    	return a + b
    }
}

add(1)(3)
  • 함수를 인수로 받거나 결과로 새로운 함수를 반환시키는 역할을 하는 함수
  • 추후에 다룰 함수 컴포넌트를 인수로 받아 새로운 함수 컴포넌트를 반환하는 고차 함수를 만들 수도 있음
    • 고차 컴포넌트
  • 고차 함수 컴포넌트를 만들면 컴포넌트 내부에서 공통으로 관리되는 로직을 분리해 관리할 수 있어 효율적으로 리팩터링할 수 있음

1.2.4 함수를 만들 때 주의해야 할 사항

함수의 부수 효과를 최대한 억제하라

더보기
더보기

함수의 부수 효과(side-effect)

함수 내의 작동으로 인해 함수가 아닌 함수 외부에 영향을 끼치는 것

 

부수 효과가 없는 함수 = 순수 함수, 부수 효과가 존재하는 함수 = 비순수 함수

순수 함수

  • 부수 효과가 없고, 언제 어디서나 어떠한 상황에서든 동일한 인수를 받으면 동일한 결과를 반환
  • 작동 와중에 외부에 어떠한 영향도 미쳐서는 안 됨
function PureComponent(props) {
	const { a, b } = props
    return <div>{a + b}</div>
}

props의 값을 기준으로 a,b를 더하고, 그 결과를 HTMLDivElement로 렌더링

=> 외부에 어떤 영향도 미치지 않고, 언제 어디서든 동일한 인수를 받아서 동일한 결과를 반환(안정적)

어떻게서든 항상 순수 함수로만 작성해야 할까?

부수 효과를 만드는 것은 애플리케이션을 만들면서 피할 수 없는 요소지만 이러한 부수 효과를 최대한 억제할 수 있는 방향으로 함수 설계

  • 리액트의 관점 : 부수 효과를 처리하는 훅인 useEffect의 작동을 최소화하는 것이 그 일환

가능한 한 함수를 작게 만들어라

더보기
더보기

ESLint: max-lines-per-function 규칙

=> 함수당 코드의 길이가 길어질수록 코드 냄새가 날 확률이 커지고, 내부에서 무슨 일이 일어나는지 추적하기 어려워진다.

코드나 프로젝트가 처한 상황에 따라 최적의 함수 크기를 단언할 수 없고 구체적인 '큰 함수의 크기'를 정의하는 것은 불필요하다 할지라도, 가능한 한 함수의 크기를 작게 하는 것이 좋음

 

누구나 이해할 수 있는 이름을 붙여라

  • 가능한 한 함수 이름은 간결하고 이해하기 쉽게 붙이는 것이 좋음
  • 리액트에서 사용하는 useEffect나 useCallback 등의 훅에 넘겨주는 콜백 함수에 네이밍을 붙여준다면 가독성에 도움이 됨
    • useEffect 코드를 유심히 살펴보지 않더라도 어떤 일을 하는지, 또 어떻게 작동하는지를 단번에 알아채는 데 도움이 됨

1.2.5 정리

  • 개발자 자신이 만든 함수가 함수답게 작동하기 위해 잘 설계돼 있는지 확인
  • 기초를 잘 다져둔다면 리액트 애플리케이션 제작뿐만 아니라 자바스크립트 생태계를 이해하는 데 많은 도움이 됨