안녕하세요! 웹파트 OB 박채연입니다! 😇
나리스 2주차 아티클 주제로 순수 컴포넌트, 조건부 렌더링, 리스트 렌더링 등이 있었는데요.
이번주차 아티클에서 저는 ‘컴포넌트의 순수성’을 주제로 아티클을 작성해보고자 합니다.
컴포넌트의 순수성 ..? 이란 말을 처음 들었을 때, 대체 무슨 말인지 갈피도 잡히지 않았었는데요 ..
그래서 이번주차 아티클을 작성하면서 순수 함수는 무엇이고, 리액트는 왜 순수해야 하는 건지 공부해보고자 합니다!
그럼 바로 가볼까요 💨
순수 함수
외부 상태에 의존하지 않고, 외부 상태를 변경하지 않으며, 같은 입력에 대해 항상 같은 출력을 반환하는 함수를 말합니다.
순수함수의 조건에 대해 정리해보면, 다음과 같습니다.
1️⃣ 함수가 호출되기 전에 존재했던 어떤 객체나 변수는 변경되지 않는다.
2️⃣ 같은 입력이 주어졌다면, 같은 결과를 반환해야 한다.
function double(number) {
return 2 * number;
}
위의 함수처럼, number에 어떤 값이 들어갔을 때, 해당 값에 대한 결과는 항상 같아야 합니다.
리액트는 작성되는 모든 컴포넌트가 순수 함수일 것을 가정하고 설계되어서, 우리는 순수 함수에 대한 개념을 인지해야 하는 것이죠!
let newArray;
const handleArrayChange = () => {
newArray.push(3)
};
순수 함수는 함수 스코프 밖의 변수나 호출 전에 생성된 객체를 변경하지 않습니다.
위의 코드를 보면, 전역에서 선언한 배열 newArray가 있고, handleArrayChange 함수에서 해당 배열에 3을 push 하고 있습니다.
즉, handleArrayChanege 함수가 호출될 때마다 전역 상태가 변경된다는 뜻입니다.
이렇게 되면 생기는 문제는, newArray를 사용하는 또 다른 부분에도 의도치 않은 결과가 발생할 수 있다는 것!
const handleArrayChange = (arr) => {
return [...arr, 3];
};
const newArray = handleArrayChange([]);
배열의 값을 변경하고자 할 경우, 함수 밖의 변수를 직접 조작하기 보단,
아래처럼 새 배열을 복사하는 형태로 조작하도록 만드는 것이 리액트의 순수성을 고려한 코드라고 할 수 있겠죠?
왜 순수 함수를 사용해야 하는가?
컴포넌트를 엄격하게 순수함수로 작성하면 코드의 규모가 점점 커지더라도 예상하지 못한 동작이나 버그를 피할 수 있기 때문입니다.
순수 함수가 아닌 함수를 사용하게 되면, 함수의 결과에 따라 외부의 값이 달라지는 등의 상황이 발생하는데요. 이럴 경우 함수의 평가 시점에 따라 다른 결과가 발생할 수 있고, 우리가 알아채지 못하는 과정들이 발생하면서 의도하지 않은 동작이 생길 수 있습니다. 반면, 순수 함수를 사용해서 코드를 작성하면, 함수의 평가 시점과 관계 없이 동일한 결과를 내며, 함수에 관계 없는 것들에 영향을 끼치지 않기 때문에 함수 자체를 인자로 넘기거나 다른 곳에서 실행되는 등 여러가지 상황에서 안정성을 보장받게 된다고 합니다!
우리는 코드를 작성할 때, 컴포넌트가 특정 순서로 렌더링할 것이라고 기대하곤 하는데요! 컴포넌트가 어느 순서로 렌더링 될 지 파악하는 것은 좋지만, 어떤 순서로 렌더링 할 것을 예측하고, 그 순서에 의존해서 코드를 짜는 것은 지양해야 합니다. 컴포넌트는 렌더링 중, 다른 컴포넌트에 의존하지 않고 독립적으로 구성하기 위함이죠.
리액트는 왜 순수함을 신경쓰는가?
✅ 하나의 컴포넌트는 많은 요청을 처리해야 합니다. 같은 입력엔 늘 같은 결과가 나와야 많은 요청을 원활하게 처리가 가능합니다.
✅ 입력이 변경되지 않은 컴포넌트의 경우, 렌더링을 하지않음으로써 프로그램의 성능을 향상시킬 수 있습니다. 즉, 순수 함수는 항상 동일한 결과를 반환하므로 캐시하기에 안전하다고 합니다.
✅ 깊은 컴포넌트 트리를 렌더링하는 중, 일부 데이터가 변경되는 경우, 렌더링을 완료하는 데 시간을 낭비하지 않고 렌더링을 다시 시작할 수 있습니다.
성능 측면에서도, 사용자의 경험 측면에서도 순수성을 유지하는 것이 더 좋은 결과를 내기 때문에 순수성을 챙기는
것이죠! 또한, 순수성을 지키게 되면, 코드의 의도가 명확해지고, 코드의 의도가 명확하면 유지보수를 할 때에도 훨씬 수월하겠죠?
항상 순수하지만은 않다고?
리액트에서는 상태 관리를 예측 가능하게 하기 위해 순수 함수를 권장하지만, 현실적으로 순수하지 않은 함수가 필요한 경우가 있습니다. 순수 함수는 외부 상태에 영향을 주지 않고 항상 동일한 입력에 동일한 출력을 반환하기 때문에, 예측 가능하고 디버깅하기가 쉽습니다. 하지만, 다음과 같은 경우에는 순수 함수만으로는 애플리케이션의 요구를 충족할 수 없기 때문에 순수하지 않은 함수가 필요해집니다!
1️⃣ 외부 데이터에 의존하는 컴포넌트 (예: API 호출)
리액트 애플리케이션에서 데이터를 서버로부터 받아와야 할 때, API 호출이 필요합니다. API 호출은 서버 상태와 네트워크 상태에 따라 응답이 달라지기 때문에, 같은 입력이 주어져도 결과가 달라질 수 있습니다. 예를 들어, 네트워크가 느리거나 서버가 응답하지 않는 경우 등 외부 요인에 의해 함수의 출력이 달라지기 때문에 순수 함수로 만들 수 없습니다.
2️⃣ 브라우저 이벤트에 의존하는 컴포넌트 (예: 스크롤, 클릭 이벤트 처리)
스크롤, 키 입력, 마우스 클릭 같은 브라우저 이벤트를 처리할 때도 순수하지 않은 함수가 필요합니다. 예를 들어, 스크롤 위치를 추적하거나 윈도우 크기에 반응하는 기능을 추가하는 것은 컴포넌트가 외부 상태(브라우저의 상태)에 의존하게 만듭니다. 브라우저 상태는 사용자 상호작용에 따라 계속 변할 수 있기 때문에 순수 함수로 처리하기 어렵습니다.
순수한 상태 관리 방식은 리액트의 상태 업데이트가 예측 가능하게 만들어주어, 코드의 유지 보수성과 디버깅 효율성을 높여줍니다. 하지만 현실적으로 외부 상태에 의존해야 하는 기능이 많은 웹 애플리케이션에서는 순수하지 않은 함수를 사용할 수밖에 없는 상황이 생깁니다. 이런 경우에는 부작용을 잘 관리하는 패턴과 리액트 훅(useEffect, useReducer 등)을 사용하여 상태 변화를 예측 가능한 방식으로 처리하는 것이 중요합니다.
리액트 함수형 컴포넌트는 순수 함수로 유지하지 않아도 동작은 가능하지만,
예측 가능한 컴포넌트 함수를 만들어 유지 보수성 등 다양한 이점을 위해서는 컴포넌트를 순수 함수로 만드려고 하는 것이 좋습니다!
또한, 리액트는 순수한 상태 관리를 지향하지만, 실제 애플리케이션에서는 API 호출이나 이벤트 핸들링 같은 외부 상태에 의존하는 코드가 필요합니다. 이러한 부분을 리액트 훅과 상태 관리 라이브러리로 잘 감싸서 예측 가능하게 만드는 방식으로 구현하는 것이 중요하겠죠?
우리 모두 순수한 리액트 코드를 만들어봐요! 😇
'나야, 리액트 스터디' 카테고리의 다른 글
[week 3] state 알아보기 (4) | 2024.11.10 |
---|---|
[week2] 리액트의 트리구조 (6) | 2024.11.04 |
[week 2] - 조건부 렌더링, 리스트 렌더링, 컴포넌트를 순수하게 유지하기, 트리로서의 UI (5) | 2024.11.03 |
[week2] 순수 컴포넌트, 사이드 이펙트, 트리로서 UI (5) | 2024.11.03 |
[week2] 순수 컴포넌트 (5) | 2024.11.03 |