|
|
|
|
|
# CLAUDE.md
|
|
|
|
|
|
|
|
|
|
|
|
이 파일은 이 저장소에서 코드 작업을 할 때 Claude Code (claude.ai/code)에게 제공하는 가이드입니다.
|
|
|
|
|
|
|
|
|
|
|
|
# ⚠️ 최우선 규칙 - 반드시 준수 ⚠️
|
|
|
|
|
|
|
|
|
|
|
|
## 🚨 패턴 통일화 필수 프로세스 (MANDATORY)
|
|
|
|
|
|
|
|
|
|
|
|
**모든 코드 작성 전 반드시 아래 프로세스를 따를 것. 이 프로세스를 건너뛰면 작업을 시작하지 말 것.**
|
|
|
|
|
|
|
|
|
|
|
|
### 1단계: 패턴 분석 (ALWAYS FIRST)
|
|
|
|
|
|
새로운 코드를 작성하기 전, **반드시** 유사한 기존 코드의 패턴을 먼저 분석 (참고대상 CRDN-단속 하위 디렉토리 or 패키지 or xml query):
|
|
|
|
|
|
|
|
|
|
|
|
- **Controller 작성 시**:
|
|
|
|
|
|
- 동일 패키지 내 다른 Controller 파일을 최소 2개 이상 Read로 읽고 분석
|
|
|
|
|
|
- 메서드 구조, 어노테이션 패턴, 파라미터 처리 방식, 응답 구조 확인
|
|
|
|
|
|
|
|
|
|
|
|
- **JSP/JavaScript 작성 시**:
|
|
|
|
|
|
- 동일 디렉토리 내 유사한 JSP 파일을 Read로 읽고 분석
|
|
|
|
|
|
- JavaScript 함수 네이밍, AJAX 호출 패턴, 에러 처리 방식 확인
|
|
|
|
|
|
- HTML 구조, CSS 클래스명, 폼 구조 확인
|
|
|
|
|
|
|
|
|
|
|
|
- **Service/Mapper 작성 시**:
|
|
|
|
|
|
- 동일 패키지의 기존 Service/Mapper 파일 읽고 분석
|
|
|
|
|
|
- 메서드 네이밍, 트랜잭션 처리, 예외 처리 패턴 확인
|
|
|
|
|
|
|
|
|
|
|
|
### 2단계: 패턴 적용 검증 (BEFORE WRITING)
|
|
|
|
|
|
코드를 작성하기 전, 다음을 확인:
|
|
|
|
|
|
|
|
|
|
|
|
1. ✅ 기존 파일을 Read 도구로 읽었는가?
|
|
|
|
|
|
2. ✅ 해당 파일의 코딩 스타일(들여쓰기, 주석, 네이밍)을 파악했는가?
|
|
|
|
|
|
3. ✅ 유사한 기능이 어떻게 구현되어 있는지 확인했는가?
|
|
|
|
|
|
4. ✅ 내가 작성할 코드가 기존 패턴과 100% 일치하는가?
|
|
|
|
|
|
|
|
|
|
|
|
**위 4가지 중 하나라도 NO면 코드를 작성하지 말고, 먼저 패턴 분석으로 돌아갈 것.**
|
|
|
|
|
|
|
|
|
|
|
|
### 3단계: 작성 후 자체 검증 (AFTER WRITING)
|
|
|
|
|
|
코드 작성 후 제출 전, 다음을 점검:
|
|
|
|
|
|
|
|
|
|
|
|
1. ✅ 들여쓰기(탭/스페이스)가 기존 파일과 동일한가?
|
|
|
|
|
|
2. ✅ 변수/함수 네이밍 규칙이 기존과 동일한가?
|
|
|
|
|
|
3. ✅ 주석 스타일(한글/영문, 위치)이 기존과 동일한가?
|
|
|
|
|
|
4. ✅ 에러 처리 방식이 기존과 동일한가?
|
|
|
|
|
|
5. ✅ API 응답 구조가 기존과 동일한가?
|
|
|
|
|
|
6. ✅ **[필수] VO 파일을 Read 도구로 다시 읽어서 필드가 정상 추가되었는지 확인했는가?** (Edit 결과를 믿지 말고 반드시 Read로 재확인)
|
|
|
|
|
|
7. ✅ **[필수] XML 쿼리를 Read 도구로 다시 읽어서 수정이 정상 반영되었는지 확인했는가?**
|
|
|
|
|
|
|
|
|
|
|
|
### 🔴 절대 금지 사항
|
|
|
|
|
|
- ❌ 기존 코드를 읽지 않고 "추측"으로 작성
|
|
|
|
|
|
- ❌ 기존 파일의 들여쓰기/포맷팅 변경
|
|
|
|
|
|
- ❌ 기존과 다른 스타일의 주석 추가
|
|
|
|
|
|
- ❌ 새로운 패턴이나 "더 나은 방법" 제안 (명시적 요청이 없는 한)
|
|
|
|
|
|
- ❌ 임의로 코드 리팩토링
|
|
|
|
|
|
|
|
|
|
|
|
### 📋 체크리스트 (모든 작업 전 필수 확인)
|
|
|
|
|
|
```
|
|
|
|
|
|
[ ] 1. 유사 기능의 기존 코드를 Read로 읽었는가?
|
|
|
|
|
|
[ ] 2. 기존 코드의 패턴(네이밍, 구조, 스타일)을 분석했는가?
|
|
|
|
|
|
[ ] 3. 내 코드가 기존 패턴과 100% 일치하는가?
|
|
|
|
|
|
[ ] 4. 들여쓰기, 주석, 변수명이 기존과 동일한가?
|
|
|
|
|
|
[ ] 5. [작업 후] VO 파일 변경 시 Read로 재확인했는가?
|
|
|
|
|
|
[ ] 6. [작업 후] XML 쿼리 변경 시 Read로 재확인했는가?
|
|
|
|
|
|
[ ] 7. [작업 후] Service/Mapper 변경 시 Read로 재확인했는가?
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**이 체크리스트를 모두 완료하지 않으면 코드를 작성하지 말 것.**
|
|
|
|
|
|
**특히 Edit 도구 사용 후에는 반드시 Read 도구로 재확인할 것. Edit 결과를 절대 믿지 말 것.**
|
|
|
|
|
|
|
|
|
|
|
|
## 프로젝트 개요
|
|
|
|
|
|
|
|
|
|
|
|
일산 동구 건축물 위법행위 통합관리시스템 - 공무원이 불법 건축물 관련 위반사항을 관리하기 위한 Spring Boot 웹 애플리케이션입니다.
|
|
|
|
|
|
|
|
|
|
|
|
## 빌드 및 개발 명령어
|
|
|
|
|
|
|
|
|
|
|
|
### 빌드 명령어
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 프로젝트 빌드
|
|
|
|
|
|
./gradlew build
|
|
|
|
|
|
|
|
|
|
|
|
# 클린 빌드
|
|
|
|
|
|
./gradlew clean build
|
|
|
|
|
|
|
|
|
|
|
|
# 애플리케이션 실행 (기본 포트: 8080)
|
|
|
|
|
|
./gradlew bootRun
|
|
|
|
|
|
|
|
|
|
|
|
# 배포용 WAR 파일 빌드
|
|
|
|
|
|
./gradlew bootWar
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 테스트
|
|
|
|
|
|
이 프로젝트에서는 프로젝트 가이드라인에 따라 테스트가 명시적으로 비활성화되어 있습니다. 테스트 파일을 생성하지 마세요.
|
|
|
|
|
|
|
|
|
|
|
|
## 고수준 아키텍처
|
|
|
|
|
|
|
|
|
|
|
|
### 기술 스택
|
|
|
|
|
|
- **JDK**: JDK1.8만 사용
|
|
|
|
|
|
- **프레임워크**: Spring Boot 2.7.18 with eGovFrame 4.3.0 (한국 정부 표준 프레임워크)
|
|
|
|
|
|
- **데이터베이스**: MariaDB with MyBatis 2.3.1 for ORM
|
|
|
|
|
|
- **프론트엔드**: JSP with Apache Tiles 3.0.8 (레이아웃), TOAST UI Grid 4.19.2 (데이터 그리드)
|
|
|
|
|
|
- **빌드 도구**: Gradle
|
|
|
|
|
|
|
|
|
|
|
|
### MVC 아키텍처 패턴
|
|
|
|
|
|
프로젝트는 엄격한 MVC 패턴을 따릅니다:
|
|
|
|
|
|
- **Model**: `model/` 패키지에 `PagingVO`를 확장한 VO 클래스들 포함
|
|
|
|
|
|
- **View**: Apache Tiles 레이아웃을 사용하는 `src/main/webapp/WEB-INF/views/`의 JSP 파일들
|
|
|
|
|
|
- **Controller**: Swagger 어노테이션을 사용하는 Spring MVC 컨트롤러가 있는 `controller/` 패키지들
|
|
|
|
|
|
|
|
|
|
|
|
### 비즈니스 도메인별 패키지 구조
|
|
|
|
|
|
```
|
|
|
|
|
|
go.kr.project/
|
|
|
|
|
|
├── system/ # 시스템 관리 (사용자, 역할, 메뉴, 코드)
|
|
|
|
|
|
├── login/ # 인증
|
|
|
|
|
|
├── common/ # 공통 컴포넌트 및 유틸리티
|
|
|
|
|
|
├── baseData/ # 마스터 데이터 관리 (건축물 지수, 위반 법령)
|
|
|
|
|
|
├── crdn/ # 핵심 비즈니스: 위반 관리
|
|
|
|
|
|
│ ├── crndRegistAndView/ # 위반 등록 및 조회
|
|
|
|
|
|
│ │ ├── main/ # 주요 위반 프로세스
|
|
|
|
|
|
│ │ ├── crdnActInfo/ # 위반 행위 정보
|
|
|
|
|
|
│ │ ├── crdnPstnInfo/ # 위치 정보
|
|
|
|
|
|
│ │ └── crdnOwnrInfo/ # 소유자 정보
|
|
|
|
|
|
│ └── ownac/ # 소유자 조치 관리
|
|
|
|
|
|
└── noti/ # 알림 시스템
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 데이터베이스 레이어 (MyBatis)
|
|
|
|
|
|
- **매퍼**: `mapper/` 패키지의 인터페이스 클래스들
|
|
|
|
|
|
- **SQL**: 동일한 패키지 구조를 따르는 `src/main/resources/mybatis/mapper/`의 XML 파일들
|
|
|
|
|
|
- **시퀀스**: 모든 기본 키는 10자리 LPAD 시퀀스 사용: `SELECT LPAD(NEXTVAL(seq_table_id), 10, '0')`
|
|
|
|
|
|
- **네이밍**: camelCase 변환 자동 사용, `DB-DDL/maria/dictionary/`의 컬럼 단어 사전 따름
|
|
|
|
|
|
|
|
|
|
|
|
### 서비스 레이어 패턴
|
|
|
|
|
|
각 비즈니스 도메인은 다음을 포함합니다:
|
|
|
|
|
|
- **Service Interface**: 비즈니스 오퍼레이션 정의
|
|
|
|
|
|
- **ServiceImpl**: 비즈니스 로직 구현, `@RequiredArgsConstructor` 사용하고 매퍼 호출
|
|
|
|
|
|
- **표준 메서드**: 일관된 네이밍을 따르는 `select*`, `insert*`, `update*`, `delete*`
|
|
|
|
|
|
|
|
|
|
|
|
### 프론트엔드 아키텍처
|
|
|
|
|
|
|
|
|
|
|
|
#### 레이아웃 (Apache Tiles)
|
|
|
|
|
|
- **base**: 헤더, 메뉴, 콘텐츠 영역을 가진 메인 관리자 레이아웃
|
|
|
|
|
|
- **login**: 로그인 페이지 레이아웃
|
|
|
|
|
|
- **popup**: 팝업 윈도우 레이아웃 (접미사 `TilesConstants.POPUP`)
|
|
|
|
|
|
|
|
|
|
|
|
#### UI 컴포넌트
|
|
|
|
|
|
- **모달**: JavaScript로 제어되는 `.modalz` CSS 클래스를 가진 JSP에 임베드
|
|
|
|
|
|
- **팝업**: 팝업 레이아웃을 사용하는 별도 JSP 페이지, `window.open()`으로 열림
|
|
|
|
|
|
- **그리드**: 구성을 위한 `XitTuiGridConfig` 헬퍼 클래스를 가진 TOAST UI Grid
|
|
|
|
|
|
|
|
|
|
|
|
#### JavaScript 패턴
|
|
|
|
|
|
- `setTimeout` 사용 피하기 (문제 야기)
|
|
|
|
|
|
- 모든 URL에 `<c:url/>` 태그 사용
|
|
|
|
|
|
- `dataSource.api.readData`를 통한 AJAX로 그리드 데이터 로딩
|
|
|
|
|
|
-
|
|
|
|
|
|
#### CSS, JavaScript 위치
|
|
|
|
|
|
src/main/webapp/resources/xit
|
|
|
|
|
|
src/main/webapp/resources/css
|
|
|
|
|
|
|
|
|
|
|
|
#### TUI Grid 는 아래 패턴을 이용
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 단속 목록 관리 네임스페이스
|
|
|
|
|
|
*/
|
|
|
|
|
|
var CrdnRegistAndViewList = {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 선택된 행 정보
|
|
|
|
|
|
*/
|
|
|
|
|
|
selectedRow: null,
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 그리드 관련 객체
|
|
|
|
|
|
*/
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 그리드 인스턴스
|
|
|
|
|
|
*/
|
|
|
|
|
|
instance: null,
|
|
|
|
|
|
```
|
|
|
|
|
|
tui grid 선언 규칙
|
|
|
|
|
|
CrdnRegistAndViewList.grid.instance
|
|
|
|
|
|
해당페이지명명규칙.그리드객체.그리드인스턴스
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 중요한 개발 가이드라인
|
|
|
|
|
|
|
|
|
|
|
|
### 모드 표준화
|
|
|
|
|
|
모든 CRUD 작업은 일관된 모드 값을 사용합니다:
|
|
|
|
|
|
- `mode: C` = Create (등록)
|
|
|
|
|
|
- `mode: U` = Update (수정)
|
|
|
|
|
|
- `mode: V` = View (보기)
|
|
|
|
|
|
- `mode: D` = Delete (삭제)
|
|
|
|
|
|
|
|
|
|
|
|
### 페이지네이션 패턴 (중요한 순서)
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 1. 먼저 총 개수 가져오기
|
|
|
|
|
|
int totalCount = service.selectTotalCount(paramVO);
|
|
|
|
|
|
// 2. 총 개수 설정
|
|
|
|
|
|
paramVO.setTotalCount(totalCount);
|
|
|
|
|
|
// 3. 페이징 활성화 (선택사항)
|
|
|
|
|
|
paramVO.setPagingYn("Y");
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 보안 및 세션
|
|
|
|
|
|
- 감사 필드에 `SessionUtil.getUserId()` 사용
|
|
|
|
|
|
- 모든 폼에 CSRF 보호 필요
|
|
|
|
|
|
- 하드코딩된 자격증명이나 민감한 데이터 금지
|
|
|
|
|
|
|
|
|
|
|
|
### 코드 품질 요구사항
|
|
|
|
|
|
- **한글 주석**: 모든 비즈니스 로직은 한글 주석 필수 (중요로직 주석)
|
|
|
|
|
|
- **Swagger**: 모든 컨트롤러는 Swagger 어노테이션 필수 (`@Tag`, `@Operation`, `@ApiResponses`)
|
|
|
|
|
|
- **포맷팅 변경 금지**: 기존 코드 포맷팅이나 공백 수정 절대 금지
|
|
|
|
|
|
|
|
|
|
|
|
### 비즈니스 로직 패턴
|
|
|
|
|
|
위반 관리(`crdn`)와 마스터 데이터(`baseData`)의 경우, 다음의 기존 패턴을 따르세요:
|
|
|
|
|
|
- 뷰 패턴: `src/main/webapp/WEB-INF/views/baseData/`와 `src/main/webapp/WEB-INF/views/crdn/`
|
|
|
|
|
|
- Java 패턴: `src/main/java/go/kr/project/baseData/bldgNewPrcCrtrAmt/`와 `src/main/java/go/kr/project/crdn/`
|
|
|
|
|
|
|
|
|
|
|
|
### 최근 아키텍처 추가: 스마트 CRUD 패턴
|
|
|
|
|
|
`CrdnLevyPrvntcController`는 생성/업데이트 작업을 처리하는 고급 패턴을 보여줍니다:
|
|
|
|
|
|
|
|
|
|
|
|
- **통합 저장 API**: `/saveLevyInfo.ajax`가 insert vs update를 자동으로 결정
|
|
|
|
|
|
- **스냅샷 복원**: 그리드 행 선택이 기존 데이터를 로드하여 이전 계산을 복원
|
|
|
|
|
|
- **스마트 데이터 로딩**: 복잡한 조인을 사용하여 표시명과 계산 비율을 함께 가져옴
|
|
|
|
|
|
- **프론트엔드 상태 관리**: JavaScript가 모드 감지와 폼 채우기를 처리
|
|
|
|
|
|
|
|
|
|
|
|
이 패턴은 데이터를 계산 스냅샷으로 저장하고 나중에 복원해야 하는 유사한 위반 처리 워크플로우에서 따라야 합니다.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# 📘 프로젝트 상세 문서
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 전체 프로젝트 구조
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 Java 패키지 구조 상세
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
go.kr.project/
|
|
|
|
|
|
│
|
|
|
|
|
|
├── common/ # 공통 기능
|
|
|
|
|
|
│ ├── controller/ # 공통 컨트롤러
|
|
|
|
|
|
│ │ ├── CommonCodeController # 공통코드 조회
|
|
|
|
|
|
│ │ ├── HtmlEditorController # HTML 에디터 파일 관리
|
|
|
|
|
|
│ │ └── SearchAddressController # 주소 검색
|
|
|
|
|
|
│ ├── service/ # 공통 서비스
|
|
|
|
|
|
│ │ ├── CommonCodeService # 공통코드 서비스
|
|
|
|
|
|
│ │ ├── CommonHeaderService # 공통 헤더 서비스
|
|
|
|
|
|
│ │ └── HtmlEditorService # HTML 에디터 서비스
|
|
|
|
|
|
│ ├── mapper/ # 공통 매퍼
|
|
|
|
|
|
│ └── model/ # 공통 모델
|
|
|
|
|
|
│ ├── PagingVO # 페이징 기본 VO (모든 VO의 부모 클래스)
|
|
|
|
|
|
│ ├── FileVO # 파일 정보 VO
|
|
|
|
|
|
│ └── CmmnCodeSearchVO # 공통코드 검색 VO
|
|
|
|
|
|
│
|
|
|
|
|
|
├── login/ # 로그인 및 인증
|
|
|
|
|
|
│ ├── service/LoginService # 로그인 서비스
|
|
|
|
|
|
│ ├── mapper/LoginMapper # 로그인 매퍼
|
|
|
|
|
|
│ └── model/ # 로그인 모델
|
|
|
|
|
|
│ ├── SessionVO # 세션 정보 VO
|
|
|
|
|
|
│ ├── UserSessionVO # 사용자 세션 VO
|
|
|
|
|
|
│ └── LoginUserVO # 로그인 사용자 VO
|
|
|
|
|
|
│
|
|
|
|
|
|
├── mypage/ # 마이페이지
|
|
|
|
|
|
│ ├── controller/MypageController
|
|
|
|
|
|
│ ├── service/MypageService
|
|
|
|
|
|
│ └── service/impl/MypageServiceImpl
|
|
|
|
|
|
│
|
|
|
|
|
|
├── system/ # 시스템 관리
|
|
|
|
|
|
│ ├── user/ # 사용자 관리
|
|
|
|
|
|
│ │ ├── controller/UserController
|
|
|
|
|
|
│ │ ├── service/UserService
|
|
|
|
|
|
│ │ ├── service/impl/UserServiceImpl
|
|
|
|
|
|
│ │ ├── mapper/UserMapper
|
|
|
|
|
|
│ │ └── model/
|
|
|
|
|
|
│ │ ├── SystemUserVO # 시스템 사용자 VO
|
|
|
|
|
|
│ │ └── SystemUserSearchVO # 사용자 검색 VO
|
|
|
|
|
|
│ ├── group/ # 그룹 관리
|
|
|
|
|
|
│ │ ├── controller/GroupController
|
|
|
|
|
|
│ │ ├── service/GroupService
|
|
|
|
|
|
│ │ └── model/GroupVO
|
|
|
|
|
|
│ ├── role/ # 역할 관리
|
|
|
|
|
|
│ │ ├── controller/RoleController
|
|
|
|
|
|
│ │ ├── service/RoleService
|
|
|
|
|
|
│ │ └── model/RoleVO
|
|
|
|
|
|
│ ├── menu/ # 메뉴 관리
|
|
|
|
|
|
│ │ ├── controller/MenuController
|
|
|
|
|
|
│ │ ├── service/MenuService
|
|
|
|
|
|
│ │ └── mapper/MenuMapper
|
|
|
|
|
|
│ ├── code/ # 코드 관리
|
|
|
|
|
|
│ │ ├── controller/CodeController
|
|
|
|
|
|
│ │ ├── service/CodeService
|
|
|
|
|
|
│ │ ├── mapper/CodeMapper
|
|
|
|
|
|
│ │ └── model/
|
|
|
|
|
|
│ │ ├── CodeGroupVO # 코드 그룹 VO
|
|
|
|
|
|
│ │ └── CodeDetailVO # 코드 상세 VO
|
|
|
|
|
|
│ ├── auth/ # 권한 관리
|
|
|
|
|
|
│ │ ├── controller/AuthController
|
|
|
|
|
|
│ │ ├── service/AuthService
|
|
|
|
|
|
│ │ ├── mapper/AuthMapper
|
|
|
|
|
|
│ │ └── model/
|
|
|
|
|
|
│ │ ├── AuthVO
|
|
|
|
|
|
│ │ ├── GroupRoleVO
|
|
|
|
|
|
│ │ ├── RoleMenuVO
|
|
|
|
|
|
│ │ └── MenuProgramVO
|
|
|
|
|
|
│ ├── loginLog/ # 로그인 로그
|
|
|
|
|
|
│ │ ├── controller/LoginLogController
|
|
|
|
|
|
│ │ ├── service/LoginLogService
|
|
|
|
|
|
│ │ └── model/LoginLogVO
|
|
|
|
|
|
│ └── execquery/ # 쿼리 실행 (개발/디버깅용)
|
|
|
|
|
|
│ ├── controller/DbQueryController
|
|
|
|
|
|
│ ├── service/DbQueryService
|
|
|
|
|
|
│ └── config/IsolatedTransactionManager
|
|
|
|
|
|
│
|
|
|
|
|
|
├── baseData/ # 기초 데이터 관리
|
|
|
|
|
|
│ └── bldgNewPrcCrtrAmt/ # 건축물 신축가격 기준액 관리
|
|
|
|
|
|
│ ├── controller/BldgNewPrcCrtrAmtController (추정)
|
|
|
|
|
|
│ ├── service/BldgNewPrcCrtrAmtService
|
|
|
|
|
|
│ ├── service/impl/BldgNewPrcCrtrAmtServiceImpl
|
|
|
|
|
|
│ ├── mapper/BldgNewPrcCrtrAmtMapper
|
|
|
|
|
|
│ └── model/BldgNewPrcCrtrAmtVO
|
|
|
|
|
|
│
|
|
|
|
|
|
├── crdn/ # 단속 관리 (핵심 비즈니스 도메인)
|
|
|
|
|
|
│ ├── crndRegistAndView/ # 단속 등록 및 조회
|
|
|
|
|
|
│ │ ├── main/ # 메인 단속 프로세스
|
|
|
|
|
|
│ │ │ ├── controller/
|
|
|
|
|
|
│ │ │ │ ├── CrdnRegistAndViewController # 단속 등록/조회
|
|
|
|
|
|
│ │ │ │ ├── CrdnLevyPrvntcController # 부과예고 관리
|
|
|
|
|
|
│ │ │ │ ├── CrdnRelevyController # 재부과 관리
|
|
|
|
|
|
│ │ │ │ └── CrdnImpltTaskController # 이행 업무 관리
|
|
|
|
|
|
│ │ │ ├── service/
|
|
|
|
|
|
│ │ │ │ ├── CrdnRegistAndViewService
|
|
|
|
|
|
│ │ │ │ ├── CrdnLevyPrvntcService
|
|
|
|
|
|
│ │ │ │ ├── CrdnRelevyService
|
|
|
|
|
|
│ │ │ │ └── CrdnImpltTaskService
|
|
|
|
|
|
│ │ │ ├── service/impl/
|
|
|
|
|
|
│ │ │ │ ├── CrdnRegistAndViewServiceImpl
|
|
|
|
|
|
│ │ │ │ ├── CrdnLevyPrvntcServiceImpl
|
|
|
|
|
|
│ │ │ │ ├── CrdnRelevyServiceImpl
|
|
|
|
|
|
│ │ │ │ └── CrdnImpltTaskServiceImpl
|
|
|
|
|
|
│ │ │ └── model/
|
|
|
|
|
|
│ │ │ ├── CrdnRegistAndViewVO # 단속 기본 정보 VO
|
|
|
|
|
|
│ │ │ ├── CrdnLevyInfoVO # 부과 정보 VO (계산식 포함)
|
|
|
|
|
|
│ │ │ ├── CrdnRelevyVO # 재부과 정보 VO
|
|
|
|
|
|
│ │ │ ├── CrdnImpltTaskVO # 이행 업무 VO
|
|
|
|
|
|
│ │ │ ├── CrdnAdsbmtnRtVO # 가감산율 VO
|
|
|
|
|
|
│ │ │ ├── CrdnCmpttnRt2VO # 산정률2 VO
|
|
|
|
|
|
│ │ │ └── LevyPrvntcActInfoVO # 부과예고 행위정보 VO
|
|
|
|
|
|
│ │ ├── crdnActInfo/ # 행위 정보 관리
|
|
|
|
|
|
│ │ │ ├── controller/CrdnActInfoController
|
|
|
|
|
|
│ │ │ ├── service/
|
|
|
|
|
|
│ │ │ │ ├── CrdnActInfoService
|
|
|
|
|
|
│ │ │ │ └── CrdnPhotoService # 사진 관리
|
|
|
|
|
|
│ │ │ ├── service/impl/
|
|
|
|
|
|
│ │ │ └── model/
|
|
|
|
|
|
│ │ │ ├── CrdnActInfoVO # 행위 정보 VO
|
|
|
|
|
|
│ │ │ └── CrdnVltnLwrgVO # 위반 법령 VO
|
|
|
|
|
|
│ │ ├── crdnPstnInfo/ # 위치 정보 관리
|
|
|
|
|
|
│ │ │ ├── controller/CrdnPstnInfoController
|
|
|
|
|
|
│ │ │ ├── service/CrdnPstnInfoService
|
|
|
|
|
|
│ │ │ └── model/CrdnPstnInfoVO # 위치 정보 VO
|
|
|
|
|
|
│ │ ├── crdnOwnrInfo/ # 소유자 정보 관리
|
|
|
|
|
|
│ │ │ ├── controller/CrdnOwnrInfoController
|
|
|
|
|
|
│ │ │ ├── service/CrdnOwnrInfoService
|
|
|
|
|
|
│ │ │ └── model/CrdnOwnrInfoVO # 소유자 정보 VO
|
|
|
|
|
|
│ │ ├── crdnActrInfo/ # 행위자 정보 관리
|
|
|
|
|
|
│ │ │ ├── controller/CrdnActrInfoController
|
|
|
|
|
|
│ │ │ ├── service/CrdnActrInfoService
|
|
|
|
|
|
│ │ │ └── model/ (추정)
|
|
|
|
|
|
│ │ ├── crdnExmnr/ # 조사자 정보 관리
|
|
|
|
|
|
│ │ │ ├── controller/CrdnExmnrController
|
|
|
|
|
|
│ │ │ ├── service/CrdnExmnrService
|
|
|
|
|
|
│ │ │ ├── mapper/CrdnExmnrMapper
|
|
|
|
|
|
│ │ │ └── model/CrdnExmnrVO
|
|
|
|
|
|
│ │ └── crdnOwnrSelect/ # 소유자 선택
|
|
|
|
|
|
│ │ ├── controller/CrdnOwnrSelectController
|
|
|
|
|
|
│ │ ├── service/CrdnOwnrSelectService
|
|
|
|
|
|
│ │ └── service/impl/CrdnOwnrSelectSelectServiceImpl
|
|
|
|
|
|
│ ├── ownac/ # 소유자 조치 관리
|
|
|
|
|
|
│ │ ├── controller/OwnActRegistAndViewController
|
|
|
|
|
|
│ │ ├── service/OwnActRegistAndViewService
|
|
|
|
|
|
│ │ └── service/impl/OwnActRegistAndViewServiceImpl
|
|
|
|
|
|
│ └── exmnr/ # 조사자 관리
|
|
|
|
|
|
│ ├── controller/ExmnrController
|
|
|
|
|
|
│ ├── service/ExmnrService
|
|
|
|
|
|
│ └── service/impl/ExmnrServiceImpl
|
|
|
|
|
|
│
|
|
|
|
|
|
├── noti/ # 알림 시스템 (추정)
|
|
|
|
|
|
│
|
|
|
|
|
|
└── ma30/ # MA30 관련 (추정)
|
|
|
|
|
|
└── model/
|
|
|
|
|
|
├── Ma30FindListVO
|
|
|
|
|
|
└── Ma30FindRlistSearchVO
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 View (JSP) 디렉토리 구조
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/main/webapp/WEB-INF/views/
|
|
|
|
|
|
│
|
|
|
|
|
|
├── common/ # 공통 뷰
|
|
|
|
|
|
│ ├── header.jsp # 헤더
|
|
|
|
|
|
│ ├── footer.jsp # 푸터
|
|
|
|
|
|
│ └── menu.jsp # 메뉴
|
|
|
|
|
|
│
|
|
|
|
|
|
├── login/ # 로그인
|
|
|
|
|
|
│ └── login.jsp
|
|
|
|
|
|
│
|
|
|
|
|
|
├── system/ # 시스템 관리
|
|
|
|
|
|
│ ├── user/ # 사용자 관리
|
|
|
|
|
|
│ ├── group/ # 그룹 관리
|
|
|
|
|
|
│ ├── role/ # 역할 관리
|
|
|
|
|
|
│ ├── menu/ # 메뉴 관리
|
|
|
|
|
|
│ ├── code/ # 코드 관리
|
|
|
|
|
|
│ └── loginLog/ # 로그인 로그
|
|
|
|
|
|
│
|
|
|
|
|
|
├── baseData/ # 기초 데이터
|
|
|
|
|
|
│ └── bldgNewPrcCrtrAmt/ # 건축물 신축가격 기준액
|
|
|
|
|
|
│
|
|
|
|
|
|
└── crdn/ # 단속 관리
|
|
|
|
|
|
├── crndRegistAndView/
|
|
|
|
|
|
│ └── main/
|
|
|
|
|
|
│ ├── list.jsp # 단속 목록
|
|
|
|
|
|
│ ├── crdnRegistPopup.jsp # 단속 등록/수정 팝업
|
|
|
|
|
|
│ ├── detailView-main.jsp # 단속 상세보기 메인
|
|
|
|
|
|
│ └── crdnLevyPrvntc/
|
|
|
|
|
|
│ ├── levyPrvntcPopup.jsp # 부과예고 팝업
|
|
|
|
|
|
│ └── LevyAddMinusPopup.jsp # 가감산 팝업
|
|
|
|
|
|
└── ownac/ # 소유자 조치
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1.3 MyBatis Mapper XML 구조
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/main/resources/mybatis/mapper/
|
|
|
|
|
|
│
|
|
|
|
|
|
├── common/
|
|
|
|
|
|
│ ├── CommonCodeMapper_maria.xml
|
|
|
|
|
|
│ └── CommonHeaderMapper_maria.xml
|
|
|
|
|
|
│
|
|
|
|
|
|
├── login/
|
|
|
|
|
|
│ └── LoginMapper_maria.xml
|
|
|
|
|
|
│
|
|
|
|
|
|
├── system/
|
|
|
|
|
|
│ ├── user/UserMapper_maria.xml
|
|
|
|
|
|
│ ├── group/GroupMapper_maria.xml
|
|
|
|
|
|
│ ├── role/RoleMapper_maria.xml
|
|
|
|
|
|
│ ├── menu/MenuMapper_maria.xml
|
|
|
|
|
|
│ ├── code/CodeMapper_maria.xml
|
|
|
|
|
|
│ ├── auth/AuthMapper_maria.xml
|
|
|
|
|
|
│ └── loginLog/LoginLogMapper_maria.xml
|
|
|
|
|
|
│
|
|
|
|
|
|
├── baseData/
|
|
|
|
|
|
│ └── bldgNewPrcCrtrAmt/BldgNewPrcCrtrAmtMapper_maria.xml
|
|
|
|
|
|
│
|
|
|
|
|
|
└── crdn/
|
|
|
|
|
|
├── crndRegistAndView/
|
|
|
|
|
|
│ ├── main/
|
|
|
|
|
|
│ │ ├── CrdnRegistAndViewMapper_maria.xml
|
|
|
|
|
|
│ │ ├── CrdnLevyPrvntcMapper_maria.xml
|
|
|
|
|
|
│ │ ├── CrdnRelevyMapper_maria.xml
|
|
|
|
|
|
│ │ └── CrdnImpltTaskMapper_maria.xml
|
|
|
|
|
|
│ ├── crdnActInfo/
|
|
|
|
|
|
│ │ ├── CrdnActInfoMapper_maria.xml
|
|
|
|
|
|
│ │ └── CrdnPhotoMapper_maria.xml
|
|
|
|
|
|
│ ├── crdnPstnInfo/
|
|
|
|
|
|
│ │ └── CrdnPstnInfoMapper_maria.xml
|
|
|
|
|
|
│ ├── crdnOwnrInfo/
|
|
|
|
|
|
│ │ └── CrdnOwnrInfoMapper_maria.xml
|
|
|
|
|
|
│ ├── crdnActrInfo/
|
|
|
|
|
|
│ │ └── CrdnActrInfoMapper_maria.xml
|
|
|
|
|
|
│ ├── crdnExmnr/
|
|
|
|
|
|
│ │ └── CrdnExmnrMapper_maria.xml
|
|
|
|
|
|
│ └── crdnOwnrSelect/
|
|
|
|
|
|
│ └── CrdnOwnrSelectMapper_maria.xml
|
|
|
|
|
|
├── ownact/
|
|
|
|
|
|
│ └── OwnActRegistAndViewMapper_maria.xml
|
|
|
|
|
|
└── exmnr/
|
|
|
|
|
|
└── ExmnrMapper_maria.xml
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 주요 업무 로직 상세
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 단속 관리 프로세스 (crdn 도메인)
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.1 단속 등록 및 조회 (CrdnRegistAndViewController)
|
|
|
|
|
|
|
|
|
|
|
|
**주요 API 엔드포인트:**
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/list.do` - 단속 목록 화면
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/list.ajax` - 단속 목록 조회 (페이징)
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnRegistPopup.do` - 단속 등록/수정 팝업
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/detailView.do` - 단속 상세보기
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/insert.ajax` - 단속 등록
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/update.ajax` - 단속 수정
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/delete.ajax` - 단속 삭제 (논리삭제)
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/selectOne.ajax` - 단속 상세 조회
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/updateStatus.ajax` - 단속 상태 업데이트
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/checkActCmpltCd.ajax` - 조치처리상태 확인
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/excel.do` - 단속 목록 엑셀 다운로드
|
|
|
|
|
|
|
|
|
|
|
|
**주요 필드 (CrdnRegistAndViewVO):**
|
|
|
|
|
|
- `crdnYr` (단속 연도) + `crdnNo` (단속 번호) = 복합키
|
|
|
|
|
|
- `sggCd` (시군구 코드)
|
|
|
|
|
|
- `crdnPrcsSttsCd` (단속 진행 상태 코드)
|
|
|
|
|
|
- `dsclMthd` (적발 방법)
|
|
|
|
|
|
- `stdgEmdCd` (법정동 읍면동 코드)
|
|
|
|
|
|
- `rgnSeCd` (지역 구분 코드)
|
|
|
|
|
|
|
|
|
|
|
|
**비즈니스 규칙:**
|
|
|
|
|
|
1. 등록 시 세션의 조직코드(orgCd)를 sggCd로 자동 설정
|
|
|
|
|
|
2. 삭제는 논리삭제 (DEL_YN='Y')
|
|
|
|
|
|
3. 페이징 처리 시 반드시 순서 준수:
|
|
|
|
|
|
- 총 개수 조회 → totalCount 설정 → pagingYn='Y' 설정
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.2 부과예고 관리 (CrdnLevyPrvntcController)
|
|
|
|
|
|
|
|
|
|
|
|
**주요 API 엔드포인트:**
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnLevyPrvntc/selectLevyInfoFirstCheck.ajax` - 부과정보 존재 여부 확인
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnLevyPrvntc/levyPrvntcPopup.do` - 부과예고 팝업
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnLevyPrvntc/selectIsAllLevyInfoCompleted.ajax` - 부과정보 완료 여부
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/crdnLevyPrvntc/actlist.ajax` - 행위정보 목록 조회
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnLevyPrvntc/LevyAddMinusPopup.do` - 가감산 팝업
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/crdnLevyPrvntc/adsbmtnRtList.ajax` - 가감산율 목록 조회
|
|
|
|
|
|
- `GET /crdn/crndRegistAndView/crdnLevyPrvntc/selectLevyInfoByActInfoId.ajax` - 기존 부과정보 조회
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/crdnLevyPrvntc/saveLevyInfo.ajax` - 부과정보 저장 (신규/수정 자동 판단)
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/crdnLevyPrvntc/delLevyInfo.ajax` - 부과정보 삭제
|
|
|
|
|
|
- `POST /crdn/crndRegistAndView/crdnLevyPrvntc/calculateAll.ajax` - **통합 계산 API (핵심 로직)**
|
|
|
|
|
|
|
|
|
|
|
|
**주요 필드 (CrdnLevyInfoVO):**
|
|
|
|
|
|
- 기본키: `levyInfoId`
|
|
|
|
|
|
- 참조키: `crdnYr`, `crdnNo`, `impltTaskSeCd`, `actInfoId`
|
|
|
|
|
|
- 계산 관련 필드:
|
|
|
|
|
|
- `bldgCrtrMprcAmt` (건물 기준 시가액)
|
|
|
|
|
|
- `strctIdx` (구조 지수)
|
|
|
|
|
|
- `usgIdx` (용도 지수)
|
|
|
|
|
|
- `pstnIdx` (위치 지수)
|
|
|
|
|
|
- `elpsYrRdvlrt` (경과 연도 잔가율)
|
|
|
|
|
|
- `bscsCstrnRt` (기초 공사율)
|
|
|
|
|
|
- `vltnArea` (위반 면적)
|
|
|
|
|
|
- `adsbmtnEnfcRt` (가감산 시행률)
|
|
|
|
|
|
- `cmpttnRt` (산정률)
|
|
|
|
|
|
- `cmpttnRt2` (산정률2)
|
|
|
|
|
|
- `bdstTxtnMprc` (건축물 과세 시가)
|
|
|
|
|
|
- `mprcStdAmt` (시가 표준액)
|
|
|
|
|
|
- `cmpttnAmt` (산정액)
|
|
|
|
|
|
- `levyWholAmt` (부과 총액)
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 비즈니스 규칙:**
|
|
|
|
|
|
1. **스마트 저장 패턴**:
|
|
|
|
|
|
- `saveLevyInfo.ajax`는 기존 데이터 존재 여부를 자동으로 확인
|
|
|
|
|
|
- 존재하면 UPDATE, 없으면 INSERT 자동 실행
|
|
|
|
|
|
|
|
|
|
|
|
2. **사전 검증**:
|
|
|
|
|
|
- 부과예고 등록 전 반드시 위치정보, 행위정보가 먼저 등록되어야 함
|
|
|
|
|
|
- `selectLevyInfoFirstCheck.ajax`로 사전 검증 수행
|
|
|
|
|
|
|
|
|
|
|
|
3. **계산 스냅샷 패턴**:
|
|
|
|
|
|
- 계산 시점의 모든 값(지수, 비율 등)을 DB에 저장
|
|
|
|
|
|
- 나중에 조회 시 저장된 값으로 복원하여 일관성 유지
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.3 행위 정보 관리 (CrdnActInfoController)
|
|
|
|
|
|
|
|
|
|
|
|
**주요 데이터:**
|
|
|
|
|
|
- 위반 행위의 상세 정보 (행위 유형, 위반 법령 등)
|
|
|
|
|
|
- 사진 정보 (CrdnPhotoService 별도 관리)
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.4 위치 정보 관리 (CrdnPstnInfoController)
|
|
|
|
|
|
|
|
|
|
|
|
**주요 데이터:**
|
|
|
|
|
|
- 단속 위치 정보
|
|
|
|
|
|
- 지번, 주소, 좌표 등
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.1.5 소유자 정보 관리 (CrdnOwnrInfoController)
|
|
|
|
|
|
|
|
|
|
|
|
**주요 데이터:**
|
|
|
|
|
|
- 건축물 소유자 정보
|
|
|
|
|
|
- 성명, 연락처, 주소 등
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 데이터 흐름 상세
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 표준 CRUD 데이터 흐름
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.1 조회 (SELECT) 흐름
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
[사용자]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JSP - list.jsp]
|
|
|
|
|
|
↓ (페이지 로드 시 TUI Grid 초기화)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - dataSource.api.readData]
|
|
|
|
|
|
↓ (AJAX POST 요청: /list.ajax)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - listAjax(@ModelAttribute VO)]
|
|
|
|
|
|
↓ (1. totalCount 조회)
|
|
|
|
|
|
↓ (2. setTotalCount)
|
|
|
|
|
|
↓ (3. setPagingYn("Y"))
|
|
|
|
|
|
↓ (4. Service 호출)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - selectList(VO)]
|
|
|
|
|
|
↓ (비즈니스 로직, 검증)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - selectList(VO)]
|
|
|
|
|
|
↓ (MyBatis 호출)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MyBatis XML - <select id="selectList">]
|
|
|
|
|
|
↓ (SQL 실행)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MariaDB]
|
|
|
|
|
|
↓ (결과 반환)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper → Service → Controller]
|
|
|
|
|
|
↓ (ApiResponseUtil.successWithGrid(list, vo))
|
|
|
|
|
|
↓
|
|
|
|
|
|
[ResponseEntity<?> 반환]
|
|
|
|
|
|
↓ (JSON 응답)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - Grid 렌더링]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자에게 표시]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 포인트:**
|
|
|
|
|
|
- PagingVO의 `page`, `perPageNum` 필드로 페이징 처리
|
|
|
|
|
|
- `totalCount` 설정 후 `pagingYn="Y"` 설정 (순서 중요!)
|
|
|
|
|
|
- MyBatis에서 `<if test="pagingYn == 'Y'">LIMIT #{startRow}, #{perPageNum}</if>` 처리
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.2 등록 (INSERT) 흐름
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
[사용자]
|
|
|
|
|
|
↓ (등록 버튼 클릭)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JSP - 팝업 열기]
|
|
|
|
|
|
↓ (window.open() or 모달)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자 입력]
|
|
|
|
|
|
↓ (폼 작성)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 저장 버튼 클릭]
|
|
|
|
|
|
↓ (유효성 검증)
|
|
|
|
|
|
↓ (AJAX POST: /insert.ajax)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - insert(@ModelAttribute VO)]
|
|
|
|
|
|
↓ (세션 정보 설정: SessionUtil.getUserId())
|
|
|
|
|
|
↓ (sggCd = SessionUtil.getSessionVO().getUser().getOrgCd())
|
|
|
|
|
|
↓ (rgtr = SessionUtil.getUserId())
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - insert(VO)]
|
|
|
|
|
|
↓ (비즈니스 검증)
|
|
|
|
|
|
↓ (트랜잭션 시작 - @Transactional)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - insert(VO)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MyBatis XML - <insert id="insert">]
|
|
|
|
|
|
↓ (시퀀스 생성: LPAD(NEXTVAL(seq_*), 10, '0'))
|
|
|
|
|
|
↓ (INSERT 실행)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MariaDB]
|
|
|
|
|
|
↓ (결과 반환: affected rows)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - 트랜잭션 커밋]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller]
|
|
|
|
|
|
↓ (result > 0 확인)
|
|
|
|
|
|
↓ (ApiResponseUtil.success(MessageConstants.Common.SAVE_SUCCESS))
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 성공 처리]
|
|
|
|
|
|
↓ (팝업 닫기, 목록 새로고침)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자에게 성공 메시지]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 포인트:**
|
|
|
|
|
|
- 등록자(rgtr) 필드에 `SessionUtil.getUserId()` 자동 설정
|
|
|
|
|
|
- 시군구코드(sggCd)는 세션의 조직코드로 자동 설정
|
|
|
|
|
|
- 시퀀스는 10자리 LPAD 형식: `LPAD(NEXTVAL(seq_name), 10, '0')`
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.3 수정 (UPDATE) 흐름
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
[사용자]
|
|
|
|
|
|
↓ (행 선택 + 수정 버튼)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 팝업 열기]
|
|
|
|
|
|
↓ (mode=U, PK 파라미터 전달)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - popup(mode=U, PK)]
|
|
|
|
|
|
↓ (기존 데이터 조회)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - selectOne(PK)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - selectOne]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[DB 조회]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[팝업에 데이터 표시]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자 수정]
|
|
|
|
|
|
↓ (저장 버튼)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - AJAX POST: /update.ajax]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - update(@ModelAttribute VO)]
|
|
|
|
|
|
↓ (mdfr = SessionUtil.getUserId() 설정)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - update(VO)]
|
|
|
|
|
|
↓ (낙관적 잠금 검증 - 선택적)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - update(VO)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MyBatis XML - <update>]
|
|
|
|
|
|
↓ (UPDATE SET ... WHERE PK = ? AND DEL_YN = 'N')
|
|
|
|
|
|
↓
|
|
|
|
|
|
[DB 업데이트]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[결과 반환]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[ApiResponseUtil.success(MessageConstants.Common.UPDATE_SUCCESS)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[팝업 닫기, 목록 새로고침]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 포인트:**
|
|
|
|
|
|
- 수정자(mdfr) 필드에 `SessionUtil.getUserId()` 설정
|
|
|
|
|
|
- WHERE 절에 `DEL_YN = 'N'` 조건 필수
|
|
|
|
|
|
- 수정일시(mdfcnDt)는 DB에서 자동 설정 (DEFAULT CURRENT_TIMESTAMP)
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.4 삭제 (DELETE) 흐름 - 논리삭제
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
[사용자]
|
|
|
|
|
|
↓ (행 선택 + 삭제 버튼)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 확인 창]
|
|
|
|
|
|
↓ (confirm("정말 삭제하시겠습니까?"))
|
|
|
|
|
|
↓
|
|
|
|
|
|
[AJAX POST: /delete.ajax]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - delete(PK)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - delete(PK)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - delete(PK)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[MyBatis XML - <update id="delete">]
|
|
|
|
|
|
↓ (UPDATE SET DEL_YN='Y', DLTR=?, DEL_DT=NOW())
|
|
|
|
|
|
↓ (WHERE PK = ? AND DEL_YN = 'N')
|
|
|
|
|
|
↓
|
|
|
|
|
|
[DB 논리삭제]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[결과 반환]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[ApiResponseUtil.success(MessageConstants.Common.DELETE_SUCCESS)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[목록 새로고침]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 포인트:**
|
|
|
|
|
|
- 물리삭제(DELETE)가 아닌 논리삭제(UPDATE) 사용
|
|
|
|
|
|
- `DEL_YN='Y'`, `DLTR=사용자ID`, `DEL_DT=현재시간` 설정
|
|
|
|
|
|
- 모든 조회 쿼리에는 `WHERE DEL_YN='N'` 조건 필수
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 부과예고 특수 흐름 (스마트 CRUD 패턴)
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.1 부과정보 저장 흐름 (자동 INSERT/UPDATE 판단)
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
[사용자]
|
|
|
|
|
|
↓ (부과예고 팝업에서 행위정보 행 선택)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 행 클릭 이벤트]
|
|
|
|
|
|
↓ (actInfoId 획득)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[AJAX GET: /selectLevyInfoByActInfoId.ajax]
|
|
|
|
|
|
↓ (기존 부과정보 존재 여부 확인)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - selectLevyInfoByActInfoId]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - selectLevyInfoByActInfoId(actInfoId)]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Mapper - SELECT WHERE actInfoId = ?]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[DB 조회]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[결과 반환]
|
|
|
|
|
|
├── (있음) → 기존 데이터 복원
|
|
|
|
|
|
│ ↓ (JavaScript에 기존 계산값 모두 표시)
|
|
|
|
|
|
│ ↓ (모드 = 수정)
|
|
|
|
|
|
└── (없음) → 신규 입력 모드
|
|
|
|
|
|
↓ (빈 폼 표시)
|
|
|
|
|
|
↓ (모드 = 신규)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자가 값 입력/수정]
|
|
|
|
|
|
↓ (계산 버튼 클릭)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[AJAX POST: /calculateAll.ajax]
|
|
|
|
|
|
↓ (모든 입력값 전송)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - calculateAll]
|
|
|
|
|
|
↓ (BigDecimal 변환)
|
|
|
|
|
|
↓ (4단계 계산 수행 - 아래 섹션 참조)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[계산 결과 반환]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 결과 필드에 표시]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[사용자 - 저장 버튼 클릭]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[AJAX POST: /saveLevyInfo.ajax]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Controller - saveLevyInfo]
|
|
|
|
|
|
↓ (행위정보 존재 확인)
|
|
|
|
|
|
↓ (sggCd 자동 설정)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[Service - saveLevyInfo]
|
|
|
|
|
|
↓ (기존 데이터 재확인)
|
|
|
|
|
|
├── (있음) → updateLevyInfo 호출
|
|
|
|
|
|
│ ↓ (기존 levyInfoId 사용)
|
|
|
|
|
|
│ ↓ (UPDATE 실행)
|
|
|
|
|
|
└── (없음) → insertLevyInfo 호출
|
|
|
|
|
|
↓ (새 levyInfoId 생성)
|
|
|
|
|
|
↓ (INSERT 실행)
|
|
|
|
|
|
↓
|
|
|
|
|
|
[트랜잭션 커밋]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[성공 메시지 반환]
|
|
|
|
|
|
↓
|
|
|
|
|
|
[JavaScript - 행위정보 그리드 새로고침]
|
|
|
|
|
|
↓ (저장된 데이터 표시 - 체크 아이콘 등)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**핵심 포인트:**
|
|
|
|
|
|
- 클라이언트는 INSERT/UPDATE 구분 불필요
|
|
|
|
|
|
- 서버에서 자동으로 존재 여부 확인 후 분기
|
|
|
|
|
|
- 계산 결과를 스냅샷으로 저장하여 데이터 일관성 보장
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 계산식 및 비즈니스 규칙
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 부과금 계산 로직 (CrdnLevyPrvntcController.calculateAll)
|
|
|
|
|
|
|
|
|
|
|
|
**위치**: `go.kr.project.crdn.crndRegistAndView.main.controller.CrdnLevyPrvntcController:473`
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.1.1 단계별 계산 공식
|
|
|
|
|
|
|
|
|
|
|
|
**입력 파라미터:**
|
|
|
|
|
|
1. `bldgNewPrcCrtrAmt` (건물 기준 시가액) - BigDecimal
|
|
|
|
|
|
2. `strctIdx` (구조 지수) - BigDecimal
|
|
|
|
|
|
3. `usgIdx` (용도 지수) - BigDecimal
|
|
|
|
|
|
4. `pstnIdx` (위치 지수) - BigDecimal
|
|
|
|
|
|
5. `elpsYrRdvlrt` (경과 년수별 잔가율) - BigDecimal (0 ~ 1 사이 값)
|
|
|
|
|
|
6. `bscsCstrnRt` (기초 공사율) - BigDecimal (0 ~ 1 사이 값)
|
|
|
|
|
|
7. `vltnArea` (위반 면적) - BigDecimal (㎡)
|
|
|
|
|
|
8. `adsbmtnEnfcRt` (가감산 시행령률) - BigDecimal (%)
|
|
|
|
|
|
9. `cmpttnRtRate` (산정률 비율값) - BigDecimal (0 ~ 1 사이 값)
|
|
|
|
|
|
10. `cmpttnRt2Rate` (산정률2 비율값) - BigDecimal (0 ~ 1 사이 값)
|
|
|
|
|
|
|
|
|
|
|
|
**계산 단계:**
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// ===== 1단계: 건축물과세시가 계산 =====
|
|
|
|
|
|
// 공식: 건물기준시가액 × 구조지수 × 용도지수 × 위치지수 × 경과년수별잔가율 × 기초공사율
|
|
|
|
|
|
BigDecimal bdstTxtnMprc = bldgNewPrcCrtrAmtDecimal
|
|
|
|
|
|
.multiply(strctIdxDecimal)
|
|
|
|
|
|
.multiply(usgIdxDecimal)
|
|
|
|
|
|
.multiply(pstnIdxDecimal)
|
|
|
|
|
|
.multiply(elpsYrRdvlrtDecimal)
|
|
|
|
|
|
.multiply(bscsCstrnRtDecimal)
|
|
|
|
|
|
.setScale(0, RoundingMode.HALF_UP); // 반올림
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 2단계: 시가표준액 계산 (1,000원 미만 절사) =====
|
|
|
|
|
|
// 공식: 건축물과세시가 ÷ 1000 (소수점 버림) × 1000
|
|
|
|
|
|
BigDecimal mprcStdAmt = bdstTxtnMprc
|
|
|
|
|
|
.divide(new BigDecimal("1000"), 0, RoundingMode.DOWN) // 1000으로 나눈 후 버림
|
|
|
|
|
|
.multiply(new BigDecimal("1000")); // 다시 1000 곱하기
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 3단계: 산정액 계산 =====
|
|
|
|
|
|
// 공식: 시가표준액 × 위반면적 × (가감산시행령률 ÷ 100) × 산정률 × 산정률2
|
|
|
|
|
|
BigDecimal cmpttnAmt = mprcStdAmt
|
|
|
|
|
|
.multiply(vltnAreaDecimal)
|
|
|
|
|
|
.multiply(adsbmtnEnfcRtDecimal.divide(new BigDecimal("100"), 10, RoundingMode.HALF_UP))
|
|
|
|
|
|
.multiply(cmpttnRtRateDecimal)
|
|
|
|
|
|
.multiply(cmpttnRt2RateDecimal)
|
|
|
|
|
|
.setScale(0, RoundingMode.DOWN); // 소수점 버림
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 4단계: 부과총액 계산 (10원 단위 절사) =====
|
|
|
|
|
|
// 공식: 산정액 ÷ 10 (소수점 버림) × 10
|
|
|
|
|
|
BigDecimal levyWholAmt = cmpttnAmt
|
|
|
|
|
|
.divide(new BigDecimal("10"), 0, RoundingMode.DOWN) // 10으로 나눈 후 버림
|
|
|
|
|
|
.multiply(new BigDecimal("10")); // 다시 10 곱하기
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**반환 데이터:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
|
|
|
data.put("bdstTxtnMprc", bdstTxtnMprc); // 건축물과세시가 (숫자)
|
|
|
|
|
|
data.put("mprcStdAmt", mprcStdAmt); // 시가표준액 (숫자)
|
|
|
|
|
|
data.put("cmpttnAmt", cmpttnAmt); // 산정액 (숫자)
|
|
|
|
|
|
data.put("levyWholAmt", levyWholAmt); // 부과총액 (숫자)
|
|
|
|
|
|
data.put("bdstTxtnMprcDisplay", "1,234,567 원"); // 표시용 포맷 문자열
|
|
|
|
|
|
data.put("mprcStdAmtDisplay", "1,234,000 원");
|
|
|
|
|
|
data.put("cmpttnAmtDisplay", "987,654 원");
|
|
|
|
|
|
data.put("levyWholAmtDisplay", "987,650 원");
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 4.1.2 계산 예시
|
|
|
|
|
|
|
|
|
|
|
|
**예시 입력값:**
|
|
|
|
|
|
- 건물기준시가액: 1,000,000원
|
|
|
|
|
|
- 구조지수: 1.0
|
|
|
|
|
|
- 용도지수: 1.0
|
|
|
|
|
|
- 위치지수: 1.0
|
|
|
|
|
|
- 경과년수별잔가율: 0.8 (80%)
|
|
|
|
|
|
- 기초공사율: 0.7 (70%)
|
|
|
|
|
|
- 위반면적: 50㎡
|
|
|
|
|
|
- 가감산시행령률: 100%
|
|
|
|
|
|
- 산정률: 0.5 (50%)
|
|
|
|
|
|
- 산정률2: 1.0 (100%)
|
|
|
|
|
|
|
|
|
|
|
|
**계산 과정:**
|
|
|
|
|
|
```
|
|
|
|
|
|
1단계: 건축물과세시가
|
|
|
|
|
|
= 1,000,000 × 1.0 × 1.0 × 1.0 × 0.8 × 0.7
|
|
|
|
|
|
= 560,000원
|
|
|
|
|
|
|
|
|
|
|
|
2단계: 시가표준액 (1,000원 미만 절사)
|
|
|
|
|
|
= 560,000 ÷ 1,000 = 560 (버림)
|
|
|
|
|
|
= 560 × 1,000
|
|
|
|
|
|
= 560,000원
|
|
|
|
|
|
|
|
|
|
|
|
3단계: 산정액
|
|
|
|
|
|
= 560,000 × 50 × (100 ÷ 100) × 0.5 × 1.0
|
|
|
|
|
|
= 560,000 × 50 × 1.0 × 0.5 × 1.0
|
|
|
|
|
|
= 14,000,000원
|
|
|
|
|
|
|
|
|
|
|
|
4단계: 부과총액 (10원 단위 절사)
|
|
|
|
|
|
= 14,000,000 ÷ 10 = 1,400,000 (버림)
|
|
|
|
|
|
= 1,400,000 × 10
|
|
|
|
|
|
= 14,000,000원
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 가감산율 적용 규칙
|
|
|
|
|
|
|
|
|
|
|
|
**가산율 (adtnRt):**
|
|
|
|
|
|
- 위반 행위의 중대성에 따라 부과금 증가
|
|
|
|
|
|
- 예: 재범, 고의성, 피해 규모 등
|
|
|
|
|
|
|
|
|
|
|
|
**감산율 (sbtrRt):**
|
|
|
|
|
|
- 자진 시정, 경미한 위반 등의 사유로 부과금 감소
|
|
|
|
|
|
- 예: 자진 신고, 즉시 시정, 초범 등
|
|
|
|
|
|
|
|
|
|
|
|
**가감산 시행령률 (adsbmtnEnfcRt):**
|
|
|
|
|
|
- 가산율과 감산율을 합산한 최종 비율
|
|
|
|
|
|
- 공식: `adsbmtnEnfcRt = 100 + adtnRt - sbtrRt`
|
|
|
|
|
|
- 예: 가산 10%, 감산 5% → 105%
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 산정률 (cmpttnRt) 및 산정률2 (cmpttnRt2)
|
|
|
|
|
|
|
|
|
|
|
|
**산정률 (cmpttnRt):**
|
|
|
|
|
|
- 이행강제금의 1차 산정 비율
|
|
|
|
|
|
- 법령에 따라 정해진 비율표에서 선택
|
|
|
|
|
|
- 예: 건축법 위반 유형별로 0.1 ~ 1.0 범위
|
|
|
|
|
|
|
|
|
|
|
|
**산정률2 (cmpttnRt2):**
|
|
|
|
|
|
- 이행강제금의 2차 산정 비율
|
|
|
|
|
|
- 회차별 증가율 적용
|
|
|
|
|
|
- 예:
|
|
|
|
|
|
- 1회차: 1.0 (100%)
|
|
|
|
|
|
- 2회차: 1.5 (150%)
|
|
|
|
|
|
- 3회차: 2.0 (200%)
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 기초공사율 (bscsCstrnRt) 계산
|
|
|
|
|
|
|
|
|
|
|
|
**기초공사 구분 코드 (bscsCstrnSeCd):**
|
|
|
|
|
|
- 기초공사 있음 (Y): 일반 건축물
|
|
|
|
|
|
- 기초공사 없음 (N): 간이 건축물
|
|
|
|
|
|
- 복층증축 (D): 기존 건축물 위에 증축
|
|
|
|
|
|
|
|
|
|
|
|
**계산 비율:**
|
|
|
|
|
|
- 기초공사Y: 1.0 (100%)
|
|
|
|
|
|
- 기초공사N: 0.7 (70%) - 기본 비율의 70%
|
|
|
|
|
|
- 복층증축: 별도 산정 비율 적용
|
|
|
|
|
|
|
|
|
|
|
|
### 4.5 경과년수별 잔가율 (elpsYrRdvlrt)
|
|
|
|
|
|
|
|
|
|
|
|
**계산 방법:**
|
|
|
|
|
|
- 건축물의 경과 년수에 따라 감가상각
|
|
|
|
|
|
- 행위시작일자 기준으로 경과 년수 계산
|
|
|
|
|
|
- 잔가율표에서 해당 년도의 비율 조회
|
|
|
|
|
|
|
|
|
|
|
|
**예시:**
|
|
|
|
|
|
- 신축 (0년): 1.0 (100%)
|
|
|
|
|
|
- 5년 경과: 0.8 (80%)
|
|
|
|
|
|
- 10년 경과: 0.6 (60%)
|
|
|
|
|
|
- 20년 경과: 0.4 (40%)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 공통 패턴 및 규칙
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 VO (Value Object) 패턴
|
|
|
|
|
|
|
|
|
|
|
|
**기본 구조:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
@EqualsAndHashCode(callSuper=true) // PagingVO 상속 시
|
|
|
|
|
|
@Data // Getter, Setter, ToString
|
|
|
|
|
|
@Builder // 빌더 패턴
|
|
|
|
|
|
@AllArgsConstructor // 모든 필드 생성자
|
|
|
|
|
|
@NoArgsConstructor // 기본 생성자
|
|
|
|
|
|
@ToString // toString() 메서드
|
|
|
|
|
|
public class ExampleVO extends PagingVO {
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 기본 필드 =====
|
|
|
|
|
|
private String exampleId; // 기본키
|
|
|
|
|
|
private String exampleNm; // 명칭
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 감사 필드 (Audit Fields) =====
|
|
|
|
|
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
|
|
|
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
|
|
|
|
|
|
private LocalDateTime regDt; // 등록 일시
|
|
|
|
|
|
private String rgtr; // 등록자
|
|
|
|
|
|
|
|
|
|
|
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
|
|
|
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
|
|
|
|
|
|
private LocalDateTime mdfcnDt; // 수정 일시
|
|
|
|
|
|
private String mdfr; // 수정자
|
|
|
|
|
|
|
|
|
|
|
|
private String delYn; // 삭제 여부
|
|
|
|
|
|
|
|
|
|
|
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
|
|
|
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
|
|
|
|
|
|
private LocalDateTime delDt; // 삭제 일시
|
|
|
|
|
|
private String dltr; // 삭제자
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 조회용 추가 필드 (조인 결과) =====
|
|
|
|
|
|
private String exampleCdNm; // 코드명 (표시용)
|
|
|
|
|
|
private String displayValue; // 표시용 값
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 Controller 패턴
|
|
|
|
|
|
|
|
|
|
|
|
**표준 구조:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Controller
|
|
|
|
|
|
@RequestMapping("/domain/subdomain")
|
|
|
|
|
|
@RequiredArgsConstructor // final 필드 생성자 자동 생성
|
|
|
|
|
|
@Slf4j // 로깅
|
|
|
|
|
|
@Tag(name = "한글 태그명", description = "설명")
|
|
|
|
|
|
public class ExampleController {
|
|
|
|
|
|
|
|
|
|
|
|
private final ExampleService service;
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 화면 메서드 =====
|
|
|
|
|
|
@GetMapping("/list.do")
|
|
|
|
|
|
@Operation(summary = "목록 화면", description = "상세 설명")
|
|
|
|
|
|
public String list(Model model) {
|
|
|
|
|
|
// 공통코드 조회하여 model에 추가
|
|
|
|
|
|
return "경로" + TilesConstants.BASE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== AJAX 메서드 =====
|
|
|
|
|
|
@PostMapping("/list.ajax")
|
|
|
|
|
|
@Operation(summary = "목록 조회", description = "상세 설명")
|
|
|
|
|
|
@ApiResponses(value = {
|
|
|
|
|
|
@ApiResponse(responseCode = "200", description = "성공"),
|
|
|
|
|
|
@ApiResponse(responseCode = "400", description = "실패")
|
|
|
|
|
|
})
|
|
|
|
|
|
public ResponseEntity<?> listAjax(@ModelAttribute ExampleVO vo) {
|
|
|
|
|
|
int totalCount = service.selectListTotalCount(vo);
|
|
|
|
|
|
vo.setTotalCount(totalCount);
|
|
|
|
|
|
vo.setPagingYn("Y");
|
|
|
|
|
|
|
|
|
|
|
|
List<ExampleVO> list = service.selectList(vo);
|
|
|
|
|
|
return ApiResponseUtil.successWithGrid(list, vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@PostMapping("/insert.ajax")
|
|
|
|
|
|
public ResponseEntity<?> insert(@ModelAttribute ExampleVO vo) {
|
|
|
|
|
|
vo.setRgtr(SessionUtil.getUserId());
|
|
|
|
|
|
int result = service.insert(vo);
|
|
|
|
|
|
|
|
|
|
|
|
if (result > 0) {
|
|
|
|
|
|
return ApiResponseUtil.success(MessageConstants.Common.SAVE_SUCCESS);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return ApiResponseUtil.error(MessageConstants.Common.SAVE_ERROR);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 Service 패턴
|
|
|
|
|
|
|
|
|
|
|
|
**인터페이스:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
public interface ExampleService {
|
|
|
|
|
|
List<ExampleVO> selectList(ExampleVO vo);
|
|
|
|
|
|
int selectListTotalCount(ExampleVO vo);
|
|
|
|
|
|
ExampleVO selectOne(ExampleVO vo);
|
|
|
|
|
|
int insert(ExampleVO vo);
|
|
|
|
|
|
int update(ExampleVO vo);
|
|
|
|
|
|
int delete(ExampleVO vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**구현체:**
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Service
|
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class ExampleServiceImpl implements ExampleService {
|
|
|
|
|
|
|
|
|
|
|
|
private final ExampleMapper mapper;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public List<ExampleVO> selectList(ExampleVO vo) {
|
|
|
|
|
|
return mapper.selectList(vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
@Transactional // 쓰기 작업에만 적용
|
|
|
|
|
|
public int insert(ExampleVO vo) {
|
|
|
|
|
|
// 비즈니스 검증 로직
|
|
|
|
|
|
return mapper.insert(vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.4 Mapper 인터페이스 패턴
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Mapper
|
|
|
|
|
|
public interface ExampleMapper {
|
|
|
|
|
|
List<ExampleVO> selectList(ExampleVO vo);
|
|
|
|
|
|
int selectListTotalCount(ExampleVO vo);
|
|
|
|
|
|
ExampleVO selectOne(ExampleVO vo);
|
|
|
|
|
|
int insert(ExampleVO vo);
|
|
|
|
|
|
int update(ExampleVO vo);
|
|
|
|
|
|
int delete(ExampleVO vo);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.5 MyBatis XML 패턴
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
|
|
|
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
|
|
|
|
<mapper namespace="go.kr.project.domain.mapper.ExampleMapper">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 공통 컬럼 -->
|
|
|
|
|
|
<sql id="exampleColumns">
|
|
|
|
|
|
EXAMPLE_ID,
|
|
|
|
|
|
EXAMPLE_NM,
|
|
|
|
|
|
REG_DT,
|
|
|
|
|
|
RGTR,
|
|
|
|
|
|
MDFCN_DT,
|
|
|
|
|
|
MDFR,
|
|
|
|
|
|
DEL_YN,
|
|
|
|
|
|
DEL_DT,
|
|
|
|
|
|
DLTR
|
|
|
|
|
|
</sql>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 목록 조회 -->
|
|
|
|
|
|
<select id="selectList" parameterType="ExampleVO" resultType="ExampleVO">
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
<include refid="exampleColumns"/>
|
|
|
|
|
|
FROM TB_EXAMPLE
|
|
|
|
|
|
WHERE DEL_YN = 'N'
|
|
|
|
|
|
<if test="searchKeyword != null and searchKeyword != ''">
|
|
|
|
|
|
AND EXAMPLE_NM LIKE CONCAT('%', #{searchKeyword}, '%')
|
|
|
|
|
|
</if>
|
|
|
|
|
|
ORDER BY REG_DT DESC
|
|
|
|
|
|
<if test="pagingYn == 'Y'">
|
|
|
|
|
|
LIMIT #{startRow}, #{perPageNum}
|
|
|
|
|
|
</if>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 총 개수 조회 -->
|
|
|
|
|
|
<select id="selectListTotalCount" parameterType="ExampleVO" resultType="int">
|
|
|
|
|
|
SELECT COUNT(*)
|
|
|
|
|
|
FROM TB_EXAMPLE
|
|
|
|
|
|
WHERE DEL_YN = 'N'
|
|
|
|
|
|
<if test="searchKeyword != null and searchKeyword != ''">
|
|
|
|
|
|
AND EXAMPLE_NM LIKE CONCAT('%', #{searchKeyword}, '%')
|
|
|
|
|
|
</if>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 단건 조회 -->
|
|
|
|
|
|
<select id="selectOne" parameterType="ExampleVO" resultType="ExampleVO">
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
<include refid="exampleColumns"/>
|
|
|
|
|
|
FROM TB_EXAMPLE
|
|
|
|
|
|
WHERE EXAMPLE_ID = #{exampleId}
|
|
|
|
|
|
AND DEL_YN = 'N'
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 등록 -->
|
|
|
|
|
|
<insert id="insert" parameterType="ExampleVO">
|
|
|
|
|
|
INSERT INTO TB_EXAMPLE (
|
|
|
|
|
|
EXAMPLE_ID,
|
|
|
|
|
|
EXAMPLE_NM,
|
|
|
|
|
|
RGTR
|
|
|
|
|
|
) VALUES (
|
|
|
|
|
|
LPAD(NEXTVAL(seq_example_id), 10, '0'),
|
|
|
|
|
|
#{exampleNm},
|
|
|
|
|
|
#{rgtr}
|
|
|
|
|
|
)
|
|
|
|
|
|
</insert>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 수정 -->
|
|
|
|
|
|
<update id="update" parameterType="ExampleVO">
|
|
|
|
|
|
UPDATE TB_EXAMPLE
|
|
|
|
|
|
SET EXAMPLE_NM = #{exampleNm},
|
|
|
|
|
|
MDFR = #{mdfr}
|
|
|
|
|
|
WHERE EXAMPLE_ID = #{exampleId}
|
|
|
|
|
|
AND DEL_YN = 'N'
|
|
|
|
|
|
</update>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 삭제 (논리삭제) -->
|
|
|
|
|
|
<update id="delete" parameterType="ExampleVO">
|
|
|
|
|
|
UPDATE TB_EXAMPLE
|
|
|
|
|
|
SET DEL_YN = 'Y',
|
|
|
|
|
|
DLTR = #{dltr},
|
|
|
|
|
|
DEL_DT = NOW()
|
|
|
|
|
|
WHERE EXAMPLE_ID = #{exampleId}
|
|
|
|
|
|
AND DEL_YN = 'N'
|
|
|
|
|
|
</update>
|
|
|
|
|
|
|
|
|
|
|
|
</mapper>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 주요 상수 및 설정
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 모드 상수
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 화면 모드
|
|
|
|
|
|
public static final String MODE_CREATE = "C"; // 등록
|
|
|
|
|
|
public static final String MODE_UPDATE = "U"; // 수정
|
|
|
|
|
|
public static final String MODE_VIEW = "V"; // 보기
|
|
|
|
|
|
public static final String MODE_DELETE = "D"; // 삭제
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 이행업무 구분 코드 (ImpltTaskSeConstants)
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public static final String LEVY_PRVNTC = "1"; // 부과예고
|
|
|
|
|
|
public static final String LEVY = "2"; // 부과
|
|
|
|
|
|
public static final String RELEVY = "3"; // 재부과
|
|
|
|
|
|
public static final String IMPLT_TASK = "4"; // 이행강제금
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 삭제 여부
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public static final String DEL_YN_N = "N"; // 미삭제
|
|
|
|
|
|
public static final String DEL_YN_Y = "Y"; // 삭제됨
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.4 페이징 설정
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// PagingVO 기본값
|
|
|
|
|
|
private int page = 1; // 현재 페이지
|
|
|
|
|
|
private int perPageNum = 10; // 페이지당 행 수
|
|
|
|
|
|
private String pagingYn = "N"; // 페이징 사용 여부
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 보안 및 권한
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 세션 정보 사용
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 사용자 ID 가져오기
|
|
|
|
|
|
String userId = SessionUtil.getUserId();
|
|
|
|
|
|
|
|
|
|
|
|
// 사용자 정보 가져오기
|
|
|
|
|
|
SessionVO sessionVO = SessionUtil.getSessionVO();
|
|
|
|
|
|
String orgCd = sessionVO.getUser().getOrgCd(); // 조직코드
|
|
|
|
|
|
String userNm = sessionVO.getUser().getUserNm(); // 사용자명
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 CSRF 보호
|
|
|
|
|
|
|
|
|
|
|
|
모든 JSP 폼에 CSRF 토큰 포함:
|
|
|
|
|
|
```jsp
|
|
|
|
|
|
<form>
|
|
|
|
|
|
<sec:csrfInput/>
|
|
|
|
|
|
<!-- 폼 필드들 -->
|
|
|
|
|
|
</form>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 에러 처리
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 MessageException 사용
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
if (data == null) {
|
|
|
|
|
|
throw new MessageException("데이터를 찾을 수 없습니다.");
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 ApiResponseUtil 패턴
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 성공 응답
|
|
|
|
|
|
ApiResponseUtil.success(data, "성공 메시지");
|
|
|
|
|
|
ApiResponseUtil.successWithGrid(list, vo);
|
|
|
|
|
|
|
|
|
|
|
|
// 실패 응답
|
|
|
|
|
|
ApiResponseUtil.error("에러 메시지");
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 9. 엑셀 다운로드
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 엑셀 다운로드 패턴
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@PostMapping("/excel.do")
|
|
|
|
|
|
public void downloadExcel(
|
|
|
|
|
|
@ModelAttribute ExampleVO paramVO,
|
|
|
|
|
|
HttpServletRequest request,
|
|
|
|
|
|
HttpServletResponse response) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 페이징 없이 전체 조회
|
|
|
|
|
|
paramVO.setPagingYn("N");
|
|
|
|
|
|
List<ExampleExcelVO> excelList = service.selectListForExcel(paramVO);
|
|
|
|
|
|
|
|
|
|
|
|
// 파일명 생성
|
|
|
|
|
|
String filename = "예시목록_" +
|
|
|
|
|
|
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) +
|
|
|
|
|
|
".xlsx";
|
|
|
|
|
|
|
|
|
|
|
|
// 엑셀 파일 생성 및 다운로드
|
|
|
|
|
|
new SxssfExcelFile(
|
|
|
|
|
|
ExcelSheetData.of(excelList, ExampleExcelVO.class, "예시 목록 " + excelList.size() + "건"),
|
|
|
|
|
|
request,
|
|
|
|
|
|
response,
|
|
|
|
|
|
filename
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
log.error("엑셀 다운로드 중 오류 발생", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 10. 참고 파일 경로
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 핵심 비즈니스 로직 파일
|
|
|
|
|
|
|
|
|
|
|
|
**단속 등록/조회:**
|
|
|
|
|
|
- Controller: `src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnRegistAndViewController.java`
|
|
|
|
|
|
- Service: `src/main/java/go/kr/project/crdn/crndRegistAndView/main/service/impl/CrdnRegistAndViewServiceImpl.java`
|
|
|
|
|
|
- Mapper XML: `src/main/resources/mybatis/mapper/crdn/crndRegistAndView/main/CrdnRegistAndViewMapper_maria.xml`
|
|
|
|
|
|
- JSP: `src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/list.jsp`
|
|
|
|
|
|
|
|
|
|
|
|
**부과예고 관리 (계산 로직 포함):**
|
|
|
|
|
|
- Controller: `src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnLevyPrvntcController.java`
|
|
|
|
|
|
- Service: `src/main/java/go/kr/project/crdn/crndRegistAndView/main/service/impl/CrdnLevyPrvntcServiceImpl.java`
|
|
|
|
|
|
- Mapper XML: `src/main/resources/mybatis/mapper/crdn/crndRegistAndView/main/CrdnLevyPrvntcMapper_maria.xml`
|
|
|
|
|
|
- VO: `src/main/java/go/kr/project/crdn/crndRegistAndView/main/model/CrdnLevyInfoVO.java`
|
|
|
|
|
|
- JSP: `src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/crdnLevyPrvntc/levyPrvntcPopup.jsp`
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 공통 컴포넌트
|
|
|
|
|
|
|
|
|
|
|
|
**페이징:**
|
|
|
|
|
|
- `src/main/java/go/kr/project/common/model/PagingVO.java`
|
|
|
|
|
|
|
|
|
|
|
|
**공통코드:**
|
|
|
|
|
|
- `src/main/java/go/kr/project/common/service/CommonCodeService.java`
|
|
|
|
|
|
|
|
|
|
|
|
**유틸리티:**
|
|
|
|
|
|
- `egovframework.util.ApiResponseUtil` (API 응답 생성)
|
|
|
|
|
|
- `egovframework.util.SessionUtil` (세션 정보 조회)
|
|
|
|
|
|
- `egovframework.util.excel.SxssfExcelFile` (엑셀 다운로드)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**이 문서는 프로젝트의 전체 구조와 주요 업무 로직을 설명합니다. 새로운 기능 개발 시 이 문서를 참고하여 기존 패턴을 따라주세요.**
|