개념
검색을 못 하면 도구를 쓰는 게 아니다
업무 항목이 200건일 때는 눈으로 훑어도 된다. 2,000건이 되면 불가능해진다. 그리고 실무 프로젝트는 1년이면 그 규모가 된다.
QA에게 검색은 편의 기능이 아니라 일의 시작점 이다. 하루를 시작할 때 필요한 질문들을 떠올려보자.
- 내가 검증해야 하는데 아직 안 본 것은?
- 어제 배포에 들어간 수정 중 검증이 안 끝난 것은?
- 한 번 고쳤다가 다시 열린 결함은?
- 두 달째 아무도 손대지 않은 결함은?
이 질문들에 매번 손으로 답하면 하루가 간다. JQL로 한 번 써두면 클릭 한 번이 된다. 이 레슨이 다루는 것이 그 전환이다.
JQL의 기본 구조
JQL(Jira Query Language) 은 업무 항목을 검색하는 질의 언어다. 기본 형태는 단순하다.
field operator value- field — 검색할 필드 (
assignee,status,priority,project…) - operator — 비교 연산자 (
=,!=,~,IN,IS,>,<…) - value — 값 (사용자명, 상태, 날짜…)
조건을 AND·OR·NOT 으로 엮고 괄호로 묶는다.
project = "SHOP" AND issuetype = Bug AND status = "In Progress"
AND priority IN ("Highest", "High")앞 레슨에서 본 대로 JQL은 여전히 issue·issuetype 을 쓴다. 화면
용어가 '업무 항목'으로 바뀌어도 쿼리는 그대로다.
검색 화면에는 기본 검색(드롭다운으로 조건 선택)과 JQL 모드가 있다. 기본 검색으로 시작해 JQL로 전환하면 문법을 빠르게 익힐 수 있다 — 드롭다운으로 조건을 고른 뒤 JQL 모드로 바꾸면 같은 조건이 문장으로 보인다.
연산자 — 자주 쓰는 것부터
| 연산자 | 의미 | 예 |
|---|---|---|
= | 정확히 일치 | status = "To Do" |
!= | 일치하지 않음 | priority != Low |
~ | 텍스트 부분 일치 | summary ~ "결제" |
!~ | 부분 일치하지 않음 | description !~ "deprecated" |
IN | 여러 값 중 하나 | priority IN (Highest, High) |
NOT IN | 여러 값 중 어느 것도 아님 | status NOT IN (Done, Closed) |
IS / IS NOT | 비어 있는지 | assignee IS EMPTY |
> < >= <= | 크기·날짜 비교 | created >= "2026-08-01" |
~ 는 텍스트 검색이라 완전 일치가 아니다. 그래서 summary ~ "로그인"
은 "로그인 실패", "소셜 로그인 오류"를 모두 잡는다. 넓게 훑을 때 쓰고,
정확히 세야 하는 집계에는 쓰지 않는다.
IS EMPTY 는 QA에게 특히 유용하다. 비어 있으면 안 되는 필드가 비어
있는 것을 찾는 용도 이기 때문이다. 담당자 없는 결함, 픽스 버전 없이
닫힌 결함처럼.
함수 — 날짜와 사용자를 손으로 쓰지 않기
| 함수 | 의미 |
|---|---|
currentUser() | 지금 로그인한 사용자 |
now() | 현재 시각 |
startOfDay() / endOfDay() | 오늘의 시작·끝 |
startOfWeek() / startOfMonth() | 이번 주·이번 달의 시작 |
인자를 넣어 상대 시점을 만들 수 있다 — startOfDay(-7) 은 7일 전
0시다.
currentUser() 를 쓰는 습관이 중요하다. 필터에 자기 계정 이름을
하드코딩하면 그 필터는 나만 쓸 수 있고, 팀에 공유하는 순간 무용지물이
된다. assignee = currentUser() 로 써두면 한 벌의 필터를 팀 전체가
각자의 것으로 쓴다.
이력 검색 — QA에게 가장 값진 기능
여기가 이 레슨의 핵심이다. 대부분의 사람은 현재 상태 만 검색한다. 그런데 QA의 질문은 종종 과거 를 향한다.
| 연산자 | 무엇을 찾는가 |
|---|---|
WAS | 과거에 그 값이었던 적이 있는 항목 |
WAS NOT | 과거에 그 값이 아니었던 항목 |
WAS IN | 과거에 목록 중 하나였던 항목 |
CHANGED | 값이 변경된 이력이 있는 항목 |
CHANGED 에는 세부 조건을 붙일 수 있다.
status CHANGED FROM "Done" TO "Reopened" AFTER startOfMonth()
status CHANGED TO "In Progress" BY currentUser() DURING ("2026-09-01", "2026-09-05")왜 이게 QA에게 결정적인가. 모듈 6(레슨 5)에서 배운 품질 지표 중 여럿이 이력 없이는 계산되지 않기 때문이다.
- 재오픈율 — "Done이었다가 다시 열린 것"은 현재 상태만으로 알 수 없다
- 결함 탈출률 — "릴리즈 이후에 발견됐는가"는 생성 시점과 릴리즈 시점의 관계다
- 핑퐁 결함 — 개발과 QA 사이를 여러 번 오간 항목은 프로세스 문제의 신호다
- 조용히 닫힌 결함 — 검증 상태를 거치지 않고 Done으로 간 항목
마지막 항목은 특히 중요하다. status WAS NOT "In QA" AND status = Done
같은 쿼리 하나로 검증 없이 종료된 결함 을 찾아낼 수 있다. 이건
프로세스 감사이고, 대개 결과가 놀랍다.
QA가 매일 쓰는 JQL 12선
바로 복사해 쓸 수 있는 형태로 정리한다. 프로젝트 키와 상태 이름은 조직에 맞게 바꿔야 한다.
일상 업무
-- 1. 내가 검증해야 할 것
project = SHOP AND status = "Ready for QA" AND assignee = currentUser()
ORDER BY priority DESC, updated ASC
-- 2. 내가 등록한 결함 중 아직 안 닫힌 것
project = SHOP AND reporter = currentUser() AND status NOT IN (Done, Closed)
-- 3. 오늘 상태가 바뀐 것 (데일리 스크럼 준비용)
project = SHOP AND status CHANGED AFTER startOfDay()
-- 4. 이번 스프린트에서 열린 결함
project = SHOP AND issuetype = Bug AND sprint IN openSprints()품질 감시
-- 5. 재오픈된 결함 (프로세스 문제의 신호)
project = SHOP AND issuetype = Bug
AND status CHANGED FROM "Done" TO "Reopened" AFTER startOfMonth(-1)
-- 6. 검증 단계를 건너뛰고 닫힌 결함
project = SHOP AND issuetype = Bug AND status = Done
AND status WAS NOT "In QA"
-- 7. 2개월 이상 방치된 결함
project = SHOP AND issuetype = Bug AND status NOT IN (Done, Closed)
AND updated <= -60d ORDER BY created ASC
-- 8. 담당자 없는 높은 우선순위 결함
project = SHOP AND issuetype = Bug AND priority IN (Highest, High)
AND assignee IS EMPTY릴리즈·리포트
-- 9. 이번 릴리즈 범위 (픽스 버전 기준)
project = SHOP AND fixVersion = "v2.4.0"
-- 10. 릴리즈 이후 발견된 결함 (결함 탈출 후보)
project = SHOP AND issuetype = Bug AND created >= "2026-09-01"
AND affectedVersion = "v2.4.0"
-- 11. 특정 기능 영역의 결함 (결함 밀도 계산용)
project = SHOP AND issuetype = Bug AND component = "결제"
AND created >= startOfMonth(-3)
-- 12. 특정 에픽 아래 전부 (범위 확인용)
"Epic Link" = SHOP-41 OR parent = SHOP-4112번은 Jira 버전·플랜에 따라 필드 이름이 다르다(parent,
"Epic Link", portfolioChildIssuesOf() 등). 자동완성이 제안하는
필드명을 쓰는 것이 가장 확실하다 — 필드명을 입력하고 스페이스를 누르면
사용 가능한 연산자와 값이 뜬다.
필터로 저장하고 공유하기
쿼리를 매번 다시 쓰는 것은 낭비다. 검색 결과 화면에서 필터 저장 을 하면 이름·설명·권한을 지정해 보관할 수 있다.
저장할 때 신경 쓸 것이 권한 이다.
| 설정 | 결과 |
|---|---|
| 조회자(Viewers) | 누가 이 필터를 볼 수 있는가 |
| 편집자(Editors) | 누가 이 필터를 수정할 수 있는가 |
필터를 공유하지 않으면 대시보드에서도 안 보인다. 다음 레슨에서 대시보드를 만들 때 "가젯에서 내 필터가 안 보인다"는 문제를 자주 겪는데, 원인의 대부분이 공유 설정이다. 팀 대시보드에 쓸 필터는 팀 또는 프로젝트 단위로 공유 해두는 것이 기본이다.
이름 짓기에도 규칙을 두면 좋다. 필터가 30개를 넘어가면 목록에서 찾기가
어려워진다. [QA] 재오픈 결함 - 월간 처럼 용도 접두어 + 대상 + 주기
형식이 실무에서 잘 작동한다.
필터는 여러 곳의 재료가 된다
저장된 필터는 검색에만 쓰이지 않는다. 이게 JQL을 배울 가치의 절반이다.
저장된 필터 (JQL)
│
┌────────────┬───┴────┬──────────────┐
▼ ▼ ▼ ▼
대시보드 보드 자동화 규칙 구독(이메일)
가젯의 원천 필터 조건 트리거 조건 정기 리포트- 대시보드 가젯 — 다음 레슨의 주제. 대부분의 가젯이 필터를 입력으로 받는다
- 보드 — 보드에 어떤 항목을 표시할지가 JQL로 정의된다
- 자동화 규칙 — "이 조건에 맞는 항목이 생기면 담당자에게 알림" 같은 규칙
- 구독 — 필터 결과를 정해진 주기로 메일 발송
즉 JQL 한 줄을 잘 써두면 그것이 대시보드가 되고 알림이 되고 리포트가 된다. QA가 "매주 월요일에 방치된 결함 목록을 팀에 공유"하는 일을 수작업으로 하고 있다면, 그건 필터 하나와 구독 설정으로 끝나는 일이다.
함정 몇 가지
1) 상태·필드 이름은 조직마다 다르다.
status = "In QA" 는 그 상태가 있는 프로젝트에서만 동작한다. 남의
블로그에서 가져온 JQL이 안 되는 이유의 대부분이 이것이다. 자동완성으로
실제 값을 확인한다.
2) 하드코딩된 사용자·날짜.
assignee = "kim.qa" 나 created >= "2026-09-01" 은 그 순간에만 맞다.
currentUser() 와 상대 날짜(-7d, startOfMonth())를 쓴다.
3) ~ 로 집계하지 않는다.
부분 일치는 의도치 않은 항목을 포함한다. 지표 계산에는 컴포넌트·레이블
같은 구조화된 필드 를 쓴다. 이건 모듈 6 레슨 5의 결함 밀도 계산이
정확해지려면 반드시 필요한 습관이다.
4) 삭제된 필터가 대시보드를 깨뜨린다. 공유된 필터를 지우면 그것을 쓰는 가젯과 보드가 함께 죽는다. 정리할 때는 참조하는 곳이 있는지 먼저 확인한다.
실무에서 왜 필요한가
JQL을 쓸 줄 아는 QA와 못 쓰는 QA는 회의에서 다른 말을 한다. "결함이 좀 쌓인 것 같습니다"와 "결제 컴포넌트에 60일 이상 방치된 High 이상 결함이 14건이고, 그중 5건은 담당자가 없습니다"의 차이다. 후자는 반박이 아니라 대응을 부른다.
그리고 이 능력은 품질 지표를 실제로 굴리는 유일한 방법 이기도 하다. 모듈 6 레슨 5에서 배운 결함 밀도·탈출률·재오픈율은 개념만으로는 아무것도 아니다. 매달 같은 기준으로 숫자를 뽑을 수 있어야 추세가 되고, 그 추세가 있어야 개선을 말할 수 있다. JQL이 그 뽑아내는 도구다.
한 가지 더. JQL은 프로세스 감사 도구다. "검증 없이 닫힌 결함", "핑퐁이 3회 이상인 결함" 같은 쿼리는 팀의 프로세스가 실제로 지켜지고 있는지를 드러낸다. 이런 것을 조용히 확인하고 데이터로 제시할 수 있는 사람은 QA밖에 없다.