Jello's development blog

Jello's development blog

redux-form을 이용한 form 데이터 관리

웹 서비스 개발에는 로그인이나, 회원가입등 여러가지 폼을 만들어야 하는 경우가 대부분이다. 나 또한 이를 React로 구현해야 할 위기(?)가 왔고, 하면서 많은 삽질과 검색을 하며 가장 좋은 방법을 찾으려고 노력했다.

버튼이 눌렸을 때 폼 안의 input 태그의 value를 알고싶다면 어떻게 해야할까? 순수 React를 사용한다면, jsx 문법으로 만들어진 input 엘리먼트에, onChange 속성에 함수를 만들고 넣어주어 값이 변경될 때마다 해당 component의 state를 수정해야 하는 아주 번거로운 작업을 해야 한다. Redux를 같이 사용한다고 해도, state 대신 reducer가 사용되는 것일 뿐, input 엘리먼트의 onChange 속성에 들어갈 짜증나는 함수를 작성해야하는 것은 마찬가지다. 이를 직접 구현해보면서, 차라리 html + jquery가 훨씬 빠르고 간단하고 직관적이겠다는 생각을 했다.

이런 생각이 들다 보니, 나같은 짜증을 다른 개발자도 느꼈을 것이라 생각했고, 다른 개발자들의 댓글과 stack-overflow를 찾아보니 역시나 redux-form이라는 아주 좋은 패키지가 있었다.

Setting

Docs에도 잘 나와있지만, 정리와 번역을 하기 위해서 초기 설정법을 기록해본다.

import { createStore, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';

const reducers = {
  // ... 다른 리듀서들
  form: formReducer     // <---- 'form' 리듀서에 할당
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);

해야할 일은 그저 다른 리듀서들 리스트에, 자기가 form 리듀서로 사용하고 싶은 이름으로 redux-form을 등록해주면 된다. 위의 코드에서는 form이라는 이름을 사용했다. 나머지는 redux 세팅 그대로이다. 바꿀 것이 거의 없다!

Form 만들기

이제 데이터를 입력받을 form을 만들어보자.

import React from 'react';
import { Field, reduxForm } from 'redux-form';

export default reduxForm({
    form: 'Login'  // form의 이름을 지어준다.
})(React.createClass({
    render(){
        const { handleSubmit } = this.props;
        return (
            <Form onSubmit={handleSubmit}>
                <Form.Field>
                    <Field name="id" type="text"
                    	placeholder="ID" component="input"/>
                </Form.Field>
                <Form.Field>
                    <Field name="password" type="password"
                    	placeholder="Password" component="input"/>
                </Form.Field>
                <Form.Field>
                    <button type="submit">
                        Login
                    </Button>
                </Form.Field>
            </Form>
        );
    }
}));

여기서 중요한 것은 컴포넌트를 reduxForm이라는 함수로 감싸주었다는 것이다. 이렇게 하면 컴포넌트의 props에 handleSubmit이라는 함수를 받아온다. 이 함수를 만드는 방법은 이후에 다룰 것이다. 이것을 form의 onSubmit이벤트에 넣어주게 되면, 폼이 submit되었을 때 실행되게 된다.

Field 엘리먼트의 component 속성에는 기본 태그 뿐만 아니라, 리액트 전용 부트스트랩과 같이 다른 라이브러리에서 가져온 엘리먼트를 넣어도 된다.

input 엘리먼트 하나하나에 이벤트함수를 넣지 않아도 알아서 모든 변화(submit, 값 입력, 포커싱, 블러 등)를 reducer에 반영시킨다.

Container 만들기

이제 방금 만든 form을 포함하는 container를 만들어 보자.

import React from 'react';
import LoginForm from './LoginForm';

export default React.createClass({
    loginSubmitClick(value){
        // value.id, value.password
    },
    render(){
        return (
            <LoginForm onSubmit={loginSubmitClick}/>
        );
    }
});

그냥 만든 form에 onSubmit이라는 함수 하나만 넘겨주면 끝이다. 바로 여기서 넘겨준 함수가, 위에서 만든 컴포넌트의 handleSubmit이라는 함수로 넘어가는 것이다.

redux-logger로 결과 보기

redux-logger로 폼에 입력을 했을 시 reducer가 어떻게 변화하는지 보자.

reducer 변화 모습

input의 값이 바뀔 때마다(onChange) reducer가 업데이트되는 것과, focus와 blur이벤트까지 잡아내는 것을 볼 수 있다. 아까 form이라는 이름을 지어주었던 form reducer에는 어떤 데이터가 있는지 보자.

form reducer 구조

먼저 가장 상위인 form object 안에, 현재 활성화된 input과, 한번이라도 포커싱 된 적이 있는지, 그 안의 value들은 어떠한지가 나와있다.


이렇게 여러 가지의 이벤트와 기능을 직접 구현하기에는 너무 많은 시간이 걸리고, 비효율적이지 않은가? redux-form을 쓰면, 생각보다 빨리, 생각보다 많은 정보를 reducer에 담을 수 있다. 위의 가장 기초적인 세팅조차 해야 할 것이 많다고 생각된다면, redux-form을 쓰지 않고 직접 구현해보는 것을 추천한다.