다음과 같이 요소에 id 프로퍼티를 달 수 있다.
<div id="root"></div>
이를 통해 css를 적용하거나 DOM 요소에 작업을 할 수 있다. 이렇게 HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다. ref가 그 방법이다.
리액트 컴포넌트 안에서 id를 사용하면 안되는가?
사용은 가능하다. 실제로 DOM에서 컴포넌트를 랜더링할 때 id가 그대로 전달이 된다. 하지만 컴포넌트를 여러 번 사용하는 경우를 생각 해보자. HTML에서 id는 유일해야하지만, 이 경우에는 DOM의 id가 중복되므로 잘못된 사용이 된다.
반면 ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 위와 같은 문제가 발생하지 않는다.
5.1 ref는 어떤 상황에서 사용해야 할까?
어떤 작업을 할 때 ref를 사용할까? 답은 DOM을 꼭 직접적으로 건드려야 할 때이다.
- 특정 input에 포커스 주기
- 스크롤 박스 조작하기
- Canvas 요소에 그름 그리기 등
5.2 ref 사용
ref를 사용하는 방법에는 콜백 함수를 통한 방법, crerateRef를 통한 방법이 있다.
5.2.1 콜백 함수를 통한 ref 설정
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것이다. ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 주면 된다. 이 콜백 함수는 ref 값을 파라미터로 전달받는다. 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해준다.
<input ref={(ref)=> {this.input=ref}} />
이렇게 하면 앞으로 this.input은 input요소의 DOM을 가리킵니다. ref의 이름은 원하는 것으로 자유롭게 지정할 수 있다. DOM 타입과 관계없이 this.superman = ref처럼 마음대로 지정한다.
5.2.2 createRef를 통한 ref 설정
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것이다. 이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있다.
class RefSample extends Component {
input = React.createRRef();
handleFocus = () => {
this.input.currrent.focus();
}
render() {
return(
<div>
<input ref={this.input}/>
</div>
)
}
}
createRef를 사용하여 ref를 만드려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 주어야 한다. 그리고 해당 멤버 변수를 ref달고자 하는 요소에 ref props로 넣어주면 ref 설정이 완료된다.
설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다. 콜백 함수를 사용할 때와 다른 점은 이렇게 뒷부분에 .current를 넣어 주어야 한다는 것이다.
앞서 작성한 예제에 버튼을 한 번 눌렀을 때, 포커스가 다시 input쪽으로 자동으롤 넘어가도록 코드를 작성해보자.
import React, {Component} from 'react';
import './ValidationSample.css';
class ValidationSample extends Component {
state ={
isClicked: false,
isSuccess: false,
password:'',
}
handleChange = e => {
this.setState({password: e.target.value});
}
handleClick = ()=>{
let isSuc = (this.state.password === '1234');
this.setState({
isClicked: true,
isSuccess: isSuc,
password: '',
});
this.input.focus();
}
handleKeypress = (e)=>{
if(e.key === 'Enter')
this.handleClick();
}
render() {
return(
<>
<input
type="password"
className={this.state.isClicked?(this.state.isSuccess ? "success" : "failure") :''}
value={this.state.password}
onChange={this.handleChange}
onKeyPress={this.handleKeypress}
ref={(ref)=> this.input= ref}
/>
<button onClick={this.handleClick}>Verify</button>
</>
);
}
}
export default ValidationSample;
5.3컴포넌트에 ref 달기
리액트에서는 컴포넌트에도 ref를 달 수 있다. 이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 사용한다. 컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.
5.3.1 사용법
<MyComponent ref={(ref)=>{this.myComponent=ref}} />
이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다. 즉 내부의 ref에도 접근할 수 있다.
(예: myComponent.handleClick, myComponent.inut)
이번에는 스크롤 박스가 있는 컴포넌트를 하나 만들고, 스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행해 보겠다.
5.3.2 컴포넌트 초기 설정
먼저 ScrollBox라는 컴포넌트 파일을 만들어 보자. JSX의 인라인 스타일링 문법으로 스크롤 박스를 만든 다음 최상위 DOM에 ref를 달아준다.
5.3.3 컴포넌트에 메서드 생성
컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 매서드를 만들겠습니다. 자바스크립트로 스크롤바를 내릴 때는 DOM노드가 가진 다음 값들을 사용 합니다.
- scrollTop: 새로 스크롤바 위치(0~350)
- scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
- clientHeight: 스크롤이 있는 박스의 높이(300)
스크롤바를 맨 아래로 내리려면 scrollHeight에서 clientHeight 높이를 빼면 된다.
scrollBox에서 만든 메서드는 부모 컴포넌트인 App 컴포넌트에서 ScrollBox에 ref를 달면 사용할 수 있다.
import React, {Component} from 'react';
class ScrollBox extends Component{
scrollBottom=()=> {
const {scrollHeight, clientHeight} = this.box;
this.box.scrollTop = scrollHeight - clientHeight;
}
render() {
const style = {
border: '1px solid black',
height: '300px',
width: '300px',
overflow: 'auto',
position: 'relative'
};
const innerStyle = {
width: "100%",
height: '650px',
background: 'linear-gradient(white, black)'
}
return(
<div
style={style}
ref={(ref)=> {this.box=ref}}
>
<div style={innerStyle}/>
</div>
);
}
}
export default ScrollBox;
5.3.4 컴포넌트에 ref 달고 내부 메서드 사용
import React, {Component} from 'react';
import ScrollBox from './ScrollBox';
// ()=>this.ScrollBox.scrollBottom()
class App extends Component{
render(){
return (
<div className="App">
<ScrollBox ref={(ref)=> this.scrollBox=ref}/>
<button onClick={()=>this.scrollBox.scrollBottom()}>
맨 아래로
</button>
</div>
);
}
}
export default App;
위 코드에서 주의할 점이 하나 있다. 문법상으로는 onClick = {this.scrollBox.scrollBottom}같은 형식으로 작성해도 된다. 하지만 컴포넌트가 처음 랜더링될 때는 this.scrollBox값이 undefined이므로 this.scrollBox.scrollBottom 값을 읽어 오는 과정에서 오류가 발생한다. 하살표 함수 문법을 사용하여 아예 새로운 함수를 만들고 그내부에서 this.scrollBox.scrollBottom 메서드를 실행하면, 버튼을 누를 떄(이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점) 메서드 값을 읽어 와서 실행하므로 오류가 발생하지 않는다.
5.4 정리
컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용한다. 다만 ref의 사용은 최소화 되어야 한다.
오해하기 쉬운 부분이 컴포넌트 간에 데이터를 교류할 때 ref를 사용한다면 이는 잘못된 사용이다. 컴포넌트에 ref를 달고 그 ref를 다른 컴포넌트로 전달하는 등.. 이러한 사용은 리액트의 사상에 어긋난 설계이다. 컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모-자식 흐름으로 교류해야 한다. 나중에 리덕스 혹은 Context API를 사용하여 효율적으로 교류하는 방법을 배우겠다.
'Frontend > React' 카테고리의 다른 글
2021-04-30 :: 리액트 라우터로 SPA 개발하기2 (0) | 2021.04.27 |
---|---|
2021-04-23 :: 리액트 라우터로 SPA 개발하기 (0) | 2021.04.27 |
2021-04-05 :: Hooks2 (0) | 2021.04.06 |
2021-04-05 :: Hooks1 (0) | 2021.04.05 |
2020-12-07 :: Event Handling (0) | 2020.12.08 |