perf(B04): 응답 압축 + 좌표 자릿수 정리로 내려보낼 양 축소
DB·파일에는 뜻 없는 자릿수가 붙어 있고(1.4000000000000001 꼴) 서버는 응답을 전혀 압축하지 않고 있었음. B05 한 번 진입에 118MB 수신(2026-09-06 실측). - main.py: 압축 미들웨어(1KB 이상). 3D 예상형상(/corridor)은 예외 — 숫자 배열이라 절반만 줄면서 압축에 493ms 듦(18.6->9.4MB). - common_util_json: round_floats(value, digits) + LONLAT_DIGITS 7(1.1cm) · METRE_DIGITS 6. 저장 파일은 그대로 두고 내려보낼 때만 줄임. 브라우저가 다시 계산에 넣는 값(종횡단 지반선)은 줄이면 안 된다고 머리에 경고 적음. - 도엽 GeoJSON: 자릿수만 줄인 표시용 사본(.display.geojson)을 한 번 만들어 서빙. 원본이 새로 깔리면 자동 재생성, 실패하면 원본 그대로. 등고선 도엽 65.9->36.8MB, 압축까지 11.3MB. - 배수유역 응답: 위경도 7자리. 151->100KB, 압축 22KB. ruff 통과, 테스트 390 통과·17 건너뜀. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ from common_util.common_util_drainage_pipes import (
|
||||
save_detail_basins,
|
||||
save_pipe_points,
|
||||
)
|
||||
from common_util.common_util_json import LONLAT_DIGITS, round_floats
|
||||
from common_util.common_util_route_geometry import StructureCandidate
|
||||
from config.config_system import DRAINAGE_PIPE_MAX_SPACING_M, DRAINAGE_PIPE_MIN_SPACING_M
|
||||
|
||||
@@ -122,7 +123,23 @@ def _payload(
|
||||
points: list[PipePoint],
|
||||
saved: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""관 목록과 세부유역을 화면 좌표(WGS84)로 정리한다."""
|
||||
"""관 목록과 세부유역을 화면 좌표(WGS84)로 정리한다.
|
||||
|
||||
좌표는 위경도 7자리(약 1.1cm)로 맞춰 내보낸다 — 저장된 값에는 뜻 없는 자리가 붙어 있어
|
||||
응답이 151KB → 100KB(압축까지 얹으면 22KB)가 된다(2026-09-06 실측). 화면이 그리는 값이고
|
||||
다시 계산에 넣지 않으므로 줄여도 결과가 안 갈린다.
|
||||
"""
|
||||
return round_floats(_raw_payload(project_id, context, detail, points, saved), LONLAT_DIGITS)
|
||||
|
||||
|
||||
def _raw_payload(
|
||||
project_id: UUID,
|
||||
context: DrainageContext,
|
||||
detail: DrainageDetail,
|
||||
points: list[PipePoint],
|
||||
saved: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""자릿수를 줄이기 전의 응답 본문."""
|
||||
to_lonlat = context.to_lonlat
|
||||
return {
|
||||
"status": "success",
|
||||
|
||||
@@ -10,6 +10,7 @@ from fastapi.responses import JSONResponse
|
||||
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
from common_util.common_util_http_cache import cached_file_response
|
||||
from common_util.common_util_json import LONLAT_DIGITS, round_floats
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from config.config_db import get_db_pool
|
||||
|
||||
@@ -150,7 +151,13 @@ async def get_project_geojson(
|
||||
if layer in _SHEET_GEOJSON_FILES:
|
||||
# 도엽 산출물은 자르거나 줄이지 않고 파일 그대로 보낸다(2026-08-01 사용자 지시).
|
||||
# 재직렬화만 건너뛰어도 요청당 2초가 사라지고, ETag로 두 번째부터는 304가 된다.
|
||||
return cached_file_response(request, filepath, "application/geo+json")
|
||||
# 다만 **자릿수만 줄인 사본**을 한 번 만들어 그것을 보낸다(2026-09-06) — 형상은
|
||||
# 그대로고 뜻 없는 자리만 사라진다.
|
||||
return cached_file_response(
|
||||
request,
|
||||
await asyncio.to_thread(_display_copy, filepath),
|
||||
"application/geo+json",
|
||||
)
|
||||
|
||||
if layer == "등고선":
|
||||
simplified_filepath = target_dir / "등고선_bounds_simplified.geojson"
|
||||
@@ -349,3 +356,36 @@ async def build_sheet_surface(project_id: UUID, request: Request) -> dict[str, A
|
||||
await connection.rollback()
|
||||
raise
|
||||
return {"status": "success", "method": method, "surface_model_ids": model_ids}
|
||||
|
||||
|
||||
def _display_copy(source: Path) -> Path:
|
||||
"""도엽 GeoJSON 의 **표시용 사본**(좌표 자릿수만 줄인 것) 경로를 돌려준다.
|
||||
|
||||
저장된 파일에는 `1.4000000000000001` 꼴로 뜻 없는 자리가 붙어 있다 — 위경도 7자리
|
||||
(1.1cm)로 맞추면 형상은 그대로면서 등고선 도엽이 65.9MB → 36.8MB 가 된다(2026-09-06 실측).
|
||||
압축까지 얹으면 11.3MB.
|
||||
|
||||
사본은 **한 번만** 만든다(66MB 기준 약 3초). 원본이 새로 깔리면 수정시각이 앞서므로
|
||||
저절로 다시 만든다. 만들다 실패하면 원본을 그대로 보낸다 — 화면을 막지 않는다.
|
||||
"""
|
||||
target = source.with_suffix(".display.geojson")
|
||||
try:
|
||||
if target.exists() and target.stat().st_mtime >= source.stat().st_mtime:
|
||||
return target
|
||||
payload = json.loads(source.read_text(encoding="utf-8"))
|
||||
target.write_text(
|
||||
json.dumps(
|
||||
round_floats(payload, LONLAT_DIGITS), ensure_ascii=False, separators=(",", ":")
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
logger.info(
|
||||
"도엽 표시용 사본 생성: %s (%.1fMB → %.1fMB)",
|
||||
source.name,
|
||||
source.stat().st_size / 1e6,
|
||||
target.stat().st_size / 1e6,
|
||||
)
|
||||
return target
|
||||
except Exception as exc: # 사본 실패가 화면을 막으면 안 된다.
|
||||
logger.warning("도엽 표시용 사본을 만들지 못해 원본을 보냅니다 (%s): %s", source.name, exc)
|
||||
return source
|
||||
|
||||
Reference in New Issue
Block a user