develop #34

병합
htlee develop 에서 main 로 3 commits 를 머지했습니다 2026-02-28 21:45:01 +09:00
18개의 변경된 파일2328개의 추가작업 그리고 141개의 파일을 삭제

파일 보기

@ -0,0 +1,47 @@
---
name: explorer
description: 코드베이스 탐색 및 분석 에이전트. 3개 이상의 파일을 탐색하거나 프로젝트 구조를 파악할 때 사용한다.
model: sonnet
tools: Read, Glob, Grep
maxTurns: 12
---
지정된 영역 내에서 코드베이스를 분석하고 구조화된 결과를 반환한다.
읽기 전용 — 파일을 수정하지 않는다.
## 자율 범위
- 메인 세션이 지정한 **탐색 영역**(디렉토리 또는 파일 목록) 내에서 자유롭게 탐색
- 영역 내 파일 간 의존성 추적, 임포트 체인 분석은 자율 수행
- 탐색 영역 밖 파일은 임포트/참조 관계 확인 목적으로만 열람 가능
## 입력 (메인 세션이 제공)
- **탐색 영역**: 디렉토리 경로 또는 파일 목록
- **목적**: 분석 목적이나 답변할 질문 (구체적일수록 좋음)
## 출력 형식
```
## 분석 결과
### 구조
- 핵심 파일/디렉토리 구성 (파일:라인 근거)
### 발견사항
- 목적에 대한 답변 (파일:라인 근거 포함)
### 패턴
- 코드 컨벤션, 반복 패턴, 아키텍처 특성
### 확신도
- 각 발견사항별: 확정 / 추정(근거) / 판단불가(필요 정보)
### 범위 외 참고
- 탐색 영역 밖에서 발견된 관련 사항 (해당 시)
```
## 제약
- 파일 수정/생성 금지
- 정보 부족 시 추측하지 않고 "판단불가 — [필요한 정보]"로 표시

파일 보기

@ -0,0 +1,55 @@
---
name: implementer
description: 모듈 단위 코드 구현 에이전트. 독립 모듈 구현이나 병렬 작업이 필요할 때 사용한다.
model: sonnet
tools: Read, Write, Edit, Glob, Grep, Bash
maxTurns: 20
---
메인 세션이 정의한 계약(인터페이스, 타입, 제약)에 따라 코드를 구현한다.
내부 구현 방식은 자율 판단하되, 계약과 제약을 벗어나지 않는다.
## 자율 범위
- 계약(함수 시그니처, API 스펙, 타입)은 메인 세션이 확정 — 변경 불가
- 내부 구현 로직, 헬퍼 함수, 에러 처리 방식은 자율 판단
- **[참조]** 파일이 제공되면 해당 파일의 코드 패턴(네이밍, 구조, 에러 처리)을 따름
## 입력 (메인 세션이 제공)
- **[파일]**: 수정/생성할 파일 경로
- **[계약]**: 인터페이스, 타입, 함수 시그니처, API 스펙 등 외부 계약
- **[참조]**: 패턴을 따를 기존 파일 (선택, 제공 시 해당 패턴 준수)
- **[제약]**: 특별한 요구사항 (선택)
## 출력 형식
```
## 구현 결과
### 수정 파일
- 파일 경로 목록
### 파일별 변경
- 각 파일에서 추가/수정한 내용 요약
### 자체 검증
- tsc --noEmit: 통과 / 실패(에러 내용)
- [추가 검증 항목]: 결과
### 계약 외 판단
- 자율 판단한 구현 결정 사항 (메인 세션 참고용)
### 보고 사항 (해당 시)
- 계약 불충분: 추가 정보가 필요한 항목
- 아키텍처 영향: 범위 밖 변경이 필요한 사항
```
## 제약
- [파일]에 명시되지 않은 파일 수정 금지
- [계약]의 시그니처/타입 임의 변경 금지
- 아키텍처 변경이 필요하면 구현하지 않고 "보고 사항"에 기록
- 커밋/푸시 금지
- any 타입 금지, strict 모드 준수
- 구현 완료 후 tsc --noEmit 자체 검증 수행

파일 보기

@ -0,0 +1,53 @@
---
name: reviewer
description: 코드 리뷰 및 품질 검증 에이전트. 커밋 전 검증이나 MR 리뷰 시 사용한다.
model: sonnet
tools: Read, Glob, Grep, Bash
maxTurns: 12
---
변경된 코드를 체크리스트 기반으로 검증한다.
읽기 전용 — 파일을 수정하지 않는다.
## 자율 범위
- 지정된 변경 파일을 자유롭게 분석
- 관련 파일(임포트 대상, 호출자)도 열람하여 영향 범위 확인 가능
- 기본 체크리스트 + 자체 판단으로 추가 이슈 탐지
## 입력 (메인 세션이 제공)
- **[대상]**: 리뷰할 파일 경로 목록 또는 git diff 범위
- **[체크리스트]**: 검증 항목 (선택, 미제공 시 기본 체크리스트 사용)
## 기본 체크리스트
1. 타입 안전성 — any, 타입 단언(as), non-null 단언(!) 사용
2. 에러 처리 — try-catch 누락, empty catch, 에러 무시
3. 보안 — 하드코딩 인증정보, injection 가능성, XSS
4. 미사용 코드 — 미사용 import, 변수, 함수
5. 팀 정책 — team-policy.md 위반 사항
6. 일관성 — 기존 코드 패턴과의 불일치
## 출력 형식
```
## 리뷰 결과
| # | 항목 | 판정 | 근거 |
|---|------|------|------|
| 1 | [항목명] | PASS / FAIL | [파일:라인] 설명 |
### 추가 발견 (자체 판단)
- [파일:라인] 설명 (심각도: Critical / Warning / Info)
### 요약
- 전체: N개 PASS / M개 FAIL
- 커밋 가능 여부: 가능 / 차단 권고(사유)
```
## 제약
- 파일 수정/생성 금지
- 각 항목에 반드시 PASS 또는 FAIL 판정 (애매하면 FAIL + 사유)
- 스타일 개선 제안은 "추가 발견"에 Info로만 기록

파일 보기

@ -0,0 +1,61 @@
# 서브에이전트 활용 정책
커스텀 에이전트(`.claude/agents/`)를 활용하여 컨텍스트를 보호하고 병렬 작업을 수행한다.
메인 세션은 리더 역할(설계, 조율, 최종 판단)에 집중하고, 실제 작업은 서브에이전트에 위임한다.
## 에이전트 구성
| 에이전트 | 역할 | 자율성 | 모델 |
|----------|------|--------|------|
| explorer | 코드베이스 탐색/분석 (읽기 전용) | 높음 | sonnet |
| implementer | 모듈 단위 코드 구현 | 중간 | sonnet |
| reviewer | 코드 리뷰/품질 검증 (읽기 전용) | 높음 | sonnet |
## 사용 시점
### explorer
- 3개 이상의 파일/디렉토리를 탐색해야 할 때
- 프로젝트 구조나 패턴을 파악할 때
- 의존성 체인, 임포트 관계를 추적할 때
### implementer
- 독립 모듈/컴포넌트를 구현할 때
- 여러 모듈을 병렬로 구현할 때 (각각 별도 implementer)
- 반복 패턴을 여러 파일에 적용할 때
### reviewer
- 구현 완료 후 커밋 전 검증
- MR 생성 전 자체 리뷰
- 변경 범위가 클 때 (5개 이상 파일)
## 사용하지 않는 경우
- 단일 파일의 간단한 수정
- 위치를 이미 아는 코드 수정
- 설정 파일 변경
## 메인 세션 작업 흐름
### 단일 모듈
1. 메인: 계약(인터페이스, 타입) 설계
2. implementer: 계약 기반 구현 + 자체 검증
3. reviewer: 변경 파일 리뷰
4. 메인: 결과 확인 → 커밋
### 다중 모듈 (병렬)
1. 메인: 모듈 간 공유 인터페이스 확정
2. implementer A + B: 각 모듈 동시 구현
3. 메인: 통합 확인 (인터페이스 일치)
4. reviewer: 전체 변경 리뷰
5. 메인: 최종 확인 → 커밋
### 분석
1. explorer: 탐색 영역 + 목적 전달 → 분석 결과 반환
2. 메인: "추정" 항목만 직접 확인 → 판단
## 핵심 원칙
- **읽기 전용 에이전트(explorer/reviewer)**: 결과가 부정확해도 손해 없음 → 높은 자율성 부여
- **쓰기 에이전트(implementer)**: 계약은 고정, 내부 구현은 자율 → 중간 자율성
- **같은 파일을 두 에이전트가 동시에 수정하지 않는다**
- **커밋/푸시는 반드시 메인 세션에서 수행**

파일 보기

@ -1,6 +1,6 @@
{
"applied_global_version": "1.3.0",
"applied_date": "2026-02-27",
"applied_global_version": "1.4.0",
"applied_date": "2026-02-28",
"project_type": "react-ts",
"gitea_url": "https://gitea.gc-si.dev"
}

파일 보기

@ -20,10 +20,9 @@ fi
# Conventional Commits 정규식
# type(scope): subject
# - type: feat|fix|docs|style|refactor|test|chore|ci|perf (필수)
# - scope: 괄호 제외 모든 문자 허용 — 한/영/숫자/특수문자 (선택)
# - subject: 1자 이상 (길이는 바이트 기반 별도 검증)
PATTERN='^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([^)]+\))?: .+$'
MAX_SUBJECT_BYTES=200 # UTF-8 한글(3byte) 허용: 72문자 ≈ 최대 216byte
# - scope: 영문, 숫자, 한글, 점, 밑줄, 하이픈 허용 (선택)
# - subject: 1~72자, 한/영 혼용 허용 (필수)
PATTERN='^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([a-zA-Z0-9가-힣._-]+\))?: .{1,72}$'
FIRST_LINE=$(head -1 "$COMMIT_MSG_FILE")
@ -59,13 +58,3 @@ if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then
echo ""
exit 1
fi
# 길이 검증 (바이트 기반 — UTF-8 한글 허용)
MSG_LEN=$(echo -n "$FIRST_LINE" | wc -c | tr -d ' ')
if [ "$MSG_LEN" -gt "$MAX_SUBJECT_BYTES" ]; then
echo ""
echo " ✗ 커밋 메시지가 너무 깁니다 (${MSG_LEN}바이트, 최대 ${MAX_SUBJECT_BYTES})"
echo " 현재 메시지: $FIRST_LINE"
echo ""
exit 1
fi

파일 보기

@ -0,0 +1,61 @@
import { Router } from 'express';
import { requireAuth } from '../auth/authMiddleware.js';
import { listOrganizations, getOrganization, listUploadLogs } from './assetsService.js';
const router = Router();
// ============================================================
// GET /api/assets/orgs — 기관 목록
// ============================================================
router.get('/orgs', requireAuth, async (req, res) => {
try {
const { orgTp, jrsd, search } = req.query as {
orgTp?: string;
jrsd?: string;
search?: string;
};
const orgs = await listOrganizations({ orgTp, jrsd, search });
res.json(orgs);
} catch (err) {
console.error('[assets] 기관 목록 조회 오류:', err);
res.status(500).json({ error: '기관 목록 조회 중 오류가 발생했습니다.' });
}
});
// ============================================================
// GET /api/assets/orgs/:sn — 기관 상세 (장비 + 담당자)
// ============================================================
router.get('/orgs/:sn', requireAuth, async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10);
if (isNaN(sn)) {
res.status(400).json({ error: '유효하지 않은 기관 번호입니다.' });
return;
}
const org = await getOrganization(sn);
if (!org) {
res.status(404).json({ error: '기관을 찾을 수 없습니다.' });
return;
}
res.json(org);
} catch (err) {
console.error('[assets] 기관 상세 조회 오류:', err);
res.status(500).json({ error: '기관 상세 조회 중 오류가 발생했습니다.' });
}
});
// ============================================================
// GET /api/assets/upload-logs — 업로드 이력
// ============================================================
router.get('/upload-logs', requireAuth, async (req, res) => {
try {
const limit = Math.min(parseInt(req.query.limit as string, 10) || 20, 100);
const logs = await listUploadLogs(limit);
res.json(logs);
} catch (err) {
console.error('[assets] 업로드 이력 조회 오류:', err);
res.status(500).json({ error: '업로드 이력 조회 중 오류가 발생했습니다.' });
}
});
export default router;

파일 보기

@ -0,0 +1,184 @@
import { wingPool } from '../db/wingDb.js';
// ============================================================
// 인터페이스
// ============================================================
interface EquipItem {
category: string;
icon: string;
count: number;
}
interface ContactItem {
role: string;
name: string;
phone: string;
}
interface OrgListItem {
orgSn: number;
orgTp: string;
jrsdNm: string;
areaNm: string;
orgNm: string;
addr: string;
tel: string;
lat: number;
lng: number;
pinSize: string;
vesselCnt: number;
skimmerCnt: number;
pumpCnt: number;
vehicleCnt: number;
sprayerCnt: number;
totalAssets: number;
}
interface OrgDetail extends OrgListItem {
equipment: EquipItem[];
contacts: ContactItem[];
}
interface UploadLogItem {
logSn: number;
fileNm: string;
uploaderNm: string;
uploadCnt: number;
regDtm: string;
}
// ============================================================
// 기관 목록 조회
// ============================================================
export async function listOrganizations(filters: {
orgTp?: string;
jrsd?: string;
search?: string;
}): Promise<OrgListItem[]> {
const conditions: string[] = ["USE_YN = 'Y'"];
const params: unknown[] = [];
let idx = 1;
if (filters.orgTp) {
conditions.push(`ORG_TP = $${idx++}`);
params.push(filters.orgTp);
}
if (filters.jrsd) {
conditions.push(`JRSD_NM LIKE '%' || $${idx++} || '%'`);
params.push(filters.jrsd);
}
if (filters.search) {
conditions.push(`(ORG_NM LIKE '%' || $${idx++} || '%' OR ADDR LIKE '%' || $${idx++} || '%')`);
params.push(filters.search, filters.search);
}
const sql = `
SELECT ORG_SN, ORG_TP, JRSD_NM, AREA_NM, ORG_NM, ADDR, TEL,
LAT, LNG, PIN_SIZE,
VESSEL_CNT, SKIMMER_CNT, PUMP_CNT, VEHICLE_CNT, SPRAYER_CNT, TOTAL_ASSETS
FROM wing.ASSET_ORG
WHERE ${conditions.join(' AND ')}
ORDER BY JRSD_NM, ORG_TP, ORG_NM
`;
const { rows } = await wingPool.query(sql, params);
return rows.map((r: Record<string, unknown>) => ({
orgSn: r.org_sn as number,
orgTp: r.org_tp as string,
jrsdNm: r.jrsd_nm as string,
areaNm: r.area_nm as string,
orgNm: r.org_nm as string,
addr: r.addr as string,
tel: r.tel as string,
lat: parseFloat(r.lat as string),
lng: parseFloat(r.lng as string),
pinSize: r.pin_size as string,
vesselCnt: r.vessel_cnt as number,
skimmerCnt: r.skimmer_cnt as number,
pumpCnt: r.pump_cnt as number,
vehicleCnt: r.vehicle_cnt as number,
sprayerCnt: r.sprayer_cnt as number,
totalAssets: r.total_assets as number,
}));
}
// ============================================================
// 기관 상세 조회 (장비 + 담당자 포함)
// ============================================================
export async function getOrganization(orgSn: number): Promise<OrgDetail | null> {
const orgSql = `
SELECT ORG_SN, ORG_TP, JRSD_NM, AREA_NM, ORG_NM, ADDR, TEL,
LAT, LNG, PIN_SIZE,
VESSEL_CNT, SKIMMER_CNT, PUMP_CNT, VEHICLE_CNT, SPRAYER_CNT, TOTAL_ASSETS
FROM wing.ASSET_ORG
WHERE ORG_SN = $1 AND USE_YN = 'Y'
`;
const { rows: orgRows } = await wingPool.query(orgSql, [orgSn]);
if (orgRows.length === 0) return null;
const r = orgRows[0] as Record<string, unknown>;
const equipSql = `
SELECT CTGR_NM, ICON, QTY FROM wing.ASSET_EQUIP
WHERE ORG_SN = $1 ORDER BY SORT_ORD, EQUIP_SN
`;
const { rows: equipRows } = await wingPool.query(equipSql, [orgSn]);
const contactSql = `
SELECT ROLE_NM, CONTACT_NM, TEL FROM wing.ASSET_CONTACT
WHERE ORG_SN = $1 ORDER BY SORT_ORD, CONTACT_SN
`;
const { rows: contactRows } = await wingPool.query(contactSql, [orgSn]);
return {
orgSn: r.org_sn as number,
orgTp: r.org_tp as string,
jrsdNm: r.jrsd_nm as string,
areaNm: r.area_nm as string,
orgNm: r.org_nm as string,
addr: r.addr as string,
tel: r.tel as string,
lat: parseFloat(r.lat as string),
lng: parseFloat(r.lng as string),
pinSize: r.pin_size as string,
vesselCnt: r.vessel_cnt as number,
skimmerCnt: r.skimmer_cnt as number,
pumpCnt: r.pump_cnt as number,
vehicleCnt: r.vehicle_cnt as number,
sprayerCnt: r.sprayer_cnt as number,
totalAssets: r.total_assets as number,
equipment: equipRows.map((e: Record<string, unknown>) => ({
category: e.ctgr_nm as string,
icon: e.icon as string,
count: e.qty as number,
})),
contacts: contactRows.map((c: Record<string, unknown>) => ({
role: c.role_nm as string,
name: c.contact_nm as string,
phone: c.tel as string,
})),
};
}
// ============================================================
// 업로드 이력 조회
// ============================================================
export async function listUploadLogs(limit = 20): Promise<UploadLogItem[]> {
const sql = `
SELECT LOG_SN, FILE_NM, UPLOADER_NM, UPLOAD_CNT, REG_DTM
FROM wing.ASSET_UPLOAD_LOG
ORDER BY REG_DTM DESC
LIMIT $1
`;
const { rows } = await wingPool.query(sql, [limit]);
return rows.map((r: Record<string, unknown>) => ({
logSn: r.log_sn as number,
fileNm: r.file_nm as string,
uploaderNm: r.uploader_nm as string,
uploadCnt: r.upload_cnt as number,
regDtm: (r.reg_dtm as Date).toISOString(),
}));
}

파일 보기

@ -16,6 +16,7 @@ import auditRouter from './audit/auditRouter.js'
import boardRouter from './board/boardRouter.js'
import hnsRouter from './hns/hnsRouter.js'
import reportsRouter from './reports/reportsRouter.js'
import assetsRouter from './assets/assetsRouter.js'
import {
sanitizeBody,
sanitizeQuery,
@ -143,6 +144,7 @@ app.use('/api/layers', layersRouter)
app.use('/api/simulation', simulationLimiter, simulationRouter)
app.use('/api/hns', hnsRouter)
app.use('/api/reports', reportsRouter)
app.use('/api/assets', assetsRouter)
// 헬스 체크
app.get('/health', (_req, res) => {

파일 보기

@ -0,0 +1,83 @@
-- ============================================================
-- 008_assets.sql — 방제자산 테이블 (기관 + 장비 + 담당자)
-- ============================================================
SET search_path TO wing;
-- ============================================================
-- 1. 기관/조직 마스터
-- ============================================================
CREATE TABLE IF NOT EXISTS ASSET_ORG (
ORG_SN SERIAL PRIMARY KEY,
ORG_TP VARCHAR(30) NOT NULL, -- 유형: 해경관할, 해경경찰서, 파출소, 관련기관, 해양환경공단, 업체, 지자체, 기름저장시설, 정유사, 해군, 기타
JRSD_NM VARCHAR(50) NOT NULL, -- 관할청명: 중부지방해양경찰청, 서해지방해양경찰청 등
AREA_NM VARCHAR(30) NOT NULL, -- 지역명: 인천, 여수, 부산 등
ORG_NM VARCHAR(100) NOT NULL, -- 기관명
ADDR VARCHAR(300), -- 주소
TEL VARCHAR(30), -- 대표 전화
LAT NUMERIC(9,6), -- 위도
LNG NUMERIC(10,6), -- 경도
PIN_SIZE VARCHAR(5) DEFAULT 'md', -- 지도 핀 크기: hq, lg, md
VESSEL_CNT SMALLINT DEFAULT 0, -- 방제선 수
SKIMMER_CNT SMALLINT DEFAULT 0, -- 유회수기 수
PUMP_CNT SMALLINT DEFAULT 0, -- 이송펌프 수
VEHICLE_CNT SMALLINT DEFAULT 0, -- 방제차량 수
SPRAYER_CNT SMALLINT DEFAULT 0, -- 살포장치 수
TOTAL_ASSETS SMALLINT DEFAULT 0, -- 총 자산 수
USE_YN CHAR(1) DEFAULT 'Y',
REG_DTM TIMESTAMPTZ DEFAULT NOW(),
MDFCN_DTM TIMESTAMPTZ,
CONSTRAINT CK_ASSET_ORG_PIN CHECK (PIN_SIZE IN ('hq','lg','md'))
);
-- ============================================================
-- 2. 장비 상세
-- ============================================================
CREATE TABLE IF NOT EXISTS ASSET_EQUIP (
EQUIP_SN SERIAL PRIMARY KEY,
ORG_SN INTEGER NOT NULL REFERENCES ASSET_ORG(ORG_SN) ON DELETE CASCADE,
CTGR_NM VARCHAR(50) NOT NULL, -- 장비 카테고리: 방제선, 유회수기, 비치크리너, ...
ICON VARCHAR(10), -- 이모지 아이콘
QTY SMALLINT DEFAULT 0, -- 수량
SORT_ORD SMALLINT DEFAULT 0,
UNIQUE(ORG_SN, CTGR_NM)
);
-- ============================================================
-- 3. 담당자
-- ============================================================
CREATE TABLE IF NOT EXISTS ASSET_CONTACT (
CONTACT_SN SERIAL PRIMARY KEY,
ORG_SN INTEGER NOT NULL REFERENCES ASSET_ORG(ORG_SN) ON DELETE CASCADE,
ROLE_NM VARCHAR(50), -- 역할: 방제과장, 방제담당, 담당 등
CONTACT_NM VARCHAR(50), -- 이름
TEL VARCHAR(30), -- 전화번호
SORT_ORD SMALLINT DEFAULT 0
);
-- ============================================================
-- 4. 업로드 이력
-- ============================================================
CREATE TABLE IF NOT EXISTS ASSET_UPLOAD_LOG (
LOG_SN SERIAL PRIMARY KEY,
FILE_NM VARCHAR(200) NOT NULL, -- 파일명
UPLOADER_NM VARCHAR(50), -- 업로더 이름/부서
UPLOAD_CNT INTEGER DEFAULT 0, -- 처리 건수
REG_DTM TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- 인덱스
-- ============================================================
CREATE INDEX IF NOT EXISTS IDX_ASSET_ORG_JRSD ON ASSET_ORG(JRSD_NM);
CREATE INDEX IF NOT EXISTS IDX_ASSET_ORG_TP ON ASSET_ORG(ORG_TP);
CREATE INDEX IF NOT EXISTS IDX_ASSET_EQUIP_ORG ON ASSET_EQUIP(ORG_SN);
CREATE INDEX IF NOT EXISTS IDX_ASSET_CONTACT_ORG ON ASSET_CONTACT(ORG_SN);
-- ============================================================
-- 초기 데이터: 업로드 이력 샘플
-- ============================================================
INSERT INTO ASSET_UPLOAD_LOG (FILE_NM, UPLOADER_NM, UPLOAD_CNT, REG_DTM) VALUES
('여수서_장비자재_2601.xlsx', '남해청_방제과', 45, '2026-01-25 14:30:00+09'),
('인천서_오일펜스현황.xlsx', '중부청_방제과', 12, '2026-01-22 10:15:00+09'),
('전체_방제정_현황.xlsx', '본청_방제과', 18, '2026-01-20 09:00:00+09');

파일 보기

@ -0,0 +1,996 @@
-- 자동 생성: assetMockData.ts → SQL INSERT
-- 생성일: 2026-02-28
SET search_path TO wing;
-- ============================================================
-- ASSET_ORG 초기 데이터 (84건)
-- ============================================================
INSERT INTO ASSET_ORG (ORG_TP, JRSD_NM, AREA_NM, ORG_NM, ADDR, TEL, LAT, LNG, PIN_SIZE, VESSEL_CNT, SKIMMER_CNT, PUMP_CNT, VEHICLE_CNT, SPRAYER_CNT, TOTAL_ASSETS) VALUES
('해경관할', '중부지방해양경찰청', '인천', '인천해양경찰서', '인천광역시 중구 북성동1가 80-8', '010-4779-4191', 37.4563, 126.5922, 'hq', 19, 30, 18, 2, 15, 234),
('해경경찰서', '중부지방해양경찰청', '평택', '평택해양경찰서', '평택시 만호리 706번지', '010-9812-8102', 36.9694, 126.83, 'lg', 14, 27, 33, 3, 22, 193),
('해경경찰서', '중부지방해양경찰청', '태안', '태안해양경찰서', '충남 태안군 근흥면 신진부두길2', '010-2965-4423', 36.7456, 126.2978, 'lg', 10, 27, 21, 8, 15, 185),
('파출소', '중부지방해양경찰청', '보령', '보령해양경찰서', '보령시 해안로 740', '010-2940-6343', 36.3335, 126.5874, 'md', 3, 8, 5, 3, 11, 80),
('해경관할', '서해지방해양경찰청', '여수', '여수해양경찰서', '광양시 항만9로 89', '010-2785-2493', 34.7407, 127.7385, 'hq', 55, 92, 63, 12, 47, 464),
('해경경찰서', '서해지방해양경찰청', '목포', '목포해양경찰서', '목포시 고하대로 597번길 99-64', '010-9812-8439', 34.7936, 126.3839, 'lg', 10, 19, 18, 3, 16, 169),
('해경경찰서', '서해지방해양경찰청', '군산', '군산해양경찰서', '전북 군산시 오식도동 506', '010-2618-3406', 35.99, 126.7133, 'lg', 6, 22, 12, 3, 17, 155),
('해경경찰서', '서해지방해양경찰청', '완도', '완도해양경찰서', '완도군 완도읍 장보고대로 383', '061-550-2183', 34.311, 126.755, 'lg', 3, 9, 7, 3, 11, 75),
('파출소', '서해지방해양경찰청', '부안', '부안해양경찰서', '전북 군산시 오식도동 506', '063-928-xxxx', 35.7316, 126.7328, 'md', 2, 8, 7, 2, 7, 66),
('해경관할', '남해지방해양경찰청', '부산', '부산해양경찰서', '부산시 영도구 해양로 293', '010-2609-1456', 35.0746, 129.0686, 'hq', 108, 22, 25, 10, 24, 313),
('해경관할', '남해지방해양경찰청', '울산', '울산해양경찰서', '울산광역시 남구 장생포 고래로 166', '010-9812-8210', 35.5008, 129.3824, 'hq', 46, 69, 26, 11, 20, 311),
('해경경찰서', '남해지방해양경찰청', '창원', '창원해양경찰서', '창원시 마산합포구 신포동 1가', '010-4634-7364', 35.1796, 128.5681, 'lg', 12, 25, 14, 10, 10, 139),
('해경경찰서', '남해지방해양경찰청', '통영', '통영해양경찰서', '통영시 광도면 죽림리 1564-4', '010-9812-8495', 34.8544, 128.4331, 'lg', 6, 15, 9, 5, 13, 104),
('해경경찰서', '남해지방해양경찰청', '사천', '사천해양경찰서', '사천시 신항만길 1길 17', '010-9812-8352', 34.931, 128.066, 'lg', 2, 9, 6, 2, 7, 80),
('해경경찰서', '동해지방해양경찰청', '동해', '동해해양경찰서', '동해시 임항로 130', '010-9812-8073', 37.5247, 129.1143, 'lg', 6, 23, 11, 6, 14, 156),
('해경경찰서', '동해지방해양경찰청', '포항', '포항해양경찰서', '포항시 남구 희망대로 1341', '010-3108-2183', 36.019, 129.3651, 'lg', 10, 13, 21, 4, 21, 135),
('파출소', '동해지방해양경찰청', '속초', '속초해양경찰서', '속초시 설악금강대교로 206', '033-634-2186', 38.207, 128.5918, 'md', 2, 6, 4, 1, 17, 85),
('파출소', '동해지방해양경찰청', '울진', '울진해양경찰서', '울진군 후포면 후포리 623-148', '010-9812-8076', 36.9932, 129.4003, 'md', 2, 6, 4, 1, 8, 66),
('해경경찰서', '제주지방해양경찰청', '제주', '제주해양경찰서', '제주시 임항로 85', '064-766-2691', 33.5154, 126.5268, 'lg', 4, 21, 17, 3, 16, 113),
('해경경찰서', '제주지방해양경찰청', '서귀포', '서귀포해양경찰서', '서귀포시 안덕면 화순해안로69', '064-793-2186', 33.2469, 126.56, 'lg', 3, 9, 15, 3, 14, 67),
('관련기관', '해양경찰청(중앙)', '중앙', '중앙특수구조단', '부산광역시 영도구 해양로 301', '051-580-2044', 35.058, 129.059, 'md', 1, 0, 5, 2, 0, 39),
('기름저장시설', '서해지방해양경찰청', '여수', '오일허브코리아여수㈜ 외 4개', '전남 여수시 신덕동 325', '061-686-3611', 34.745, 127.745, 'md', 0, 0, 0, 0, 0, 1),
('기름저장시설', '남해지방해양경찰청', '부산', 'SK에너지 외 2개', '부산시 영도구 해양로 1', '051-643-3331', 35.175, 129.075, 'md', 0, 0, 0, 0, 0, 3),
('기름저장시설', '남해지방해양경찰청', '울산', 'SK지오센트릭 외 5개', '울산광역시 남구 신여천로 2', '052-208-2851', 35.535, 129.305, 'md', 0, 0, 0, 0, 0, 9),
('기름저장시설', '남해지방해양경찰청', '통영', '한국가스공사 통영기지본부', '통영시 광도면 안정로 770', '055-640-6014', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 1),
('기름저장시설', '동해지방해양경찰청', '동해', 'HD현대오일뱅크㈜ 외 4개', '강릉시 옥계면 동해대로 206', '033-534-2093', 37.52, 129.11, 'md', 0, 0, 0, 0, 0, 9),
('기름저장시설', '동해지방해양경찰청', '포항', '포스코케미칼 외 1개', '포항시 남구 동해안로 6262', '054-290-8222', 37.73, 129.01, 'md', 0, 0, 0, 0, 1, 2),
('기름저장시설', '서해지방해양경찰청', '목포', '흑산도내연발전소 외 2개', '전남 신안군 흑산일주로70', '061-351-2342', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 4),
('기름저장시설', '서해지방해양경찰청', '여수', '오일허브코리아여수㈜ 외 4개', '전남 여수시 신덕동 325', '061-686-3611', 34.75, 127.735, 'md', 0, 0, 0, 0, 0, 4),
('기름저장시설', '중부지방해양경찰청', '인천', 'GS칼텍스㈜ 외 10개', '인천광역시 중구 월미로 182', '010-8777-6922', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 13),
('기름저장시설', '중부지방해양경찰청', '태안', 'HD현대케미칼 외 4개', '충남 서산시 대산읍 평신2로 26', '041-924-1068', 36.91, 126.415, 'md', 0, 0, 0, 0, 0, 4),
('기름저장시설', '중부지방해양경찰청', '평택', '현대오일터미널(주) 외 4개', '평택시 포승읍 포승공단순환로 11', '031-683-5101', 36.985, 126.835, 'md', 0, 0, 0, 0, 1, 4),
('기타', '남해지방해양경찰청', '사천', '한국남동발전(주) 외 2개', '고성군 하이면 하이로1', '070-4486-7474', 34.965, 128.56, 'md', 0, 0, 0, 0, 0, 8),
('기타', '남해지방해양경찰청', '울산', 'HD현대미포', '울산광역시 동구 방어진순환도로100', '052-250-3551', 35.53, 129.315, 'md', 0, 0, 0, 0, 0, 1),
('기타', '남해지방해양경찰청', '통영', '삼성중공업 외 1개', '거제시 장평3로 80', '055-630-5373', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 2),
('기타', '동해지방해양경찰청', '동해', '한국남부발전㈜', '삼척시 원덕읍 삼척로 734', '070-7713-5153', 37.45, 129.17, 'md', 0, 0, 0, 0, 0, 2),
('기타', '동해지방해양경찰청', '울진', '한울원전', '울진군 북면 울진북로 2040', '054-785-4833', 37.065, 129.39, 'md', 0, 0, 0, 0, 0, 4),
('기타', '서해지방해양경찰청', '여수', '㈜HR-PORT 외 5개', '여수시 제철로', '061-791-0358', 34.755, 127.73, 'md', 0, 0, 0, 0, 0, 16),
('기타', '중부지방해양경찰청', '인천', '삼광조선공업㈜ 외 1개', '인천 동구 보세로42번길41', '010-3321-2959', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 5),
('업체', '중부지방해양경찰청', '인천', '방제유창청소업체(㈜클린포트)', '㈜클린포트', '032-882-8279', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 3),
('업체', '남해지방해양경찰청', '부산', '방제유창청소업체(대용환경㈜ 외 38개)', '㈜태평양해양산업', '051-242-0622', 35.18, 129.085, 'md', 0, 0, 0, 0, 0, 51),
('업체', '남해지방해양경찰청', '울산', '방제유창청소업체((주)한유마린서비스 외 8개)', '대상해운(주)', '010-5499-7401', 35.54, 129.295, 'md', 0, 0, 0, 0, 0, 12),
('업체', '동해지방해양경찰청', '포항', '방제유창청소업체(블루씨 외 1개)', '(주)블루씨', '054-278-8200', 36.015, 129.365, 'md', 0, 0, 0, 0, 1, 3),
('업체', '서해지방해양경찰청', '목포', '방제유창청소업체(㈜한국해운 외 1개)', '㈜한국해운 목포지사', '010-8615-4326', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 1),
('업체', '서해지방해양경찰청', '여수', '방제유창청소업체(마로해운 외 11개)', '㈜우진실업', '061-654-9603', 34.74, 127.75, 'md', 0, 0, 0, 0, 2, 54),
('업체', '중부지방해양경찰청', '태안', '방제유창청소업체(우진해운㈜)', '우진해운㈜', '010-4384-6817', 36.905, 126.42, 'md', 0, 0, 0, 0, 0, 6),
('업체', '중부지방해양경찰청', '평택', '방제유창청소업체((주)씨앤 외 3개)', '㈜씨앤', '031-683-2389', 36.99, 126.825, 'md', 0, 0, 0, 0, 0, 6),
('업체', '남해지방해양경찰청', '부산', '방제유창청소업체(㈜지앤비마린서비스)', '㈜지앤비마린서비스', '051-242-0622', 35.185, 129.07, 'md', 0, 0, 0, 0, 0, 1),
('정유사', '남해지방해양경찰청', '울산', 'SK엔텀(주) 외 4개', '울산광역시 남구 고사동 110-64', '052-231-2318', 35.545, 129.31, 'md', 0, 0, 0, 0, 0, 5),
('정유사', '서해지방해양경찰청', '여수', 'GS칼텍스㈜', '여수시 낙포단지길 251', '061-680-2121', 34.735, 127.755, 'md', 0, 0, 0, 0, 20, 27),
('정유사', '중부지방해양경찰청', '태안', 'HD현대오일뱅크㈜', '서산시 대산읍 평신2로 182', '010-2050-5291', 36.915, 126.41, 'md', 0, 0, 0, 0, 0, 2),
('지자체', '남해지방해양경찰청', '부산', '부산광역시 외 8개', '부산광역시 동구 좌천동', '051-607-4484', 35.17, 129.08, 'md', 0, 0, 0, 0, 0, 12),
('지자체', '남해지방해양경찰청', '사천', '사천시 외 3개', '사천시 신항로 3', '055-670-2484', 34.935, 128.075, 'md', 0, 0, 0, 0, 0, 6),
('지자체', '남해지방해양경찰청', '울산', '울산북구청 외 2개', '울산광역시 북구 구유동 654-2', '051-709-4611', 35.55, 129.32, 'md', 0, 0, 0, 0, 0, 4),
('지자체', '남해지방해양경찰청', '창원', '창원 진해구 외 1개', '창원시 진해구 천자로 105', '051-970-4482', 35.055, 128.645, 'md', 0, 0, 0, 0, 0, 2),
('지자체', '동해지방해양경찰청', '동해', '삼척시 외 1개', '삼척시 근덕면 덕산리 107-74', '033-640-5284', 37.45, 129.16, 'md', 0, 0, 0, 0, 0, 4),
('지자체', '동해지방해양경찰청', '울진', '영덕군', '남정면 장사리 74-1', '054-730-6562', 36.35, 129.4, 'md', 0, 0, 0, 0, 0, 3),
('지자체', '서해지방해양경찰청', '목포', '영광군 외 1개', '영광군 염산면 향화로', '061-270-3419', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 5),
('지자체', '서해지방해양경찰청', '여수', '광양시 외 1개', '순천시 진상면 성지로 8', '061-797-2791', 34.76, 127.725, 'md', 0, 0, 0, 0, 0, 3),
('지자체', '중부지방해양경찰청', '인천', '옹진군청 외 4개', '인천광역시 옹진군 덕적면 진리 387', '010-2740-9388', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 10),
('지자체', '중부지방해양경찰청', '태안', '태안군청', '충남 태안군 근흥면 신진도리 75-36', '041-670-2877', 36.745, 126.305, 'md', 0, 0, 0, 0, 0, 1),
('지자체', '중부지방해양경찰청', '평택', '안산시청 외 2개', '경기도 안산시 단원구 진두길 97', '041-350-4292', 37.32, 126.83, 'md', 0, 0, 0, 0, 0, 6),
('기타', '서해지방해양경찰청', '여수', '㈜HR-PORT 외 5개', '여수시 제철로', '061-791-0358', 34.748, 127.74, 'md', 0, 0, 0, 0, 0, 1),
('해군', '동해지방해양경찰청', '동해', '해군1함대사령부 외 1개', '동해시 대동로 430', '033-539-7323', 37.525, 129.115, 'md', 0, 0, 0, 0, 0, 1),
('해군', '중부지방해양경찰청', '인천', '해병대 제9518부대', '인천광역시 옹진군', '010-4801-3473', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 2),
('해양환경공단', '남해지방해양경찰청', '부산', '부산지사', '창원시 진해구 안골동', '051-466-3944', 35.105, 128.715, 'md', 0, 0, 0, 0, 6, 14),
('해양환경공단', '남해지방해양경찰청', '사천', '마산지사', '사천시 신항만1길 23', '010-3598-4202', 34.925, 128.065, 'md', 0, 0, 0, 0, 0, 9),
('해양환경공단', '남해지방해양경찰청', '울산', '울산지사', '울산광역시 남구 장생포고래로 276번길 27', '052-238-7718', 35.538, 129.3, 'md', 0, 0, 0, 0, 1, 16),
('해양환경공단', '남해지방해양경찰청', '창원', '마산지사', '창원시 마산합포구 드림베이대로59', '010-2265-3928', 35.055, 128.645, 'md', 0, 0, 0, 0, 1, 7),
('해양환경공단', '남해지방해양경찰청', '통영', '마산지사', '거제시 장승로 112', '010-2636-5313', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 8),
('해양환경공단', '동해지방해양경찰청', '동해', '동해지사', '동해시 대동로 210', '010-7499-0257', 37.515, 129.105, 'md', 0, 0, 0, 0, 2, 17),
('해양환경공단', '동해지방해양경찰청', '울진', '포항지사', '울진군 죽변면 죽변리 36-88', '054-273-5595', 37.06, 129.42, 'md', 0, 0, 0, 0, 0, 2),
('해양환경공단', '동해지방해양경찰청', '포항', '포항지사', '포항시 북구 해안로 44-10', '054-273-5595', 36.025, 129.375, 'md', 0, 0, 0, 0, 2, 8),
('해양환경공단', '서해지방해양경찰청', '군산', '군산지사', '군산시 임해로 452', '063-443-4813', 35.975, 126.715, 'md', 0, 0, 0, 0, 4, 12),
('해양환경공단', '서해지방해양경찰청', '목포', '목포지사', '전남 목포시 죽교동 683', '061-242-9663', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 10),
('해양환경공단', '서해지방해양경찰청', '여수', '여수지사', '여수시 덕충동', '061-654-6431', 34.742, 127.748, 'md', 0, 0, 0, 0, 3, 12),
('해양환경공단', '서해지방해양경찰청', '완도', '목포지사 완도사업소', '완도군 완도읍 해변공원로 20-1', '061-242-9663', 34.315, 126.755, 'md', 0, 0, 0, 0, 0, 3),
('해양환경공단', '제주지방해양경찰청', '서귀포', '제주지사(서귀포)', '서귀포시 칠십리로72번길 14', '064-753-4356', 33.245, 126.565, 'md', 0, 0, 0, 0, 1, 1),
('해양환경공단', '제주지방해양경찰청', '제주', '제주지사(제주)', '제주시 임항로97', '064-753-4356', 33.517, 126.528, 'md', 0, 0, 0, 0, 3, 20),
('해양환경공단', '중부지방해양경찰청', '보령', '대산지사(보령)', '보령시 해안로 740', '041-664-9101', 36.333, 126.612, 'md', 0, 0, 0, 0, 0, 5),
('해양환경공단', '중부지방해양경찰청', '인천', '인천지사', '인천광역시 중구 연안부두로 128번길 35', '010-7133-2167', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 11),
('해양환경공단', '중부지방해양경찰청', '태안', '대산지사(태안)', '서산시 대산읍 대죽1로 325', '041-664-9101', 36.908, 126.413, 'md', 0, 0, 0, 0, 1, 17),
('해양환경공단', '중부지방해양경찰청', '평택', '평택지사', '당진시 송악읍 고대공단2길', '031-683-7973', 36.905, 126.635, 'md', 0, 0, 0, 0, 1, 13),
('기타', '남해지방해양경찰청', '통영', '삼성중공업 외 1개', '거제시 장평3로 80', '055-630-5373', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 1);
-- ============================================================
-- ASSET_EQUIP 초기 데이터
-- ============================================================
-- 인천해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제선', '🚢', 19, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '유회수기', '', 30, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '이송펌프', '🔧', 18, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '고압세척기', '💧', 26, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '저압세척기', '🚿', 3, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '동력분무기', '💨', 14, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제창고', '🏭', 19, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '발전기', '', 9, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '지원장비', '🔩', 9, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '장비부품', '🔗', 46, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '경비함정방제', '', 18, 15),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '살포장치', '🌊', 15, 16);
-- 평택해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제선', '🚢', 14, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '유회수기', '', 27, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '이송펌프', '🔧', 33, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '고압세척기', '💧', 12, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '저압세척기', '🚿', 5, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제창고', '🏭', 35, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '발전기', '', 9, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '지원장비', '🔩', 10, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '경비함정방제', '', 14, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '살포장치', '🌊', 22, 15);
-- 태안해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '유회수기', '', 27, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '비치크리너', '🏖', 4, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '이송펌프', '🔧', 21, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제차량', '🚛', 8, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '해안운반차', '🚜', 8, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '고압세척기', '💧', 14, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '저압세척기', '🚿', 8, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제창고', '🏭', 28, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '발전기', '', 11, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '지원장비', '🔩', 16, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '경비함정방제', '', 8, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '살포장치', '🌊', 15, 14);
-- 보령해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '유회수기', '', 8, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '이송펌프', '🔧', 5, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제창고', '🏭', 22, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '발전기', '', 2, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '지원장비', '🔩', 6, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '경비함정방제', '', 6, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '살포장치', '🌊', 11, 14);
-- 여수해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제선', '🚢', 55, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '유회수기', '', 92, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '비치크리너', '🏖', 5, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '이송펌프', '🔧', 63, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제차량', '🚛', 12, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '해안운반차', '🚜', 4, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '고압세척기', '💧', 48, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '저압세척기', '🚿', 7, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '동력분무기', '💨', 25, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제창고', '🏭', 37, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '발전기', '', 16, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '지원장비', '🔩', 14, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '장비부품', '🔗', 14, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '경비함정방제', '', 22, 15),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '살포장치', '🌊', 47, 16);
-- 목포해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '유회수기', '', 19, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '이송펌프', '🔧', 18, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '저압세척기', '🚿', 4, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '발전기', '', 4, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '지원장비', '🔩', 31, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '장비부품', '🔗', 17, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '경비함정방제', '', 15, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '살포장치', '🌊', 16, 14);
-- 군산해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '유회수기', '', 22, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '이송펌프', '🔧', 12, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '저압세척기', '🚿', 4, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제창고', '🏭', 6, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '발전기', '', 5, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 3, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '장비부품', '🔗', 50, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '경비함정방제', '', 5, 15),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '살포장치', '🌊', 17, 16);
-- 완도해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '유회수기', '', 9, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '이송펌프', '🔧', 7, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제창고', '🏭', 24, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '발전기', '', 2, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '경비함정방제', '', 8, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '살포장치', '🌊', 11, 12);
-- 부안해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '유회수기', '', 8, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '이송펌프', '🔧', 7, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제창고', '🏭', 15, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '발전기', '', 3, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '지원장비', '🔩', 6, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '경비함정방제', '', 7, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '살포장치', '🌊', 7, 14);
-- 부산해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제선', '🚢', 108, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '유회수기', '', 22, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '이송펌프', '🔧', 25, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제차량', '🚛', 10, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '고압세척기', '💧', 38, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '저압세척기', '🚿', 8, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '발전기', '', 11, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '경비함정방제', '', 16, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '살포장치', '🌊', 24, 14);
-- 울산해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제선', '🚢', 46, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '유회수기', '', 69, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '비치크리너', '🏖', 4, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '이송펌프', '🔧', 26, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제차량', '🚛', 11, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '해안운반차', '🚜', 5, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '고압세척기', '💧', 23, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '저압세척기', '🚿', 6, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제창고', '🏭', 32, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '발전기', '', 7, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '지원장비', '🔩', 40, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '경비함정방제', '', 14, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '살포장치', '🌊', 20, 15);
-- 창원해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제선', '🚢', 12, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '유회수기', '', 25, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '이송펌프', '🔧', 14, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제차량', '🚛', 10, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '발전기', '', 4, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '경비함정방제', '', 7, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '살포장치', '🌊', 10, 15);
-- 통영해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '유회수기', '', 15, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '이송펌프', '🔧', 9, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제차량', '🚛', 5, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제창고', '🏭', 18, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '발전기', '', 4, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '경비함정방제', '', 12, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '살포장치', '🌊', 13, 15);
-- 사천해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '유회수기', '', 9, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '이송펌프', '🔧', 6, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '동력분무기', '💨', 4, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제창고', '🏭', 31, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '발전기', '', 2, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '지원장비', '🔩', 1, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '경비함정방제', '', 8, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '살포장치', '🌊', 7, 14);
-- 동해해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '유회수기', '', 23, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '이송펌프', '🔧', 11, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제차량', '🚛', 6, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '해안운반차', '🚜', 3, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '동력분무기', '💨', 5, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제창고', '🏭', 38, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '발전기', '', 2, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '장비부품', '🔗', 10, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '경비함정방제', '', 8, 15),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '살포장치', '🌊', 14, 16);
-- 포항해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '유회수기', '', 13, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '이송펌프', '🔧', 21, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제차량', '🚛', 4, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '동력분무기', '💨', 3, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제창고', '🏭', 15, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '발전기', '', 5, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '경비함정방제', '', 10, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '살포장치', '🌊', 21, 15);
-- 속초해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '유회수기', '', 6, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '이송펌프', '🔧', 4, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제차량', '🚛', 1, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제창고', '🏭', 16, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '발전기', '', 2, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '장비부품', '🔗', 11, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '경비함정방제', '', 8, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '살포장치', '🌊', 17, 14);
-- 울진해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '유회수기', '', 6, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '이송펌프', '🔧', 4, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제차량', '🚛', 1, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제창고', '🏭', 13, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '발전기', '', 4, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '지원장비', '🔩', 4, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '경비함정방제', '', 10, 14),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '살포장치', '🌊', 8, 15);
-- 제주해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제선', '🚢', 4, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '유회수기', '', 21, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '이송펌프', '🔧', 17, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '저압세척기', '🚿', 3, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '동력분무기', '💨', 4, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제창고', '🏭', 24, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '발전기', '', 6, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '경비함정방제', '', 4, 13),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '살포장치', '🌊', 16, 14);
-- 서귀포해양경찰서
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '유회수기', '', 9, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '이송펌프', '🔧', 15, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 6),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제창고', '🏭', 10, 9),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '발전기', '', 3, 10),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '경비함정방제', '', 4, 11),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '살포장치', '🌊', 14, 12);
-- 중앙특수구조단
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '방제선', '🚢', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '이송펌프', '🔧', 5, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '방제차량', '🚛', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '유량계측기', '📏', 1, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '발전기', '', 2, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '지원장비', '🔩', 27, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '경비함정방제', '', 1, 6);
-- 오일허브코리아여수㈜ 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '고압세척기', '💧', 1, 0);
-- SK에너지 외 2개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '동력분무기', '💨', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '방제창고', '🏭', 1, 2);
-- SK지오센트릭 외 5개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '동력분무기', '💨', 4, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '방제창고', '🏭', 5, 1);
-- 한국가스공사 통영기지본부
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국가스공사 통영기지본부' LIMIT 1), '방제창고', '🏭', 1, 0);
-- HD현대오일뱅크㈜ 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '동력분무기', '💨', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '방제창고', '🏭', 6, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '발전기', '', 1, 2);
-- 포스코케미칼 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '동력분무기', '💨', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '살포장치', '🌊', 1, 1);
-- 흑산도내연발전소 외 2개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '발전기', '', 1, 1);
-- 오일허브코리아여수㈜ 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '동력분무기', '💨', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '방제창고', '🏭', 3, 1);
-- GS칼텍스㈜ 외 10개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '고압세척기', '💧', 7, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '동력분무기', '💨', 6, 1);
-- HD현대케미칼 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '해안운반차', '🚜', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '동력분무기', '💨', 2, 1);
-- 현대오일터미널(주) 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '고압세척기', '💧', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '발전기', '', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '살포장치', '🌊', 1, 2);
-- 한국남동발전(주) 외 2개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '동력분무기', '💨', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '방제창고', '🏭', 5, 1);
-- HD현대미포
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대미포' LIMIT 1), '방제창고', '🏭', 1, 0);
-- 삼성중공업 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '동력분무기', '💨', 2, 0);
-- 한국남부발전㈜
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남부발전㈜' LIMIT 1), '방제창고', '🏭', 2, 0);
-- 한울원전
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '방제창고', '🏭', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '지원장비', '🔩', 2, 1);
-- ㈜HR-PORT 외 5개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '동력분무기', '💨', 7, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '방제창고', '🏭', 3, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '발전기', '', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '지원장비', '🔩', 3, 5);
-- 삼광조선공업㈜ 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼광조선공업㈜ 외 1개' LIMIT 1), '고압세척기', '💧', 5, 0);
-- 방제유창청소업체(㈜클린포트)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '고압세척기', '💧', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '발전기', '', 1, 1);
-- 방제유창청소업체(대용환경㈜ 외 38개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '고압세척기', '💧', 31, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '저압세척기', '🚿', 5, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '동력분무기', '💨', 3, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '방제창고', '🏭', 5, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '발전기', '', 6, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '현장지휘소', '🏕', 1, 5);
-- 방제유창청소업체((주)한유마린서비스 외 8개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '고압세척기', '💧', 11, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '방제창고', '🏭', 1, 1);
-- 방제유창청소업체(블루씨 외 1개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '고압세척기', '💧', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '살포장치', '🌊', 1, 1);
-- 방제유창청소업체(㈜한국해운 외 1개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜한국해운 외 1개)' LIMIT 1), '고압세척기', '💧', 1, 0);
-- 방제유창청소업체(마로해운 외 11개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '고압세척기', '💧', 28, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '동력분무기', '💨', 15, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '방제창고', '🏭', 5, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '발전기', '', 4, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '살포장치', '🌊', 2, 4);
-- 방제유창청소업체(우진해운㈜)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '발전기', '', 2, 2);
-- 방제유창청소업체((주)씨앤 외 3개)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '발전기', '', 2, 2);
-- 방제유창청소업체(㈜지앤비마린서비스)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜지앤비마린서비스)' LIMIT 1), '방제창고', '🏭', 1, 0);
-- SK엔텀(주) 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK엔텀(주) 외 4개' LIMIT 1), '방제창고', '🏭', 5, 0);
-- GS칼텍스㈜
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '방제창고', '🏭', 4, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '살포장치', '🌊', 20, 2);
-- HD현대오일뱅크㈜
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜' LIMIT 1), '동력분무기', '💨', 2, 0);
-- 부산광역시 외 8개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '방제창고', '🏭', 11, 1);
-- 사천시 외 3개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천시 외 3개' LIMIT 1), '방제창고', '🏭', 6, 0);
-- 울산북구청 외 2개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산북구청 외 2개' LIMIT 1), '방제창고', '🏭', 4, 0);
-- 창원 진해구 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '방제창고', '🏭', 1, 1);
-- 삼척시 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '방제창고', '🏭', 3, 1);
-- 영덕군
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영덕군' LIMIT 1), '방제창고', '🏭', 3, 0);
-- 영광군 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영광군 외 1개' LIMIT 1), '방제창고', '🏭', 5, 0);
-- 광양시 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '광양시 외 1개' LIMIT 1), '방제창고', '🏭', 3, 0);
-- 옹진군청 외 4개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '고압세척기', '💧', 5, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '동력분무기', '💨', 4, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '발전기', '', 1, 2);
-- 태안군청
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안군청' LIMIT 1), '방제창고', '🏭', 1, 0);
-- 안산시청 외 2개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '동력분무기', '💨', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '방제창고', '🏭', 5, 1);
-- ㈜HR-PORT 외 5개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '방제창고', '🏭', 1, 0);
-- 해군1함대사령부 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해군1함대사령부 외 1개' LIMIT 1), '방제창고', '🏭', 1, 0);
-- 해병대 제9518부대
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해병대 제9518부대' LIMIT 1), '발전기', '', 2, 0);
-- 부산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '방제창고', '🏭', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '발전기', '', 2, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '현장지휘소', '🏕', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '살포장치', '🌊', 6, 5);
-- 마산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '방제창고', '🏭', 8, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '발전기', '', 1, 1);
-- 울산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '고압세척기', '💧', 6, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '방제창고', '🏭', 4, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '발전기', '', 4, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '살포장치', '🌊', 1, 4);
-- 마산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '고압세척기', '💧', 4, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '발전기', '', 2, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '살포장치', '🌊', 1, 2);
-- 마산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '방제창고', '🏭', 8, 0);
-- 동해지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '해안운반차', '🚜', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '고압세척기', '💧', 2, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '동력분무기', '💨', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '방제창고', '🏭', 8, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '발전기', '', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '살포장치', '🌊', 2, 5);
-- 포항지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '방제창고', '🏭', 2, 0);
-- 포항지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '고압세척기', '💧', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '동력분무기', '💨', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '발전기', '', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '현장지휘소', '🏕', 1, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '살포장치', '🌊', 2, 4);
-- 군산지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '고압세척기', '💧', 2, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '저압세척기', '🚿', 2, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '동력분무기', '💨', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '발전기', '', 3, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '살포장치', '🌊', 4, 4);
-- 목포지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '저압세척기', '🚿', 2, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '방제창고', '🏭', 6, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '발전기', '', 1, 3);
-- 여수지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '고압세척기', '💧', 5, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '발전기', '', 3, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '살포장치', '🌊', 3, 3);
-- 목포지사 완도사업소
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '방제창고', '🏭', 2, 1);
-- 제주지사(서귀포)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(서귀포)' LIMIT 1), '살포장치', '🌊', 1, 0);
-- 제주지사(제주)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '동력분무기', '💨', 2, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '방제창고', '🏭', 10, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '발전기', '', 1, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '살포장치', '🌊', 3, 5);
-- 대산지사(보령)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '고압세척기', '💧', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '방제창고', '🏭', 4, 1);
-- 인천지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '고압세척기', '💧', 5, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '저압세척기', '🚿', 1, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '동력분무기', '💨', 3, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '발전기', '', 2, 3);
-- 대산지사(태안)
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '해안운반차', '🚜', 1, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '고압세척기', '💧', 5, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '저압세척기', '🚿', 1, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '동력분무기', '💨', 1, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '방제창고', '🏭', 5, 4),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '발전기', '', 3, 5),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '살포장치', '🌊', 1, 6);
-- 평택지사
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '고압세척기', '💧', 3, 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '저압세척기', '🚿', 2, 1),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '방제창고', '🏭', 3, 2),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '발전기', '', 4, 3),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '살포장치', '🌊', 1, 4);
-- 삼성중공업 외 1개
INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0);
-- ============================================================
-- ASSET_CONTACT 초기 데이터
-- ============================================================
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제과장', '김○○', '032-835-0001', 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제담당', '이○○', '032-835-0002', 1);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제담당', '박○○', '031-682-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제담당', '최○○', '041-674-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제담당', '정○○', '041-931-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제과장', '윤○○', '061-660-0001', 0),
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제담당', '장○○', '061-660-0002', 1);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제담당', '조○○', '061-244-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제담당', '한○○', '063-462-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제담당', '이○○', '061-550-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제담당', '김○○', '063-928-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제과장', '임○○', '051-400-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제과장', '강○○', '052-228-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제담당', '송○○', '055-220-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제담당', '서○○', '055-640-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제담당', '박○○', '055-830-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제담당', '남○○', '033-530-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제담당', '오○○', '054-244-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제담당', '양○○', '033-633-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제담당', '배○○', '054-782-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제담당', '문○○', '064-750-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제담당', '고○○', '064-730-0001', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '구조단장', '김○○', '051-580-2044', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '담당', '오일허브코리아여수㈜', '061-686-3611', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '051-643-3331', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '담당', 'SK엔텀㈜', '052-208-2851', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국가스공사 통영기지본부' LIMIT 1), '담당', '한국가스공사', '055-640-6014', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '033-534-2093', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '담당', 'OCI(주)', '054-290-8222', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '담당', '안마도내연발전소', '061-351-2342', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '담당', '오일허브코리아여수㈜', '061-686-3611', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '담당', 'GS칼텍스㈜', '010-8777-6922', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '담당', 'HD현대케미칼', '041-924-1068', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '담당', '(주)경동탱크터미널', '031-683-5101', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '담당', '(주)고성그린파워', '070-4486-7474', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대미포' LIMIT 1), '담당', 'HD현대미포', '052-250-3551', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '담당', '삼성중공업', '055-630-5373', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남부발전㈜' LIMIT 1), '담당', '한국남부발전㈜', '070-7713-5153', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '담당', '한울원전', '054-785-4833', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '담당', '㈜ 한진', '061-791-0358', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼광조선공업㈜ 외 1개' LIMIT 1), '담당', '삼광조선공업㈜', '010-3321-2959', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '담당', '㈜클린포트', '032-882-8279', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '담당', '(주)경원마린서비스', '051-242-0622', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '담당', '(주)골든씨', '010-5499-7401', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '담당', '(주)블루씨', '054-278-8200', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜한국해운 외 1개)' LIMIT 1), '담당', '㈜아라', '010-8615-4326', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '담당', '(유)피케이엘', '061-654-9603', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '담당', '우진해운㈜', '010-4384-6817', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '담당', '(주)소스코리아', '031-683-2389', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜지앤비마린서비스)' LIMIT 1), '담당', '(주)경원마린서비스', '051-242-0622', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK엔텀(주) 외 4개' LIMIT 1), '담당', 'S-OIL㈜', '052-231-2318', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '담당', 'GS칼텍스㈜', '061-680-2121', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '010-2050-5291', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '담당', '남구청', '051-607-4484', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천시 외 3개' LIMIT 1), '담당', '고성군', '055-670-2484', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산북구청 외 2개' LIMIT 1), '담당', '부산기장군청', '051-709-4611', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '담당', '부산 강서구', '051-970-4482', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '담당', '강릉시', '033-640-5284', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영덕군' LIMIT 1), '담당', '영덕군', '054-730-6562', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영광군 외 1개' LIMIT 1), '담당', '목포시', '061-270-3419', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '광양시 외 1개' LIMIT 1), '담당', '광양시', '061-797-2791', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '담당', '김포시청', '010-2740-9388', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안군청' LIMIT 1), '담당', '태안군청', '041-670-2877', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '담당', '당진시청', '041-350-4292', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '담당', '㈜ 한진', '061-791-0358', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해군1함대사령부 외 1개' LIMIT 1), '담당', '1함대 사령부', '033-539-7323', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해병대 제9518부대' LIMIT 1), '담당', '해병대 제9518부대', '010-4801-3473', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '담당', '부산지사', '051-466-3944', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-3598-4202', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '담당', '울산지사', '052-238-7718', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-2265-3928', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-2636-5313', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '담당', '동해지사', '010-7499-0257', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '담당', '포항지사', '054-273-5595', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '담당', '포항지사', '054-273-5595', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '담당', '군산지사', '063-443-4813', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '담당', '목포지사', '061-242-9663', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '담당', '여수지사', '061-654-6431', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '담당', '목포지사', '061-242-9663', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(서귀포)' LIMIT 1), '담당', '제주지사', '064-753-4356', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '담당', '제주지사', '064-753-4356', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '담당', '대산지사', '041-664-9101', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '담당', '인천지사', '010-7133-2167', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '담당', '대산지사', '041-664-9101', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '담당', '평택지사', '031-683-7973', 0);
INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES
((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '담당', '삼성중공업', '055-630-5373', 0);

파일 보기

@ -416,6 +416,55 @@ const result = await authPool.query('SELECT * FROM AUTH_USER WHERE USER_ID = $1'
---
## 8. Mock → API 전환 가이드
각 탭의 mock 데이터를 DB/API로 전환하는 프로세스는 **[MOCK-TO-API-GUIDE.md](./MOCK-TO-API-GUIDE.md)** 참조.
### 전환 완료 탭
| 탭 | MR | API 경로 | 비고 |
|----|-----|----------|------|
| Board (게시판) | MR#29 | `/api/board` | PUT/DELETE 사용 (레거시, POST 전환 예정) |
| Reports (보고서) | MR#31 | `/api/reports` | GET/POST only 적용 |
### Reports API 엔드포인트
| Method | Path | 설명 | 권한 |
|--------|------|------|------|
| GET | `/api/reports/templates` | 템플릿 목록 + 섹션 정의 | requireAuth |
| GET | `/api/reports/categories` | 분석 카테고리 목록 + 섹션 | requireAuth |
| GET | `/api/reports` | 보고서 목록 (필터: jrsdCd, tmplCd, sttsCd, search) | reports READ |
| GET | `/api/reports/:sn` | 보고서 상세 (섹션 데이터 포함) | reports READ |
| POST | `/api/reports` | 보고서 생성 | reports CREATE |
| POST | `/api/reports/:sn/update` | 보고서 수정 | reports UPDATE |
| POST | `/api/reports/:sn/delete` | 보고서 삭제 (논리) | reports DELETE |
| POST | `/api/reports/:sn/sections/:sectCd` | 개별 섹션 수정 | reports UPDATE |
### 프론트엔드 API 서비스
```typescript
// frontend/src/tabs/reports/services/reportsApi.ts
import { api } from '@common/services/api'
// 조회 (GET)
const templates = await fetchTemplates() // GET /reports/templates (캐싱)
const categories = await fetchCategories() // GET /reports/categories (캐싱)
const list = await fetchReports({ tmplCd, sttsCd }) // GET /reports
const detail = await fetchReport(sn) // GET /reports/:sn
// 생성/수정/삭제 (POST)
await createReportApi({ tmplSn, title, sections }) // POST /reports
await updateReportApi(sn, { title, sections }) // POST /reports/:sn/update
await deleteReportApi(sn) // POST /reports/:sn/delete
// 고수준 함수 (OilSpillReportData ↔ API 변환 포함)
await saveReport(reportData) // create 또는 update 자동 분기
const reports = await loadReportsFromApi() // 전체 목록 + 변환
const detail = await loadReportDetail(sn) // 상세 + 섹션 복원
```
---
## 파일 구조 요약
```
@ -433,6 +482,8 @@ backend/src/
├── auth/ 인증 (JWT, OAuth, 미들웨어, requirePermission)
├── users/ 사용자 관리
├── roles/ 역할/권한 관리 (permResolver, roleService)
├── board/ 게시판 CRUD (boardService, boardRouter)
├── reports/ 보고서 CRUD (reportsService, reportsRouter)
├── settings/ 시스템 설정
├── menus/ 메뉴 설정
├── audit/ 감사 로그
@ -445,5 +496,7 @@ database/
├── init.sql 운영 DB DDL
└── migration/ 마이그레이션 스크립트
├── 003_perm_tree.sql 리소스 트리 (AUTH_PERM_TREE)
└── 004_oper_cd.sql 오퍼레이션 코드 (OPER_CD) 추가
├── 004_oper_cd.sql 오퍼레이션 코드 (OPER_CD) 추가
├── 006_board.sql 게시판 (BOARD_POST)
└── 007_reports.sql 보고서 (REPORT_TMPL, REPORT, REPORT_SECT_DATA 등 7개)
```

435
docs/MOCK-TO-API-GUIDE.md Normal file
파일 보기

@ -0,0 +1,435 @@
# Mock → API 전환 개발 지침
이 문서는 각 탭의 mock 데이터를 PostgreSQL DB + REST API 기반으로 전환할 때 따라야 할 표준 프로세스를 정의한다.
CRUD API 작성 규칙은 [CRUD-API-GUIDE.md](./CRUD-API-GUIDE.md) 참조.
---
## 1. 전환 프로세스 (탭당 반복)
### Step A. 브랜치 생성
`feature/{탭명}-crud` 형식으로 develop에서 분기한다.
```bash
git checkout develop
git pull
git checkout -b feature/{탭명}-crud
```
### Step B. Mock 전수 조사 (필수!)
탭 디렉토리 전체에서 mock/하드코딩 데이터를 빠짐없이 검색한다.
**검색 키워드**: `mock`, `Mock`, `MOCK`, `sample`, `initial`, `hardcod`, `localStorage`, 인라인 배열 상수
```bash
grep -rn "mock\|Mock\|MOCK\|sample\|initial\|hardcod\|localStorage" frontend/src/tabs/{탭}/
```
**체크리스트 형식**으로 정리한다:
```
□ 파일명:라인 — 변수명 (N건) — 전환방법
□ components/AssetList.tsx:12 — MOCK_ASSETS (30건) — DB 이전
□ services/assetService.ts:5 — INITIAL_FILTER — 프론트 상수 유지
□ hooks/useAsset.ts:88 — localStorage.getItem('draft') — DB 이전
```
board 전환 시 mock 참조 누락으로 런타임 에러가 발생한 경험이 있다. 전수 조사를 건너뛰지 말 것.
### Step C. 프론트 상수 vs DB 데이터 판단
조사한 mock/하드코딩 데이터를 아래 기준으로 분류한다.
| 분류 | 유지/이전 | 예시 |
|------|-----------|------|
| UI 전용 색상 매핑 | 프론트 상수 유지 | 상태별 뱃지 색, 심각도 색상 |
| 레이아웃/뷰 설정 | 프론트 상수 유지 | 기본 페이지 크기, 컬럼 너비 |
| 비즈니스 목록 데이터 | DB 이전 | 자산 목록, 사고 목록, 보고서 |
| 검색/필터 대상 데이터 | DB 이전 | 카테고리, 기관명, 상태값 |
| 유형/카테고리 코드 | DB 이전 또는 CHECK 제약 | 자산유형, 오염물질유형 |
### Step D. DB 스키마 설계 + 마이그레이션
DDL 규칙은 [CRUD-API-GUIDE.md](./CRUD-API-GUIDE.md) (4. DB 설계 규칙) 참조.
1. 기존 테이블 활용 가능 여부 확인 (예: ACDNT, LAYER 등)
2. `database/migration/NNN_{탭명}.sql` 파일 작성 (번호는 기존 파일 다음 순번)
3. 초기 데이터 INSERT (mock 데이터를 SQL로 변환)
4. psql로 원격 DB에 직접 실행
```bash
# 원격 wing DB에 마이그레이션 실행
PGPASSWORD=Wing2026 psql -h 211.208.115.83 -U wing -d wing \
-f database/migration/NNN_{탭명}.sql
# 실행 결과 검증 (마이그레이션 파일 끝의 SELECT 확인)
```
마이그레이션 파일 규칙:
- 모든 DDL에 `IF NOT EXISTS` / `IF EXISTS` 사용 (재실행 안전)
- 파일 끝에 검증 SELECT 포함
### Step E. 백엔드 Service + Router 구현
Service/Router 패턴은 [CRUD-API-GUIDE.md](./CRUD-API-GUIDE.md) (5. Service 레이어 패턴, 6. Router 레이어 패턴) 참조.
**HTTP 메소드 규칙** (보안취약점 가이드 준수):
| 메소드 | 용도 |
|--------|------|
| GET | 단순 조회 (민감하지 않은 경우) |
| POST | 생성/수정/삭제 및 복잡한 조회 파라미터 |
PUT, DELETE, PATCH는 사용하지 않는다. 자세한 내용은 [2. HTTP 메소드 정책](#2-http-메소드-정책-필독) 참조.
**URL 패턴**:
| URL | 설명 |
|-----|------|
| `GET /api/{domain}` | 목록 (간단한 파라미터) |
| `GET /api/{domain}/:sn` | 상세 |
| `POST /api/{domain}/list` | 목록 (복잡한 필터 파라미터) |
| `POST /api/{domain}/detail` | 상세 |
| `POST /api/{domain}/create` | 생성 |
| `POST /api/{domain}/update` | 수정 |
| `POST /api/{domain}/delete` | 삭제 |
| `GET /api/{domain}/templates` | 메타데이터/코드 조회 |
**인증 패턴**:
```typescript
// 현재 로그인 사용자 UUID 추출
const userId = req.user!.sub // JWT payload의 사용자 UUID
// ❌ 절대 사용 금지 (reports 전환 시 실제 발생한 버그)
const user = (req as unknown as { user: { id: string } }).user
const userId = user.id // undefined → DB NOT NULL 제약 위반
```
구현 후 `backend/src/server.ts`에 라우터를 등록한다.
### Step F. 프론트엔드 API 서비스 + 컴포넌트 전환
1. `frontend/src/tabs/{탭}/services/{탭}Api.ts` 생성
2. API 응답 타입 (`interface Api{탭명}Item` 등) 정의
3. API ↔ 프론트 모델 변환 함수 작성 (필요 시)
4. 정적 마스터 데이터 캐싱: 모듈 변수 또는 TanStack Query `staleTime: Infinity`
5. 컴포넌트에서 mock import → API 호출로 교체
6. `api.post()` 사용 (`api.put()`, `api.delete()` 사용 금지)
```typescript
// frontend/src/tabs/{탭}/services/{탭}Api.ts
import { api } from '@common/services/api'
export interface Api{탭명}Item {
sn: number
// ...
}
export async function fetch{탭명}List(params: {
search?: string
page?: number
size?: number
}): Promise<{ items: Api{탭명}Item[]; totalCount: number }> {
const response = await api.post('/{ 탭명}/list', params)
return response.data
}
// 수정 — POST /update 사용
export async function update{탭명}(sn: number, data: Update{탭명}Input): Promise<void> {
await api.post('/{탭명}/update', { sn, ...data })
}
// 삭제 — POST /delete 사용
export async function delete{탭명}(sn: number): Promise<void> {
await api.post('/{탭명}/delete', { sn })
}
```
### Step G. 빌드 검증
```bash
# 백엔드 TypeScript 컴파일
cd backend && npm run build
# 프론트엔드 타입 체크 + ESLint
cd frontend && npx tsc --noEmit && npx eslint .
```
빌드/린트 에러가 0건이어야 다음 단계로 진행한다.
### Step H. 로컬 API 동작 테스트
```bash
# 백엔드 개발 서버 시작
cd backend && npm run dev
# 로그인 — 쿠키 파일 획득
curl -s -c /tmp/wing.cookie -X POST http://localhost:3001/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"account":"admin","password":"admin1234"}' | jq .
# 목록 조회
curl -s -b /tmp/wing.cookie -X POST http://localhost:3001/api/{탭명}/list \
-H 'Content-Type: application/json' \
-d '{"page":1,"size":10}' | jq .
# 생성
curl -s -b /tmp/wing.cookie -X POST http://localhost:3001/api/{탭명}/create \
-H 'Content-Type: application/json' \
-d '{...}' | jq .
# 수정
curl -s -b /tmp/wing.cookie -X POST http://localhost:3001/api/{탭명}/update \
-H 'Content-Type: application/json' \
-d '{"sn": 1, ...}' | jq .
# 삭제
curl -s -b /tmp/wing.cookie -X POST http://localhost:3001/api/{탭명}/delete \
-H 'Content-Type: application/json' \
-d '{"sn": 1}' | jq .
```
CRUD 전체 흐름(생성 → 조회 → 수정 → 삭제 → 필터)을 확인하고 테스트 데이터를 정리한다.
### Step I. Mock 잔여 확인
```bash
grep -rn "mock\|Mock\|MOCK\|localStorage" frontend/src/tabs/{탭}/
# → UI 상수(색상, 레이아웃) 외 결과 0건이어야 함
```
잔여가 있으면 Step F로 돌아가 처리한다.
### Step J. 커밋 + 푸시 + MR
```bash
# 커밋 (Conventional Commits 형식, 한국어)
git add -p
git commit -m "feat({탭명}): mock 데이터 DB + REST API 전환"
# 푸시
git push -u origin feature/{탭명}-crud
```
`feature/{탭명}-crud``develop` MR을 Gitea에서 생성한다.
`/push` 또는 `/mr` 스킬 활용 가능.
---
## 2. HTTP 메소드 정책 (필독)
한국 보안취약점 점검 가이드에 따라 GET/POST만 사용한다.
### 허용
| 메소드 | 용도 | 예시 |
|--------|------|------|
| GET | 단순 조회 (파라미터가 단순하고 민감하지 않은 경우) | `GET /api/reports`, `GET /api/reports/:sn` |
| POST | 생성/수정/삭제, 복잡한 필터 파라미터 조회 | `POST /api/reports/create`, `POST /api/reports/list` |
### 금지
| 메소드 | 이유 |
|--------|------|
| PUT | 보안취약점 가이드 위반 |
| DELETE | 보안취약점 가이드 위반 |
| PATCH | 보안취약점 가이드 위반 |
### 기존 API 현황
`boardRouter`, `userRouter`, `roleRouter` 등은 아직 PUT/DELETE를 사용 중이다.
별도 세션에서 POST 패턴으로 마이그레이션 예정.
**신규 탭 전환 시 반드시 POST 패턴을 적용한다.**
---
## 3. 전환 시 주의사항 (실전 교훈)
### 3.1 req.user 접근 패턴
```typescript
// 올바른 패턴
const userId = req.user!.sub // JWT payload의 사용자 UUID
// 잘못된 패턴 (런타임 에러 발생)
const user = (req as unknown as { user: { id: string } }).user
const userId = user.id // undefined → DB NOT NULL 제약 위반
```
Reports 전환 시 실제 발생한 버그. `boardRouter.ts`의 패턴을 확인하고 `req.user!.sub`을 사용한다.
JWT 페이로드 전체 구조:
```typescript
interface JwtPayload {
sub: string // 사용자 UUID (USER_ID)
acnt: string // 계정명 (USER_ACNT)
name: string // 사용자명 (USER_NM)
roles: string[] // 역할 코드 목록
}
// 사용 예시
const userId = req.user!.sub // UUID
const userName = req.user!.name // 이름
```
### 3.2 AUTH_USER 테이블 컬럼명
```sql
-- 올바른 컬럼명
SELECT u.USER_NM as author_name FROM AUTH_USER u
-- 잘못된 컬럼명 (500 에러 발생)
SELECT u.NM as author_name FROM AUTH_USER u
```
Reports 전환 시 실제 발생한 버그. 반드시 `USER_NM`을 사용한다.
`AUTH_USER` 주요 컬럼 참조:
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `USER_ID` | UUID PK | 사용자 UUID (`req.user!.sub`과 일치) |
| `USER_ACNT` | VARCHAR | 계정명 (`req.user!.acnt`와 일치) |
| `USER_NM` | VARCHAR | 사용자명 (`req.user!.name`와 일치) |
| `EMAIL` | VARCHAR | 이메일 |
### 3.3 Mock 전수 조사 누락
Board 전환 시 일부 mock 참조를 놓쳐 런타임 에러가 발생했다.
[Step B](#step-b-mock-전수-조사-필수)의 전수 조사를 건너뛰지 말 것.
특히 다음 위치를 반드시 확인한다:
- 컴포넌트 파일 내 인라인 배열 (`const ITEMS = [{ id: 1, ... }]`)
- 커스텀 훅 초기값 (`useState([{ ... }])`)
- `localStorage.getItem` / `localStorage.setItem` 호출
- 서비스 파일 내 하드코딩 반환값
### 3.4 프론트 api.put() / api.delete() 금지
```typescript
// 올바른 POST 패턴
await api.post(`/reports/update`, { sn, ...input })
await api.post(`/reports/delete`, { sn })
// 금지 — PUT/DELETE 사용 불가
await api.put(`/reports/${sn}`, input)
await api.delete(`/reports/${sn}`)
```
### 3.5 트랜잭션 사용 시점
- 단일 테이블 INSERT/UPDATE: 트랜잭션 불필요
- 다중 테이블 동시 변경 (예: 헤더 + 섹션, 보고서 + 첨부파일): 반드시 트랜잭션 사용
```typescript
const client = await wingPool.connect()
try {
await client.query('BEGIN')
// 헤더 INSERT
const headerResult = await client.query(
'INSERT INTO REPORT_HDR (...) VALUES ($1, $2) RETURNING HDR_SN',
[...]
)
const hdrSn = headerResult.rows[0].hdr_sn
// 섹션 INSERT (헤더 FK 참조)
for (const section of sections) {
await client.query(
'INSERT INTO REPORT_SECT (HDR_SN, ...) VALUES ($1, ...)',
[hdrSn, ...]
)
}
await client.query('COMMIT')
return { hdrSn }
} catch (err) {
await client.query('ROLLBACK')
throw err
} finally {
client.release()
}
```
### 3.6 에러 처리 일관성
모든 라우트 핸들러에서 동일한 에러 처리 구조를 사용한다.
```typescript
try {
// 비즈니스 로직
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message })
return
}
console.error('[{탭명}] 작업 오류:', err)
res.status(500).json({ error: '처리 중 오류가 발생했습니다.' })
}
```
Board의 GET 목록 라우트에서 `AuthError` 분기 누락 이슈가 있었다.
목록 조회처럼 평범해 보이는 라우트도 예외 없이 동일한 구조를 사용한다.
### 3.7 정적 마스터 데이터 캐싱
코드 목록, 기관 목록 등 변경이 드문 마스터 데이터는 매 호출마다 DB 조회하지 않는다.
```typescript
// 방법 1: 모듈 변수 캐싱 (서버 재시작 시까지 유지)
let cachedOrgList: OrgItem[] | null = null
export async function getOrgList(): Promise<OrgItem[]> {
if (cachedOrgList) return cachedOrgList
const result = await wingPool.query('SELECT * FROM ORG WHERE USE_YN = $1', ['Y'])
cachedOrgList = result.rows.map(mapOrg)
return cachedOrgList
}
// 방법 2: TanStack Query staleTime 설정 (프론트엔드)
const { data: orgList } = useQuery({
queryKey: ['orgList'],
queryFn: fetchOrgList,
staleTime: 1000 * 60 * 10, // 10분간 리패치 없음
})
```
---
## 4. 탭별 전환 우선순위
| # | 탭 | 난이도 | 상태 | 비고 |
|---|---|--------|------|------|
| 1 | Reports (보고서) | ★★★ | 완료 | 7개 DB 테이블, 섹션 단위 JSONB |
| 2 | Assets (방제자산) | ★★☆ | 대기 | mock 1파일 집중, ORG 테이블 활용 |
| 3 | Incidents (사고관리) | ★★★ | 대기 | mock 5파일 분산, ACDNT 테이블 존재 |
| 4 | SCAT (해안조사) | ★★★★ | 대기 | 1,084개 세그먼트, 스키마 격차 |
| 5 | Rescue (구조시나리오) | ★★★★ | 대기 | DB 미정의, 시뮬레이션 복잡 |
| 6 | Prediction (확산예측) | ★★★★★ | 대기 | 시뮬레이션 엔진 의존, 부분 API 연동 |
제외: Weather (KHOA API 연동 완료), HNS (API 연동 완료), Board (API 연동 완료), Aerial (스켈레톤 수준)
---
## 5. 완료 검증 체크리스트 (탭당)
- [ ] 백엔드 빌드 통과 (`cd backend && npm run build`)
- [ ] 프론트 타입 체크 통과 (`cd frontend && npx tsc --noEmit`)
- [ ] 프론트 ESLint 통과 (`cd frontend && npx eslint .`)
- [ ] API CRUD 전체 테스트 (curl: 생성, 조회, 수정, 삭제, 필터)
- [ ] Mock/localStorage 잔여 0건 (UI 상수 제외)
- [ ] PUT/DELETE 사용 0건 (프론트/백엔드 모두)
- [ ] 커밋 + 푸시 + MR 생성
---
## 관련 문서
- [CRUD-API-GUIDE.md](./CRUD-API-GUIDE.md) — CRUD API 표준 (DB 설계, Service/Router 패턴, 권한 모델)
- [COMMON-GUIDE.md](./COMMON-GUIDE.md) — 공통 로직 (인증, 감사 로그, 메뉴, API 통신)
- [MENU-TAB-GUIDE.md](./MENU-TAB-GUIDE.md) — 새 메뉴 탭 추가 절차

파일 보기

@ -1,19 +1,46 @@
import { useState, useEffect } from 'react'
import type { AssetOrg } from './assetTypes'
import { typeTagCls } from './assetTypes'
import { organizations } from './assetMockData'
import { fetchOrganizations, fetchOrganizationDetail } from '../services/assetsApi'
import type { AssetOrgCompat } from '../services/assetsApi'
import AssetMap from './AssetMap'
function AssetManagement() {
const [viewMode, setViewMode] = useState<'list' | 'map'>('list')
const [selectedOrg, setSelectedOrg] = useState<AssetOrg>(organizations[0])
const [organizations, setOrganizations] = useState<AssetOrgCompat[]>([])
const [selectedOrg, setSelectedOrg] = useState<AssetOrgCompat | null>(null)
const [detailTab, setDetailTab] = useState<'equip' | 'material' | 'contact'>('equip')
const [regionFilter, setRegionFilter] = useState('all')
const [searchTerm, setSearchTerm] = useState('')
const [typeFilterVal, setTypeFilterVal] = useState('all')
const [currentPage, setCurrentPage] = useState(1)
const [loading, setLoading] = useState(true)
const pageSize = 15
// API에서 기관 목록 로드
useEffect(() => {
let cancelled = false
fetchOrganizations()
.then(data => {
if (cancelled) return
setOrganizations(data)
if (data.length > 0) setSelectedOrg(data[0])
})
.catch(err => console.error('[assets] 기관 목록 로드 실패:', err))
.finally(() => { if (!cancelled) setLoading(false) })
return () => { cancelled = true }
}, [])
// 기관 선택 시 상세 조회 (장비/담당자)
const handleSelectOrg = async (org: AssetOrgCompat) => {
setSelectedOrg(org)
try {
const detail = await fetchOrganizationDetail(org.id)
setSelectedOrg(detail)
} catch {
// 상세 조회 실패 시 기본 정보 유지
}
}
const filtered = organizations.filter(o => {
if (regionFilter !== 'all' && !o.jurisdiction.includes(regionFilter)) return false
if (typeFilterVal !== 'all' && o.type !== typeFilterVal) return false
@ -38,6 +65,14 @@ function AssetManagement() {
return '제주청'
}
if (loading) {
return (
<div className="flex items-center justify-center h-full">
<div className="text-text-3 text-sm font-korean"> ...</div>
</div>
)
}
return (
<div className="flex flex-col h-full">
{/* View Switcher & Filters */}
@ -129,9 +164,9 @@ function AssetManagement() {
<tr
key={org.id}
className={`border-b border-border/50 hover:bg-[rgba(255,255,255,0.02)] cursor-pointer transition-colors ${
selectedOrg.id === org.id ? 'bg-[rgba(6,182,212,0.03)]' : ''
selectedOrg?.id === org.id ? 'bg-[rgba(6,182,212,0.03)]' : ''
}`}
onClick={() => { setSelectedOrg(org); setViewMode('map') }}
onClick={() => { handleSelectOrg(org); setViewMode('map') }}
>
<td className="px-2.5 py-2 text-center font-mono text-[10px]">{(safePage - 1) * pageSize + idx + 1}</td>
<td className="px-2.5 py-2">
@ -200,129 +235,131 @@ function AssetManagement() {
<div className="flex-1 relative overflow-hidden">
<AssetMap
organizations={filtered}
selectedOrg={selectedOrg}
onSelectOrg={setSelectedOrg}
selectedOrg={selectedOrg!}
onSelectOrg={handleSelectOrg}
regionFilter={regionFilter}
onRegionFilterChange={setRegionFilter}
/>
</div>
{/* Right Detail Panel */}
<aside className="w-[340px] min-w-[340px] bg-bg-1 border-l border-border flex flex-col">
{/* Header */}
<div className="p-4 border-b border-border">
<div className="text-sm font-bold mb-1 font-korean">{selectedOrg.name}</div>
<div className="text-[11px] text-text-2 font-semibold font-korean mb-1">{selectedOrg.type} · {regionShort(selectedOrg.jurisdiction)} · {selectedOrg.area}</div>
<div className="text-[11px] text-text-3 font-korean">{selectedOrg.address}</div>
</div>
{selectedOrg && (
<aside className="w-[340px] min-w-[340px] bg-bg-1 border-l border-border flex flex-col">
{/* Header */}
<div className="p-4 border-b border-border">
<div className="text-sm font-bold mb-1 font-korean">{selectedOrg.name}</div>
<div className="text-[11px] text-text-2 font-semibold font-korean mb-1">{selectedOrg.type} · {regionShort(selectedOrg.jurisdiction)} · {selectedOrg.area}</div>
<div className="text-[11px] text-text-3 font-korean">{selectedOrg.address}</div>
</div>
{/* Sub-tabs */}
<div className="flex border-b border-border">
{(['equip', 'material', 'contact'] as const).map(t => (
<button
key={t}
onClick={() => setDetailTab(t)}
className={`flex-1 py-2.5 text-center text-[11px] font-semibold font-korean border-b-2 transition-colors cursor-pointer ${
detailTab === t
? 'text-primary-cyan border-primary-cyan'
: 'text-text-3 border-transparent hover:text-text-2'
}`}
>
{t === 'equip' ? '장비' : t === 'material' ? '자재' : '연락처'}
</button>
))}
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-3.5 scrollbar-thin">
{/* Summary */}
<div className="grid grid-cols-3 gap-1.5 mb-3">
{[
{ value: `${selectedOrg.vessel}`, label: '방제선' },
{ value: `${selectedOrg.skimmer}`, label: '유회수기' },
{ value: String(selectedOrg.totalAssets), label: '총 자산' },
].map((s, i) => (
<div key={i} className="bg-bg-3 border border-border rounded-sm p-2.5 text-center">
<div className="text-lg font-bold text-primary-cyan font-mono">{s.value}</div>
<div className="text-[9px] text-text-3 mt-0.5 font-korean">{s.label}</div>
</div>
{/* Sub-tabs */}
<div className="flex border-b border-border">
{(['equip', 'material', 'contact'] as const).map(t => (
<button
key={t}
onClick={() => setDetailTab(t)}
className={`flex-1 py-2.5 text-center text-[11px] font-semibold font-korean border-b-2 transition-colors cursor-pointer ${
detailTab === t
? 'text-primary-cyan border-primary-cyan'
: 'text-text-3 border-transparent hover:text-text-2'
}`}
>
{t === 'equip' ? '장비' : t === 'material' ? '자재' : '연락처'}
</button>
))}
</div>
{detailTab === 'equip' && (
<div className="flex flex-col gap-1">
{selectedOrg.equipment.length > 0 ? selectedOrg.equipment.map((cat, ci) => {
const unitMap: Record<string, string> = {
'방제선': '척', '유회수기': '대', '비치크리너': '대', '이송펌프': '대', '방제차량': '대',
'해안운반차': '대', '고압세척기': '대', '저압세척기': '대', '동력분무기': '대', '유량계측기': '대',
'방제창고': '개소', '발전기': '대', '현장지휘소': '개', '지원장비': '대', '장비부품': '개',
'경비함정방제': '대', '살포장치': '대',
}
const unit = unitMap[cat.category] || '개'
return (
<div key={ci} className="flex items-center justify-between px-2.5 py-2 bg-bg-3 border border-border rounded-sm hover:bg-bg-hover transition-colors">
<span className="text-[11px] font-semibold flex items-center gap-1.5 font-korean">
{cat.icon} {cat.category}
</span>
<span className="text-[11px] font-bold font-mono"><span className="text-primary-cyan">{cat.count}</span><span className="text-text-3 font-normal ml-0.5">{unit}</span></span>
</div>
)
}) : (
<div className="text-center text-text-3 text-xs py-8 font-korean"> .</div>
)}
</div>
)}
{detailTab === 'material' && (
<div className="flex flex-col gap-1.5">
{/* Content */}
<div className="flex-1 overflow-y-auto p-3.5 scrollbar-thin">
{/* Summary */}
<div className="grid grid-cols-3 gap-1.5 mb-3">
{[
['방제선', `${selectedOrg.vessel}`],
['유회수기', `${selectedOrg.skimmer}`],
['이송펌프', `${selectedOrg.pump}`],
['방제차량', `${selectedOrg.vehicle}`],
['살포장치', `${selectedOrg.sprayer}`],
['총 자산', `${selectedOrg.totalAssets}`],
].map(([k, v], i) => (
<div key={i} className="flex justify-between px-2.5 py-2 bg-bg-0 rounded text-[11px]">
<span className="text-text-3 font-korean">{k}</span>
<span className="font-mono font-semibold text-text-1">{v}</span>
{ value: `${selectedOrg.vessel}`, label: '방제선' },
{ value: `${selectedOrg.skimmer}`, label: '유회수기' },
{ value: String(selectedOrg.totalAssets), label: '총 자산' },
].map((s, i) => (
<div key={i} className="bg-bg-3 border border-border rounded-sm p-2.5 text-center">
<div className="text-lg font-bold text-primary-cyan font-mono">{s.value}</div>
<div className="text-[9px] text-text-3 mt-0.5 font-korean">{s.label}</div>
</div>
))}
</div>
)}
{detailTab === 'contact' && (
<div className="bg-bg-3 border border-border rounded-sm p-3">
{selectedOrg.contacts.length > 0 ? selectedOrg.contacts.map((c, i) => (
<div key={i} className="flex flex-col gap-1 mb-3 last:mb-0">
{[
['기관/업체', c.name],
['연락처', c.phone],
].map(([k, v], j) => (
<div key={j} className="flex justify-between py-1 text-[11px]">
<span className="text-text-3 font-korean">{k}</span>
<span className="font-mono text-text-1">{v}</span>
</div>
))}
{i < selectedOrg.contacts.length - 1 && <div className="border-t border-border my-1" />}
</div>
)) : (
<div className="text-center text-text-3 text-xs py-4 font-korean"> .</div>
)}
</div>
)}
</div>
{detailTab === 'equip' && (
<div className="flex flex-col gap-1">
{selectedOrg.equipment.length > 0 ? selectedOrg.equipment.map((cat, ci) => {
const unitMap: Record<string, string> = {
'방제선': '척', '유회수기': '대', '비치크리너': '대', '이송펌프': '대', '방제차량': '대',
'해안운반차': '대', '고압세척기': '대', '저압세척기': '대', '동력분무기': '대', '유량계측기': '대',
'방제창고': '개소', '발전기': '대', '현장지휘소': '개', '지원장비': '대', '장비부품': '개',
'경비함정방제': '대', '살포장치': '대',
}
const unit = unitMap[cat.category] || '개'
return (
<div key={ci} className="flex items-center justify-between px-2.5 py-2 bg-bg-3 border border-border rounded-sm hover:bg-bg-hover transition-colors">
<span className="text-[11px] font-semibold flex items-center gap-1.5 font-korean">
{cat.icon} {cat.category}
</span>
<span className="text-[11px] font-bold font-mono"><span className="text-primary-cyan">{cat.count}</span><span className="text-text-3 font-normal ml-0.5">{unit}</span></span>
</div>
)
}) : (
<div className="text-center text-text-3 text-xs py-8 font-korean"> .</div>
)}
</div>
)}
{/* Bottom Actions */}
<div className="p-3.5 border-t border-border flex gap-2">
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean text-white border-none cursor-pointer" style={{ background: 'linear-gradient(135deg, var(--cyan), var(--blue))' }}>
📥
</button>
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean bg-bg-3 border border-border text-text-2 cursor-pointer hover:bg-bg-hover transition-colors">
</button>
</div>
</aside>
{detailTab === 'material' && (
<div className="flex flex-col gap-1.5">
{[
['방제선', `${selectedOrg.vessel}`],
['유회수기', `${selectedOrg.skimmer}`],
['이송펌프', `${selectedOrg.pump}`],
['방제차량', `${selectedOrg.vehicle}`],
['살포장치', `${selectedOrg.sprayer}`],
['총 자산', `${selectedOrg.totalAssets}`],
].map(([k, v], i) => (
<div key={i} className="flex justify-between px-2.5 py-2 bg-bg-0 rounded text-[11px]">
<span className="text-text-3 font-korean">{k}</span>
<span className="font-mono font-semibold text-text-1">{v}</span>
</div>
))}
</div>
)}
{detailTab === 'contact' && (
<div className="bg-bg-3 border border-border rounded-sm p-3">
{selectedOrg.contacts.length > 0 ? selectedOrg.contacts.map((c, i) => (
<div key={i} className="flex flex-col gap-1 mb-3 last:mb-0">
{[
['기관/업체', c.name],
['연락처', c.phone],
].map(([k, v], j) => (
<div key={j} className="flex justify-between py-1 text-[11px]">
<span className="text-text-3 font-korean">{k}</span>
<span className="font-mono text-text-1">{v}</span>
</div>
))}
{i < selectedOrg.contacts.length - 1 && <div className="border-t border-border my-1" />}
</div>
)) : (
<div className="text-center text-text-3 text-xs py-4 font-korean"> .</div>
)}
</div>
)}
</div>
{/* Bottom Actions */}
<div className="p-3.5 border-t border-border flex gap-2">
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean text-white border-none cursor-pointer" style={{ background: 'linear-gradient(135deg, var(--cyan), var(--blue))' }}>
📥
</button>
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean bg-bg-3 border border-border text-text-2 cursor-pointer hover:bg-bg-hover transition-colors">
</button>
</div>
</aside>
)}
</div>
)}
</div>

파일 보기

@ -1,13 +1,13 @@
import { useEffect, useRef } from 'react'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import type { AssetOrg } from './assetTypes'
import type { AssetOrgCompat } from '../services/assetsApi'
import { typeColor } from './assetTypes'
interface AssetMapProps {
organizations: AssetOrg[]
selectedOrg: AssetOrg
onSelectOrg: (o: AssetOrg) => void
organizations: AssetOrgCompat[]
selectedOrg: AssetOrgCompat
onSelectOrg: (o: AssetOrgCompat) => void
regionFilter: string
onRegionFilterChange: (v: string) => void
}

파일 보기

@ -1,9 +1,17 @@
import { useState } from 'react'
import { uploadHistory } from './assetMockData'
import { useState, useEffect } from 'react'
import { fetchUploadLogs } from '../services/assetsApi'
import type { UploadLogItem } from '../services/assetsApi'
function AssetUpload() {
const [uploadMode, setUploadMode] = useState<'add' | 'replace'>('add')
const [uploaded, setUploaded] = useState(false)
const [uploadHistory, setUploadHistory] = useState<UploadLogItem[]>([])
useEffect(() => {
fetchUploadLogs(10)
.then(setUploadHistory)
.catch(err => console.error('[assets] 업로드 이력 로드 실패:', err))
}, [])
const handleUpload = () => {
setUploaded(true)
@ -106,11 +114,11 @@ function AssetUpload() {
{/* Upload History */}
<div className="text-[13px] font-bold mb-3.5 font-korean">📋 </div>
<div className="flex flex-col gap-2">
{uploadHistory.map((h, i) => (
<div key={i} className="flex justify-between items-center p-3.5 px-4 bg-bg-3 border border-border rounded-sm">
{uploadHistory.map((h) => (
<div key={h.logSn} className="flex justify-between items-center p-3.5 px-4 bg-bg-3 border border-border rounded-sm">
<div>
<div className="text-xs font-semibold font-korean">{h.filename}</div>
<div className="text-[10px] text-text-3 mt-0.5 font-korean">{h.date} · {h.uploader} · {h.count}</div>
<div className="text-xs font-semibold font-korean">{h.fileNm}</div>
<div className="text-[10px] text-text-3 mt-0.5 font-korean">{new Date(h.regDtm).toLocaleString('ko-KR')} · {h.uploaderNm} · {h.uploadCnt}</div>
</div>
<span className="px-2 py-0.5 rounded-full text-[10px] font-semibold bg-[rgba(34,197,94,0.15)] text-status-green"></span>
</div>

파일 보기

@ -1,6 +1,14 @@
import { useState } from 'react'
import type { InsuranceRow } from './assetTypes'
import { insuranceDemoData } from './assetMockData'
// 샘플 데이터 (외부 한국해운조합 API 연동 전 데모용)
const INSURANCE_DEMO_DATA: InsuranceRow[] = [
{ shipName: '유조선 한라호', mmsi: '440123456', imo: '9876001', insType: 'P&I 보험', insurer: '한국P&I클럽', policyNo: 'PI-2025-1234', start: '2025-07-01', expiry: '2026-06-30', limit: '50억' },
{ shipName: '화학물질운반선 제주호', mmsi: '440345678', imo: '9876002', insType: '선주책임보험', insurer: '삼성화재', policyNo: 'SF-2025-9012', start: '2025-09-16', expiry: '2026-09-15', limit: '80억' },
{ shipName: '방제선 OCEAN STAR', mmsi: '440123789', imo: '9876003', insType: 'P&I 보험', insurer: '한국P&I클럽', policyNo: 'PI-2025-3456', start: '2025-11-21', expiry: '2026-11-20', limit: '120억' },
{ shipName: 'LNG운반선 부산호', mmsi: '440567890', imo: '9876004', insType: '해상보험', insurer: 'DB손해보험', policyNo: 'DB-2025-7890', start: '2025-08-02', expiry: '2026-08-01', limit: '200억' },
{ shipName: '유조선 백두호', mmsi: '440789012', imo: '9876005', insType: 'P&I 보험', insurer: 'SK해운보험', policyNo: 'MH-2025-5678', start: '2025-01-01', expiry: '2025-12-31', limit: '30억' },
]
function ShipInsurance() {
const [apiConnected, setApiConnected] = useState(false)
@ -44,7 +52,7 @@ function ShipInsurance() {
}
const loadDemoData = () => {
setResultData(insuranceDemoData)
setResultData(INSURANCE_DEMO_DATA)
setViewState('result')
setApiConnected(false)
setLastSync(new Date().toLocaleString('ko-KR'))

파일 보기

@ -0,0 +1,115 @@
import api from '@common/services/api';
// ============================================================
// 타입
// ============================================================
export interface OrgListItem {
orgSn: number;
orgTp: string;
jrsdNm: string;
areaNm: string;
orgNm: string;
addr: string;
tel: string;
lat: number;
lng: number;
pinSize: 'hq' | 'lg' | 'md';
vesselCnt: number;
skimmerCnt: number;
pumpCnt: number;
vehicleCnt: number;
sprayerCnt: number;
totalAssets: number;
}
export interface EquipItem {
category: string;
icon: string;
count: number;
}
export interface ContactItem {
role: string;
name: string;
phone: string;
}
export interface OrgDetail extends OrgListItem {
equipment: EquipItem[];
contacts: ContactItem[];
}
export interface UploadLogItem {
logSn: number;
fileNm: string;
uploaderNm: string;
uploadCnt: number;
regDtm: string;
}
// ============================================================
// AssetOrg 호환 인터페이스 (기존 컴포넌트와 호환)
// ============================================================
export interface AssetOrgCompat {
id: number;
type: string;
jurisdiction: string;
area: string;
name: string;
address: string;
phone: string;
lat: number;
lng: number;
pinSize: 'hq' | 'lg' | 'md';
vessel: number;
skimmer: number;
pump: number;
vehicle: number;
sprayer: number;
totalAssets: number;
equipment: EquipItem[];
contacts: ContactItem[];
}
function toCompat(item: OrgListItem, equipment: EquipItem[] = [], contacts: ContactItem[] = []): AssetOrgCompat {
return {
id: item.orgSn,
type: item.orgTp,
jurisdiction: item.jrsdNm,
area: item.areaNm,
name: item.orgNm,
address: item.addr,
phone: item.tel,
lat: item.lat,
lng: item.lng,
pinSize: item.pinSize,
vessel: item.vesselCnt,
skimmer: item.skimmerCnt,
pump: item.pumpCnt,
vehicle: item.vehicleCnt,
sprayer: item.sprayerCnt,
totalAssets: item.totalAssets,
equipment,
contacts,
};
}
// ============================================================
// API 호출
// ============================================================
export async function fetchOrganizations(): Promise<AssetOrgCompat[]> {
const { data } = await api.get<OrgListItem[]>('/assets/orgs');
return data.map(item => toCompat(item));
}
export async function fetchOrganizationDetail(sn: number): Promise<AssetOrgCompat> {
const { data } = await api.get<OrgDetail>(`/assets/orgs/${sn}`);
return toCompat(data, data.equipment, data.contacts);
}
export async function fetchUploadLogs(limit = 20): Promise<UploadLogItem[]> {
const { data } = await api.get<UploadLogItem[]>(`/assets/upload-logs?limit=${limit}`);
return data;
}