", "suggestion": "텍스트 색상을 더 어둡게 하거나 배경을 더 밝게 조정하세요" } ] } ``` #### 수락 기준 (Acceptance Criteria) - [ ] Playwright로 대상 URL을 로드하고 axe-core를 실행한다 - [ ] WCAG 2.1 AA 기준으로 접근성 위반 사항을 감지한다 - [ ] axe-core의 violations 결과를 한국어 메시지로 변환한다 - [ ] 각 이슈에 WCAG 기준 번호(예: 1.4.3)가 포함된다 - [ ] 점수가 0-100 범위로 계산된다 - [ ] 색상 대비 검사에서 실제 대비율 수치가 포함된다 - [ ] 이슈별 해당 HTML 요소 정보가 포함된다 --- ### F-004: SEO 최적화 검사 - **설명**: 검색엔진 최적화 관련 항목을 검사하고 0-100 점수와 이슈 목록을 제공한다. - **우선순위**: Must - **관련 사용자 스토리**: "웹사이트 관리자로서, 내 사이트가 검색엔진에 최적화되어 있는지 확인하고 싶다, 검색 노출을 개선하기 위해." #### 검사 항목 | # | 검사 항목 | 심각도 기본값 | 설명 | |---|----------|-------------|------| | S-01 | title 태그 | Critical | 존재, 길이 (10-60자), 고유성 | | S-02 | meta description | Major | 존재, 길이 (50-160자) | | S-03 | meta keywords | Info | 존재 여부 (참고용) | | S-04 | OG 태그 | Major | og:title, og:description, og:image 존재 | | S-05 | Twitter Card | Minor | twitter:card, twitter:title 존재 | | S-06 | canonical URL | Major | `` 존재 | | S-07 | robots.txt | Major | /robots.txt 접근 가능 여부 | | S-08 | sitemap.xml | Major | /sitemap.xml 접근 가능 여부 | | S-09 | H1 태그 | Critical | H1 존재, 1개만 존재 | | S-10 | 구조화 데이터 | Minor | JSON-LD, Microdata, RDFa 존재 여부 | | S-11 | favicon | Minor | favicon 존재 여부 | | S-12 | 모바일 친화성 | Major | viewport meta 태그, 반응형 | | S-13 | URL 구조 | Minor | 깔끔한 URL (특수문자 최소) | | S-14 | 이미지 alt 속성 | Major | SEO 관점에서 이미지 설명 존재 | #### 점수 계산 ``` 점수 = 100 - (Critical * 15 + Major * 8 + Minor * 3 + Info * 1) 최소 0점, 최대 100점 ``` #### 출력 구조 ```json { "category": "seo", "score": 68, "total_issues": 6, "issues": [ { "code": "S-04", "severity": "major", "message": "Open Graph 태그가 누락되었습니다: og:image", "suggestion": "를 추가하세요" } ], "meta_info": { "title": "사이트 제목", "title_length": 25, "description": "사이트 설명...", "description_length": 120, "has_robots_txt": true, "has_sitemap": false, "structured_data_types": ["JSON-LD"] } } ``` #### 수락 기준 (Acceptance Criteria) - [ ] HTML 파싱으로 meta 태그, OG 태그, 구조화 데이터를 검사한다 - [ ] robots.txt와 sitemap.xml의 존재 여부를 HTTP 요청으로 확인한다 - [ ] title 길이, description 길이를 측정하고 권장 범위를 벗어나면 이슈로 보고한다 - [ ] H1 태그가 없거나 2개 이상이면 이슈로 보고한다 - [ ] 구조화 데이터(JSON-LD, Microdata) 존재 여부를 감지한다 - [ ] 점수가 0-100 범위로 계산된다 - [ ] 각 이슈에 개선 제안(suggestion)이 포함된다 - [ ] meta_info 객체에 현재 상태 요약이 포함된다 --- ### F-005: 성능/보안 검사 - **설명**: 보안 헤더, HTTPS 적용, 기본 성능 메트릭을 검사하고 0-100 점수와 이슈 목록을 제공한다. - **우선순위**: Must - **관련 사용자 스토리**: "웹사이트 운영자로서, 보안 취약점과 성능 문제를 파악하고 싶다, 안전하고 빠른 서비스를 제공하기 위해." #### 검사 항목 | # | 검사 항목 | 심각도 기본값 | 설명 | |---|----------|-------------|------| | P-01 | HTTPS 사용 | Critical | HTTPS 프로토콜 사용 여부 | | P-02 | SSL 인증서 유효성 | Critical | 인증서 만료/유효성 확인 | | P-03 | Strict-Transport-Security | Major | HSTS 헤더 존재 및 max-age | | P-04 | Content-Security-Policy | Major | CSP 헤더 존재 여부 | | P-05 | X-Content-Type-Options | Minor | nosniff 설정 여부 | | P-06 | X-Frame-Options | Minor | clickjacking 방지 설정 | | P-07 | X-XSS-Protection | Info | XSS 방지 헤더 (deprecated 알림) | | P-08 | Referrer-Policy | Minor | 리퍼러 정책 설정 여부 | | P-09 | Permissions-Policy | Minor | 권한 정책 헤더 존재 | | P-10 | 응답 시간 | Major | 초기 응답 시간 (TTFB) | | P-11 | 페이지 크기 | Minor | HTML 문서 크기 (권장 < 3MB) | | P-12 | 리다이렉트 수 | Minor | 리다이렉트 체인 길이 (권장 < 3) | | P-13 | Gzip/Brotli 압축 | Minor | 응답 압축 적용 여부 | | P-14 | 혼합 콘텐츠 | Major | HTTPS 페이지에서 HTTP 리소스 로드 | #### 점수 계산 ``` 보안 점수 (70% 가중치): HTTPS/SSL(30%) + 보안 헤더(40%) 성능 점수 (30% 가중치): 응답 시간(40%) + 페이지 크기(30%) + 압축(30%) 종합 = 보안 점수 * 0.7 + 성능 점수 * 0.3 최소 0점, 최대 100점 ``` #### 출력 구조 ```json { "category": "performance_security", "score": 78, "total_issues": 4, "sub_scores": { "security": 82, "performance": 70 }, "issues": [ { "code": "P-04", "severity": "major", "message": "Content-Security-Policy 헤더가 설정되지 않았습니다", "suggestion": "CSP 헤더를 추가하여 XSS 공격을 방지하세요" } ], "metrics": { "ttfb_ms": 450, "page_size_bytes": 1250000, "redirect_count": 1, "compression": "gzip", "https": true, "ssl_valid": true, "ssl_expiry_days": 89 } } ``` #### 수락 기준 (Acceptance Criteria) - [ ] HTTPS 사용 여부와 SSL 인증서 유효성을 검사한다 - [ ] 주요 보안 헤더 9개(P-03~P-09)의 존재 여부를 검사한다 - [ ] TTFB(Time To First Byte)를 측정한다 - [ ] 페이지 크기를 측정하고 권장 범위를 초과하면 이슈로 보고한다 - [ ] 응답 압축(Gzip/Brotli) 적용 여부를 확인한다 - [ ] HTTPS 페이지에서 HTTP 리소스 로드(혼합 콘텐츠)를 감지한다 - [ ] 보안/성능 각각의 서브 점수와 종합 점수를 계산한다 - [ ] metrics 객체에 측정값 요약이 포함된다 --- ### F-006: 실시간 검사 진행 상태 - **설명**: 검사 진행 중 4개 카테고리의 진행률을 SSE(Server-Sent Events)로 실시간 스트리밍한다. - **우선순위**: Must - **관련 사용자 스토리**: "사용자로서, 검사가 얼마나 진행되었는지 실시간으로 확인하고 싶다, 검사 완료까지 기다리는 동안 진행 상태를 파악하기 위해." #### SSE 이벤트 구조 ``` event: progress data: { "inspection_id": "uuid", "status": "running", "overall_progress": 45, "categories": { "html_css": { "status": "completed", "progress": 100 }, "accessibility": { "status": "running", "progress": 60, "current_step": "색상 대비 검사 중..." }, "seo": { "status": "running", "progress": 30, "current_step": "robots.txt 확인 중..." }, "performance_security": { "status": "pending", "progress": 0 } } } event: category_complete data: { "inspection_id": "uuid", "category": "html_css", "score": 85, "total_issues": 5 } event: complete data: { "inspection_id": "uuid", "status": "completed", "overall_score": 76, "redirect_url": "/inspections/{inspection_id}" } event: error data: { "inspection_id": "uuid", "status": "error", "message": "검사 중 오류가 발생했습니다" } ``` #### 처리 규칙 (Business Rules) 1. SSE 엔드포인트는 `GET /api/inspections/{inspection_id}/stream` 2. 검사 시작 후 클라이언트가 SSE 연결 3. 각 카테고리의 진행 단계마다 progress 이벤트 발행 4. 카테고리 완료 시 category_complete 이벤트 발행 (부분 결과 제공) 5. 모든 카테고리 완료 시 complete 이벤트 발행 6. 오류 발생 시 error 이벤트 발행 7. SSE 연결 타임아웃: 120초 (검사 최대 시간) #### 수락 기준 (Acceptance Criteria) - [ ] GET /api/inspections/{id}/stream 엔드포인트가 SSE 스트림을 반환한다 - [ ] Content-Type이 text/event-stream이다 - [ ] 4개 카테고리의 개별 진행률(0-100)이 실시간으로 업데이트된다 - [ ] 현재 검사 단계 텍스트(current_step)가 제공된다 - [ ] 카테고리 완료 시 해당 카테고리의 점수가 즉시 제공된다 - [ ] 모든 검사 완료 시 종합 점수와 결과 페이지 URL이 제공된다 - [ ] 오류 발생 시 error 이벤트가 발행된다 - [ ] 프론트엔드에서 SSE 이벤트를 수신하여 프로그레스 바를 업데이트한다 --- ### F-007: 검사 결과 대시보드 - **설명**: 검사 완료 후 종합 점수와 4개 카테고리별 점수를 시각적으로 표시하는 대시보드 페이지. - **우선순위**: Must - **관련 사용자 스토리**: "사용자로서, 검사 결과를 한눈에 파악하고 싶다, 웹사이트의 전반적인 품질 수준을 빠르게 이해하기 위해." #### 대시보드 구성 요소 1. **종합 점수 게이지**: 0-100 원형 게이지 (색상: 0-49 빨강, 50-79 주황, 80-100 초록) 2. **카테고리별 점수 카드**: 4개 카드에 각 카테고리 점수 + 이슈 수 3. **점수 등급 레이블**: A+(90-100), A(80-89), B(70-79), C(60-69), D(50-59), F(0-49) 4. **이슈 요약 바**: Critical/Major/Minor/Info 각각의 개수 막대 5. **검사 메타 정보**: URL, 검사 일시, 소요 시간 #### API 엔드포인트 ``` GET /api/inspections/{inspection_id} ``` #### 응답 구조 ```json { "inspection_id": "uuid", "url": "https://example.com", "created_at": "2026-02-12T10:00:00Z", "duration_seconds": 35, "overall_score": 76, "grade": "B", "categories": { "html_css": { "score": 85, "grade": "A", "total_issues": 5, "critical": 0, "major": 2, "minor": 2, "info": 1 }, "accessibility": { "score": 72, "grade": "B", "total_issues": 8, "critical": 1, "major": 3, "minor": 3, "info": 1 }, "seo": { "score": 68, "grade": "C", "total_issues": 6, "critical": 1, "major": 2, "minor": 2, "info": 1 }, "performance_security": { "score": 78, "grade": "B", "total_issues": 4, "critical": 0, "major": 2, "minor": 1, "info": 1 } }, "summary": { "total_issues": 23, "critical": 2, "major": 9, "minor": 8, "info": 4 } } ``` #### 수락 기준 (Acceptance Criteria) - [ ] 검사 결과 페이지 URL이 `/inspections/{inspection_id}` 형식이다 - [ ] 종합 점수가 원형 게이지 형태로 표시된다 - [ ] 점수에 따라 게이지 색상이 빨강/주황/초록으로 변한다 - [ ] 4개 카테고리별 점수와 이슈 수가 카드 형태로 표시된다 - [ ] 점수 등급(A+, A, B, C, D, F)이 표시된다 - [ ] 심각도별 이슈 개수가 요약되어 표시된다 - [ ] 검사 URL, 일시, 소요 시간이 표시된다 --- ### F-008: 상세 이슈 목록 - **설명**: 검사에서 발견된 모든 이슈를 카테고리별/심각도별로 필터링하여 상세하게 표시한다. - **우선순위**: Must - **관련 사용자 스토리**: "웹 개발자로서, 발견된 이슈들의 상세 내용과 개선 방법을 확인하고 싶다, 문제를 하나씩 수정하기 위해." #### 이슈 목록 기능 1. **카테고리 필터**: 전체 / HTML/CSS / 접근성 / SEO / 성능/보안 2. **심각도 필터**: 전체 / Critical / Major / Minor / Info 3. **정렬**: 심각도 높은 순 (기본값) / 카테고리별 4. **이슈 카드**: 코드, 심각도 배지, 메시지, 해당 요소, 개선 제안 #### API 엔드포인트 ``` GET /api/inspections/{inspection_id}/issues?category=html_css&severity=critical ``` #### 수락 기준 (Acceptance Criteria) - [ ] 전체 이슈 목록이 카드 형태로 표시된다 - [ ] 카테고리별 필터링이 동작한다 (4개 카테고리 + 전체) - [ ] 심각도별 필터링이 동작한다 (Critical/Major/Minor/Info + 전체) - [ ] 기본 정렬은 심각도 높은 순이다 - [ ] 각 이슈에 코드, 심각도 배지, 메시지, 개선 제안이 표시된다 - [ ] 해당 HTML 요소가 코드 블록으로 표시된다 (있는 경우) --- ### F-009: 검사 이력 저장 - **설명**: 모든 검사 결과를 MongoDB에 자동 저장하여 이후 조회 및 비교에 활용한다. - **우선순위**: Should - **관련 사용자 스토리**: "웹사이트 관리자로서, 과거 검사 결과를 다시 볼 수 있길 원한다, 시간에 따른 품질 변화를 추적하기 위해." #### 처리 규칙 (Business Rules) 1. 검사 완료 시 결과를 MongoDB inspections 컬렉션에 자동 저장 2. 저장 항목: URL, 검사 일시, 4개 카테고리 결과(점수, 이슈), 종합 점수 3. 동일 URL에 대한 다중 검사 결과 저장 (덮어쓰기 아님) 4. 저장 용량 관리: URL당 최대 100건, 초과 시 가장 오래된 결과 삭제 #### 수락 기준 (Acceptance Criteria) - [ ] 검사 완료 시 결과가 MongoDB에 자동 저장된다 - [ ] 저장된 결과에 URL, 검사 일시, 전체 점수, 카테고리별 점수가 포함된다 - [ ] 동일 URL로 여러 번 검사해도 각각 개별 레코드로 저장된다 - [ ] 저장된 결과를 inspection_id로 조회할 수 있다 --- ### F-010: 검사 이력 목록 조회 - **설명**: 저장된 검사 이력을 목록 형태로 조회한다. - **우선순위**: Should - **관련 사용자 스토리**: "사용자로서, 이전에 검사했던 URL들의 결과 목록을 보고 싶다, 특정 검사 결과를 다시 확인하기 위해." #### API 엔드포인트 ``` GET /api/inspections?page=1&limit=20&url=example.com ``` #### 파라미터 | 파라미터 | 타입 | 필수 | 기본값 | 설명 | |---------|------|------|--------|------| | page | int | N | 1 | 페이지 번호 | | limit | int | N | 20 | 페이지당 항목 수 (최대 100) | | url | string | N | - | URL 필터 (부분 일치) | | sort | string | N | -created_at | 정렬 기준 | #### 수락 기준 (Acceptance Criteria) - [ ] 검사 이력 목록이 테이블 형태로 표시된다 - [ ] 각 행에 URL, 검사 일시, 종합 점수, 등급이 표시된다 - [ ] 페이지네이션이 동작한다 - [ ] URL 검색 필터가 동작한다 - [ ] 행 클릭 시 해당 검사 결과 상세 페이지로 이동한다 - [ ] 최신 검사 순으로 기본 정렬된다 --- ### F-011: 트렌드 비교 차트 - **설명**: 동일 URL에 대한 과거 검사 결과를 시계열 라인 차트로 비교한다. - **우선순위**: Should - **관련 사용자 스토리**: "웹사이트 관리자로서, 시간에 따른 점수 변화를 차트로 보고 싶다, 개선 노력의 효과를 확인하기 위해." #### API 엔드포인트 ``` GET /api/inspections/trend?url=https://example.com&limit=10 ``` #### 응답 구조 ```json { "url": "https://example.com", "data_points": [ { "inspection_id": "uuid", "created_at": "2026-02-01T10:00:00Z", "overall_score": 72, "html_css": 80, "accessibility": 65, "seo": 70, "performance_security": 73 }, { "inspection_id": "uuid", "created_at": "2026-02-10T10:00:00Z", "overall_score": 78, "html_css": 85, "accessibility": 72, "seo": 68, "performance_security": 86 } ] } ``` #### 수락 기준 (Acceptance Criteria) - [ ] 동일 URL의 과거 검사 결과가 시계열 라인 차트로 표시된다 - [ ] 종합 점수 + 4개 카테고리 점수 라인이 각각 표시된다 - [ ] 각 데이터 포인트에 마우스 호버 시 상세 점수가 툴팁으로 표시된다 - [ ] 데이터 포인트 클릭 시 해당 검사 결과 페이지로 이동한다 - [ ] 최소 2건 이상의 검사 결과가 있어야 차트가 표시된다 - [ ] 검사 결과가 1건이면 "비교할 이력이 없습니다" 메시지 표시 --- ### F-012: PDF 리포트 내보내기 - **설명**: 검사 결과를 PDF 파일로 생성하여 다운로드한다. - **우선순위**: Should - **관련 사용자 스토리**: "프로젝트 매니저로서, 검사 결과를 PDF로 다운로드하고 싶다, 팀이나 클라이언트에게 공유하기 위해." #### PDF 리포트 구성 1. **표지**: 프로젝트명, 검사 URL, 검사 일시 2. **종합 점수 요약**: 종합 점수 + 4개 카테고리 점수 3. **카테고리별 상세**: 각 카테고리의 이슈 목록 (심각도순) 4. **개선 제안 요약**: 우선순위별 개선 제안 리스트 #### API 엔드포인트 ``` GET /api/inspections/{inspection_id}/report/pdf ``` #### 수락 기준 (Acceptance Criteria) - [ ] PDF 파일이 정상적으로 생성되어 다운로드된다 - [ ] Content-Type이 application/pdf이다 - [ ] 파일명이 `web-inspector-{url-slug}-{date}.pdf` 형식이다 - [ ] PDF에 종합 점수, 카테고리별 점수, 이슈 목록이 포함된다 - [ ] PDF에 한국어 텍스트가 올바르게 렌더링된다 - [ ] 파일 크기가 합리적이다 (일반적으로 < 5MB) --- ### F-013: JSON 리포트 내보내기 - **설명**: 검사 결과를 JSON 파일로 다운로드한다. - **우선순위**: Should - **관련 사용자 스토리**: "개발자로서, 검사 결과를 JSON으로 다운로드하고 싶다, 자동화 파이프라인에서 결과를 처리하기 위해." #### API 엔드포인트 ``` GET /api/inspections/{inspection_id}/report/json ``` #### 수락 기준 (Acceptance Criteria) - [ ] JSON 파일이 정상적으로 생성되어 다운로드된다 - [ ] Content-Type이 application/json이다 - [ ] Content-Disposition이 attachment로 설정된다 - [ ] 파일명이 `web-inspector-{url-slug}-{date}.json` 형식이다 - [ ] JSON에 전체 검사 결과(점수, 이슈, 메트릭)가 포함된다 - [ ] JSON 구조가 GET /api/inspections/{id} 응답과 동일하다 --- ## 3. API 엔드포인트 요약 | Method | Path | 기능 ID | 설명 | |--------|------|---------|------| | POST | /api/inspections | F-001 | 검사 시작 | | GET | /api/inspections/{id}/stream | F-006 | SSE 진행 상태 스트림 | | GET | /api/inspections/{id} | F-007 | 검사 결과 조회 | | GET | /api/inspections/{id}/issues | F-008 | 이슈 목록 조회 | | GET | /api/inspections | F-010 | 검사 이력 목록 | | GET | /api/inspections/trend | F-011 | 트렌드 데이터 | | GET | /api/inspections/{id}/report/pdf | F-012 | PDF 리포트 다운로드 | | GET | /api/inspections/{id}/report/json | F-013 | JSON 리포트 다운로드 | | GET | /api/health | - | 헬스체크 | --- ## 4. 비즈니스 규칙 (Global Business Rules) - 모든 API 응답은 JSON 형식 (`Content-Type: application/json`, 리포트 다운로드 제외) - 에러 응답은 `{ "detail": "에러 메시지" }` 형식 - 검사 타임아웃: 카테고리당 60초, 전체 120초 - URL 접근 타임아웃: 10초 - 동시 검사 제한: 없음 (MVP 단계) - 검사 결과는 삭제 기능 없이 영구 보존 (MVP 단계) - 모든 시간은 UTC 기준으로 저장, 프론트엔드에서 로컬 타임존으로 변환 표시 --- ## 5. 비기능 요구사항 | 항목 | 기준 | |------|------| | API 응답 시간 | 검사 시작 API < 2초 | | 검사 소요 시간 | 전체 검사 < 120초 | | SSE 이벤트 지연 | < 500ms | | 동시 사용자 | 최소 10명 (MVP) | | 페이지 로드 | 프론트엔드 초기 로드 < 3초 | | PDF 생성 | < 10초 | | 브라우저 지원 | Chrome, Firefox, Safari, Edge 최신 버전 | | 한국어 지원 | UI 전체 한국어, PDF 리포트 한국어 |