PcDevice 검색 확장: 정규식 제한 및 인보이스 필드 통합
요약
내부 재고 관리 도구(pcview)의 검색 기능을 개선하여 협력자 번호 정규식을 3~6자리 숫자로 제한하고, PcDevice 모델에 인보이스 및 구매 주문서 필드를 통합했습니다. 이 변경은 Prisma 스키마와 글로벌 검색 로직을 업데이트하여 데이터 무결성과 사용자 경험을 향상시킵니다.
핵심 포인트
- 협력자 번호 정규식을 3~6자리로 강화하여 오탐지(false positives)를 방지합니다.
- PcDevice 모델에 invoiceNumber 및 purchaseOrder 필드를 추가하고 검색 가능하게 했습니다.
- Prisma 스키마와 TypeScript 타입을 업데이트하여 전체 코드베이스의 타입 안전성을 확보했습니다.
PcDevice 검색 확장: 정규식 제한 및 인보이스 필드 통합
요약: 협력자 번호(collaborator-number)의 정규식을 3~6자리 숫자만 허용하도록 수정하고, PcDevice 모델에 invoiceNumber와 purchaseOrder 필드를 추가하여 검색 가능하게 하고 장치 상세 시트에 표시되도록 했습니다. 이 변경 사항은 Prisma 스키마, 글로벌 검색 로직, 디바이스 스토어 훅, 그리고 장치 세부 정보를 렌더링하는 UI 컴포넌트에 영향을 미칩니다.
문제점
내부 재고 관리 도구(pcview)는 사용자에게 길이 제한 없이 협력자 번호를 검색할 수 있도록 허용했습니다. 이로 인해 사용자가 긴 숫자 문자열(예: 티켓 ID)을 입력했을 때, 느슨한 /^\\\d{3,}$/ 패턴과 일치하여 오탐지(false positives)가 발생했습니다. 또한, 제품 팀은 구매 정보—invoiceNumber(factura)와 purchaseOrder(ODC)—를 각 PcDevice에 저장하고 검색 가능하게 할 것을 요청했지만, 스키마와 UI에서는 이 필드들을 노출하지 않았습니다.
증상:
- “1234567” (7자리 숫자)로 검색했을 때, 협력자 번호가 최대 6자리임에도 불구하고 협력자 결과가 반환되었습니다.
- 인보이스나 구매 주문서로 장치를 조회할 방법이 없었습니다.
- 장치 상세 시트에는 구매 메타데이터가 표시되지 않아, 사용자가 외부 스프레드시트를 통해 교차 참조해야 했습니다.
처음 시도한 것
인보이스 필드의 경우, 저는 마이그레이션 스크립트에 원시 SQL(raw SQL) 컬럼을 직접 추가하여 Prisma를 우회하는 빠른 스키마 마이그레이션을 시도했습니다. 이는 DB 측면에서는 작동했지만, 코드베이스의 TypeScript 타입은 새로운 컬럼에 대해 알지 못했기 때문에 use-devices-store.ts와 DeviceDetailSheet.tsx에서 컴파일 타임 오류가 발생했습니다.
두 접근 방식 모두 유지보수성이 떨어지고 타입 안전성(type-safe)을 확보하지 못했으므로, 저는 변경 사항을 되돌리고 근본적인 원인, 즉 스키마와 공유 검색 유틸리티에서 문제를 해결하기로 했습니다.
1. Collaborator Number 정규식 강화
파일: src/features/search/GlobalSearch.tsx
@@ -58,7 +58,9 @@ interface CollaboratorAssets {
}
...
- 이유: 업데이트된 정규식은 비즈니스 규칙을 검증 계층(validation layer)에서 직접 강제하여 UI 측의 길이 제한(length guards) 필요성을 제거합니다.
- 영향: 검색 디스패처는 이제
123456과 같은 쿼리를 협업자 번호로 올바르게 분류하고,1234567은 무시한 채 일반 장치 검색으로 폴백(fallback)합니다.
2. Prisma 모델 확장
파일: prisma/schema.prisma
@@ -63,6 +63,12 @@ model PcDevice {
// en el sync normal (no está en RawPcDevice).
collaboratorNumber String?
...
- 마이그레이션:
npx prisma migrate dev --name add-invoice-fields를 실행하여 적절한 ALTER TABLE 구문을 생성했습니다. - 타입 안전성: Prisma는 이제 생성된
PcDevice타입에invoiceNumber와purchaseOrder를 포함하며, 이는 전체 TypeScript 스택으로 전파됩니다.
3. 장치 스토어 훅 업데이트
파일: src/hooks/use-devices-store.ts
@@ -27,6 +27,8 @@ export interface PcDeviceRow {
assignedTechnician: string | null;
custodyNote: string | null;
...
- 근거: 이 훅은 Prisma 클라이언트에서 행(row)을 가져와 UI 친화적인 객체로 매핑합니다. 두 필드를 추가함으로써, 별도의 매핑 로직 없이 DB에서 UI까지 데이터가 전달되도록 보장했습니다.
4. 새 필드 검색 가능하게 만들기
4. 새 필드 검색 가능하게 만들기
글로벌 검색 서비스는 쿼리 유형에 따라 동적 Prisma 쿼리를 생성합니다. 저는 구매 메타데이터를 위한 브랜치를 추가했습니다:
// src/features/search/GlobalSearch.tsx (발췌)
if (isInvoiceNumber(query)) {
return prisma.pcDevice.findMany({
...
헬퍼 함수 isInvoiceNumber는 협업자 로직을 반영합니다:
const isInvoiceNumber = (q: string) =>
/^[A-Z0-9]{5,12}$/i.test(q.trim()); // 간단한 영숫자 패턴
5. 상세 시트에 구매 정보 표시
파일: src/features/devices/DeviceDetailSheet.tsx
@@ -37,6 +37,13 @@ interface DeviceDetail {
lastContactTime: string | null;
department: string | null;
...
- 디자인 결정: UI 컴포넌트를 순수하고 선언적으로 유지했습니다. 즉, 값이 존재하는 경우에만 새로운 필드를 렌더링하여 기존 레이아웃을 보존했습니다.
6. 종단 간(End-to-End) 테스트
변경 사항 적용 후 통합 테스트 스위트(npm run test:e2e)를 실행했습니다. 새 검색 경로는 예상되는 장치를 반환했으며, 상세 시트는 기존 테스트를 깨뜨리지 않고 인보이스 데이터를 표시했습니다.
PASS src/features/search/GlobalSearch.test.tsx
PASS src/features/devices/DeviceDetailSheet.test.tsx
모든 124개 테스트가 통과했습니다.
핵심 시사점 (Key Takeaway)
UI 검사를 패치하는 대신, 소스(정규식, 스키마)에서 도메인 제약 조건을 검증하세요. 데이터 모델, 유효성 검사 로직, UI 렌더링을 일치시킴으로써, 과도하게 매칭되는 쿼리 같은 미묘한 버그를 방지하고 중복된 유효성 검사 코드를 제거하는 단일 진실 공급원(single source of truth)을 얻게 됩니다.
다음 계획 (What's Next)
- 분할 검색 UI (Faceted Search UI): 사용자가 패턴 감지에 의존하기보다
Tags: #vibecoding #buildinpublic #typescript #react #prisma #frontend #backend #search #database
제(My) Build in Public 시리즈의 일부 — 멕시코 플라야 델 카르멘에서 SaaS 프로젝트를 구축하는 실제 과정을 공유합니다.
레포: zaerohell/pcview · 2026-10-05
#playadev #buildinpublic
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기