feat: 직업과 신체 부위로 메인 검색 화면을 바꿉니다 - #13
Draft
wagurano wants to merge 24 commits into
Draft
Conversation
KSCO 직업분류와 추출 데이터를 연동한 새 메인 검색 화면을 추가합니다. 판정서에 직업·근무조건·신체부위 정보를 추가하고 이를 이용한 검색 화면을 만듭니다.
.ruby-version 대신 여러 도구 버전을 한 곳에서 관리하는 .tool-versions를 사용합니다.
메인 화면(/)의 직업·근무조건·신체부위·사망여부·신청서유형 필터와 KSCO 직업분류 연동을 위해 필요한 rake task 실행 순서를 안내합니다.
bin/dev(Procfile.dev 실행)가 Homebrew의 overmind와 낡은 asdf 셰임(다른 프로젝트의 overmind gem용, ruby 3.4.x)이 충돌해 "No version is set for command overmind"로 실패했습니다. mix4처럼 overmind를 프로젝트 gem으로 관리하면 bundle exec가 asdf 셰임보다 먼저 이 ruby 버전의 gem bin을 찾아 충돌 없이 해결됩니다.
burden_body_part는 LLM이 추출한 파이프 구분 자유 텍스트라 실 데이터 기준 distinct 토큰이 1,000개를 넘어 체크박스가 감당할 수 없이 많았습니다. 빈도 상위 12개만 체크박스로 노출하고, 나머지는 네이티브 <datalist> 자동완성 텍스트 입력으로 찾도록 바꿉니다. 자유 텍스트 필터도 기존 체크박스와 동일한 파이프 경계 인식 LIKE 매칭을 재사용해 "목" 검색이 "손목"까지 잘못 걸리지 않습니다.
목록 각 행의 사망 배지가 체크박스 라벨("사망 사례만 보기")을 그대로 재사용해
행마다 반복 노출되던 문제를 고쳤습니다. 배지 전용 로케일 키(death_badge)를
따로 두고, <mark>의 기본 음영 스타일 대신 <span>을 씁니다.
KSCO 코드 태그가 목록 한 줄을 과도하게 길게 만들어 가독성을 해쳐서 뺍니다. 더 이상 목록에서 ksco_codes를 렌더링하지 않으므로 N+1 방지용 eager load (includes(:ksco_codes))도 함께 제거합니다. 판정서 상세 화면에는 영향 없습니다.
disease_case_ksco_codes에 FK가 걸려있는데(20260824000002 마이그레이션) DiseaseCase#has_many :disease_case_ksco_codes에 dependent 옵션이 없어서, KSCO 매핑이 붙은 판정서를 destroy하면 ActiveRecord::InvalidForeignKey가 났습니다. dependent: :delete_all로 매핑을 함께 정리합니다.
burden_body_part_options와 burden_body_part_datalist_options가 각각 burden_body_part_token_counts를 따로 호출해, 메인 화면 접속마다 같은 집계(비어있지 않은 burden_body_part 전체 pluck + 파이프 split)를 두 번 수행했습니다. main 액션에서 한 번만 계산해 두 메서드에 넘겨 재사용합니다.
DiseaseCases::MainSearchable.apply_main_filters가 MCP search_disease_cases_tool처럼 legacy DiseaseCase.search 결과에 재사용하도록 이미 public으로 열려있어(주석 참고), 같은 패턴으로 index 액션에도 적용합니다. /search에서 기존 심의결과·질병분류· 신체부위·판정일 필터에 더해 직업(직종명/담당업무), 부담 신체 부위(체크박스+자동완성), 사망 여부, 신청서 유형까지 조합해 검색할 수 있습니다. /(메인 화면)의 동작은 그대로입니다.
RESTful 관례상 index는 컬렉션의 기본(루트) 목록 액션이어야 하는데, 지금까지는 거꾸로 /search(상세 검색)가 index를, /(메인 화면)가 main을 썼습니다. main을 index로, 기존 index(상세 검색)를 search로 바꿔 라우트와 액션 이름이 일치하도록 정리합니다. 뷰 템플릿(index.html.erb ↔ search.html.erb)과 결과 행 부분 템플릿(index/_disease_case.html.erb ↔ search/_disease_case.html.erb)도 액션 이름에 맞춰 함께 옮겼습니다. root_path/search_path 라우트 헬퍼와 동작은 그대로입니다.
- burden_body_part: distinct 토큰 1,174개 실측 후 상위 12개 체크박스 + <datalist> 자동완성으로 확정된 최종 결정 반영 (3.2절, 6.1절) - DiseaseCase#has_many :disease_case_ksco_codes에 dependent: :delete_all 추가 (코드 리뷰에서 발견된 FK 오류) 반영 (4.2절) - 컨트롤러/concern 코드 스니펫을 실제 구현과 일치하도록 갱신 (5.2절, 5.3절) - 결과 목록에서 KSCO 컬럼 제거, 사망 배지 버그 수정 반영 (8.1절) - /search에 메인 화면 필터 통합 반영 (5.2절, 6.1절, 8.2절) - Phase 1~5 체크리스트를 완료로 표시하고, QA·코드 리뷰 후속 수정을 다루는 Phase 6 신설 (9절) - 리스크 표·결론에 위 변경 사항 정리 (10절, 11절)
상단 네비게이션에 이미 /search로 가는 "검색" 링크가 있어 중복입니다. 더 이상 쓰지 않는 go_to_advanced_search 로케일 키도 함께 제거합니다.
/search에 메인 화면 필터를 통합한 김에(6a74c77), 결과 목록도 같은 정보를 보여주도록 맞춥니다. 기존 심의결과·신체부위·질병분류·신청질병·심의연도·원문 컬럼 앞에 배치했습니다.
q가 required: true라 "사망한 버스운전원 사례 전부"처럼 순수 구조화 필터 검색을 시도해도 actionmcp 프레임워크 단에서 "내용을 입력해 주세요"로 막혀 우리 코드에 도달하지도 못했습니다(코드 리뷰에서 발견·재현 확인). 모델 레이어는 q가 없어도 이미 정상 동작하므로(main_search가 raw_query blank 시 scope=all로 처리) q를 optional로 바꿉니다.
apply_main_filters/main_search_params는 employment_type/work_type/ work_relevance_eval/ksco_code를 이미 받고 있었지만 어느 화면(/, /search)에도 입력 필드가 없어 URL을 직접 조작하거나 MCP로만 접근 가능했습니다(코드 리뷰에서 발견·확인). - employment_type(distinct 1,583개)/ksco_code(약 500개, KscoCode 전체)는 burden_body_part_text와 같은 <datalist> 자동완성 텍스트 입력으로 노출. 값을 목록에서 그대로 골라 제출하므로 기존 exact match 그대로 둬도 된다. - work_relevance_eval은 추출 스키마상 6개뿐인 진짜 enum이라 <select> 드롭다운으로. - work_type은 distinct 값이 14,582개로 사실상 자유 텍스트라(예: "02:30~11:30 (평일 및 토요일)") <datalist>에 담을 수 없다. 순수 텍스트 입력으로 노출하고, exact match였던 필터를 job_name/job_description과 같은 LIKE 부분 일치로 바꿨다 — 그대로 뒀으면 직접 입력한 값이 한 글자만 달라도 0건이 됐을 것이다.
메인 화면은 간단한 검색을 위한 화면이라 4개 필드를 빼고, 상세 검색(/search)에만 남깁니다. 직전 커밋을 통으로 되돌리는 대신 뷰 마크업만 빼고, 그 필드들만 쓰는 옵션 조회(employment_type_options/ksco_code_options 등)도 /search에서만 계산하도록 컨트롤러를 분리해 메인 화면에서 불필요한 쿼리가 돌지 않게 했습니다. work_type LIKE 매칭 수정과 MCP q optional 수정은 그대로 유지됩니다.
사용자가 KSCO 직업분류 코드를 외워서 검색할 이유가 없습니다. /search에 있던 ksco_code 텍스트+datalist 입력과 관련 옵션 조회(ksco_code_options)를 제거합니다. apply_main_filters/main_search_params의 ksco_code 파라미터 자체는 그대로 두어 MCP 클라이언트(직업 설명을 KSCO 코드로 매핑해 구조화 검색)는 계속 쓸 수 있습니다.
- employment_type도 ksco_code처럼 사용자가 직접 골라 검색할 이유가 없어(값이 1,583개로 잘게 쪼개져 있음) /search 폼에서 뺍니다. apply_main_filters/ main_search_params는 그대로 둬서 MCP·쿼리스트링으로는 계속 필터링됩니다. - 메인 화면(/)과 상세 검색(/search) 결과 목록 둘 다에 근무 형태(work_type)· 업무관련성 평가(work_relevance_eval) 컬럼을 추가합니다.
메인 화면(/) 결과 목록에는 원문 링크가 아예 없었고, 상세 검색(/search)에는 맨 끝에 있었습니다. 둘 다 첫 번째 컬럼으로 옮겨서 판정서 원문에 바로 접근할 수 있게 합니다.
q를 optional로 바꾼 뒤(d63f131) q도 구조화 필터도 하나 없이 호출하면 DiseaseCase.search(q: nil)이 스코프 없이 전체 코퍼스를 반환해, LLM이 사용자 발화에서 필터를 하나도 못 뽑아냈을 때 6만여 건을 confidence_score 0.5로 정상 검색 결과처럼 내보내는 회귀가 있었다(코드 리뷰에서 발견·재현 확인: 61,821건 반환). q와 모든 구조화 필터가 다 비어있으면 에러를 반환하도록 가드를 추가한다.
solid_mcp 0.5.0의 플랫폼별 precompiled 바이너리는 ruby < 3.5.dev로 제한돼 있어 4.0.6에서는 매번 소스(Rust 익스텐션)를 빌드해야 했다(커밋 2c05d77에서 이 문제로 Dockerfile에 Rust 툴체인 설치 단계를 통째로 추가했었음). Ruby 3.4.9는 3.5 미만 중 최신 안정 버전이고 로컬에 이미 설치돼 있으며, mix4도 이미 같은 버전을 쓴다. - .tool-versions: ruby 4.0.6 → 3.4.9 - Dockerfile: RUBY_VERSION 3.4.9로, Rust 툴체인 설치 블록과 libclang-dev 제거 - Gemfile.lock: `bundle lock --update solid_mcp`로 arm64-darwin/x86_64-linux/ x86_64-linux-musl 플랫폼별 spec 복원 검증: - Gemfile.lock에 고정된 gem 155개 전부 required_ruby_version이 3.4.9와 호환 (Bundler.load.specs로 확인, 영향받는 다른 gem 없음) - bin/rails test: 기존에도 있던 무관한 실패 3건 외 전부 통과(회귀 없음) - docker build: Rust 툴체인 없이 성공. solid_mcp가 (아마도 FFI 폴백 경로로) ~5초 만에 설치되고, Rails runner로 SolidMCP::MessageWriter가 정상 기동/종료됨
README: - 루비 버전 4.0.6 → 3.4.9 - /search가 이제 메인 화면의 구조화 필터 전부(+ 근무형태·업무관련성 평가)를 포함한다는 점, 고용형태·KSCO 코드는 웹 UI에 없다는 점 반영 - search_disease_cases MCP 툴 설명에 q optional + 빈 호출 거부 동작 추가 docs/workercare-search.plan.md: - 컨트롤러 코드 스니펫을 set_common_filter_options/set_advanced_filter_options 분리 구조로 갱신 (5.2절) - 필터 UI 표에 노출 화면 컬럼 추가하고 employment_type/work_type/ work_relevance_eval/ksco_code의 최종 노출 범위(두 차례 조정 있었음) 반영 (6.1절) - 결과 목록 변경사항(원문 컬럼 추가+맨 앞 배치, 근무형태·업무관련성평가 컬럼, "상세 검색(기존 화면)" 링크 제거) 반영 (8.1절, 8.2절) - MCP q-optional 변경과 그 직후 발견된 빈 호출 회귀를 리스크 표·Phase 6에 추가 - 결론(11절)에 최종 필터 분포와 현재 루비 버전 반영
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
개요
/(전문 검색 화면)를/search로 옮기고, 직업, 근무조건, 신체부위, 사망 여부, 신청서 유형으로 판정서를 검색 화면을 만듭니다.기존 검색은 심의결과, 질병분류, 신체부위, 판정일을 필터할 수 있어서, "내 직업/하는 일과 비슷한 사례"를 찾지 못하였습니다. cerebras 에서 gemma4 api 로 비어있지 않는 55,377건 추출하여 직업, 근무조건, KSCO(표준직업분류) 매핑하였고 한국표준직업분류 코드 체계를 활용하였습니다.
extract_disease_cases_details_cerebras-ksco.csv(LLM으로 추출 직업, 근무조건, KSCO 매핑)ksco-level-4-details.csv(한국표준직업분류 코드 체계)DiseaseCase)에 컬럼 추가합니다job_name,job_description,employment_type,work_type,job_tenure_months,weekly_work_hours,daily_work_hours,burden_body_part,bad_posture,heavy_lifting,max_item_weight,daily_total_weight,other_harmful_factors,work_relevance_eval,aggravating_factors,main_reasoning,death_status,application_typeKscoCode,DiseaseCaseKscoCode(업무상 질병 판정서와 KSCO 코드를 매핑합니다)DiseaseCasesController#main(/), 기존#index는/search로 라우트만 이동app/mcp/tools/search_disease_cases_tool.rb에job_name/job_description/death_status/ksco_code구조화 필터 추가, 기존work_match?/symptom_match?같은 ad-hoc 키워드 매칭 제거후속 작업
application_type), 근무 형태(work_type) 컬럼을 분석하고 enum 형태처럼 바꿉니다.참고 사항
.ruby-version대신.tool-versions를 사용합니다.solid_mcp 0.5.0의 플랫폼별 사전 컴파일 바이너리가ruby < 3.5.dev로 제한돼 있어 4.0.6에서는 매번 소스(Rust)로 빌드해야 했고, 이 때문에Dockerfile에 Rust 툴체인 설치까지 추가돼 있었습니다. 3.4.9로 내려 사전 컴파일 바이너리를 쓰도록 하고, Dockerfile의 Rust 툴체인 설치 단계를 제거했습니다(Docker 빌드 시간 단축).Gemfile.lock에 고정된 gem 155개 전부 3.4.9와 호환되는 것을 확인했습니다.<datalist>사용하고 텍스트 입력(burden_body_part_text)으로 자동완성합니다.병합 전후 체크리스트
/에서 직업(직종명)·하는일(담당 업무) 텍스트 필터, 아픈 신체 부위 체크박스(상위 12개)와 자동완성 텍스트 입력, 사망 여부 토글, 신청서 유형 드롭다운을 조합하여 검색합니다./search(기존 전문 검색 화면) 동작 확인합니다search_disease_cases툴에job_name/death_status/ksco_code파라미터를 넘겨 정상 필터링되는지 확인합니다.병합 시점
빠를수록 좋습니다
배포 전후 체크리스트
bin/rails db:migrate로 새 마이그레이션 4개(특히disease_cases_extracted_fts가상 테이블)가 정상 적용됐는지 확인합니다.판정서 없음,KSCO 코드 없음경고는 참조 데이터 누락으로 소량이면 정상이지만, 대량으로 나오면 CSV 경로나 매칭을 다시 확인합니다.