코규리
article thumbnail

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로 변환되니까.