React 스터디 2일차

god1hyuk·2022년 4월 6일
1

react study

목록 보기
2/4

오늘 강의 실습을 하며 배운 내용 토대로 나만의 예제를 만들어 보았다.

<오늘의 키워드>
1. Component Tag (컴포넌트 태그 생성 및 정의)
2. props
3. object destructuring (객체 구조 분해 할당)
4. event

오늘의 예제코드

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>react day2</title>
	<style>
		body {
			background: #61dbfb;
		}

		li {
			list-style: none;
		}

		a {
			color: inherit;
			text-decoration: none;
		}

		a:hover {
			text-decoration: underline;
		}

		.list-content {
			width: 500px;
			margin: 0 auto;
			text-align: center;
		}

		.frameworks {
			padding: 0 30px;
		}

		.list-item {
			display: flex;
			align-items: center;
			justify-content: space-between;
			height: 45px;
			margin: 30px 0;
			padding: 0 15px;
			color: #fff;
			background: #3d3d3d;
			border-radius: 5px;
			box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset;
		}

		.like-btn {
			font-size: 20px;
			padding: 0;
			background: transparent;
			border: none;
		}

		.like-btn:hover {
			cursor: pointer;
		}
	</style>
</head>

<body>
	<div id="app"></div>

	<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
	<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
	<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
	<script type="text/babel">

		/* ===== 컴포넌트 정의 ===== */

		// 타이틀
		function Title(props) {
			return <h1>{props.children}</h1>;
		}

		// 리스트 아이템
		const ListItem = ({ name, link }) => {
			let isLiked = false;
			function handleLikeClick(event) {
				const likeBtn = event.target;

				if (!isLiked) {
					isLiked = true;
					likeBtn.textContent = '❤️';
					console.log("I like it");
				} else {
					isLiked = false;
					likeBtn.textContent = '🤍';
					console.log("I don't like it");
				}
			}

			return (
				<li className="list-item">
					<a href={link} target="_blank">{name}</a>
					<button type="button" className="like-btn" onClick={handleLikeClick}>🤍</button>
				</li>
			);
		}

		// 리스트
		const List = () => (
			<ul className="frameworks">
				<ListItem name="React.js" link="https://reactjs.org/" />
				<ListItem name="Vue.js" link="https://vuejs.org/" />
				<ListItem name="Angular.js" link="https://angularjs.org/" />
			</ul>
		);

		// 최종적으로 렌더링 될 컴포넌트
		const content = (
			<div className="list-content">
				<Title>Hello Frontend!</Title>
				<List />
			</div>
		);

		// 렌더링 할 최상위 부모 컴포넌트
		const app = document.querySelector('#app');

		// 렌더링 함수 호출
		ReactDOM.render(content, app);

	</script>
</body>

</html>
  • 예제 소개
    리스트 아이템 컴포넌트를 정의하고 생성된 아이템을 리스트 컴포넌트에 담아 렌더링 하는 예제이다. 그와 더불어 각 아이템에 props로 전달받은 아이템의 이름과 링크를 부여하고, 좋아요 버튼을 클릭하면 체크되는 이벤트까지 구현해보았다.

찬찬히 코드를 살펴보자.

우선 컴포넌트들을 정의한다.

// 타이틀
function Title(props) {

	// 함수의 결과로 태그를 return
	return <h1>{props.children}</h1>;
}

타이틀 컴포넌트는 기본적인 일반 함수 표현식으로 선언하였다.

(!) 컴포넌트 선언 시 함수의 이름은 반드시 첫 문자를 대문자로 표기한다.

이렇게 선언한 컴포넌트는

// 자체닫기 태그 (self-closing tag)
<Title/>

또는

// 열고 닫는 태그 (opening & closing tag)
<Title></Title>

두가지 형태로 사용할 수 있게 된다.

여기서 잠깐,
컴포넌트 선언 함수에서 매개변수로 지정된 props는 무엇을 의미할까?

React에서의 props(properties)란?
컴포넌트로 작성한 엘리먼트(태그)의 JSX attribute(속성)값은 해당 컴포넌트 선언 함수에 단일 객체로 전달된다. 그 전달 되는 객체를 props라고 한다. 쉽게 말해 부모 컴포넌트에서 자식 컴포넌트로 전달해주는 데이터 객체이다.

위에 선언한 Title 컴포넌트로 설명하자면
함수로 컴포넌트를 선언하면 하나의 컴포넌트 태그로 사용할 수 있게 된다고 하였다.

// 최종적으로 렌더링 될 컴포넌트
const content = (
	<div className="list-content">
    	<Title>Hello Frontend!</Title>
        <List />
    </div>
);

이렇게 부모 컴포넌트인 content에 Title 태그를 넣어주면 자식 컴포넌트 등록이 된 것이다.

지금 Title 컴포넌트에 props 데이터를 넘겨주고 있다.
"Hello Frontend!"

원래 props를 넘겨주는 가장 기본적인 형태는

<Title text="Hello Frontend!" />
// (!) 속성의 이름은 꼭 text가 아니어도 된다. 자식 컴포넌트가 전달 받을 데이터에 이름을 붙여주는 것.

자식 컴포넌트 선언 함수를 살펴보면

// 타이틀
function Title(props) {	// 마찬가지 매개변수명은 꼭 props가 아니어도 된다.
	console.log(props);	// 콘솔에 출력해서 확인
	return <h1>{props.text}</h1>;
}

매개변수를 지정해 놓은 것을 확인 할 수 있다.
저 props에 부모 컴포넌트에서 지정한 속성(text="Hello Frontend!")이 데이터 객체 형태로 들어오는 것이다.

콘솔에서 확인해보면

key와 value 형태로 text: "Hello Frontend!"가 출력되는 것을 확인 할 수 있다.

return <h1>{props.text}</h1>;

props 객체의 text를 h1 태그안에 {props.text}로 넣어 return 하면 props 및 컴포넌트 정의가 완료된다.

(!) {props.text} 처럼 {} 안에 작성하는 이유는?
이것은 JSX 문법으로, 일반적으로는 html 태그안에 자바스크립트 문법을 사용할 수 없다. 하지만 {} 안에 작성을 해준다면 html에서도 자바스크립트 문법을 사용할 수 있게 된다.

props는 이렇게도 가능하다.

<Title>Hello Frontend!</Title>

열고 닫는 태그 형태로 작성해주고 그 태그 내부에 텍스트 형태로 작성하면
그 내용이 객체의 key가 children인 value가 들어오게 된다.

콘솔에 출력해보면

children으로 확인된다.

(!) "children"은 변경할 수 없이 자체적으로 할당되는 key값이다.

만약 이 형태로 props를 받는다면 컴포넌트 선언 함수의 return 부분에 이렇게 값을 대입해주면 된다.

return <h1>{props.children}</h1>;

다음은 리스트 아이템 컴포넌트를 생성하였다.

// 리스트 아이템
const ListItem = ({ name, link }) => {

	let isLiked = false;	// 좋아요 여부
    
	function handleLikeClick(event) {
    
		// event가 발생한 요소를 변수에 저장
		const likeBtn = event.target;

			if (!isLiked) {	// isLiked가 false 이면
            	isLiked = true; 
                
                // 해당 요소의 텍스트를 빨간 하트로 교체
                likeBtn.textContent = '❤️';
                console.log("I like it");
                
			} else {	// isLiked가 true이면
            	isLiked = false;
                
                // 해당 요소의 텍스트를 하얀 하트로 교체
                likeBtn.textContent = '🤍';
                console.log("I don't like it");
                
            }
		}

		// 함수의 결과로 태그를 return
		return (
        	<li className="list-item">
            	<a href={link} target="_blank">{name}</a>
                <button type="button" className="likebtn" onClick={handleLikeClick}>🤍</button>
            </li>
		);
	}

리스트 아이템 컴포넌트를 생성하는 함수는 일반 함수가 아닌 익명함수, 화살표 함수를 사용하여 선언하였다.
(여기서는 화살표 함수에 표현식을 알고 있다는 가정하에 진행하겠다.)

그런데 조금 이상한 점이 있다.
방금 전까지 매개변수로 props 데이터를 받아온다고 설명은 하였는데 매개변수로 { name, link }를 받아 온 것을 볼 수 있다.

(!) 조금 전 설명한 html 태그 내부에서 자바스크립트를 작성할 수 있는 JSX문법의 중괄호{}와는 다른 개념이다.

이 문법은 자바스크립트 es6의 최신 문법이며 "destructuring", "구조 분해 할당"이라 부른다.

destructuring(구조 분해 할당)이란?
배열 또는 객체를 Destructuring(비구조화, 파괴)하여 개별적인 변수에 할당하는 것이다. 배열 또는 객체 리터럴에서 필요한 값만을 추출하여 변수에 할당하거나 반환할 때 쓰인다.

여기서는 필요한 객체 구조분해에 대해서만 보도록 하자.

기본적으로 es5의 문법으로 객체의 값을 변수로 사용하기 위해서는

// ES5
var obj = { firstName: 'God', lastName: '1hyuk' };

var firstName = obj.firstName;
var lastName  = obj.lastName;

console.log(firstName, lastName); // God 1hyuk

이렇게 Object.key로 접근하여 값을 꺼낸 다음 변수에 담아 사용해야 했다.

es6에서는 좀 더 쉽게 객체의 값을 사용할 수 있다.

// ES6 Destructuring
const obj = { firstName: 'God', lastName: '1hyuk };

const { firstName, lastName } = obj;

console.log(firstName, lastName);	// God 1hyuk

똑같은 결과 값을 낸다는 것을 확인할 수 있다.

문법
{ 객체의 key } = 객체 데이터의 출처

쉽게 말해 객체에 접근해서 값을 꺼내와서 변수에 담지 않고
특정 객체의 값만 쏙 꺼내서 변수처럼 바로 사용할 수 있다는 것이다.
결국 객체의 key값은 변수명이 되는 것이다.

다시 본론으로 돌아와서

// 리스트 아이템
const ListItem = ({ name, link }) => {
    ...
 
    // 함수의 결과로 태그를 return
    return (
    	<li className="list-item">
        	<a href={link} target="_blank">{name}</a>
            <button type="button" className="likebtn" onClick={handleLikeClick}>🤍</button>
        </li>
	);
}

이렇게 바로 객체 구조 분해 형태의 인자로 넣어주게 되면

<a href={link} target="_blank">{name}</a>

{link}, {name} 같이 변수처럼 바로 사용할 수 있는 것이다.

// 리스트 아이템
const ListItem = ({ name, link }) => {

	let isLiked = false;	// 좋아요 여부
    
	function handleLikeClick(event) {
    
		// event가 발생한 요소를 변수에 저장
		const likeBtn = event.target;

		if (!isLiked) {	// isLiked가 false 이면
        	isLiked = true; 
                
            // 해당 요소의 텍스트를 빨간 하트로 교체
            likeBtn.textContent = '❤️';
            console.log("I like it");
                
        } else {	// isLiked가 true이면
            isLiked = false;
                
            // 해당 요소의 텍스트를 하얀 하트로 교체
            likeBtn.textContent = '🤍';
            console.log("I don't like it");
                
        }
    }

	...
}

이벤트에 대해 설명하려 한다.
좋아요 여부(isLiked)에 따라 버튼의 텍스트(하트)가 교체 되는 이벤트를 작성해보았다.

handleLikeClick()

  • handle + 키워드 + 이벤트타입()

react에서 클릭, 마우스오버 등 이벤트핸들러 함수의 이름을 짓는 관례이다.
함수이름을 꼭 이렇게 정하지 않아도 작동 되는데 아무런 문제가 없다. 그래도 관례라는 것은 따르는 것이 좋다. 여러 사람과 함께 프로젝트 협업을 하거나 내가 만든 웹사이트를 다른 누군가가 유지보수를 하는 상황도 생길 것이다. 관례를 모두가 지켜준다면 모두가 편하고 이해하기 쉬운 좋은 코드가 되지 않을까?

그 다음, 이벤트 함수를 선언한 뒤

<button type="button" className="like-btn" onClick={handleLikeClick}>🤍</button>

onClick 속성으로 JSX문법의 자바스크립트 작성 스코프 {}에 handleLikeClick 이벤트를 호출해주었다.

(!) 일반적인 html 태그에서는 소문자(onclick)로 작성하든 카멜케이스(onClick)로 작성하든 모두 가능하다. 하지만 JSX문법에서는 반드시 카멜케이스(onClick)로 속성을 정의 해주어야한다.

저장을 하고 브라우저에서 좋아요 버튼을 클릭해보면

좋아요 해제 시에도

잘 작동되는 것을 확인 할 수 있다. (완성 된 예제코드를 실행했을 시)

리스트 아이템 컴포넌트 정의가 완료 되었다면
리스트(ul) 컴포넌트에 리스트 아이템을 넣어주는 작업이 필요하다.

...

// 리스트
const List = () => (
	<ul className="frameworks">
		<ListItem name="React.js" link="https://reactjs.org/" />
		<ListItem name="Vue.js" link="https://vuejs.org/" />
		<ListItem name="Angular.js" link="https://angularjs.org/" />
	</ul>
);

정의한 리스트아이템 컴포넌트(ListItem)를 부모 컴포넌인 리스트 컴포넌트(List)에 자식 컴포넌트로 삽입해준다.

조금 전, ListItem에 { name, link }로 받아왔던 name속성과 link속성의 값을 작성해준다. 이렇게 부모 컴포넌트에서 작성한 속성 값은 props 데이터 객체로 자식 컴포넌트의 인자로 전달이 되는 것이다.

(!) 여기서도 특이점이 발견 된다.
화살표 함수를 선언할 때 왜 중괄호{}를 열고 닫지 않았을까?
화살표 함수의 내용이 단지 return 밖에 없을 경우 이렇게 축약하여 작성할 수 있다.

문법
const 함수명 = () => 리턴할 요소

하지만, 리턴할 요소 이외에 다른 표현식이 있다면 축약형으로 작성할 수 없다. 반드시 중괄호{}를 열고 그 안에 작성해 주어야 한다.

최종적으로 렌더링을 할 수 있게 앞서 선언한 Title, ListItem, List 컴포넌트들을 하나의 태그로 묶어주어야 한다.

// 최종적으로 렌더링 될 컴포넌트
	const content = (
		<div className="list-content">
			<Title>Hello Frontend!</Title>
			<List />
		</div>
	);

	// 렌더링 할 최상위 부모 컴포넌트
	const app = document.querySelector('#app');

	// 렌더링 함수 호출
	ReactDOM.render(content, app);

content는 컴포넌트 선언 함수가 아닌 단지 부모 태그로 감싸준 것이기 때문에
변수명을 대문자로 작성하지 않아도 된다.

하나의 묶어 준 content를 렌더링 함수에 최상위 부모 app 컴포넌트와 함께 넘겨주어 렌더링한다.

좋아요 이벤트, 각 리스트 아이템의 href 값으로 {link}를 담아주었기에 링크(제목)를 클릭하면 해당 사이트로 이동하는 것도 확인 할 수 있다.

0개의 댓글