출처: 리액트를 다루는 기술-VELOPERT
DOM(Document Object Model)
= 객체로 문서 '구조'를 표현하는 방법. xml이나 html로 작성.
=웹페이지를 구성하고 있는 문서/다양한 구성요소(=Document) 예를 들어 a태그 div라던지 이런 것들을 하나하나 객체로 만들어서 자바스크립트가 제어할 수 있도록 한 것이 DOM
-트리 형태-> 특정 node를 검색/수정/제거/삽입이 가능.
->웹 페이지를 핸들링하기 위한 프로그래밍 인터페이스. 자바스크립트를 통해 DOM를 제어해서 웹페이지를 제어
DOM사용하는 일반적인 순서
제어할 대상(각각의 엘리먼트)을 찾기
대상이 가지고 있는 메소드를 실행하거나, 이벤트 핸들러를 설치하여 엘리먼트를 제어
-단점) 웹 브라우저 단에서 DOM에 변화가 일어나면 웹 브라우저가 CSS 다시 연산/레이아웃 구성/페이지 리페인트를 함 = 시간 허비.
-해결책) DOM을 최소한으로 조작하여 작업을 처리하자->Virtual DOM 사용하기
->DOM 업데이트를 추상화함으로써 DOM 처리 횟수를 최소화하고 효율적으로 진행한다.
Virtual DOM이란? (리액트의 주요 특징 중 하나)
-실제 DOM에 접근하여 조작하는 방식X
-실제 DOM을 추상화환 자바스크립트 객체를 구성하여 사용. -> 실제 DOM의 가벼운 사본과 비슷.
Ex)
[데이터가 변하여 웹 브라우저에 실제 DOM을 업데이트할 때]의 절차
데이터 업데이트 시 전체 UI를 Virtual DOM에 리렌더링함
이전 Virtual DOM에 있던 내용과 현재 내용을 비교
바뀐 부분만 실제 DOM에 적용
but,,,
Virtual DOM사용한다고 해서 항상 빠른 것 X
지속적으로 데이터가 변화하는 대규모 애플리케이션 구축할 때 진가를 발휘
즉, 리액트와 Virtual DOM의 장점이자 특징은:
-> 언제나 업데이트 처리 간결성을 제공
-> UI 업데이트 과정에서 생기는 복잡함을 모두 해소
-> 더욱 쉽게 업데이트에 접근 가능
ref: DOM에 이름 다는 방법
일반 HTML의 경우)
DOM 요소에 이름을 달 때 id를 사용
이유: 특정 DOM요소에 어떤 작업을 해야할 때
효과: css에서 특정 id에 특정 스타일 을 적용하거나, 자바스킓트에서 해당 id를 가진 요소를 찾아서 작업 가능
ex)
public/index.html의 id가 root인 div 요소가 있음
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>이 요소를 src/index.js 파일에서 리액트 컴포넌를 렌더링하라는 코드 써서 사용함.
ReactDOM.render(<App />, document.getElementById('root'));
=> 이렇게 HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼,
리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다
: ref(reference) 개념
react 컴포넌트 안에서도 id 사용 '가능' (dom에 id달면 렌더링할 때 그대로 전달되긴 함)하지만 권장 x
ex. 하나의 컴포넌트를 여러번 사용하는 경우, (html에서 DOM의 id는 유니크해야함) 이런 상황에서는 중복 id를 가진 DOM이 여러개 생기니 잘못된 사용.
그에 반해 ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기에 위 문제 생기지 않는다.
즉, 리액트에선 특정 DOM요소에 작업할 때는 ref를 사용해야한다.
언제? 사용?
DOM을 꼭 직접적으로 건드려야할 때
어쩔수없이 DOM에 직접적으로 접근해야할 때
ref말고 대체법
근데, input값을 검정해야하는 경우는 굳이 DOM 접근 안해도 되고 state로 구현할 수 있다
예제)
js
import React, { Component } from 'react';
import './ValidationSample.css';
class ValidationSample extends Component {
state = {
password: '',
clicked: false,
validated: false
}
handleChange = (e) => {
this.setState({
password: e.target.value
});
}
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === '0000' //validated값을 검증 결과로 설정
})
this.input.focus();
}
render() {
return (
<div>
<input
//ref={(ref)=>this.input=ref}
type="password"
value={this.state.password}
onChange={this.handleChange} //onChange가 발생하면 handelChange를 호출하여 state의 password를 업데이트함
className={this.state.clicked && (this.state.validated ? 'success' : 'failure')}
/>
<button onClick={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
css
.success {
background-color: lightgreen;
}
.failure {
background-color: lightcoral;
}
ref사용법
DOM에 ref 속성 추가: props 설정하듯이
ref 값: 콜백 함수를 전달
콜백 함수: ref를 파라메터로 가짐, 함수 내부에서 컴포넌트의 멤버 변수에 ref를 담는 코드를 작성
<input //DOM에 직접적으로 접근하기 위해 DOM에 ref 속성을 추가함
ref={(ref)=>this.input=ref} //이렇게 하면, this.input은 input요소의 DOM을 가리킨다.
</input>
컴포넌트에 ref달기
사용법: DOM에 ref 다는 방법과 동일
<MyComponent ref={(ref) => this.myComponent=ref}} />
컴포넌트에 메서드 생성
만든 메서드는 부모 컴포넌트인 App 컴포넌트에서 만든 메서드가 있는 컴포넌트(ScrollBox.js)에 ref를 달면 사용할 수 있다.
ScrollBox.js 안에 있는 메서드 부분
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
this.box.scrollTop = scrollHeight - clientHeight;
}
부모 컴포넌트에 만든 메서드에 ref 달아서 사용하기
App.js 일부
render() {
return (
<div>
<ScrollBox ref={(ref) => this.scrollBox=ref}/>
<button onClick={()=>this.scrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모<->자식 흐름으로 교류해야한다
(나중엔 꼬여서 유지보수 힘들기 때문에) 컴포넌트끼리 데이터 교류할 때 ref 사용하면 좀 그럼
react-router
뷰 렌더링을 유저의 웹 브라우저가 담당하도록 하고, 애플리케이션을 우선 웹 브라우저에 로드시킨 후 필요한 데이터만 전달 받아 보여 주게 하자.
*라우팅=> 주소에 따라 다른 뷰를 보여주는 것
리액트는 view만 담당하는 라이브러리이기에 라투팅을 담당하는 react-router를 따로 설치해줘야 한다.
react-router를 사용하면 SPA(하나의 페이지/고정된 레이아웃에서 내용만 바뀌는)처럼 깜박임이 없으면서도 주소를 가질 수 있게 해준다.
-> 새 페이지에서 필요한 데이터만 받아와 그에 따라 웹 브라우저가 다른 종류의 뷰를 만들어준다.
but,
SPA prob) 하나의 페이지이기에 앱의 규모가 커지면 자바스크립트 파일 사이즈도 같이 커지게 된다는 점.
->sol) 코드 스플리팅(=라우트 별로 파일들을 나눠서 트래픽과 로딩속도를 개선할 수 있).
기본 설정
-절대 경로 설정하기 (윈도우 기준) : 디렉터리 구조가 깊어질수록 / ~/ ~/~가 복잡해지기에 프로젝트의 루트 경로를 지정하여 파일을 절대 경로로 쉽게 불러오자~
package.json 일부
"scripts": {
"start": "cross-env NODE_PATH=src react-scripts start",
"build": "cross-env NODE_PATH=src react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
-컴포넌트 생성/준비해두기
App.js 예시 ( 웹 브라우저의 주소에 따라서 어떤 컴포넌트를 보여 줄지 정의)
import React from 'react';
import { Route } from 'react-router-dom';
import {
Home,
About,
Posts
} from 'pages';
import Menu from 'components/Menu';
const App = () => {
return (
<div>
<Menu/>
<Route exact path="/" component={Home}/>
<Route path="/about/:name?" component={About}/>
<Route path="/posts" component={Posts}/>
</div>
);
};
export default App;
->Route 컴포넌트에서 경로는 path값으로 설정, 보여 줄 컴포넌트는 component값으로 설정
라우트 이동 (=앱 내에서 다른 라우트로 이동하기!)
일반적으로 떠올릴 수 있는 <a href ~~~로 하면 안되고 link 컴포넌트를 사용해야 한다.
a 태그 클릭 시 새로고침을 해버려서 link 컴포넌트를 사용해 페이지를 새로 불러오는 것을 막고 원하는 라우트로 화면 전환을 해주기 때문
설정한 url이 활성화가 되면 특정 스타일/클래스로 지정해 줄 수 있는 NavLink컴포넌트
사용법:
Menu.js
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/about/foo">About Foo</Link></li>
<li><NavLink exact to="/about" activeStyle={activeStyle}>About</NavLink></li>
<li><NavLink to="/about/foo" activeStyle={activeStyle}>About Foo</NavLink></li>
</ul>
<hr/>
</div>
-> 이동할 주소를 컴포넌트의 to값으로 지정해주면 된다.
-> 중첩될 수 있는 라우트들은 exact로 설정. (switch도 있)
이후, App.js에서 이 컴포넌트를 불러오면 됨!
import {Route} from 'react-router-dom';
import{
Home,
About
} from 'pages';
import Menu from 'components/Menu';
const App=()=> {
return(
<div>
<Menu/>
<Route exact path="/" component={Home}/>
<Route path="/about/:name?" component={About}/>
</div>
);
};
자바트크립트에서 페이지를 이동해야하는 로직을 작성할 경우
ex)
const Home = ({ history }) => {
return (
<div>
<h2>홈</h2>
<button onClick={() => {
history.push('/about/javascript')
}}>자바스크립트를 사용하여 이동</button>
</div>
);
};
-> 라우트로 사용된 컴포넌트가 받아 오는 props중 하나인 history 객체의 push함수를 활용하기
history는 현재 라우터를 조작할 때 사용됨. (ex. 뒷/앞 페이지로 넘어가거나 새로운 주소로 이동할 때)
헷갈릴 수 있는 함수 [push 함수 vs replace 함수]
replace함수는 push와 다르게 방문 기록을 남기지 않아 페이지 이동 후 뒤로가기 버튼을 눌렀을 때 방금 전의 페이지가 아니라 방금 전의 전 전 페이지가 나타난다.
라우트 안의 라우트
라우트 안에 또 다른 라우트를 정의하는 방법
posts.js
<div>
<h3>포스트 목록</h3>
<ul>
<li><Link to={`${match.url}/1`}>포스트 #1</Link></li>
<li><Link to={`${match.url}/2`}>포스트 #2</Link></li>
<li><Link to={`${match.url}/3`}>포스트 #3</Link></li>
</ul>
<Route exact path={match.url} render={() => (<p>포스트를 선택하세요</p>)}/>
<Route exact path={`${match.url}/:id`} component={Post}/>
</div>
->match.url을 사용하여 링크 설정: 나중에 라우트 주소 변경되도 내부 주소도 같이 자동으로 반영되기 때문에 좋음.
->match.url은 현재 라우트에 설정된 경로를 알려줌. (지금의 경우 /posts)
-> 첫 번째 라우트: id값 없을때. match.url과 정확히 일치할때만 render에 있는 내용 보여줘라
-> 두 번째 라우트: id값 있을때 Post 컴포넌트를 보여줘라
'JAVASCRIPT > React' 카테고리의 다른 글
[생활코딩 React 강의2] 컴포넌트 만들기, Props, 컴포넌트 파일로 쪼개기 (0) | 2019.04.26 |
---|---|
[생활코딩 React 강의 1] 컴포넌트란? && React 기본 앱 뜯어보기 && 배포해보기 (0) | 2019.04.24 |
1_추가 내용 (0) | 2018.12.17 |
1_props & state (0) | 2018.12.17 |
1_es6, es7 학습, JSX 문법 (0) | 2018.12.17 |
Comments