
isOpen인지 확인한다.div 태그의 클래스 이름이 active인지 확인한다.isVisible이든 show든, 클래스명이 on이든 active든 사용자는 관심이 없습니다.
오직 src/components/ToggleButton.test.tsx 파일을 생성합니다.
우리는 내부 구현(State 이름 등)은 전혀 신경 쓰지 않고, 오직 import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, test, expect } from 'vitest';
// 컴포넌트는 잠시 후에 만듭니다.
import ToggleButton from './ToggleButton';
describe('ToggleButton 리팩토링 내성 테스트', () => {
test('버튼을 클릭하면 숨겨진 메시지가 나타나고, 다시 클릭하면 사라진다', async () => {
const user = userEvent.setup();
render(<ToggleButton />);
const toggleBtn = screen.getByRole('button', { name: '토글' });
console.log('[Step 1] 초기 상태 확인');
// 처음에는 메시지가 없어야 합니다. (queryBy 사용)
const hiddenMessage = screen.queryByText('짜잔! 숨겨진 메시지입니다.');
expect(hiddenMessage).not.toBeInTheDocument();
console.log('[Step 2] 버튼 클릭 (열기)');
await user.click(toggleBtn);
screen.debug(); // 화면 출력
// 클릭 후에는 메시지가 보여야 합니다.
expect(screen.getByText('짜잔! 숨겨진 메시지입니다.')).toBeInTheDocument();
console.log('[Step 3] 버튼 클릭 (닫기)');
await user.click(toggleBtn);
screen.debug(); // 화면 출력
// 다시 클릭하면 메시지가 사라져야 합니다.
expect(screen.queryByText('짜잔! 숨겨진 메시지입니다.')).not.toBeInTheDocument();
});
});useState를 하나 사용하여 상태를 관리합니다.import { useState } from 'react';
export default function ToggleButton() {
// 상태 이름을 'show'라고 지었습니다.
const [show, setShow] = useState(false);
const toggle = () => {
setShow((prev) => !prev);
};
return (
<div>
<button onClick={toggle}>토글</button>
{/* show 상태가 true일 때만 메시지 표시 */}
{show && <p>짜잔! 숨겨진 메시지입니다.</p>}
</div>
);
}npm test를 실행합니다.npm test -- src/components/ToggleButton.test.tsx
[Step 1] 초기 상태 확인
[Step 2] 버튼 클릭 (열기)
<body>
<div>
<button>토글</button>
<p>짜잔! 숨겨진 메시지입니다.</p>
</div>
</body>
[Step 3] 버튼 클릭 (닫기)
<body>
<div>
<button>토글</button>
</div>
</body>
PASS src/components/ToggleButton.test.tsx'변수명이show가 뭡니까?isVisible로 바꾸세요. 그리고 로직도 커스텀 훅으로 분리합시다.'
import { useState } from 'react';
// [리팩토링] 로직을 커스텀 훅으로 분리하고 변수명을 바꿉니다.
function useToggle(initialValue = false) {
// 변수명 변경: show -> isVisible
const [isVisible, setIsVisible] = useState(initialValue);
const toggle = () => setIsVisible(v => !v);
return { isVisible, toggle };
}
export default function ToggleButton() {
// 기존 useState 코드를 삭제하고 훅을 사용합니다.
const { isVisible, toggle } = useToggle();
return (
<div>
<button onClick={toggle}>토글</button>
{/* 변수명이 바뀌었지만 화면에 그리는 결과물은 똑같습니다 */}
{isVisible && <p>짜잔! 숨겨진 메시지입니다.</p>}
</div>
);
}npm test -- src/components/ToggleButton.test.tsx PASS src/components/ToggleButton.test.tsxgit clone https://github.com/PROMLEE/my-tdd-app.git
cd my-tdd-app
git checkout part12다음 시간에는13편. 프론트엔드 테스트 유형 정리를 통해, 우리가 배운 단위 테스트 외에 통합 테스트, E2E 테스트 등 전체적인 숲을 보는 시간을 갖겠습니다.