목차
상태가 없는(Stateless) 컴포넌트
컴포넌트는 상태(state) 보유 여부에 따라 구분할 수 있다. 상태가 없다면 상태 없는 컴포넌트(Stateless Component) 또는 프레젠테이셔널 컴포넌트(Presentational Component)로 구분할 수 있고, 상태가 있다면 상태 있는 컴포넌트(Stateful Component) 또는 컨테이너 컴포넌트(Container Component)로 구분할 수 있다.
상태 없는(Stateless) 컴포넌트는 시각적 표현에 집중하는 컴포넌트로 스타일 정보만 포함하여 UI 화면을 렌더링하는 역할을 담당한다. 이 컴포넌트는 복잡한 비즈니스 로직과 상태(state)를 가질 필요가 없다. 오직 외부에서 전달받은 데이터를 기반으로 화면을 그리고, 필요한 데이터는 속성(props)으로 전달받으며, 이를 바탕으로 리액트 엘리먼트를 생성한다.
상태가 있는(Stateful) 컴포넌트
상태를 가지는 컴포넌트는 스테이트풀(stateful) 또는 컨테이너(container) 컴포넌트라고 부른다. React가 제공하는 컴포넌트 유형 중 클래스 컴포넌트는 Stateful 컴포넌트로 사용된다. Stateful 컴포넌트는 비즈니스 로직을 가지며, 화면을 그리는 최소한의 스타일 정보만 가진다.
상태(state)는 시간이 지나면서 바뀔 수 있는 데이터다. 사용자가 입력하거나 버튼을 클릭하면 바뀌는 값들이 상태에 해당된다. 상태가 바뀌면 리액트는 컴포넌트를 다시 렌더링해서 화면을 최신 상태로 바꿔준다.
상태 종류
React의 주요 작업은 애플리케이션 상태를 가져와 DOM 노드로 전환하는 것이다. 애플리케이션에서 다루는 상태 또는 데이터는 다음과 같다.
상태 유형 (영문) | 설명 | 예시 변수명 | 예시 값 / 설명 |
Model 상태 | 핵심 데이터, 비즈니스 모델 | list | 도서 목록, 사용자 목록 등 |
View / UI 상태 | UI 표현 / 제어 상태 | order | 오름차순 / 내림차순, 탭 선택 등 |
Session 상태 | 사용자 세션, 인증 정보 | isSigned | 로그인 / 로그아웃, 사용자 ID 등 |
Communication 상태 | 서버와의 통신 / 요청 상태 | loading | 로딩 중, 에러, 성공 등 |
Form 상태 | 입력 폼의 값 및 유효성 | form, errors | 입력 값, 유효성 메시지 |
Routing 상태 | 현재 화면 / 경로 정보 | route | /home, /profile 등 |
Feature Flag 상태 | 기능 활성화 여부 | isFeatureOn | true / false(실험적 기능 등) |
Notification 상태 | 알림, 메시지 | notification | "저장되었습니다", "에러 발생" 등 |
Theme 상태 | 테마 / 모드 | theme | "light" / "dark", color scheme 등 |
Device 상태 | 디바이스 / 브라우저 정보 | isMobile | true / false(반응형 UI 등) |
클래스 vs. 함수 컴포넌트
리액트에서 UI를 만드는 주요 방법인 클래스 컴포넌트와 함수 컴포넌트는 데이터를 다루는 방식에 차이가 있다. 클래스 컴포넌트는 this를 사용해 속성(props)과 상태(state)를 관리하며, 상태가 변경되면 render 메서드가 재실행된다. 반면, 함수 컴포넌트는 렌더링될 때마다 전체 함수가 다시 실행되기 때문에 이전 값을 기억할 수 없다.
컴포넌트가 외부로부터 전달 받은 속성(props)은 읽기 전용(readonly)이라 값을 바꿀 수 없다. 사용자 상호작용에 따라 UI가 변경되어야 할 때는 컴포넌트가 직접 관리하는 상태(state)를 사용해야 한다.
클래스 컴포넌트의 상태(State) 관리
1. 상태 선언
클래스 컴포넌트의 상태는 this.state 객체를 통해 관리된다.
- 생성자(Constructor): 생성자 메서드 안에서 this.state = {} 형식으로 상태를 초기화한다.
- 클래스 필드(Class Field): 생성자 없이 state = {} 형식으로 더 간결하게 상태를 선언할 수 있다.
class App extends React.Component {
// Class Field 방식으로 상태 선언
state = {
name: '박재인',
age: 22
}
}
2. 상태 업데이트
클래스 컴포넌트의 상태는 직접 변경할 수 없고, 반드시 리액트가 제공하는 this.setState() 메서드를 사용해야 한다.
// 올바른 상태 업데이트
this.setState({ age: this.state.age + 1 });
setState()는 비동기로 동작한다. 여러 번의 상태 업데이트 요청을 하나로 묶어 불필요한 렌더링을 줄여 성능을 최적화하기 위해서다. 업데이트 후 특정 동작이 필요하다면 setState()의 두 번째 인수로 콜백 함수를 전달할 수 있다.
상태를 불변(Immutable)으로 관리하는 이유
리액트는 상태를 불변(Immutable)으로 관리하는 것을 중요하게 생각한다. 불변성이란 객체가 생성된 이후 그 상태를 변경할 수 없는 디자인 패턴을 의미한다.
- 예측 가능성: 상태가 직접 변경되지 않기 때문에 예기치 않은 버그나 부수 효과(side effects)를 줄일 수 있다.
- 성능 최적화: 리액트는 이전 상태와 현재 상태를 비교해 변경된 부분만 효율적으로 다시 렌더링한다. 불변 상태는 이러한 변경 추적을 쉽게 만들어 성능을 향상시킨다.
- 메모리 절약: 모든 값을 복사하는 대신, 변경된 부분만 새로 만들어 기존 데이터는 그대로 재사용해 메모리를 절약할 수 있다.
리액트 훅의 등장과 변화
리액트 훅은 함수형 컴포넌트에서도 상태(state) 관리와 같은 기능을 사용할 수 있도록 만든 특별한 함수다. 훅은 기존의 복잡한 로직 재사용 방식을 단순화하고, 함수형 컴포넌트의 한계를 극복하기 위해 탄생했다.
이전에는 클래스 컴포넌트에서 로직을 재사용하려면 Render Props나 HOC(Higher-Order Components) 같은 복잡한 패턴을 사용해야 했다. 반면, 함수는 코드를 재사용하기에 좋지만, 자체적으로 상태를 가질 수 없다는 한계가 있었다.
훅은 이 문제를 해결하면서 다음과 같은 이점을 제공한다.
- 로직 재사용의 단순화: 훅은 함수형 컴포넌트 안에서 상태 저장 로직을 공유하는 간단한 방법을 제공한다.
- 캡슐화: 각 훅은 자신만의 고립된 로컬 상태를 가진다. 이는 여러 컴포넌트에서 동일한 훅을 사용해도 서로의 상태에 영향을 주지 않아 안전하다.
- 간결한 컴포넌트 트리: 훅은 컴포넌트 트리에 불필요한 레이어를 추가하지 않아 코드를 더 깔끔하게 유지할 수 있다.
결론적으로, 훅은 컴포넌트의 복잡성을 줄이고, 로직을 더 효율적으로 재사용할 수 있게 해주면서 리액트 개발 방식을 혁신적으로 바꿨다. 훅의 상태는 클래스 컴포넌트의 상태와 마찬가지로 리액트가 내부적으로 관리한다.
오늘 하루를 돌아보며
상태가 없는 컴포넌트와 있는 컴포넌트를 잘 구분해야 한다는 생각이 들었다. 상태 없이 속성만으로 관리할 수 있는 컴포넌트인지, 상태가 필요한 컴포넌트인지 확실히 구분해야 한다. 그러면 불필요한 상태를 추가하지 않아도 되기 때문이다. UI가 변한다고 상태가 있는 컴포넌트로 착각할 수도 있지만, 단순히 속성의 변화만 필요한 것일 수 있다. 많이 사용하고 많이 연습하면서 익숙해지자!
'부트캠프' 카테고리의 다른 글
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 72 (리액트 빌드 환경 구성, 리액트 프로젝트 템플릿 구성) (0) | 2025.08.18 |
---|---|
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 71 (리액트 JSX 구문 확장, 선언적 vs. 명령형 프로그래밍, 바닐라 리액트의 한계) (0) | 2025.08.13 |
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 70 (리액트 라이브러리 API, 리액트 컴포넌트, 컴포넌트 렌더링) (0) | 2025.08.12 |
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 69 (안녕! 리액트) (0) | 2025.08.11 |
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 57-68 (Vanilla 프로젝트 회고) (0) | 2025.08.11 |