React 스터디 4일차

god1hyuk·2022년 4월 11일
1

react study

목록 보기
4/4

오늘의 키워드
1. 상태 끌어올리기(Lifting state up)
2. map()
3. 스프레드 연산자(Spread operator)
4. onChange 이벤트
5. toUpperCase()

이전 시간까지는 내가 시청하는 강의 내용을 다루지 않고 배운 내용을 응용하여 직접 예제를 만들어 설명을 했으나 직장인으로써 시간이 너무 소요되는 탓에 앞으로는 시청하는 강의 내용으로 일지를 작성하는 식으로 진행을 할 것이다...
(판단 미스였던 것...)

1강부터 다루지 않고 현 시점부터 진행하겠다..!

지금까지 강의를 진행하며 작성한 코드는 이러하다.

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

<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>고양이 가라사대</title>
</head>
<style>
	body {
		text-align: center;
	}

	.main-card button {
		position: relative;
		left: -45px;
		bottom: 15px;
	}

	.favorites {
		list-style: none;
		display: flex;
		justify-content: center;
		flex-wrap: wrap;
		gap: 15px;
	}
</style>

<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">
		console.log("야옹");

		const Title = (props) => {
			return <h1>{props.children}</h1>;
		};

		const Form = () => {
			const [counter, setCounter] = React.useState(1);

			console.log("카운터", counter);

			function handleFormSubmit(event) {
				event.preventDefault();
				console.log("폼 전송됨");
				setCounter(counter + 1);
			}

			return (
				<form onSubmit={handleFormSubmit}>
					<input
						type="text"
						name="name"
						placeholder="영어 대사를 입력해주세요"
					/>
					<button type="submit">생성</button>
				</form>
			);
		};

		function CatItem(props) {
			return (
				<li>
					<img src={props.img} style={{ width: "150px" }} />
				</li>
			);
		}

		function Favorites() {
			return (
				<ul className="favorites">
					<CatItem img="https://cataas.com//cat/5e9970351b7a400011744233/says/inflearn" />
					<CatItem img="https://cataas.com/cat/595f280b557291a9750ebf65/says/JavaScript" />
				</ul>
			);
		}

		const MainCard = ({ img }) => {
			function handleHeartClick() {
				console.log("하트 눌렀음");
			}
			function handleHeartMouseOver() {
				console.log("하트 스쳐 지나감");
			}
			return (
				<div className="main-card">
					<img src={img} alt="고양이" width="400" />
					<button
						onClick={handleHeartClick}
						onMouseOver={handleHeartMouseOver}
					>
						🤍
					</button>
				</div>
			);
		};

		const app = (
			<div>
				<Title>1번째 고양이 가라사대</Title>
				<Form />
				<MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
				<Favorites />
			</div>
		);

		const 여기다가그려 = document.querySelector("#app");

		ReactDOM.render(app, 여기다가그려);
	</script>
</body>

</html>

input에 영어 대사를 입력하고 생성 버튼을 클릭하면
고양이 이미지에 텍스트가 적용되어 짤이 생성된다.
그리고 생성된 고양이짤의 하트 버튼을 클릭하면 아래에 내가 찜한 목록에
고양이짤이 추가되는 예제이다.

이전의 상황에 대해 설명하자면,
필요한 자식 컴포넌트 요소들을 정의하고 부모컴포넌트에 등록을 해둔 상태이다.
그리고 자식의 자식 컴포넌트 요소에 props 데이터를 전달하여 사용한 것을 확인할 수 있고 Form 컴포넌트에 카운터(상태:useState)를 정의해 생성버튼을 클릭하면 기본 값으로 정의해둔 counter 값이 1씩 증가하는 것을 확인할 수 있다. 또 이벤트 함수를 정의하여 onClick, onSubmit 속성을 통해 호출하기도 한다.

현재 시점부터 차근차근 설명해나가도록 하겠다.

현재 타이틀의 텍스트 "1번째 고양이 가라사대"는 고정적인 상태이다.

아이템이 추가됨에 따라 "1번째", "2번째" 카운트가 증가할 수 있도록 처리를 해줄 것이다.

그러기 위해서는 이전에 선언해둔 상태값 counter를 활용해야 하는데
Form 컴포넌트에 정의되어 다른 형제 컴포넌트들과는 그 상태값을 공유할 수가 없는 상황이다.

그렇다면 어떻게 해야 다른 형제들에게도 상태값을 전달해줄 수가 있을까?

부모 컴포넌트로 상태를 끌어올려주면 된다.

Form 컴포넌트에 선언해둔

const [counter, setCounter] = React.useState(1);

이 코드를 Form 컴포넌트가 아닌 부모 컴포넌트(app)에 선언하는 것이다.

부모 컴포넌트로 옮겨주려고 보니 부모 요소가 컴포넌트로 정의되어 있지 않은 것을 볼 수 있다.

const app = (
	<div>
    	<Title>1번째 고양이 가라사대</Title>
        <Form />
        <MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
        <Favorites />
    </div>
);

우선 이 부모요소를 컴포넌트 형태로 만들어 주겠다.

const App = () => {
	return (
    	<div>
    		<Title>1번째 고양이 가라사대</Title>
        	<Form />
        	<MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
        	<Favorites />
    	</div>
    );
}

(!) 컴포넌트 선언 시에는 변수명(함수명)의 첫 문자를 대문자로 작성하는 것 잊지말자.

렌더링 함수에서도 변수로 등록된 app에서 App 컴포넌트로 수정 해준다.

ReactDOM.render(app, 여기다가그려);
ReactDOM.render(<App />, 여기다가그려);

이제 준비 되었으니 Form 컴포넌트의 상태값(counter)을 부모 컴포넌트에 끌어올려보자.

const App = () => {
	const [counter, setCounter] = React.useState(1);

	console.log("카운터", counter);

	return (
    	<div>
    		<Title>1번째 고양이 가라사대</Title>
        	<Form />
        	<MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
        	<Favorites />
    	</div>
    );
}

그런데 counter을 증가시키기 위해서는 생성 버튼을 클릭해야 하는데 그 이벤트 함수는 자식 컴포넌트(Form)에 있다. handleFormSubmit 이벤트도 부모로 끌어올려 관리 해보도록 하자.

const App = () => {
	const [counter, setCounter] = React.useState(1);

	console.log("카운터", counter);
    
    function handleFormSubmit(event) {
		event.preventDefault();
		console.log("폼 전송됨");
		setCounter(counter + 1);
	}

	return (
    	<div>
    		<Title>1번째 고양이 가라사대</Title>
        	<Form />
        	<MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
        	<Favorites />
    	</div>
    );
}

아, 그리고! handleFormSubmit 이벤트에 event.preventDefault()는
기본적으로 form태그의 submit타입의 버튼은 클릭하게 되면 post action을 취하게 되어 링크가 이동되게 된다. 지금은 그 동작을 막기 위해 preventDefault()를 사용하였다.

이벤트 함수의 매개변수로 지정된 event를 콘솔에 출력해보면 여러가지 이벤트 속성을 내포하고 있는 객체 형태로 출력되는 것을 확인할 수 있는데 필요한 속성을 용도에 따라 사용하면 된다. 지금은 모든 부분을 깊게 다룰 수 없기 궁금하다면 "이벤트 객체(event object)"라는 키워드로 검색해보자.
(!) 전부터 강조하듯 매개변수명은 꼭 event가 아니어도 된다. 단지 내가 정한 이름일 뿐이다.

useState에 대한 사용법은 이전 시간에도 다루었기 때문에 넘어가도록 하고
이전 강의(React 스터디 2일차)

handleFormSubmit 이벤트가 호출되면
setCounter(counter + 1)이 호출되어 counter의 상태값을 1씩 증가하게 된다.

지금 handleFormSubmit 이벤트가 부모 컴포넌트에 있기 때문에 Form 컴포넌트에 이벤트를 위임해주어야한다.

const App = () => {
	...

	return (
    	...
        	<Form handleFormSubmit={handleFormSubmit} />
        ...
    );
}

태그에 지정하는 속성 이름은 자유롭게 지정할 수 있지만 쉽게 구분하기 위해 우선은 메소드명과 동일하게 지정하였다.

부모 컴포넌트에서 자식 컴포넌트로 이벤트를 위임 해주었으면 자식 컴포넌트에서 이를 받아주어야 하지 않겠는가?

const Form = ({ handleFormSubmit }) => {
	...
}

이렇게 destructuring(구조분해할당) 형태로 이벤트를 받아

<form onSubmit={handleFormSubmit}>

form 태그에 onSubmit 이벤트로 등록해주었다.

(!) 생성 버튼에 onClick 이벤트가 아닌 form 태그에 onSubmit 이벤트를 등록한 점 유의할 것! 간단히 말하자면 form태그의 특성 때문이다. 데이터를 전송하는 것은 버튼이 아닌 form의 데이터이기 때문에 form의 데이터가 전송되는 이벤트가 발생하는 시점에 다른 이벤트도 함께 발생될 수 있게 처리한 것이다.

지금까지 자식 컴포넌트의 상태나 이벤트를 부모 컴포넌트로 옮겨 다른 자식들에게도 위임할 수 있게 처리한 것은 "상태 끌어올리기"라고 한다.
"상태 끌어올리기"를 하면 부모 -> 자식으로 데이터 또는 이벤트를 전달할 수 있어 데이터를 폭 넓게 활용할 수 있다는 장점이 있다.

타이틀의 내용을 유동적인 상태로 만들어 주기 위해

const App = () => {
	...

	return (
		<Title>{counter}번째 고양이 가라사대</Title>
        ...
    );
}

바뀌어야 할 숫자부분을 선언한 상태 값으로 변경해주었다.

결과를 확인해보자.

타이틀 "1번째 고양이 가라사대" 지금은 처음과 같다.
여기서 생성 버튼을 클릭하게 되면 "2번째" "3번째" counter가 증가되는 것을 확인할 수 있다.

그 다음은 내가 하트 버튼을 눌러 추가될 찜리스트(Favorites)컴포넌트에 각각 다른 이미지로 뿌려볼 것이다.

function Favorites() {
	return (
		<ul className="favorites">
			<CatItem img="https://cataas.com//cat/5e9970351b7a400011744233/says/inflearn" />
			<CatItem img="https://cataas.com/cat/595f280b557291a9750ebf65/says/JavaScript" />
		</ul>
    );
}

Favorites 컴포넌의 자식 요소인 CatItem의 img(props) 값이 고정적인 상태인 것을 눈치챘을 것이다. 이미지가 추가될 때 마다 일일이 태그를 적고 저렇게 긴 이미지 url을 적어줄 수는 없는 노릇이다.

여러장의 이미지를 변수 그리고 배열에 담아두고 그 배열의 요소들을 순차적으로 뿌려주면 어떨까?

우선 Favorites에 변수로 이미지 url 2개를 선언하고 그 변수들을 배열에 담아서 사용해보자.

function Favorites() {
	const CAT1 = "https://cataas.com/cat/60b73094e04e18001194a309/says/react";
	const CAT2 = "https://cataas.com//cat/5e9970351b7a400011744233/says/inflearn";
    const cats = [CAT1, CAT2];
	
    ...
}

저 정의한 데이터를 어떻게 하나씩 뿌려줄 수 있을까?

function Favorites() {
	...
    const cats = [CAT1, CAT2];
	
	return (
    	<CatItem img={cats[0]} />
        <CatItem img={cats[1]} />
    );
}

이렇게? 이건 아까와 별반 다를 것이 없는 상태이다.
저럴거면 굳이 배열에 담아서 사용할 필요가 있었을까?
굳이 배열에 담는 수고를 했다면 좀 더 보람차게 써먹어야 할 것 아닌가!

이 시점에서 배워볼 것이 map 메소드(함수)이다.

map 메소드란?
자바스크립트에서 제공된 누군가에 의해 이미 만들어진 함수이자 for문을 응용하여 만들어진 메소드이다. for문을 활용하여 만들어진 메소드는 map 이외에도 filter, forEach 등 수가지가 존재하고 용도에 따라 원하는 메소드를 활용하면 된다.
map 메소드에 대한 더 자세한 내용은 구글링하자!

map 메소드의 문법은 이렇다.

배열변수명.map((배열 요소, 인덱스, 배열) => {
	return ...;
});

3번째 인자인 배열은 잘 사용하진 않고 보통은 배열의 요소나 인덱스를 많이 사용한다.

설명은 이쯤하고 map 메소드를 활용해 이미지를 뿌려보자.

function Favorites() {
	...

	return (
    	<ul className="favorites">
        	{cats.map(cat => <CatItem img={cat} />)}
        </ul>
    );
}
{cats.map(cat => <CatItem img={cat} />)}

(return만 반환해주기 때문에 스코프{}를 열지 않고 화살표 함수 축약형으로 작성하였다.)

cats 배열의 요소 0번부터 순차적으로 반복하며 각 요소에 접근해 활용할 수 있게 되는 원리인데, 현재 cats 배열의 요소는 2개이기 때문에 return으로 반환된 CatItem 태그는 2개이다. 그리고 CatItem 컴포넌트의 속성인 img(props)의 값은 map 메소드의 1번째 인자인 배열의 요소(url)를 속성값으로 매핑해주었다. 배열의 0번 url, 1번 url이 순차적으로 반복된 것이다.

저장하고 브라우저를 보게 되면

하단에 고양이 이미지 2장이 잘 출력이 된 것을 확인할 수 있다.
그런데, 콘솔 창을 보면...

너무도 강렬하게 빨강으로 심각히 무언가를 알리고 있다.
너무 걱정하지 않아도 된다. 다행히도 에러는 아니니까!
단지 "Warning" 경고 메세지이다.

무엇을 경고하는걸까?

배열을 순차적으로 접근하여 잘 매핑해주었으나, 각각의 배열 요소를 뚜렷하게 식별할 수 있는 key. 쉽게 말해 이름이 필요하다는 것이다.

에러가 나면 작동이 되지 않는 것과 달리 key가 없어도 경고는 하지만 작동은 될 수 있다.

그래도 안전하게 코드를 작성하는게 좋으니 이름을 붙여주도록하자.

{cats.map(cat => <CatItem img={cat} key={cat} />)}

이렇게 해당 태그에 key라는 속성을 지정하고 값으로 map의 1번째 인자로 받아온 요소를 넣어주면 된다.
현재 cats 배열의 요소는 단순한 값이기 때문에 요소 그 자신을 지정해주면 된다. 만약 배열의 요소가 객체라면?

const cats = [
	{
    	id: "CAT1",
        name: "야옹이"
	}, {
    	id: "CAT2",
        name: "야순이"
    }
];

그렇다면 더 구체적인 이름을 지정해주어야한다.

{cats.map(cat => <CatItem img={cat} key={cat.id} />)}

이렇게 대표할 객체의 요소까지 짚어주어야 한다.

다음은 메인이미지 컴포넌트인 MainCard에서 하트버튼을 클릭하면
방금 구현한 Favorites 컴포넌트에 이미지가 추가되는 기능을 구현해볼 것이다. 지금은 내가 찜한 이미지가 추가되지는 않고 다른 이미지 한장이 추가될 수 있도록만 구현해 볼 것이다.

const MainCard = ({ img }) => {
	function handleHeartClick() {
    	console.log("하트 눌렀음");
    }
    function handleHeartMouseOver() {
    	console.log("하트 스쳐 지나감");
    }
			
    return (
    	<div className="main-card">
        	<img src={img} alt="고양이" width="400" />
        	<button
        		onClick={handleHeartClick}
            	onMouseOver={handleHeartMouseOver}
        	>
				🤍
			</button>
    	</div>
    );
};

여기 MainCard 컴포넌트에 props로 전달된 img는 App 컴포넌트에 정의 되어 있는데 그 url은 고정적인 값이다.

const App = () => {
	...

	return (
    	...
        	<MainCard img="https://cataas.com/cat/60b73094e04e18001194a309/says/react" />
        ...
    );
}

이 데이터도 유동적인 상태로 한번 바꿔보자.

Form 컴포넌트처럼 MainCard도 부모(App) 컴포넌트에 상태를 선언할 것이다.

그러고 보니, Favorites 컴포넌트에서 선언했던 CAT1, CAT2를 MainCard에서도 활용할 수 있을 것 같지 않은가?

"상태 끌어올리기"를 다시 한번 사용해볼 기회다.

Favorites 컴포넌트에서 선언했던 변수 CAT1, CAT2를 부모 컴포넌트로 가져오자.
cats는 아마 필요 없을 것 같아서 cats는 지워주고 CAT3도 변수로 하나 더 추가해주도록 하겠다.

const App = () => {
	const CAT1 = "https://cataas.com/cat/60b73094e04e18001194a309/says/react";
	const CAT2 = "https://cataas.com//cat/5e9970351b7a400011744233/says/inflearn";
    const CAT3 = "https://cataas.com/cat/595f280b557291a9750ebf65/says/JavaScript";
    ...
}

Favorites에서 상태 끌어올리기를 해주었기 때문에 코드 수정이 필요하지만
일단 보류하고 MainCard를 먼저 작업하도록 하겠다.

왜냐하면 MainCard에서 하트버튼을 클릭했을 때 Favorites의 상태가 바뀌어야 하기 때문에 MainCard를 먼저 작업하면서 후 처리를 하는 것이다.

MainCard의 상태, Favorites의 상태 이 두가지를 먼저 정의한다.

const App = () => {
	...
    const CAT3 = "https://cataas.com/cat/595f280b557291a9750ebf65/says/JavaScript";
    // MainCard 컴포넌트에 표시될 state
    const [mainCat, setMainCat] = React.useState(CAT1);
    // Favorites 컴포넌트에 표시될 state
    const [favorites, setFavorites] = React.useState([CAT1, CAT2]);
    ...
}

MainCard에 들어갈 상태는 기본적으로 CAT1 이미지를 기본 값으로 정의했고

// MainCard 컴포넌트에 표시될 state
const [mainCat, setMainCat] = React.useState(CAT1);

Favorites에 들어갈 상태값은 CAT1, CAT2 이 두개의 이미지를 배열 형태로 기본값을 정의하였다.

// Favorites 컴포넌트에 표시될 state
const [favorites, setFavorites] = React.useState([CAT1, CAT2]);

App 컴포넌트의 MainCard의 img(props)로 mainCat state를 전달해준다.

...
<MainCard img={mainCat} />
...

MainCard 컴포넌트의 이벤트(handleHeartClick)도 부모 컴포넌트로 끌올 해주겠다. handleHeartMouseOver 이벤트는 테스트용으로 작성했던 코드이기에 사용하지 않으니 지워주도록 하겠다. MainCard 컴포넌트에 있는 하트버튼의 도 필히 지워줘야 한다!
(지우지 않으면 콘솔창 피범벅 됨...)

const App = () => {
	...
    const [favorites, setFavorites] = React.useState([CAT1, CAT2]);
    
    function handleHeartClick() {
		console.log("하트 눌렀음");
	}
    
    ...
}

하트버튼 클릭 이벤트 발생 시 일어날 상황들을 작성해주자.

하트버튼 클릭 시 Favorites 컴포넌트에 CAT3 이미지를 추가해볼텐데

어떻게 추가할 수 있을까?..
favorites state에 기본값인 배열 [CAT1, CAT2]에 CAT3를 추가해주면 되지 않을까?

그렇다면 handleHeartClick 함수에 setFavorites 메소드를 사용해 state를 변경해주면 되겠다.

function handleHeartClick() {
	console.log("하트 눌렀음");
    setFavorites([...favorites, CAT3]);
}
[...favorites, CAT3]

'...?'
우리에게 생소한 문법일 수 있다.
ES6에서 새롭게 추가 된 문법인데 스프레드 연산자(Spread operator)이다.

스프레드 연산자(Spread operator)란?
전개 구문, 펼침 연산자라고도 칭한다. 배열의 요소를 전개해주는 연산자. 기존의 배열을 일일이 표시할 필요없이 [...배열변수명]으로 표기하면 기존의 배열을 펼쳐 사용할 수 있게 된다.

기존의 배열 favorites [CAT1, CAT2][CAT3]를 concat으로 붙여준 것과 동일하다고 보면 된다.

이벤트 함수 정의가 끝났다면 MainCard 컴포넌트로 해당 이벤트를 전달 해주어야 한다.

...
<MainCard img={mainCat} handleHeartClick={handleHeartClick} />
...

MainCard에서도 받아준다.

const MainCard = ({ img, handleHeartClick }) => {
	return (
    	<div className="main-card">
        	<img src={img} alt="고양이" width="400" />
            <button onClick={handleHeartClick}>🤍</button>
        </div>
    );
};

여기까지 저장하고 브라우저를 보면
MainCard 컴포넌트에 CAT1 이미지가 잘 출력되고 있다.
(처음과 다른게 없어 보이지만 엄청난 변화가 일어나고 있다.)

이제 보류해뒀던 Favorites 컴포넌트 코드 수정을 해보자.
부모 컴포넌트로 변수를 끌올했기 때문에 props로 상태를 전달해주어야 한다.

<Favorites favorites={favorites} />

favorites라는 속성이름으로 상태값(favorites)를 전달해주었다.

Favorites 컴포넌트에서 props를 받아준다.

function Favorites({ favorites }) {
	return (
    	<ul className="favorites">
        	{favorites.map(cat => <CatItem img={cat} key={cat} />)}
        </ul>
	);
}

그리고

{cats.map(cat => <CatItem img={cat} key={cat} />)}

부모로 변수를 끌어올리기 전 사용했던 변수 cats는 props 데이터 favorites로 대체 되었기 때문에 이 부분도 수정해준다.

{favorites.map(cat => <CatItem img={cat} key={cat} />)}

이제 끝이 보인다...

오늘의 마지막 키워드 onChange, toUpperCase 메소드이다.
Form 컴포넌트의 input에 영문을 입력하면 소문자로 입력하든 대문자로 입력하든 무조건 대문자로 입력이 되도록 처리하는 실습이다.

우선 Form 컴포넌트를 살펴보자.

const Form = ({ handleFormSubmit }) => {

	return (
    	<form onSubmit={handleFormSubmit}>
        	<input
            	type="text"
                name="name"
                placeholder="영어 대사를 입력해주세요"
            />
            <button type="submit">생성</button>
        </form>
    );
};

input에 텍스트를 입력하여 변화가 감지되면 이벤트가 발생할 수 있도록 onChange 속성에 이벤트를 등록해줄 것이다.

const Form = ({ handleFormSubmit }) => {
	function handleInputChange(e) {
    	console.log(e);
    }
    
	return (
    	...
};

일단은 콘솔에 이벤트 객체가 출력 되도록만 정의했다.

input 태그에 handleInputChange를 등록해준다.

<input
	type="text"
    name="name"
    onChange={handleInputChange}
    placeholder="영어 대사를 입력해주세요"
/>	

저장하고 브라우저에서 input에 텍스트를 입력해보자.
콘솔도 함께 확인해보자.

텍스트를 입력하고 콘솔을 보면 한 글자 입력할 때마다 매개변수 e로 받아온 이벤트 객체가 출력된다.

출력된 이벤트 객체를 한번 살펴보면

이벤트 객체 내부 target 속성의 value 속성에 방금 입력한 "aaa"가 확인된다.

우리가 필요한 값은 바로 저거다.

그렇다면 handleInputChange에서 target의 value 값이 출력되도록 해보자.

function handleInputChange(e) {
	console.log(e.target.value);
}

객체 속성에 접근하는 방식으로 접근하면 된다. 이벤트 {객체}이니까.

저장하고 텍스트를 입력해보면


한 글자 한 글자 입력될 때마다 실시간으로 콘솔에 출력되는 것을 확인할 수 있다.

여기서 저 소문자를 어떻게 대문자로 변환을 할 수 있을까?
이번에도 마찬가지 자바스크립트에서 기본적으로 제공하는 메소드가 있다.
바로 toUpperCase() 메소드이다.

toUpperCase() 메소드
소문자로 입력한 영문자를 대문자로 바꿔주는 메소드.
반대로 대문자로 입력하면 소문자로 바꿔주는 toLowerCase() 메소드도 있다.

우리가 원하는 대상에 toUpperCase()를 체이닝 해주기만 하면 된다.

function handleInputChange(e) {
	console.log(e.target.value.toUpperCase());
}

소문자로 입력하면

대문자로 변환되어 출력이 된다.

콘솔에 출력이 된다고 값을 활용할 수 있는건 아니다 이제는 값을 활용할 수 있도록 input의 value값을 state로 정의해주도록 하겠다.

Form 컴포넌트에

const Form = ({ handleFormSubmit }) => {
	const [value, setValue] = React.useState("");
    
    function handleInputChange(e) {
	...
};

기본값이 빈 문자열인 상태로 정의한다.

handleInputChange 함수가 실행되면 value가 대문자로 변환이 된 문자로 바뀌도록 setValue() 메소드를 handleInputChange 함수에 정의 해주도록 하겠다.

function handleInputChange(e) {
	console.log(e.target.value.toUpperCase());
    setValue(e.target.value.toUpperCase());
}

그런데 콘솔에는 대문자로 변환되어 잘 출력이 되지만 정작 input에 보이는 텍스트는 여전히 소문자이다.

상태값(value)를 input태그의 value속성 값으로 지정해주면 된다.

<input
	type="text"
    name="name"
    onChange={handleInputChange}
    value={value}
    placeholder="영어 대사를 입력해주세요"
/>	

그러면 input의 value속성에 value state 값이 들어오게 되는 것이다.

마지막으로 저장하고 브라우저의 input에 텍스트를 입력해보면

input에도 대문자가 출력이 되는 걸 확인할 수 있다.

오늘의 실습은 여기까지이다.

오늘의 실습 코드 최종본

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

<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>고양이 가라사대</title>
</head>
<style>
	body {
		text-align: center;
	}

	.main-card button {
		position: relative;
		left: -45px;
		bottom: 15px;
	}

	.favorites {
		list-style: none;
		display: flex;
		justify-content: center;
		flex-wrap: wrap;
		gap: 15px;
	}
</style>

<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">
		console.log("야옹");

		const Title = (props) => {
			return <h1>{props.children}</h1>;
		};

		const Form = ({ handleFormSubmit }) => {
			const [value, setValue] = React.useState("");

			function handleInputChange(e) {
				console.log(e.target.value.toUpperCase());
				setValue(e.target.value.toUpperCase());
			}

			return (
				<form onSubmit={handleFormSubmit}>
					<input
						type="text"
						name="name"
						onChange={handleInputChange}
						value={value}
						placeholder="영어 대사를 입력해주세요"
					/>
					<button type="submit">생성</button>
				</form>
			);
		};

		function CatItem(props) {
			return (
				<li>
					<img src={props.img} style={{ width: "150px" }} />
				</li>
			);
		}

		function Favorites({ favorites }) {
			return (
				<ul className="favorites">
					{favorites.map(cat => <CatItem img={cat} key={cat} />)}
				</ul>
			);
		}

		const MainCard = ({ img, handleHeartClick }) => {
			return (
				<div className="main-card">
					<img src={img} alt="고양이" width="400" />
					<button onClick={handleHeartClick}>🤍</button>
				</div>
			);
		};

		const App = () => {
			const CAT1 = "https://cataas.com/cat/60b73094e04e18001194a309/says/react";
			const CAT2 = "https://cataas.com//cat/5e9970351b7a400011744233/says/inflearn";
			const CAT3 = "https://cataas.com/cat/595f280b557291a9750ebf65/says/JavaScript";
			const [counter, setCounter] = React.useState(1);
			const [mainCat, setMainCat] = React.useState(CAT1);
			const [favorites, setFavorites] = React.useState([CAT1, CAT2]);

			console.log("카운터", counter);

			function handleFormSubmit(event) {
				event.preventDefault();
				console.log("폼 전송됨");
				setCounter(counter + 1);
			}

			function handleHeartClick() {
				console.log("하트 눌렀음");
				setFavorites([...favorites, CAT3]);
			}

			return (
				<div>
					<Title>{counter}번째 고양이 가라사대</Title>
					<Form handleFormSubmit={handleFormSubmit} />
					<MainCard img={mainCat} handleHeartClick={handleHeartClick} />
					<Favorites favorites={favorites} />
				</div>
			);
		}

		const 여기다가그려 = document.querySelector("#app");

		ReactDOM.render(<App />, 여기다가그려);
	</script>
</body>

</html>

0개의 댓글

관련 채용 정보