# 킨텍스 백엔드 — M2~M5 매퍼 배선 + 계약 확장 (Phase C, D-4) > 작성: kintex-backend-dev · 근거: `_workspace/01_backend_contracts.md`·`_workspace/02_db_schema.md`·`_workspace/02_frontend_progress.md`(계약 공백 5건)·IMPLEMENTATION_BACKLOG C-M2~C-M5 > 대상: **frontend·qa 대조**. 이번 회차는 db-engineer가 완성한 MyBatis 매퍼에 ServiceImpl을 배선해 **M2~M5·인증의 501(NOT_IMPLEMENTED)을 200으로 전환**하고, 프론트가 지적한 계약 공백을 **additive**(하위호환)로 보강한다. 스키마 마이그레이션·2FA·프론트·build.gradle 미변경. 커밋 없음(파일만). --- ## 1. 501 → 200 전환된 엔드포인트 | 모듈 | 메서드 | 엔드포인트 | 배선 요지 | |---|---|---|---| | 인증 | login | `POST /api/auth/login` | `UserMapper.findAuthByEmail` + BCrypt 대조 + `findEventRoles`→토큰·워크스페이스 | | 인증 | listWorkspaces | `GET /api/auth/workspaces` | `findEventRoles(userId)`→WorkspaceDto[] | | 인증 | acceptInvite | `POST /api/auth/accept-invite` | 등록업체 검증(`findCompanyRegistered`)→토큰 재발급. ★초대코드 매핑은 후속(아래 TODO) | | M2 | getLayout | `GET …/halls/{hallId}/layout` | `findLayout`+`findBooths`(GeoJSON 파싱)+`HallMapper`+검증 요약 | | M2 | saveLayout | `PUT …/layout` | 폴리곤→WKT `upsertLayout`+`replaceBooths`, 저장 후 규정 재검증 반영 | | M2 | validate | `POST …/layout/validate` | `minAisleWidth`·`countExitsBlocked`+부스 최댓값→룰엔진 M2 | | M2 | autoGenerate | `POST …/layout/auto-generate` | 제약 기반 그리드 패킹으로 **정확히 optionCount(기본 3)안** + S7 발행 | | M2 | applyOption | `POST …/layout/apply-option` **(신규)** | 선택/병합 부스열→새 버전 저장+규정 재검증 | | M3 | getDesign | `GET …/booths/{boothId}/design` | `findDesign`+spec(jsonb) 역직렬화 | | M3 | saveDesign | `PUT …/design` | spec 직렬화 `upsertDesign`(버전 next), 최신본 반환 | | M4 | computeWiring | `POST …/utility/wiring` | `isAssumedTrench`+단말별 `shortestPath`(LineString 파싱)·색상 규약 | | M4 | submitOrder | `POST …/utility/order` | `quote`+`UtilityOrderMapper.insertOrder`(영속)+위치표시도 URL·마감/릴레이 고지 | | M4 | getOrder | `GET …/utility` | `UtilityOrderMapper.findOrder`(부스 최신) | | M5 | listByBooth | `GET …/render-jobs` | `RenderJobMapper.findByBooth`→갤러리 목록(워터마크 항상 포함) | | M5 | publish(보강) | `POST …/render` | 쿼터 정본을 `countSucceededByEvent`(DB DONE)로 전환 + `insertJob` 내구 이력 | | M5 | callback(보강) | `POST /api/internal/render/callback` | `updateStatus` 내구 갱신(쿼터는 DONE 집계에서 자동) | --- ## 2. 계약 확장 (additive · 하위호환 · 프론트 gap 해소) 모두 `@JsonInclude(NON_NULL)`로 **기존 필드 불변, 신규 nullable 필드만 추가**. ### 2-1. gap #1 — 위반 오버레이 좌표 (`rules.Violation`) - 추가 필드: `boothId`(String, nullable) · `anchor`([x,y] 미터, nullable). - **부스 단위 규칙(HEIGHT_MAX·FLOOR_LOAD)** 위반에 한해 해당 부스 id + 폴리곤 중심 앵커를 부여. 레이아웃 단위(AISLE_WIDTH_MIN·EXIT_ACCESS)는 null(캔버스에서 목록 핀만). - 프론트: `violations[].anchor` 존재 시 캔버스 핀 위치로 사용, 없으면 기존처럼 목록 핀만. ```json { "pin":1, "code":"HEIGHT_MAX", "severity":"block", "measured":"측정 5.4m", "boothId":"b-102", "anchor":[3.0,1.5] } ``` ### 2-2. gap #2 — 홀 치수·트렌치 (`m2.dto.LayoutDto.hall` = `HallInfo`) - `LayoutDto`에 `hall` 필드 추가(신규 `HallInfo`). 캔버스 하드코딩(126×90m·트렌치 그리드) 제거 근거. - `HallInfo`: `hallId·label·dimsM[w,d]·ceilingM·targetBoothCount·floorLoadTPerM2·assumedTrench·trenches[]·exits[]`. - `HallInfo.Trench`: `{trenchId,x,y,assumed,supplies[power|water|air|network|gas]}` · `HallInfo.Exit`: `{x,y,clearanceM}`. - 원천: 신규 `HallMapper`(hall·trench·hall_exit read-only). `assumedTrench=true`면 SCR-07 "가정 트렌치" 배지(PLANNING R4). ```json "hall": { "hallId":"H7","label":"제2전시장 홀7","dimsM":[126,90],"ceilingM":12, "targetBoothCount":510,"floorLoadTPerM2":5.0,"assumedTrench":true, "trenches":[{"trenchId":"H7-T-3-3","x":3,"y":3,"assumed":true,"supplies":["power","water","air","network","gas"]}], "exits":[{"x":63,"y":0,"clearanceM":3.0}] } ``` ### 2-3. gap #3 — 자동배치 안의 부스 + 적용 경로 (`AutoLayoutOption.booths` + apply-option) - `AutoLayoutOption`에 `booths`(BoothDto[]) 추가 — "이 안으로 편집 시작"/병합 소스. - 신규 `POST …/layout/apply-option` (ORGANIZER): body `ApplyOptionRequest {name, booths[]}` → 새 버전 저장 + 규정 재검증된 `LayoutDto`. - **3안 흐름 마감**: auto-generate(3안+각 booths) → 클라 선택/구역 병합(booths 합성) → apply-option(영속+재검증). 단일 안 적용도 동일 엔드포인트. --- ## 3. backend 추가 매퍼 (db-engineer 계약 §3 밖 — 기존 테이블 read/write, 마이그레이션 불변) > db-engineer 인수·정본화 요청. 모두 V2~V5 기존 테이블만 사용. | 매퍼 | 메서드 | 테이블 | |---|---|---| | `m2.mapper.HallMapper` | findHall·findTrenches·findExits | hall·trench·hall_exit (read-only) | | `m4.mapper.UtilityOrderMapper` | insertOrder·findOrder | utility_order (V3) | | `auth.mapper.UserMapper`(+1) | findCompanyRegistered | company (read-only) | --- ## 4. 남은 TODO (경계·후속) - **초대코드→행사/부스/역할 매핑**: invite 테이블·매퍼 미제공 → acceptInvite는 등록업체 게이트 + 현재 역할 재발급까지만. 역할 upsert는 매핑 매퍼 인수 시 배선(db-engineer). - **위치표시도 PDF(M4-3)**: submitOrder는 신청 영속 + 자산 URL(`…/utility/location-diagram`)·고지 반환. 실제 PDF 렌더는 문서 큐(후속). 현재 해당 GET 자산은 미구현(URL만). - **wiring 지오메트리 영속**: computeWiring은 산출·반환만. utility_order.wiring(MultiLineString) 저장은 후속. - **자동배치 최적화**: 현재 결정적 그리드 패킹(3안=통로폭/규격 변주, 무위반 골격). 제약 솔버 고도화는 후속(흐름은 성립). - **트렌치 실측(R4)**: 가정 그리드 유지(is_assumed=true). CAD 추출 교체 시 HallInfo.assumedTrench=false 자동 반영. ## 5. 검증 - `./gradlew compileJava` · `./gradlew build -x test` 통과. 매퍼 id/namespace 정합 확인. PostgreSQL 미기동으로 런타임 왕복은 dev 배포 후. - 보안 불변 유지: password_hash/otp 응답 제외, 스택트레이스 미노출(요약 메시지), AI 이미지 워터마크·고지 항상 포함, 워커 에러 요약화.