정상의 리액트 개발자와 엔지니어들로부터의 리액트 자바스크립트 팁들.
니콜라스 에이드리언, 2020년 8월 12일
리액트는 모든 프론트엔드 개발자가 자기 공구 벨트에 가지고 있어야 하는 것이다. 새로운 개념들도 배울 필요가 있고 전통적인 MVP 앱에서 사용해왔던 몇 가지는 잊을 필요가 있다.
그것이 내가 당신에게 연락해서 지식을 공유하고 몇몇 정상의 리액트 개발자들과 엔지니어들로부터의 최고 팁들을 주려고 하는 이유이다.
1. 프로미스 안에서 this 참조를 어떻게 다뤄야 할까?
리액트로 작업하는 동안 프로미스 안에서부터 this에 어떻게 접근하는지에 대한 문제를 마주했을 것이다. 예를 들면 아래 코드에서 보여주는 것과 같이 프로미스 안에서 this는 undefined로 나온다는 것이 문제들 중 하나다:
class Component extends React.Component {
componentDidMount() {
axios.get(‘http://…’).then(function(data) {
this.setState( { name: data.blah } );
});
}
}
프로미스 안에서의 this 참조를 결정하는 데에는 하나 이상의 방법이 있다. 앞의 접근 방법은 self = this 참조를 설정하는 것이다. 이렇게 해도 되지만 추천하는 방법은 ES6에 더 비중을 두고 화살표 함수를 사용하는 것이다:
class Component extends React.Component {
componentDidMount() {
let component = this;
axios.get(‘http://…’).then(function(data) {
component.setState( { name: data.blah } );
});
}
}
화살표 문법은 위에 적힌 대로 this가 React.Component 클래스들을 참조할 수 있도록 만들어주는 훨씬 더 믿을 수 있는 방식이다. 아래에서 분석한 것처럼 말이다.
class Component extends React.Component {
componentDidMount() {
axios.get(‘http://…’).then(data => {
this.setState( { name: data.blah } );
});
}
}
function(data) { //body}를 사용하는 대신에 data => { //body }를 사용했다는 것과 이 경우 this 참조는 프로미스 인스턴스를 돌려 받지 못한다는 것을 기억하길 바란다.
2. rc-chartjs 또는 react-chartjs를 webpack-dev-server 하에 돌아가는 리액트 애플리케이션에서 사용하는 방법은?
즉시 새로고침(핫 릴로딩)이 제대로 작동하게 만든 후에, 위의 제목에서 강조된 react-chartjs 컴포넌트와 같은 제 3의 리액트 컴포넌트들 안에서 몇 가지 자바스크립트를 참조하는 문제에 봉착할 수도 있다. 특히 이 경우에는 대부분 react-chartjs와 그 곁가지인 rc-chartjs는 유사한 문제를 가지고 있다: 스크립트가 웹팩으로 묶여 있고 webpack-dev-server 에 의해 제공될 때 window 객체 참조에 접근할 수가 없다는 것이다.
이를 고치기 위해 나는 웹팩이 제공하는 import-loader 를 사용했다. 아래의 ... 환경 설정 예시를 보라.
module: {
loaders: [
{test: require.resolve(‘chart.js’), loader: ‘imports?this=>window’},
]
}
보시다시피 우리는 window 객체를 참조하는 this를 chart.js 라이브러리에 바로 투입하고 있다.
3. 리액트 애플리케이션에서 종속 투입을 사용하는 방법은?
자바스크립트 개발자로 종속 투입은 우리 마음대로 사용할 수 있는 정밀한 소프트웨어 디자인 중 하나다. 종속을 사용함으로써 우리는 주로 서비스들의 초기화와 환경설정을 고려하면서 애플리케이션 내에서 다수의 중복된 코드를 피할 수 있다.
아래의 예시에서 나는 Jimple 이라는 종속 투입 라이브러리를 사용하여 나의 서비스 레이어 코드 컴포넌트로 투입하는 것을 처리했다.
하지만 첫째로 나는 짐플 컨테이너가 각 리액트 컴포넌트에 확실히 제대로 들어가도록 했다.
먼저 컨테이너에 대해 자세히 보자.
import Jimple from ‘jimple’;
import { UsersService } from ‘./services/UsersService’;
export let container = new Jimple();
container.set(‘API_URL’, function © {
return ‘http://localhost:8080/api';
});
container.set(‘API_VERSION’, function © {
return ‘1.0.0’;
});
container.set(‘USERS_SERVICE’, function © {
return new UsersService(c.get(‘API_URL’), c.get(‘API_VERSION’));
});
다음, Router 컴포넌트에서 그 루트에 의해 구현되는 각 Component에 컨테이너를 주입해야 한다. 이를 위해 우리는 createElement로 이름 지은 함수를 정의했다. 이것은 컴포넌트 안에서 컨테이너가 하나의 속성으로 향상될 수 있게 해준다.
import { Router, IndexRedirect, Route, History } from ‘react-router’;import { container } from ‘./container’;class MoneyApp extends React.Component {
render() {
return <div>{this.props.children}</div>
}
}
// Pass container as prop to children components
function createElement(Component, props) {
return <Component container={container} {…props}/>
}
render((
<Router history={history} createElement={createElement}>
<Route path=”/” component={MyApp}>
<Route path=”/login” component={Login} />
<Route path=”/dashboard” component={Dashboard}>
<Route path=”/dashboard/overview” component={Overview} />
</Route>
<IndexRedirect to=”dashboard” />
</Route>
</Router>),
document.getElementById(‘app’));
마지막에 우리는 종속된 것들을 아래의 예시처럼 꺼낼 수 있다.
class Overview extends React.Component {
constructor() {
super();
this.state = { pending: [], lastMonths: [] }
}
componentDidMount() {
let service = this.props.container.get(‘SERVICE’);
service.yourMethod();
}
render() {
return <div><span>Some content</span></div>
}
}
4. 컴포넌트들 안에서 클래스네임과 스타일을 피하라
당신의 애플리케이션이 일관성 있는 모습과 느낌을 가지길 원한다면 className과 스타일을 임기응변식으로 돌려쓰지 않는 게 좋다. 어떤 스타일이나 className이든 다른 컴포넌트에 넘어갈 가능성을 가지고 있다는 것은 무슨 뜻이냐면:
- 당신이 원하는 대로 보일 수 있게 만들기 위해 요구되는 스타일이 무엇인지 고려해야 한다
- 시작점부터 스타일들을 알 수 있도록 기본 스타일들의 세트를 이해한다.
- 말이 안 되거나 UI를 방해할 수 있는 스타일들을 어느 것이든 넘길 수 있다
내가 무슨 얘기를 하는 것인지 몇 가지 예시를 보자.
'primary' 버튼을 구현하는 것으로 시작하자.
<button type=”button” className=”btn btn-primary”>Press me!</button>
위의 예시에서 우리는 btn-primary 클래스를 전달해야 한다는 것을 알아야 한다. 또한 우리는 make-this-look-like-a-pig 처럼 지혜롭지 못한 어떤 className 이더라도 전달할 수 있다.
그 대신 다음 코드 토막을 보라:
<Button primary>Press me!</Button>
위의 코드 패턴에서 우리는 내부적으로 최초의 접근을 사용할 수 있는 개별 설정 Button 컴포넌트를 사용하지만 primary 불리언 속성을 이렇게 노출한다.
class Button extends React.Component {
render() {
// Using https://github.com/JedWatson/classnames
var className = classNames(‘btn’, this.props.primary && ‘btn-primary’);
return
<button type=”button” className={className} onClick={this.props.onClick}>
{this.props.children}
</button>;
}
}
또 다른 예시를 살펴보자. 우리는 왼쪽에 주요한 내용을, 오른쪽에 사이드바를 구현하고 싶다.
<div style={{float: ‘left’, width: ‘80%’}}>
Main content
</div>
<div style={{float: ‘left’, width: ‘20%’}}>
Sidebar content
</div>
플렉스박스를 사용하고 싶다면 어쩌지? 여기로 와서 div로 전달하는 스타일들을 수정해야 한다. 그에 더해 말도 안 되는 스타일이나 다른 사람들이 사용하길 원하지 않는 스타일들을 포함한 다른 어떤 스타일이라도 전달할 수 있다.
이제 대신에 다음 코드를 보라:
<Main>
Main content
</Main>
<Sidebar>
Sidebar content
</Sidebar>
레이아웃 하나 씩을 다른 컴포넌트들로 감쌌다. 이것은 스타일 실행 세부사항을 숨기고 있고, 우리는 그것들만 사용하면 된다. 이 특정 코드는 사이드바가 오른쪽에 있는지 왼쪽에 있는지조차 상관하지 않는다.
성공적인 알림을 구현하는 예시 하나 더:
<div className=”notification notification-success”>
A good notification
</div>
첫번째 예시와 같은 문제다. 저 클래스들이 실행되어야 한다는 것을 알아야 한다.
<Notification type=”success”>
A good notification
</Notification>
미리 정의된 세트의 어느 값이든 React.PropTypes 를 통해 입증할 수 있는 type을 받는 개별 설정된 알림을 사용함으로써, 사용자는 어떤 클래스 규칙이든지 다시 생각할 필요가 없다.
마지막 예시. 자식들의 너비를 상정하는 Layout 컴포넌트를 정의해서 한 서식의 레이아웃을 정하는 것을 단순화할 수 있는 방법을 고려해 보자.
여기에 우리는 세 가지 입력필드들을 유사한 줄에 늘어 놓는데, 거기서 두 가지는 크기가 동일하고 세번째 것은 다른 것들의 반절 크기이며 그 줄은 컨테이너의 전체 너비를 차지한다. 그리고 나서 우리는 전체 너비를 차지하는 버튼이 있는 또다른 줄을 구현한다.
<Layout sizes={[1, 1, 0.5]}>
<Input placeholder=”First Name” />
<Input placeholder=”Last Name” />
<Input placeholder=”Age” />
</Layout>
<Layout>
<Button>Submit</Button>
</Layout>
스타일들과 classNames을 피하는 것이 어떻게 보일지 대 어떻게 놓을지 와 같은 스타일링 고민들을 어떻게 구분하도록 만들어주는지 생각해주길 바란다. 이는, 각각을 시행할 서로 다른 컴포넌트들을 가질 것을 제안한다. 마지막 예시에서 Input과 Buttons는 자기들이 어떻게 놓여질지 아무런 단서도 없다. 그것은 부모 컴포넌트의 일이다.
컴포넌트들의 속성들을 그들의 API 처럼 생각하라. classNames나 스타일들을 노출하는 것은 수 백 개의 루트들을 가지고 있을 수도 있는 'options' 객체를 노출하는 것과 같다. 당신의 컴포넌트들을 위해 이렇게 하는 걸 정말 생각하나? 그것들을 사용할 때 가능한 모든 옵션들을 생각해보기를 실제로 원하는가?
스타일들과 className은 나뭇잎 컴포넌트들에서 사용되도록 하고, 모습과 느낌을 만들 컴포넌트들의 세트와 레이아웃을 정의하는 또 다른 세트를 만드는 것이 좋을 것이다.
이것을 실행할 때 새로운 컴포넌트들, 페이지들, 앱의 뷰들을 생성하는 것이 훨씬 더 간단해질 것이다. CSS나 스타일들을 걱정할 필요조차 없이 컴포넌트들의 외양의 세트와 레이아웃을 정리하고 그것들에게 옳은 프롭들을 전달 할 필요 밖에 없기 때문이다.
추가 혜택으로 당신의 애플리케이션의 모듭과 느낌을 수정하거나 스타일들을 약간 변형시키고 싶다면 그냥 그 라이브러리 컴포넌트들만 바꾸면 된다. 또, 그것들을 위해 동일한 API를 보유하고 있다면 그것들을 사용하는 컴포넌트들은 바뀌지 않고 그대로 있을 것이다.
5. 한 가지만 하는 아주 작은 컴포넌트들을 만들어라
당신의 UI에 페이지의 특정 부분들만 담당하거나 특정 새로운 행동양식을 더하는 작은 컴포넌트들을 만들어라. 각 조각들을 개별적으로 생각할 수 있게될 것이고, 훨씬 더 세분화된 방식으로 shouldComponentUpdate와 같은 성능 최적화나 다른 라이프사이클 컴포넌트들로부터의 혜택을 받을 수 있게 될 것이다.
게다가 애플리케이션 전반적으로 그것들은 더 재사용이 가능해질 것이다. 리액트 커뮤니티 전체까지를 가리키는 것은 아니지만, 충분히 일반적이라면은.
컴포넌트를 분리해야 할지 알수 있는 몇 가지 힌트:
- render 함수를 짧고 간단하게 유지한다. render 함수를 하나의 유사한 컴포넌트 안에서 여러개의 함수로 쪼갤 수 있지만, 말이 된다면 render 부분들을 별개의 컴포넌트들로 쪼개기를 시도해본다.
- 애플리케이션의 상태 논리 및 프레젠테이션을 같은 컴포넌트 안에서 작업하고 있는가? 상태만 처리하는 컴포넌트 하나와 그 상태가 하나의 속성으로 자식 컴포넌트들로 전달되는 프레젠테이션을 다루는 하나 내지는 여러개의 컴포넌트들로 분리하는 것을 시도하라.
- render 메소드에 다른 것들 보다 훨씬 더 자주 업데이트 되어야 하는 요소들이 있는가? 그 부분을 별개의 컴포넌트로 뽑아내어 상태 업데이트들을 지역화 하거나(이게 된다면), shouldComponentUpdate를 다른 컴포넌트들에 사용하여 모든 불필요한 재구현을 방지하도록 한다.
- 다양한 섹션들과 필드들이 있는 서식이 있지만 그 모든 중간 상태들에 대해서는 정말 상관하지 않는가? 그 서식을 자기만의 컴포넌트로 뽑아내어 (또는 심지어 그 서식의 각 섹션들을 자기만의 컴포넌트들로 나누거나) 주요 값들을 속성들로 수용하고, 그 서식의 최종값과 함께 불러지는 onSubmit 콜백 속성을 가져서, 그 부모가 서식에서 중간에 변화가 있을 때마다 재구현할 필요가 없도록 한다. 내부적으로 그러한 섹션 컴포넌트들에는 제어되지 않는 컴포넌트들을 사용하거나 컴포넌트의 내부 상태를 실행한다.
- 컴포넌트들의 세트 레이아웃을 하는 데에 많은 스타일링을 하고 있는가? 그것을 환경설정 가능하지만 미리 정의된 레이아웃 컴포넌트들로 뽑아내고 컴포넌트들을 그것의 자식으로 놓는다.
- 상태를 통해 축소, 확장, 마우스올림, 또는 팝업과 같은 행동을 추가하고 있는가? 그 행동을 구현하는 함수인 하나의 자식만 받는 개별 컴포넌트로 옮겨서 패턴들을 통해 그 상태를 정한다. 이렇게:
<Hoverable>
{hovered => <div>{hovered ? “I’m hovered!” : “I’m not hovered”}</div>}
</Hoverable>
결론
당신의 현재 컴포넌트를 쪼개야 할 몇 가지 명확한 이유들이 있다. 리액트의 컴포넌트들은 함수와 같다는 것을 명심하라. 함수 하나를 여러 개로 쪼갤 수 있다. 또 그것들을 당신이 원하는 어떤 방식으로도 구성하거나 합치고도 여전히 같은 결과를 얻을 수 있다.
'웹개발' 카테고리의 다른 글
52가지 프론트엔드 면접 질문들 - 자바스크립트 (1) | 2023.08.21 |
---|---|
자바스크립트의 함수형 프로그래밍: 함수, 합성, 다듬기 (0) | 2020.08.15 |
리덕스는 무슨 일을 할까? (그리고 언제 사용해야 할까?) (0) | 2020.08.08 |
2020년에 웹 개발자가 되는 방법 - 완성 가이드 (0) | 2020.08.01 |
리액트를 사용하여 마이크로프론트엔드 개발하는 방법: 단계적인 가이드 (0) | 2020.07.29 |