React에 대한 개인학습을 기록합니다.
체크리스트
☑ | TITLE |
✔ | 연습용 레포 생성 |
✔ | 리액트 개요 확인 |
✔ | JSX다루기 |
✔ | 리액트 컴포넌트 |
✔ | 리액트 스타일링 |
✔ | 컴포넌트 복잡하게 다루기 |
✔ | 속성 전달 |
✔ | JSX 복잡하게 다루기 |
✔ | 상태 다루기, 카운터 설정 |
✔ | 데이터를 UI로 불러오기 |
이벤트 다루기 | |
컴포넌트 생명주기 (생명주기 메소드) | |
DOM 엘리먼트 접근하기 | |
라우터를 통한 싱글 페이지 앱 구축 | |
Todo list 앱 제작 | |
리액트 개발 환경 구성 |
6. 속성다루기
6.1. 나쁜, 짜증나는 방법 하나
목적지로 향하는 경로에 있는 모든 컴포넌트들기 각 속성에 접근하고 재정의 해 전달하는 것.
6.2. 해결책: 스프레드 연산자
var items = ["1", "2", "3"]; // 배열 선언 및 초기화
function printStuff(a b, c) {
console.log("Printing: " + a + " " + b + " " + c);
}
//출력방법1 -> 흔한 방법
printStuff(items[0], items[1], items[2]);
//출력방법2 -> 이게 바로 스프레드 연산자 (...)
printStuff(...items);
6.3. 정리
- 리액트의 표준 확장을 통해 props객체와 같은 객체 리터럴에도 스프레드 여난자를 사용 가능하게 됨
- 사실 객체 리터럴에 대한 스프레드 연산자 사용 지원 브라우저는 없음(책이 쓰인 시점 2017기준)
- 그럼에도 예제가 작동할 수 있었던 이유: 바벨
7. JSX 자세히보기
7.1. 앞서 작성했던 코드 중, JSX 라인
<div style={cardStyle}>
<Square color = {this.props.color}/>
<Label color={this.props.color}/>
</div>
- JSX는 인간의 눈에 맞춰진 언어임
return React.createElement(
"div",
{style: cardStyle},
React.createElement(Square, {color: this.props.color}),
React.createElement(Label, { color: this.props.color})
);
- JSX가 브라우저에 도달했을 때에는 위와 같이 순수 JS로 변환되어 있음
7.2. 기억해야할 JSX특징
7.2.1. 단 하나의 루트 노트만 리턴 가능
ReactDOM.render(
<Letter>G</Letter>
<Letter>Y</Letter>
<Letter>U</Letter>
<Letter>R</Letter>
<Letter>Y</Letter>,
documemt.querySelector(”#container”)
)
ReactDOM.render(
</div>
<Letter>G</Letter>
<Letter>Y</Letter>
<Letter>U</Letter>
<Letter>R</Letter>
<Letter>Y</Letter>
</div>,
documemt.querySelector(”#container”)
);
- 상단의 코드: 작동하지 않음
- 하단의 코드: 작동함(하나의 엘리먼트로 모두 감싼 경우)
7.2.2. 인라인 CSS사용불가
- JSX의 style속성 ≠ HTML의 style속성
- HTML의 style속성: CSS속성을 style이란 엘리먼트 속성에 직접 지정 가능
- JSX의 style속성: CSS포함 불가, 대신 스타일 정보를 담은 객체를 참조해야 함
<div style="font-family:Artial; font-size:24px">
<p>Kyu!</p>
</div>
var Letter = React.createClass({
render: function() {
var letterStyle = {
padding: 10,
margin: 10,
backgroundColor: this.props.bgcolor,
color: "#333",
display: "inline-block",
fontFamily: "monospace",
fontSize: "32",
textAlign: "center"
};
return (
<div style={letterStyle}>
{this.props.children}
</div>
);
}
};
7.2.3. 예약어와 className
: 개발자 임의로 변수나 객체 이름 등에 사용할 수 없음(전의 프로젝트에서 class가아닌 className을 사용한 것처럼)
debugger | default | delete | do | else | export |
extends | finally | for | function | if | import |
in | instanceof | new | return | super | switch |
this | throw | try | typeof | var | void |
while | with | yield |
7.2.4. 대소문자 구별하기
- HTML 엘리먼트를 나타낼 시 태그를 소문자로 써야 함
- 컴포넌트를 나타낼 때는 그 이름에 대문자가 사용되어야 함
- 컴포넌트를 정의할 때, 그걸 사용하는 JSX코드 상 모두.
ReactDOM.render(
<div>
<section>
<p>Something goes here!</p>
</section>
</div>,
document.querySelector("#container")
);
7.2.5. JSX는 어디서나 가능하다
- JSX코드는 render나 return 함수 안에만 얌전히 있는 것이 아니다
var swatchComponent = <Swatch color="#2F004F"></Swatch>;
ReactDOM.render(
<div>
{swatchComponent}
</div>,
document.querySelector("#container")
);
7.2.6. 기타
- 큰 덩어리의 html코드를 복사해 jsx에 붙여넣고 작업을 할 시, 속성을 카멜 표기법으로 바꾸는 등 유효한 JSX코드가 되도록 조정 작업을 해야한다.
8. 상태 다루기
8.1. 상태 사용하기: 번개 횟수 기록 카운터 앱 제작
- 번개는 초당 100번씩 지구 표면을 때린다고 한다
- 우리의 카운터가 단순히 그 횟수(100)만큼 카운트를 증가시키도록 한다
8.1.1. Base html 작성
<!DOCYPE html>
<html>
<head>
<title>kyuCounter</title>
<script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
<script src="https://cdnjs.Cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
var LightningCounter = React.createClass({
render: function() {
return (
//앞으로 채워나갈 공간
<h1> Kyu!</h1>
);
}
});
var LightningCounterDisplay = React.createClass({
render: function() {
// 둥근 모서리의 배경을 위한 스타일 정보를 담고 있는 divStyle객체
var divStyle = {
width: 250,
textAlign: "center",
backgroundColor: "black",
padding: 40,
fontFamily: "sans-serif",
color: "#999",
borderRadius: 10
};
return (
//LIghtningCounter 컴포넌트를 감싸는 div 엘리먼트를 리턴함
<div style={divStyle}>
<LightningCounter/> {/* Counter 집어넣기 */}
</div>
);
}
});
ReactDOM.render(
// 최종결과는 LightningCounterDisplay와 LightningCounter컴포넌트, ReactDOM.render 메소드가 조합된 마크업임.
<LightningCounterDisplay/>,
document.querySelector("#container")
);
</script>
</body>
</html>
8.2. 카운터 기능 사용하기
8.2.1. 카운터 작동 방법
- setInterval 함수를 통해 1,000밀리초마다 특정 코드를 호출하기
- 특정코드: 한 번에 100만큼의 값을 증가
- 위에 사항을 위해 리액트 컴포넌트가 제공하는 세 가지 API
- getInitialState: 컴포넌트가 마운트되기 전에 실행, 컴포넌트의 state객체를 변경할 수 있게 함
- componentDidMount: 컴포넌트가 렌더링||마운팅된 후에 실행
- setState: state객체의 값을 갱신할 수 있게 함
8.2.2. 초기 상태 값 설정
var LightningCounter = React.createClass({
getInitialState: function() { // *
return {
strikes: 0
};
},
render: function() {
return (
<h1> {this.state.strikes}</h1> // strikes 속성 시각화하게 함
);
}
});
- 카운터 역할을 할 변수: strikes
- 컴포넌트 상태의 일부, 그 값을 화면에 보이게 할 것임
- getInitialState메소드를 사용해 strikes변수를 초기화할 것임
- getInitialState메소드
- 컴포넌트가 렌더링되기 전에 자동으로 실행됨
- 0으로 초기화된 strikes속성을 담은 객체 리턴
- 리턴되는 객체는 컴포넌트의 state객체를 위해 초기 값으로 설정됨
8.2.3. 타이머 시작과 상태 설정
var LightningCounter = React.createClass({
getInitialState: function() {
return {
strikes: 0
};
},
timerTick: function() {
this.setState({
strikes: this.state.strikes + 100
});
},
componentDidMount: function() { // *
setInterval(this.timerTick, 1000);
},
render: function() {
return (
<h1> {this.state.strikes}</h1> // strikes 속성 시각화하게 함
);
}
});
- 타이머를 시작시키고 strikes속성 값을 증가시킴
- setInterval함수 사용 → strikes속성을 매초마다 100씩 증가
- 컴포넌트가 렌더링 된 후 실행되는 componenetDidMount메소드를 이용하면 됨
- timerTick함수 사용 → setState 호출
- setState 메소드
- 여러 형태로 사용할 수 있으나, 여기선 객체 하나를 인자로 받도록 함
- state객체로 병합시키길 원하는 모든 속성을 넣을 수 있음
*데이터와 UI의 동기화를 유지하는 이일은 UI개발에서 가장 어려운 문제 중 하나다
8.2.4. 스프레드 연산자 등을 통해 살을 붙인 전체 코드
<!DOCYPE html>
<html>
<head>
<title>kyuCounter</title>
<script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
<script src="https://cdnjs.Cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
var LightningCounter = React.createClass({
getInitialState: function() {
return {
strikes: 0
};
},
timerTick: function() {
this.setState({
strikes: this.state.strikes + 100
});
},
componentDidMount: function() { // *
setInterval(this.timerTick, 1000);
},
render: function() {
var counterStyle = {
color: "#66FFFF",
fontSize: 50
};
var count = this.state.strikes.toLocaleString();
return (
<h1 style={counterStyle}>{count}</h1> // strikes 속성 시각화하게 함
);
}
});
var LightningCounterDisplay = React.createClass({
render: function() {
var commonStyle = {
margin: 0,
padding: 0
}
var divStyle = {
width: 250,
textAlign: "center",
backgroundColor: "#020202",
padding: 40,
fontFamily: "sans-serif",
color: "#AAAAAA",
borderRadius: 10
};
var textStyles = {
emphasis: {
fontSize: 38,
...commonStyle
},
smallEmphasis: {
...commonStyle
},
small: {
fontSize: 17,
opacity: 0.5,
...commonStyle
}
}
return (
<div style={divStyle}>
<LightningCounter/>
<h2 style={textStyles.smallEmphasis}>KIM KYU RU</h2>
<h2 style={textStyles.emphasis}>KYURY-BLOG</h2>
<p style={textStyles.small}>2022년은 흑호띠, 하지만 나는 용띠</p>
</div>
);
}
});
ReactDOM.render(
// 최종결과는 LightningCounterDisplay와 LightningCounter컴포넌트, ReactDOM.render 메소드가 조합된 마크업임.
<LightningCounterDisplay/>,
document.querySelector("#container")
);
</script>
</body>
</html>
내 이름 틀리게 적음
8.3. 정리
- 상태 보존 컴포넌트로 무엇을 할 수 있는지 보았음
- 사용자와의 상호작용과 상태 조합이 생기면 비로소 본격적인 작업이 들어간다
9. 데이터를 UI로 불러오기
9.1. 예제로 확인하기
<!DOCYPE html>
<html>
<head>
<title>ch9</title>
<script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
<script src="https://cdnjs.Cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/babel">
var Circle = React.createClass({
render: function() {
var circleStyle = {
padding: 10,
margin: 20,
display: "inline-block",
backgroundColor: this.props.bgColor,
borderRadius: "50%",
width: 100,
height: 100,
};
return(
<div style={circleStyle}>
</div>
);
}
});
var destination = document.querySelector("#container");
ReactDOM.render(
<div>
<Circle bgColor="#F9C240"/>
</div>,
destination
);
</script>
</body>
</html>
- circleSytle객체
- 인라인 스타일 속성을 포함함
- backgroundColor속성 이외의 모든 스타일 = 하드코딩
- baackgroundColor속성은 bgColor속성으로부터 값을 가져옴
- ReactDOM.render
- 최종적인 화면 출력 담당
- render메소드에 Circle컴포넌트 사용에 대해선 약간의 제한이 존재
- Circle컴포넌트 동작에 영향을 주는 데이터를 다룰 경우가 특히 그럼.
9.2. JSX 다시보기
9.2.1. JSX가 render함수 밖에 존재할 때
var theCircle = <Circle bColor="#F9C240"/>; //컴포넌트 인스턴스화 JSX
ReactDOM.render{
<div>
{theCircle}
</div>,
destination
);
- theCircle변수에 Circle 컴포넌트를 인스터화한 JSX
- ReactDOM.render함수 안에서 불려 화면에 원을 출력시킴
- 결과는 같으나, Circle컴포넌트 인스턴화가 render메소드의 구속에서 벗어남
- 더 다양한 일처리 가능
9.2.2. JSX가 render함수 밖에 존재할 때2
function showCircle() {
var colors = ["#393E41", "#E94F37", "1C89BF", "#A1D363"];
var ran = Math.floor(Math.random() * colors.length);
//무작위 선택 컬러의 Circle을 리턴함
return <Circle bgColor={colors[ran]}/>;
};
ReactDOM.render(
<div>
{showCircle()}
</div>,
destination
);
- 표현식이 JSX를 리턴하는 한, 중괄호 안에 원하는 것을 얼마든지 넣을 수 있음
- 즉 유연성 존재
9.2.3. JSX에서 배열 다루기
var destination = document.querySelector("#container");
var colors = ["#000000","#393E41", "#E94F37", "1C89BF", "#A1D363", "#85FFC7","#297373", "#FF8552", "#A40E4C" ]
var renderData = [];
for (var i =0; i< colors.length; i++) {
renderData.push(<Circle bgColor={colors[i]}/>);
}
ReactDOM.render(
<div>
{renderData}
</div>,
destination
);
9.2.3. 각 엘리먼트를 일종의 식별자로 마킹해주기
for(var i =0; i< colors.length; i++){
var color = colors[i];
// JSX코드조각 밀어넣기
renderData.push(<Circle key={i + color} bgColor = {color}/>);
}
- 마킹은 원래 JSX에서 엘리먼트를 명시적으로 지정할 땐 자동 수행함
- 위의 Circle 에서는 동적으로 엘리먼트를 만들었으므로 자동으로 식별자 부여가 되지 않음
- 따라서, 리액트가 각 컴포넌트를 유일하게 식별하기 위한 key속성을 추가해 주었음
9.3. 정리
- JSX는 JS이므로, JS가 있는 곳엔 JSX도 존재할 수 있다
- 9.2.3. 에서 코드조각 밀어넣기가 가능했던 이유가 이런 이유다, 결국엔 순수 JS로 변환되니까.