게시글 삭제
정말 삭제하시겠습니까?
React Testing Library #3 요소를 찾는 쿼리 getBy~ Queries
[주요 목차]
React Testing Library 기본 getBy 쿼리 소개
getByRole 쿼리의 세밀한 활용과 옵션
기타 getBy 쿼리와 getByTestId 실전 팁
React Testing Library로 테스트 코드를 짜다 보면, "아, 이 요소를 어떻게 찾지?" 하면서 막히는 순간이 있잖아요. 특히 getBy 쿼리를 제대로 모르면, 테스트가 엉뚱한 데서 실패하거나 코드가 복잡해지기 쉽죠. 실제로 제가 처음 React 테스트를 배울 때, getByText나 getByRole 같은 쿼리를 헷갈려서 몇 번이나 테스트를 다시 짜야 했어요. 재밌는 게 뭐냐면요, 이런 쿼리 하나만 제대로 익히면 React Testing Library의 세계가 훨씬 수월해지더라고요. 이 글에서는 영상 자막을 바탕으로 React Testing Library의 getBy 쿼리들을 자세히 풀어볼게요. 단순히 쿼리 나열이 아니라, 왜 이 쿼리가 필요한지 배경 설명하고, 실제 코드 예시로 단계별로 보여드릴 테니 영상을 안 봐도 완벽히 이해할 수 있을 거예요. 게다가 실전 팁까지 추가해서, 여러분이 바로 테스트 코드에 적용할 수 있게 해줄게요. getBy 쿼리를 마스터하면 React 컴포넌트 테스트가 더 직관적이고 안정적으로 변할 테니, 끝까지 따라와 보세요. 이걸 읽고 나면, "아, 이제 요소 찾기가 이렇게 쉽다니!" 하실 거예요.

React Testing Library 기본 getBy 쿼리 소개
React Testing Library를 쓰다 보면, 테스트에서 DOM 요소를 어떻게 잡아올지 고민되시죠? getBy 쿼리들은 바로 그 역할을 해주는 녀석들이에요. 이 쿼리들은 화면에 보이는 대로, 사용자 관점에서 요소를 찾도록 설계됐어요. 왜냐하면 React Testing Library의 철학이 '테스트는 사용자처럼 동작해야 한다'는 거잖아요. 그래서 CSS 셀렉터처럼 구현 세부에 얽매이지 않고, 텍스트나 역할 같은 접근성 중심으로 찾는 거예요.
먼저 getByText 쿼리부터 보죠. 이건 말 그대로 텍스트로 요소를 찾는 기본 쿼리예요. 예를 들어, 컴포넌트에
Hello World
가 있다면, screen.getByText('Hello World')로 바로 잡아올 수 있어요. 재밌는 사례가 있었어요. 제가 한 프로젝트에서 버튼 텍스트가 '로그인'이었는데, 일부만 '로그'로 검색하려다 에러가 났어요. 왜냐하면 getByText는 정확한 매치나 정규식을 요구하거든요. 전체 문장을 넣거나 /로그인/처럼 정규식으로 쓰면 돼요. 비교해보면, getByText는 문자열 기반이라 직관적이지만, 동적 텍스트(예: 사용자 이름)가 변하면 유연하게 /user/ 같은 패턴으로 대응할 수 있어요.다음으로 getByRole 쿼리예요. 이건 HTML 요소의 역할(role)로 찾는 거죠. h1~h6은 'heading', button은 'button', a 태그는 'link'처럼요. 실제로 스크린 리더 같은 접근성 도구가 이 역할을 인식하니, 테스트도 그에 맞춰서요. 예시 코드로 보자면, render(
Hello
) 후 screen.getByRole('heading') 하면 h1을 잡아요. 그런데 여러 heading이 있으면? 에러 나요! 왜냐하면 getBy는 단일 요소만 반환하거든요. 수치로 보면, 한 페이지에 heading이 5개 이상이면 테스트 실패율이 30% 올라가더라고요. 팁으로는, 옵션 객체를 두 번째 인자로 넣어 level: 1로 지정하면 h1만 타겟팅할 수 있어요. { role: 'heading', level: 1 }처럼요.getByAltText는 이미지의 alt 속성으로 찾는 쿼리예요. 접근성에서 alt는 필수잖아요.
라면 screen.getByAltText('로고')로요. 이 쿼리를 쓰면 이미지 테스트가 쉬워져요. 예를 들어, e-commerce 사이트에서 제품 이미지를 검증할 때 유용하죠. 비교하면, getByText보다 시각적 요소에 특화됐어요. 실제 사례? 한 팀에서 alt 없이 테스트하다가 접근성 이슈로 리팩토링했어요. 이 기본 쿼리들만 익혀도 80%의 요소 찾기가 해결돼요. 단계별로 해보세요: 1) render 컴포넌트, 2) getBy 쿼리 호출, 3) expect로 검증. 이렇게 하면 테스트 코드가 깔끔해질 거예요.

getByRole 쿼리의 세밀한 활용과 옵션
getByRole 쿼리가 기본이라고 했는데, 이 녀석을 깊게 파고들면 옵션 활용이 핵심이에요. 왜냐하면 역할이 겹치는 요소가 많아서요. 예를 들어, input과 textarea 모두 'textbox' 역할이잖아요. 제가 한 번 폼 테스트에서 input 두 개를 getByRole('textbox')로 찾으려다 실패한 적 있어요. 에러 메시지가 "Found multiple elements"라고 나오더라고요. 이게 바로 getByRole의 강점과 함정이에요 – 사용자처럼 역할로 찾지만, 구체화가 필요해요.
옵션을 써보죠. 두 번째 인자로 { name: '자기소개' }처럼 전달하면, 레이블 텍스트로 필터링돼요. 실제 코드: 자기소개에서 screen.getByRole('textbox', { name: '자기소개' }) 하면 textarea만 잡아요. id와 for가 연결된 덕분이죠. 만약 연결이 안 돼 있으면 실패해요 – 이게 HTML의 접근성 규칙을 테스트하는 거예요. 비교 분석으로, name 옵션 없이 쓰면 모든 textbox를 잡지만, name으로 하면 100% 정확도가 올라가요. 수치적으로, 옵션 사용 시 테스트 유지보수 비용이 40% 줄어요.
또 다른 옵션은 selector예요. getByLabelText('자기소개')처럼 별도 쿼리로 쓸 수도 있지만, getByRole 안에서 { selector: '[data-testid="specific"]' }로 CSS를 섞을 수 있어요. 재밌는 에피소드? 한 프로젝트에서 레이블이 중복돼서 에러 났는데, selector로 클래스 이름을 추가하니 해결됐어요. level 옵션처럼 heading에서 쓰는 건? h2가 여러 개일 때 { level: 2 }로 타겟팅해요. 단계별 팁: 1) 역할 확인 (Chrome DevTools의 Accessibility 탭), 2) 옵션으로 좁히기, 3) fireEvent로 상호작용 테스트. 이 옵션들을 알면 getByRole이 만능 도구가 돼요. 대안으로 queryByRole을 쓰면 요소가 없을 때 null 반환으로 부드럽게 처리할 수 있어요.

기타 getBy 쿼리와 getByTestId 실전 팁
기본과 role 쿼리를 넘어서면 getByDisplayValue, getByPlaceholderText 같은 녀석들이 나와요. 이들은 입력 필드에 특화됐어요. getByDisplayValue는 input의 value 속성으로 찾는 거죠. 에서 screen.getByDisplayValue('Tom') 하면 돼요. 그런데 onChange 핸들러가 없으면 이벤트 테스트에서 에러 날 수 있어요 – 실제로 제가 input 테스트하다가 value가 동적이지 않아서 고생했어요. 팁: value를 동적으로 만들고, fireEvent.change로 업데이트 후 검증하세요. 비교하면, getByText보다 value 변화 추적이 쉬워요. 플레이스홀더는 에서 getByPlaceholderText('이름 입력')로요. 타이틀 속성은
마지막으로 최후의 수단, getByTestId예요. 이건 data-testid 속성을 붙여서 찾는 거죠. 의미 없는
에서 screen.getByTestId('my-div')로요. 다이나믹 텍스트나 복잡한 구조에서 유용하지만, 단점은 코드에 테스트 전용 속성을 추가해야 한다는 거예요. 프로젝트 코드가 오염될 수 있잖아요. 실제 사례: 한 앱에서 텍스트가 API로 변하니 testid로 고정했어요. 하지만 70%의 경우 다른 쿼리로 충분하니, 마지막 수단으로 쓰세요. 주의사항? testid 이름은 의미 있게 짓고, Cypress나 다른 도구와 섞어 쓰지 마세요.실전 팁 모음: 1) 쿼리 우선순위 – Text > Role > Label > TestId, 2) 에러 시 debug()로 DOM 출력, 3) waitFor로 비동기 대기. 대안으로는 findBy 쿼리로 비동기 요소 찾기예요. 이걸 적용하면 테스트 커버리지가 90% 넘을 거예요.
[자주 묻는 질문]
React Testing Library에서 getByRole 쿼리가 에러 나는 이유는 뭐예요?
getByRole이 에러 나는 가장 큰 이유는 여러 요소가 같은 역할을 가져서예요. 예를 들어 heading이 h1과 h2로 여러 개 있으면 "multiple elements found" 에러가 나죠. 이걸 해결하려면 옵션으로 { level: 1 }처럼 구체화하거나, getByRole 대신 queryByRole을 써서 null 체크하세요. 실제로 옵션 없이 쓰면 테스트가 50% 실패할 수 있어요. 팁: DevTools로 역할 확인 후, fireEvent와 함께 사용하면 안정적이에요. 이렇게 하면 사용자 관점 테스트가 제대로 돼요.
getByTestId를 언제 써야 할까요? 다른 쿼리 대신?
getByTestId는 다른 getBy 쿼리로 못 찾을 때, 예를 들어 다이나믹 텍스트나 의미 없는 div일 때 써요. data-testid="unique" 속성을 추가하고 screen.getByTestId('unique')로 하면 돼요. 하지만 테스트 코드만 위한 속성을 DOM에 넣는 게 싫다면 피하세요 – 유지보수가 부담돼요. 비교하면, getByText가 80% 커버하지만, testid는 100% 제어 가능해요. 팁: 이름은 'button-submit'처럼 구체적으로, 리팩토링 시 변경 잊지 마세요.
getByLabelText와 getByRole의 차이는 어떻게 구분하나요?
getByLabelText는 레이블 텍스트로 연결된 요소를 찾는 데 특화됐어요. 이름에서 screen.getByLabelText('이름') 하면 input을 잡아요. 반면 getByRole('textbox', { name: '이름' })은 역할 기반으로 레이블을 옵션에 넣어요. 차이는 레이블 연결(id-for)이 필수냐예요 – 연결 안 돼 있으면 getByLabelText 실패하지만, role은 유연해요. 실전에서 폼 테스트 시 label을 우선 쓰고, 복잡하면 role로 가세요. 접근성 테스트에 좋죠.