🤍Dev : FE/React
React : 아코디언 컴포넌트 기능 구현해보기
jini-dev
2025. 5. 13. 18:38
SMALL
요구사항
카드가 접히고 열리는 것을 구현한다 (아코디언 컴포넌트 구현)
카드가 접혔을 땐 제목 우측에 '+' 를 열렸을 땐 '-' 가 표시 되도록 한다
기본 스타일 코드와 App.js 는 구현되어있다.
// App.js - Accordion 부모 컴포넌트 import Accordion from './test/toggle/Accordion'; function App() { return ( <div style={{ fontSize: '2rem', padding: 30 }}> <Accordion title="제목" content="콘텐츠" /> </div> ); }
라이프사이클을 생각해보면서 구현 해보기 :
React : Lifecycle(라이프 사이클) - 클래스형 컴포넌트 vs 함수형 컴포넌트
React : 클래스형 컴포넌트와 훅의 등장 이유 React : 클래스형 컴포넌트와 훅의 등장 이유React : 컴포넌트와 Props, State (+ useState 훅) 화면을 각 요소로 쪼갠 것- 하나의 JSX 를 반환하는 함수 컴포넌트
jini-dev.tistory.com
풀이
// Accordion 컴포넌트
import React, { useState, useEffect } from 'react';
export default function Accordion({ title, content }) {
const [isOpen, setIsOpen] = useState(false); // 처음 카드가 접혀있도록 false로 적용했다
// 라이프 사이클 복습을 위해 일부러 body에 event를 추가하고 제거하는 코드를 작성했다.
useEffect(() => {
const handleChangeOpenState = () => {
setIsOpen((prev) => !prev);
};
// 마운트 시 클릭이벤트 생성
document.body.addEventListener('click', handleChangeOpenState);
return () => { // 언마운트시 클릭이벤트 제거
document.body.removeEventListener('click', handleChangeOpenState);
};
}, [isOpen]);
return (
<>
<div>
<div
style={{
background: '#666',
color: 'white',
fontWeight: 'bold',
padding: 10,
display: 'flex',
justifyContent: 'space-between',
}}
>
{/* flex 값이 주어져서 제목과 '+', '-' 부분은 span 태그로 각각 구현했다 */}
<span>{title}</span>
<span> {isOpen ? '-' : '+'}</span>
</div>
{isOpen && (
<div
style={{
border: '1px solid #382727',
padding: 20,
}}
>
{content}
</div>
)}
</div>
</>
);
}
정답
여기서는 useEffect 를 사용하지 않고 onClick 이벤트를 div에 적용해서 바로 isOpen이 바뀌도록 했다.
import React, { useState } from 'react';
export default function Accordion({ title, content }) {
const [isOpened, setIsOpened] = useState(false);
return (
<>
<div>
<div
style={{
background: '#666',
color: 'white',
fontWeight: 'bold',
padding: 10,
display: 'flex',
justifyContent: 'space-between',
}}
onClick={() => {
setIsOpened(!isOpened); // 또는 setIsOpened((state) => !state);
}}
>
<div>{title}</div>
<div> {isOpened ? '-' : '+'}</div>
</div>
{isOpened && (
<div
style={{
border: '1px solid #382727',
padding: 20,
}}
>
{content}
</div>
)}
</div>
</>
);
}
LIST