React에서 원하는 동작을 수행하는 캡슐화된 별개의 컴포넌트를 생성할 수 있습니다. 또한, 어플리케이션의 state에 의존하여 그 중 일부만 렌더링시키는 것도 가능합니다. 주로 특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용합니다.
간단한 로그인 페이지를 만들어봅시다!
첫 접속은 로그아웃된(로그인되지 않은) 상태로
사용자의 이름을 받을 수 있는 input과 로그인 버튼을 보여줍니다.
그리고 사용자의 이름을 입력받아 로그인 버튼을 누르면
input창 대신 "로그인 성공!"이라는 문구를 보여주고,
그 아래 "사용자 이름
님 반갑습니다!"를 보여줄 수 있어야 합니다.
기본 첫 화면의 구조는 아래 코드처럼 짤 수 있습니다.
import React from "react";
const App = () => {
return (
<>
<form>
<input placeholder="이름을 입력해주세요" />
<button>로그인</button>
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
이제 input의 내용을 입력받아 inputValue에 저장할 수 있도록 합니다.
console.log를 찍어보면서 내용이 잘 들어오는지를 확인할 수 있습니다.
import React, { useState } from "react";
const App = () => {
const [inputValue, setInputValue] = useState("");
return (
<>
<form>
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button>로그인</button>
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
input의 내용 입력받으면서 유효성 검사를 할 필요가 없다면
useRef()
를 사용할 수도 있습니다. 그러나useRef()
는 DOM을 직접 가져와서 조작하는 JS에 더 가까운 방식이며, React적인 사고방식이 아닙니다. 상황에 맞추어 적절하게 사용하되, 가능한useState()
로 구현하는 연습을 먼저 해보는 것을 추천합니다!
- SOPT 29th WEB 파트장 김의진님의 의견
form이 submit되면(엔터를 누르거나 버튼을 눌렀을 때) 버튼의 내용이 로그인
에서 로그아웃
으로 변경됩니다. 이를 위해 현재가 로그인된 상태인지 아닌지를 확인할 수 있는 state가 하나 더 필요해집니다.
const [isLoggedIn, setIsLoggedIn] = useState(false);
form이 submit될 때 사용될 함수를 정의하기 위해서 onSubmit
을 사용합니다. onSubmit이 되면 isLoggedIn의 상태를 로그인에서 로그아웃으로, 로그아웃에서 로그인으로 변경합니다.
isLoggedIn
으로 버튼의 내용을 바꾸는 직관적인 코드는 다음과 같습니다.
import React, { useState } from "react";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
let button;
if (isLoggedIn) button = <button>로그아웃</button>;
else button = <button>로그인</button>;
return (
<>
<form>
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
{button}
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
button
이라는 변수를 만들어서 조건에 따라 button태그 전체를 대입하는 방식입니다. 여기서 쓰인 if-else문을 인라인으로 작성한 코드는 아래와 같습니다.
const button = isLoggedIn ? <button>로그아웃</button> : <button>로그인</button>;
위 코드의 구조는 다음과 같습니다.
const 변수명
= 조건(참/거짓)
? 조건이 참일 때 넣을 값
: 조건이 거짓일 때 넣을 값
;
조건에 맞추어 변수에 대입할 값을 html tag로 바꾸어 사용이 가능합니다.
자세히 보기 button태그가 중복으로 들어가있습니다. isLoggedIn의 상태에 따라 달라지는 것은 태그 전체가 아니라 태그 안의 내용입니다. 중복을 제거하여 작성하면 다음과 같은 코드가 될 수 있습니다.
const buttonText = isLoggedIn ? "로그아웃" : "로그인";
const button = <button>{buttonText}</button>;
아주 단순한 코드인데, 불필요한 변수 선언이 많아보입니다. 메모리 낭비를 줄이기 위해 buttonText를 흡수하여 button 변수 하나로 합친 코드는 다음과 같습니다.
const button = <button>{isLoggedIn ? "로그아웃" : "로그인"}</button>;
같은 이유로 App()의 return문 안의 {button}
에 해당 코드를 한 번에 작성할 수도 있을 것 같습니다. 이에 전체 코드를 적어보면 아래와 같이 작성할 수 있습니다.
import React, { useState } from "react";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<>
<form>
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button>{isLoggedIn ? "로그아웃" : "로그인"}</button>
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
이제 isLoggedIn의 상태를 조건으로 확인하여 알맞은 값으로 렌더링할 수 있게 되었습니다.
isLoggedIn의 상태가 변경되지 않아서 버튼을 눌러도 변화가 없습니다. input의 내용을 입력받고 submit을 하면 isLoggedIn의 상태가 변경되어야 합니다. 이를 위한 코드를 작성해보면 다음과 같습니다. 로그아웃된 상태에서 submit이 되면 isLoggedIn은 false에서 true가 되어야하고, 반대의 경우에 버튼을 누르면 true에서 false로 상태가 변경되어야합니다.
import React, { useState } from "react";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleSubmit = (e) => {
// onSubmit의 기본적으로 내장된 새로고침을 막기
e.prevenDefault();
if (isLoggedIn) setIsLoggedIn(false);
else setIsLoggedIn(true);
};
return (
<>
<form onSubmit={(e) => handleSubmit(e)}>
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button>{isLoggedIn ? "로그아웃" : "로그인"}</button>
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
false라면 true가 되고, true라면 false가 되는 것을 조금 더 생각해보면 서로 반대의 상태로 변한다는 특징이 있다는 것을 알 수 있습니다. handleSubmit을 더 깔끔하게 작성해보면 다음과 같습니다.
import React, { useState } from "react";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleSubmit = (e) => {
e.prevenDefault();
setIsLoggedIn(!isLoggedIn);
};
return (
<>
<form onSubmit={(e) => handleSubmit(e)}>
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button>{isLoggedIn ? "로그아웃" : "로그인"}</button>
<p>Guest Greeting</p>
</form>
</>
);
};
export default App;
로그아웃된 상태에서는 이름을 입력받을 input이 필요하지만 로그인 상황에서는 숨기는 것이 나아보입니다. isLoggedIn
이 false일 때는 input을 보여주고 true일 때(로그인되었을 때)는 input 대신 "로그인 성공!"을 보여주는 코드는 다음과 같습니다.
import React, { useState, useEffect } from "react";
import UserGreeting from "./UserGreeting";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const greeting = isLoggedIn ? (
<UserGreeting userName={inputValue} />
) : (
<div>Guest Greeting</div>
);
const handleSubmit = (e) => {
e.preventDefault();
setIsLoggedIn(!isLoggedIn);
};
return (
<>
<form onSubmit={(e) => handleSubmit(e, !isLoggedIn)}>
{!isLoggedIn ? (
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
) : (
"로그인 성공!"
)}
<button>{isLoggedIn ? "로그아웃" : "로그인"}</button>
</form>
{greeting}
</>
);
};
export default App;
무리없이 작동되지만 로그인 후 다시 로그아웃했을 때, 이전의 값이 여전히 남아있는 것을 발견할 수 있습니다. setInputValue()로 inputValue값을 ''으로 변경해주면 되는데, handleSubmit에 이 코드를 작성하면 아무런 값을 받아올 수 없습니다.
const handleSubmit = (e) => {
e.preventDefault();
setIsLoggedIn(!isLoggedIn);
setInputValue('');
};
setState는 비동기함수이기 때문에 위에서부터 순서대로 작동하는 것이 아닙니다. 로그인 후에 inputValue를 초기화하길 원했지만 그렇게 작동하지 않는 것입니다,, 그래서 useEffect로 isLoggedIn이 변경될 때에 setInputValue를 초기화하도록 하겠습니다.
useEffect(() => {
if (!isLoggedIn) setInputValue("");
}, [isLoggedIn]);
지금은 input의 내용이 없어도 "로그인 성공"을 출력합니다. inputValue의 내용이 존재할 때만 "로그인 성공"을 출력하도록 하는 코드는 다음과 같습니다.
{!isLoggedIn ? (
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
) : (
<span>{inputValue && <span>로그인 성공!</span>}</span>
)}
자바스크립트에서 true && expression 은 항상 expression 으로 평가되고, false && expression 은 항상 false 로 평가되기 때문에 이 코드는 문제없이 동작합니다. 따라서 조건이 true 라면 && 다음에 오는 요소가 노출됩니다. 만약 조건이 false 라면, React는 이를 무시하고 건너뜁니다.
UserGreeting에서 props로 받은 userName이 비어있다면 return null
을 넣어줌으로써 UserGreeting 컴포넌트의 렌더링 자체를 방지하는 것도 가능합니다.
// UserGreeting.js
import React from "react";
const UserGreeting = ({ userName }) => {
if (!userName) return null;
return <div>{userName}님 반갑습니다!</div>;
};
export default UserGreeting;
// App.js
import React, { useState, useEffect } from "react";
import UserGreeting from "./UserGreeting";
const App = () => {
const [inputValue, setInputValue] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const greeting = isLoggedIn ? (
<UserGreeting userName={inputValue} />
) : (
<div>Guest Greeting</div>
);
const handleSubmit = (e) => {
e.preventDefault();
setIsLoggedIn(!isLoggedIn);
};
useEffect(() => {
if (!isLoggedIn) setInputValue("");
}, [isLoggedIn]);
return (
<>
<form onSubmit={(e) => handleSubmit(e, !isLoggedIn)}>
{!isLoggedIn ? (
<input
placeholder="이름을 입력하세요"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
) : (
<span>{inputValue && <span>로그인 성공!</span>}</span>
)}
<button>{isLoggedIn ? "로그아웃" : "로그인"}</button>
</form>
{greeting}
</>
);
};
export default App;
// UserGreeting
import React from "react";
const UserGreeting = ({ userName }) => {
if (!userName) return null;
return <div>{userName}님 반갑습니다!</div>;
};
export default UserGreeting;
출처: React 공식문서