📝 이번주는 HTML, CSS로 만들어진 블로그를 리액트로 변환하는 필수 과제를 진행중이다. 팀프로젝트를 들어가기 전에 리액트 토이프로젝트를 해보면서 익숙해질 수 있는 좋은 기회라고 생각했다. 사실 처음에는 컴포넌트 몇개 만들고 합성하면 끝일 거라고............ 단순하게 생각했다. 하지만 그러기엔 내 리액트 실력이 응애였던 것,... 컴포넌트별 파일을 만들고 헤더, 푸터 컴포넌트만 만들었는데 주말 이틀이 사라지는 신기한 경험을 했다. 이번에 해결한 이슈는 이슈라고 하기도 민망한 해프닝 정도이지만 다시 정리해볼겸 기록을 해두려고 한다!
📌 과제 명세는 다음과 같다.
로그인 state에 따라서 헤더 컴포넌트에 보이는 버튼 컴포넌트들을 다르게 렌더링 해야 한다. (로그인 기능은 따로 X)
그래서 일단 각 컴포넌트들을 만들어두고 useState
을 사용해 다음과 같은 코드를 작성했다. 처음에 useEffect
도 써야 하나 생각했는데, 어차피 상태값이 바뀌면 헤더내 컴포넌트들이 재렌더링이 되고 렌더링 이외의 실행되어야 하는 코드가 없기 때문에 이번 경우에는 굳이 필요 없다고 생각했다. (이틀동안 안돼서 혹시 이거...?하고 useEffect
안에 콜백 함수로 setLogin
을 넣어봤다가 무한루프도 처음 겪어봤다. 로그인 상태가 바뀌면 콜백 함수가 실행되고 그 함수는 로그인 상태를 바꾸니 당연한 것,,,)
// Header 컴포넌트
export default function Header() {
const [login, setLogin] = useState(false);
return (
<nav>
<Logo />
<div>
{login ? <ProfileImg /> : null}
{login ? <WriteBtn /> : <LoginBtn onClick={() => {setLogin(true);}}/>}
{login ? <LogoutBtn onClick={() => setLogin(false)} /> : <RegisterBtn/>}
</div>
</nav>
);
}
// 로그인 버튼 컴포넌트
export default function LoginBtn() {
return (
<button className="login-btn">
<img src={icon} alt="로그인 버튼" />
<span>Login</span>
</button>
);
}
오.... 다시 보니까 정말 말도 안되는 코드... 자식 컴포넌트에 버튼이 클릭됐을 때 실행될 함수를 props로 전달해줘야 하는데 그냥 냅다 바로 함수 넣어버린 부모와 아무것도 받을 준비가 안되어있는 자식? 기본 개념에 충실했어야 하는데 훅이 아직 익숙하지 않은 상황에서 시행착오를 많이 겪었다. 해결 과정에서 useContext
훅을 써서 로그인 여부를 공유하면 상단 헤더와, 유저 정보를 나타내는 about 섹션에서 해당 내용을 동시에 사용할 수 있을 것 같아서 더 공부중이다.
이것저것 하다보니 분명 어려운 문제는 아닌데 머리가 더 혼란스러워져서 다시 개념을 돌아봤다.
- 클릭 이벤트를 실행시키는 당사자는 버튼 컴포넌트 (자식)
- 따라서 이 자식 컴포넌트가 실행할 함수를 props로 받아와야 함
- props를 주는 주체는 부모 컴포넌트 (여기서 Header)
- 이때 클릭 이벤트 핸들러는 login 여부를 바꿔주는 setLogin을 실행시킨다
- 부모는 이 함수를 프로퍼티에 전달만 해주면 끝!
(초반에 헷갈려서 자식컴포넌트를 지지고 볶고 로그인 상태값을 다시 받아오고 했지만 그럴 필요가 없었던 것. 무엇이 들어올 예정인지만 명시해주고 구체적인 props들은 부모가 건네줄 것이니 기다리기만 하면 된다)
- props는 단방향 흐름을 갖는다. (상위 컴포넌트가 하위 컴포넌트에 값을 전달할때 사용)
- props는 자식 입장에선 읽기 전용인 데이터이다.
✍ 쓰고나니 더 별거 아닌 내용이지만 그만큼 기본 개념에 더 익숙해질 필요가 있다는 것을 스스로 상기하고자 적었습니다. 부정확한 내용이 있을 수 있으니 있다면 꼭 알려주세요! 더 배우고 수정하겠습니다.