React.js 상태 관리 튜토리얼 보기
게시 됨: 2022-03-11프론트엔드 웹 개발에서 가장 크고 가장 일반적인 문제 중 하나는 상태 관리입니다. 저와 같은 프리랜스 프론트엔드 개발자는 상태 객체를 뷰 및 DOM 표현과 동기화하는 데 지속적으로 집중하고 있습니다. 사용자는 다양한 방법으로 응용 프로그램과 상호 작용할 수 있으며 한 상태에서 다른 상태로의 명확한 전환을 제공하는 것은 큰 작업입니다.
약간의 역사
얼마 전까지만 해도 웹 애플리케이션은 훨씬 단순한 데이터 흐름을 가지고 있었습니다. 브라우저는 서버에 요청을 보내고, 모든 애플리케이션 로직은 서버에서 실행되며, 전체 HTML 보기는 사용자에게 표시하기 위해 브라우저로 다시 보내집니다. 후속 사용자 작업(예: 클릭, 양식 제출 등)은 동일한 흐름을 다시 트리거합니다. 응용 프로그램은 사용자 상태에 대해 걱정할 필요가 없었고 서버에 새 요청을 보내 각 보기를 다시 생성할 수 있었습니다.
그러나 웹 애플리케이션의 복잡성이 증가하고 UI/UX에 대한 사용자 요구도 함께 향상되었습니다. 페이지의 한 부분만 변경될 때 전체 페이지를 다시 로드하는 것은 비효율적이고 느렸습니다. 우리는 UI에 즉각적인 영향을 주는 빠르고 빠르고 반응이 빠른 상호 작용이 필요했습니다.
JavaScript가 구출되었습니다. 개발자는 요청이 서버로 전송되기 전에 브라우저에서 실행되는 상당한 양의 코드를 작성하기 시작했습니다. jQuery는 클라이언트 측 유효성 검사, 모달 창, 경고 메시지, 애니메이션 및 Ajax 기반 부분 페이지 업데이트와 같은 간단하고 효과적인 즉시 사용 가능한 기능을 제공했기 때문에 프런트 엔드 웹 개발에도 상당한 발전을 가져왔습니다.
복잡성 이해
암호의 강도를 평가하는 간단한 예를 살펴보겠습니다. 암호가 정상이면 입력 상자에 녹색 테두리가 있어야 하고 멋진 메시지가 표시되어야 합니다. 암호가 약한 경우 입력 상자에 빨간색 테두리가 있어야 하고 경고 메시지가 표시되어야 합니다. 비밀번호가 충분히 강력하면 웃는 얼굴을 보일 수도 있습니다.
다음 코드는 DOM 조작으로 이를 수행하는 방법을 보여줍니다. 여기에는 "if"가 많이 있으며 코드는 읽기가 쉽지 않습니다.
if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }위와 같이 먼저 사용자가 암호를 제공했는지 확인하고 암호 필드가 비어 있는 경우를 처리해야 합니다. 그리고 모든 경우에 관련된 모든 DOM 요소가 제대로 업데이트되었는지 확인해야 합니다. 여기에는 메시지, 테두리 및 웃는 얼굴이 포함됩니다.
비밀번호 필드는 비어 있음, 약함 또는 강함의 세 가지 상태 중 하나일 수 있습니다. 그리고 언급한 바와 같이 비밀번호 필드 상태의 영향을 받는 세 가지 다른 DOM 요소가 있습니다. 모든 조합을 처리하고 보기가 제대로 표시되는지 확인하면 이와 같은 간단한 코드에 대해서도 순환적 복잡성이 증가합니다.
DOM은 유지 모드 에서 작동합니다. 즉, 현재 상태만 기억합니다. 보기를 수정하려면 각 DOM 요소에 대한 지침을 제공하고 전환을 프로그래밍해야 합니다.
상태 대신 전환을 코딩하는 것은 복잡할 수 있습니다. 코드에서 수행해야 하는 분기 및 검사의 수는 관리할 보기 상태의 수와 함께 기하급수적으로 증가합니다.
이 예에서는 3 * 2 = 6 전환을 제공하는 세 가지 보기 상태를 정의했습니다. 일반적으로 N 상태가 주어지면 모델링해야 하는 N * (N - 1) = N^2 - N 전환이 있습니다. 예제에 네 번째 상태를 추가하면 복잡성이 증가하는 것을 생각해 보십시오.
일반적으로 전환 모델링과 관련된 코드가 너무 많습니다. 뷰 상태를 정의하고 한 상태에서 다른 상태로 전환하는 모든 세부 사항에 대해 걱정하지 않을 수 있다면 훨씬 더 좋을 것입니다.
복잡성 감소
한 상태에서 다른 상태로의 전환을 명시적으로 코딩하는 대신 모델 상태를 기반으로 뷰 상태를 선언 할 수 있다고 가정하면 다음과 같이 될 수 있습니다.
var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }여기에는 앱의 세 가지 가능한 상태를 나타내는 세 가지 간단한 코드 분기가 있습니다. 모델 상태에 따라 각 분기의 보기 사양을 반환하기만 하면 됩니다. 모든 DOM 조작 코드가 제거됩니다. 우리는 단지 우리가 원하는 것에 대한 정보를 제공할 뿐 거기에 도달 하는 방법 은 제공하지 않습니다.
이 접근 방식은 코드 복잡성을 크게 줄이지만 우리를 대신하여 실제 DOM 조작을 처리할 누군가 또는 다른 사람이 있다고 가정합니다.
이것이 React가 들어오는 곳입니다. React는 기본 데이터 모델의 상태에 따라 뷰 상태가 즉시 관리되고 업데이트되도록 합니다.
반응
React는 Facebook에서 만든 JavaScript 라이브러리입니다. 웹 애플리케이션의 UI 부분을 처리하도록 설계되었습니다. MVC 아키텍처의 V라고 생각할 수 있습니다. 매우 집중되어 있습니다. 나머지 기술 스택에 대해 가정하지 않으며 구성 요소를 렌더링하는 것 외에는 아무 것도 처리하지 않습니다. 일반적으로 더 큰 프레임워크에 번들로 제공되는 라우팅 메커니즘, 모델 또는 기타 기능을 제공하지 않습니다. 따라서 원하는 다른 라이브러리나 프레임워크와 혼합하여 사용할 수 있습니다.
React를 사용하면 UI를 복합 구성 요소의 트리로 정의할 수 있습니다. React 개발자는 주어진 입력 상태에서 구성 요소를 설명하는 렌더링 기능 을 지정하여 해당 구성 요소를 정의합니다. 그 함수는 순수 해야 합니다(즉, 부작용이 있거나 명시적 입력 이외의 다른 것에 의존해서는 안 됩니다).
render 함수는 뷰 설명을 반환하는데, React는 이를 Virtual DOM 이라고 부릅니다. 렌더링된 DOM 요소에 해당하는 JavaScript 객체로 생각하십시오.
구성 요소의 상태를 변경하면 구성 요소 자체와 모든 하위 요소를 다시 렌더링하여 새 Virtual DOM 을 반환합니다.
또한 React는 한 상태에서 다른 상태로 전환할 때 간단한 HTML 교체를 수행하지 않습니다. 이전 상태와 새 상태 간의 차이점을 찾고 전환을 실행하는 데 가장 효과적인 DOM 작업 집합을 계산합니다.
성능을 염두에 두지 않더라도 코드 복잡성의 감소는 그 자체로 중요하므로 애플리케이션의 더 독특하고 복잡한 부분에 노력을 집중할 수 있습니다.
조금 더 구체적으로 보기 상태를 관리하기 위해 React를 사용하여 튜토리얼 예제를 만드는 방법입니다.
참고: 다음 코드 샘플은 React 기반 UI를 작성하는 일반적인 방법인 JSX 전처리기로 작성되었습니다.
function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body); <Emoji value="smiley" /> 와 함께 암호 강도가 정상일 때 렌더링되는 Emoji 구성 요소는 또 다른 사용자 지정 구성 요소일 뿐입니다( PasswordWithStrength 처럼). 다음과 같이 정의됩니다.

var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });React.js 대 기타
그러나 공정하게 말하면 보기 상태 관리 문제를 해결하고 더 많은 기능을 추가한 다른 클라이언트 측 JavaScript 프레임워크(예: Ember, Angular, Knockout 및 기타)가 있습니다. 그렇다면 다른 프레임워크 대신 React를 사용하려는 이유는 무엇입니까?
다른 대부분의 라이브러리와 비교하여 React에는 두 가지 주요 이점이 있습니다.
데이터 바인딩 없음
다른 대체 프레임워크 중 일부는 데이터 바인딩을 사용하여 DOM 요소를 상태 속성에 매핑하고 속성 변경을 관찰하여 동기화를 유지합니다. 이 접근 방식을 사용하면 뷰를 한 번 렌더링할 수 있으며 각 변경 사항은 영향을 받는 DOM 요소의 수정만 트리거합니다. 다른 대안은 더티 검사 를 사용합니다. 즉, 개별 속성 변경을 관찰하는 대신 이전 상태와 새 상태 간의 차이점만 수행합니다. React는 후자의 접근 방식과 더 유사하지만 상태를 비교하는 대신 뷰 표현을 비교합니다.
React에는 데이터 바인딩이 없습니다. 개발자는 상태가 변경될 때 setState 메서드를 호출하거나 최상위 구성 요소를 다시 렌더링해야 합니다. 상태에서 뷰로의 단방향 흐름을 수용합니다.
이 개념은 개발자가 일반적으로 데이터 바인딩에 대해 생각하지 않기 때문에 채택하기 쉽습니다. 데이터의 시각적 표현에 중점을 둡니다. 따라서 종속 속성, 형식 지정, 특수 HTML 태그 바인딩 등에 대해 생각할 필요가 없습니다. React를 사용하면 모델이 변경될 때 구성 요소를 다시 렌더링하기만 하면 됩니다.
여기에서 보기 상태 관리의 차이점을 이해하기 위해 Ember와 React를 비교하겠습니다 . 우리는 전체 이름을 대문자로 출력할 개체 person 을 만들 것입니다. 2초 후에 변경 사항을 시뮬레이션하고 보기를 업데이트합니다.
// EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script> firstName , lastName 및 fullName 속성을 사용하여 개체를 만들었습니다. Ember는 속성 변경 사항을 관찰하고 있으므로 fullName 이 firstName 및 lastName 에 종속되도록 지정해야 했습니다. 이를 위해 fullName 을 정의할 때 .property('firstName', 'lastName') 를 추가했습니다.
2초 후, person.set('firstName', 'Harry'); 실행됩니다. 이로 인해 뷰와 바인딩이 업데이트되었습니다.
이제 React에서도 동일한 작업을 수행해 보겠습니다.
// EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000); Ember 코드는 간단하고 읽기 쉽지만 단순함에서 React가 이기는 것은 분명합니다. person 은 fullName 이 단순히 함수인 일반 JavaScript 객체입니다.
템플릿 없음
각 대체 프레임워크에는 템플릿을 처리하는 다른 방법이 있습니다. 일부는 JavaScript로 컴파일된 문자열을 사용하고 다른 일부는 DOM 요소를 직접 사용합니다. 대부분은 사용자 정의 HTML 속성과 태그를 사용하여 HTML로 "컴파일"됩니다.
템플릿은 JavaScript 코드의 일부가 아닙니다. 이 때문에 각 대안에는 공통 작업, 조건, 반복, 호출 함수 등을 나타내는 사용자 지정 방법이 필요 합니다. 결국 개발자가 배워야 하는 새로운 의사 언어를 생성하게 됩니다.
React에는 템플릿이 없으며 모든 것이 평범한 오래된 JavaScript입니다.
React는 보기를 생성하기 위해 JavaScript의 모든 기능을 사용합니다. 구성 요소의 렌더링 메서드는 JavaScript 함수입니다.
JSX는 "HTML과 유사한 구문"을 일반 JavaScript로 변환하는 전처리기로 사용할 수 있지만 JSX는 선택 사항이며 전처리기 없이 표준 JavaScript를 자유롭게 사용할 수 있습니다. 기존 JavaScript 도구를 활용할 수도 있습니다. 린터, 전처리기, 유형 주석, 축소, 데드 코드 제거 등
다시 구체적인 예를 사용하여 보기 상태 관리를 위한 대체 프레임워크 중 하나와 React를 비교하겠습니다.
다음 튜토리얼은 AngularJS 를 사용하여 해시태그와 각각의 트윗 수를 나열하는 예입니다. 목록은 개수별로 정렬되며 해시태그가 없으면 메시지가 표시됩니다.
<!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div> 이 목록을 만들 수 있으려면 개발자는 AngularJS directives , ng-show 및 ng-repeat 에 대해 배워야 합니다. 그런 다음 그는 orderBy 를 이해하기 위해 AngularJS filters 에 대해 배워야 합니다. 목록 출력과 같은 간단한 작업을 위해 많은 작업이 필요합니다!
이제 동일한 작업을 수행하는 React 예제를 살펴보겠습니다.
// EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } } "고급" 접근 방식과 JSX를 사용하더라도 JavaScript에 대한 기본적인 이해가 있는 모든 웹 개발자는 위의 코드를 쉽게 읽고 그 기능을 이해할 수 있습니다. if 를 사용하는 표준 조건부 검사, map() 을 사용하는 반복 및 표준 'sort()'는 모든 개발자에게 자연스럽게 제공되므로 배울 추가 구문이나 기타 개념이 없습니다.
결론
이 React.js 튜토리얼의 주요 내용은 React를 사용하면 전환이 아닌 실제 보기 상태 관리에 집중할 수 있으므로 작업과 애플리케이션이 단순화된다는 사실입니다.
React 채택을 위한 학습 곡선은 상당히 간단합니다. 마스터할 사용자 지정 템플릿 언어가 없고 걱정할 데이터 바인딩이 없으며 모든 것이 UI 요소를 설명하는 JavaScript 함수로 귀결됩니다.
React를 사용하여 애플리케이션 코드를 단순화하는 방법에 대해 자세히 알아보려면 Steven Luscher의 이 강연, Decomplexifying Code with React를 참조하십시오.
다음은 다음 단계로 나아가 React 사용을 시작하려는 사람을 위한 몇 가지 추가 정보입니다.
- http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome
