React(생활코딩)_21일차_ReactRedux(3)_리덕스에 종속된 기능제거

Lina Hongbi Ko·2023년 10월 17일
0

React_생활코딩

목록 보기
22/23

저번 시간에는 리덕스가 무엇인지, 왜 사용하는지에 대해 배우고, 바닐라자바스크립트와 리액트(생코님의 책 실습하기)에서 리덕스를 사용해보는 실습까지 했다.

그리고 이번시간에는 저번시간에 이어서 생코님의 책 내용을 바탕으로 리덕스에 종속된 기능을 제거하는 실습을 따라해보려 한다.

✏️ 리덕스에 종속된 기능을 제거

저번시간에 구현한 AddNumber 컴포넌트와 리덕스를 도입하기 전의 컴포넌트를 비교해 보았을때,

// AddNumber.js 파일
// 리덕스를 도입하기 전의 AddNumber 컴포넌트
... 생략 ...

class AddNumber extends Component {
	state = { size: 1}
    render() {
    	return(
        	<div>
            	<h1>Add Number</h1>
                <input type="button" value="+" onClick={function(){
                	this.props.onClick(this.state.size);
                }.bind(this)}></input>
                <input type="text" value={this.state.size} onChange={function(e){
                	this.setState({sisze:Number(e.target.value)});
                }.bind(this)}></input>
            </div>
        );
    }
}

export deafult AddNumber;

리덕스를 적용하기 전의 AddNumber 컴포넌트는 부품으로써 가치가 있다. 부품이라는 것은 현재 앱 안의 여러 군데에서도 재사용 할 수 있고, 다른 앱에서도 원한다면 다시 사용할 수 있는 컴포넌트를 말한다.

부품으로써의 가치가 있는 컴포넌트는 onClick 이라고 하는 props로 이벤트 핸들러를 제공하면 [+]버튼을 클릭했을때 이벤트를 실행하는 동작을 한다. props로 제공되는 함수의 구현에 따라 어디서든 필요하다면 재사용할 수 있는 것이다.

하지만 리덕스가 적용된 AddNumber 컴포넌트의 경우,

// AddNumber.js 파일
// 리덕스를 도입한 후의 AddNumber 컴포넌트

class AddNumber extends Component {
	state = { size: 1}
    render() {
    	return(
        	<div>
            	<h1>Add Number</h1>
                <input type="button" vale="+" onClick={function(){
                	store.dispatch({type:'INCREMEMT', size:this.state.size});
                }.bind(this)}></input>
                <input type="text" vlaue={this.state.size} onChange={function(e){
                	this.setState({size:Numbr(e.target.value)});
                }.bind(this)}></input>
            </div>
        );
    }
}

export deafult AddNumber;

보다시피 리덕스의 스토어를 필요로 한다. 즉, 어플리케이션에서 사용하는 리덕스의 상태에 의존하므로 AddNumber 컴포넌트를 다른 곳에서 사용할 수 없다. 이렇게 되면 AddNumber 컴포넌트는 재사용 가능한 컴포넌트가 아닌 상태가 된다고 한다.

그래서 이 문제를 해결하기 위한 방법으로 '컴포넌트를 래핑(wrapping)'하는 것을 알려주었다.

AddNumber 컴포넌트를 감싸는 새로운 컴포넌트를 만들고, 그 컴포넌트가 리덕스의 스토어를 담당하는 컴포넌트로 만드는 것이다. 이전에 언급했던, 어플리케이션과 종속된 컴포넌트를 말하는데, 앱과 관련된 작업을 실질적으로 처리하는 컴포넌트인 '컨테이너 컴포넌트'를 만들어 감싸주는 것이다.

그리고 AddNumber 컴포넌트는 스토어 관련 코드는 없고 단순히 props로 전달받은 데이터를 출력하거나 함수를 호출하는 일만 담당하도록 한다. 즉, '프레젠테이셔널 컴포넌트'로 만들어준다.

보통 컨테이너 컴포넌트를 만들때, 'containers'이라는 폴더를 만들어 이곳에 저장한다고 한다.

그리고

  • containers에 만들어질 컴포넌트는 반드시 하나의 컴포넌트만 래핑해야하는 것은 아니다. 여러개를 래핑할 수도 있다.

  • 리덕스 스토어만을 다루기 위한 컴포넌트가 아닌, 여러 다른 비즈니스 로직을 처리하는 컴포넌트가 올 수 있다.

  • 파일 이름은 어떤 이름으로 하든지 상관 없다.

이런 특징을 가진다.

그렇다면 이제 컨테이너 컴포넌트인 래퍼 컴포넌트를 만들어보자.

// src / containers / AddNumber.js 파일

import React, { Component } from 'react';
import AddNumber from '../components/AddNumber';

export default class extends Component {
	render(){
    	return <AddNumber></AddNumber>
    }
}

요로코롬 생성했다. AddNumber 컴포넌트를 감쌀 것이기 때문에 같은 이름으로 파일을 생성했다. 그리고 components의 AddNumber 컴포넌트를 사용하기 때문에 import 하였고, 이 컴포넌트는 익명 컴포넌트이다.

그리고나서 AddNumberRoot에 이제 AddNumber 컴포넌트를 직접 사용하지 않고, 방금 추가한 AddNumber를 감싸는 컴포넌트를 사용하도록 변경한다.

// AddNumberRoot.js 파일

improt React, { Component } from 'react';
import AddNumber from '../containers/AddNumber';

export default class AddNumberRoot extends Component {
	render() {
    	return(
        	<div>
            	<h1>Add Number Root</h1>
                <AddNumber></AddNumber>
            </div>
        );
    }
    
}

자, 이렇게 하고 결과화면을 보면 이전과 같이 작동한다.
그리고 크롬 개발자 도구를 통해 컴포넌트의 계층구조를 보면,

원래는 AddNumberRoot 아래에 AddNumber 컴포넌트가 바로 있었지만, 이제 어떤 하나의 컴포넌트가 끼어 들어간 것을 확인 할 수 있다.

이 컴포넌트에 리덕스 스토어와 관련된 작업을 넘길 것이다. 다시 말해 어플리케이션과 종속되는 작업을 이 컴포넌트가 담당하고, 기존의 AddNumber 컴포넌트는 화면에 표시하는 것에 집중하는 프레젠테이셔널 컴포넌트로써의 역할로 돌아가게 된다.

__WEBPACK_DEFAULT_EXPORT__ 라는 컴포넌트를 수정해보자.

// containers / AddNumber.js 파일

import React, { Component } from 'react';
import AddNumber from '../components/AddNumber';
import store from '../components/store';

export default class extends Component {
	render(){
    	return <AddNumber onClick={function(size){
        	store.dispatch({type:'INCREMENT', size:size});
        }.bind(this)}></AddNumber>
    }
}

AddNumber의 onClick props로 전달된 함수 내에서 리덕스 스토어에 디스패치 하도록 구현하였다. 이에 따라 AddNumber 컴포넌트도 수정해보자.

// components / AddNumber.js 파일

import React, { Component } from 'react';

export default AddNumber extends Component {
	state = {size:1}
    render() {
    	return(
    	<div>
        	<h1>Add Number</h1>
            <input type="button" value="+" onClick={function(){
            	this.props.onClick(this.state.size);
            }.bind(this)}></input>
            <input type="text" value={this.state.size} onChange={function(e){
            	this.setState({size:Number(e.target.value)});
            }.bind(this)}></input>
        </div>
    	);
    }
}

AddNumber은 props로 전달된 onClick 함수를 호출하도록 변경했다.이제 AddNumber 컴포넌트는 더는 리덕스에 종속되지 않고 그냥 화면에 뭔가를 표시하는 역할만 담당하는 프레젠테이셔널 컴포넌트가 되었다. 그리고 리덕스와 관련된 작업은 가짜 AddNumber 컨테이너 컴포넌트가 담당한다.

✏️ 컴포넌트의 재사용성을 높이기 위한 컨테이너 컴포넌트 도입

이제 DisplayNumber라는 컴포넌트를 리덕스로부터 해방시켜보자.

그럼 DisplayNumber 라는 컴포넌트는 어느 부분이 리덕스에 의존하고 있을까?

텍스트 입력상자의 value값이 컴포넌트의 state로부터 오는데, state값은 구독중인 리덕스 스토어에서 온다.

이제 이 부분을 컨테이너 컴포넌트를 만들어서 넣어준다.

// containers / DisplayNumber.js 파일

import React, { Component } from 'react';
import DisplayNumber from '../components/DisplayNumber';
import store from '../components/store';

export default class extends Component {
	state = {number : store.getState().number}
    constructor(props) {
    	super(props);
        store.subscribe(function(){
        	this.setState({number:store.getState().number});
        }.bind(this));
    }
    render(){
    	return(
        	<DisplayNumber number={this.state.number}></DisplayNumber>
        )
    }
}

이 컴포넌트도 마찬가지로 DisplayNumber 컴포넌트를 import했다. store도 import하고, 원래 DisplayNumber의 constructor 부분에 있던 구독하는 기능을 그대로 익명 컴포넌트가 가져왔다.

DisplayNumber 컴포넌트를 위한 컨테이너 컴포넌트를 만들었으면 기존의 DisplayNumber 컴포넌트를 프레젠테이셔널 컴포넌트가 되도록 수정해보자.

// components / DisplayNumber.js 파일

import React, { Component } from 'react';

export default class DisplayNumber extends Component {
	render(){
    	return(
        	<div>
              <h1>Display Number</h1>
              <input type="text" value={this.props.number}></input>
        	</div>
        );
    }
}

DisplayNumber 컴포넌트에서는 리덕스 스토어와 관련된 기능이 필요 없기 때문에 constructor에 구현된 리덕스 구독 관련 코드를 모두 삭제하였다. 그리고 state를 사용하는 것이 아니라 props로부터 주입된 number값을 사용하도록 변경했다.

DisplayNumberRoot 컴포넌트도 변경하자.

// DisplayNumberRoot.js 파일

import React, { Component } from 'react';
import DisplayNumber from '../containers/DisplayNumber';

export default class DisplayNumberRoot extends Component {
	render(){
    	return(
        	<div>
            	<h1>Display Number Root</h1>
                <DisplayNumber></DisplayNumber>
            </div>
        );
    }
}

containers / DisplayNumber 파일을 사용하도록 변경했다.
이렇게 하면 DisplayNumber 컴포넌트도 프레젠테이셔널 컴포넌트가 되었고, 이 컴포넌트를 감싸는 래퍼 컴포넌트는 컨테이너 컴포넌트의 역할을 하게 되었다.

자, 이제 리덕스의 종속성을 모두 제거 하였다.

요로코롬 시각적인 표현을 담당하는 기능과 리덕스에 종속된 기능을 모두 가지고 있던 컴포넌트들이 이제는 기능은 빼고 재사용할 수 있는 컴포넌트가 되었다.


출처
생활코딩! 리액트 프로그래밍 책

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글