최초커밋

This commit is contained in:
kjs
2025-08-21 09:41:46 +09:00
commit a0e5b57a24
2454 changed files with 1476904 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
---
description:
globs:
alwaysApply: true
---
# 아키텍처 가이드
## 전체 아키텍처
이 애플리케이션은 전형적인 Spring MVC 3-tier 아키텍처를 따릅니다:
- **Presentation Layer**: JSP + jQuery (Frontend)
- **Business Layer**: Spring Controllers + Services (Backend Logic)
- **Data Access Layer**: MyBatis + PostgreSQL (Database)
## 패키지 구조
```
src/com/pms/
├── controller/ # Spring MVC Controllers (@Controller)
├── service/ # Business Logic (@Service)
├── mapper/ # MyBatis XML Mappers
├── common/ # 공통 유틸리티 및 설정
├── salesmgmt/ # 영업관리 모듈
└── ions/ # 특수 모듈
```
## 주요 컴포넌트
### Controllers
모든 컨트롤러는 [BaseService](mdc:src/com/pms/common/service/BaseService.java)를 상속받습니다.
- URL 패턴: `*.do` (예: `/admin/menuMngList.do`)
- 주요 컨트롤러: [AdminController](mdc:src/com/pms/controller/AdminController.java)
### Services
비즈니스 로직을 처리하는 서비스 계층입니다.
- 예시: [AdminService](mdc:src/com/pms/service/AdminService.java)
- MyBatis SqlSession을 직접 사용하여 데이터베이스 접근
### MyBatis Mappers
SQL 쿼리를 XML로 정의합니다.
- 위치: `src/com/pms/mapper/`
- 예시: [admin.xml](mdc:src/com/pms/mapper/admin.xml)
### JSP Views
JSP 뷰 파일들은 `WebContent/WEB-INF/view/` 디렉토리에 위치합니다.
- InternalResourceViewResolver 사용
- prefix: `/WEB-INF/view`, suffix: `.jsp`
## 데이터베이스 설정
- JNDI DataSource 사용: `plm`
- PostgreSQL 연결
- 초기 데이터: [ilshin.pgsql](mdc:db/ilshin.pgsql)
## 설정 파일 위치
- Spring 설정: [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml)
- 로깅 설정: [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml)
- 웹 설정: [web.xml](mdc:WebContent/WEB-INF/web.xml)
+207
View File
@@ -0,0 +1,207 @@
---
description:
globs:
alwaysApply: true
---
# 데이터베이스 가이드
## 데이터베이스 설정
### PostgreSQL 연결
- **JNDI 리소스명**: `plm`
- **드라이버**: `org.postgresql.Driver`
- **설정 파일**: [context.xml](mdc:tomcat-conf/context.xml)
### 초기 데이터
- **스키마 파일**: [ilshin.pgsql](mdc:db/ilshin.pgsql)
- **역할 설정**: [00-create-roles.sh](mdc:db/00-create-roles.sh)
## MyBatis 설정
### SqlSession 사용 패턴
```java
public List<Map<String, Object>> getData(Map<String, Object> paramMap) {
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
try {
return sqlSession.selectList("namespace.queryId", paramMap);
} finally {
sqlSession.close(); // 반드시 리소스 해제
}
}
```
### 트랜잭션 처리
```java
public void saveData(Map<String, Object> paramMap) {
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false); // autoCommit=false
try {
sqlSession.insert("namespace.insertQuery", paramMap);
sqlSession.update("namespace.updateQuery", paramMap);
sqlSession.commit(); // 명시적 커밋
} catch (Exception e) {
sqlSession.rollback(); // 오류 시 롤백
throw e;
} finally {
sqlSession.close();
}
}
```
## 매퍼 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="admin">
<!-- 쿼리 정의 -->
</mapper>
```
### 파라미터 바인딩
```xml
<!-- 안전한 파라미터 바인딩 (권장) -->
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM users
WHERE user_id = #{userId}
AND status = #{status}
</select>
<!-- 동적 조건 처리 -->
<select id="selectUserList" parameterType="map" resultType="map">
SELECT * FROM users
WHERE 1=1
<if test="userName != null and userName != ''">
AND user_name LIKE '%' || #{userName} || '%'
</if>
<if test="deptCode != null and deptCode != ''">
AND dept_code = #{deptCode}
</if>
</select>
```
### PostgreSQL 특화 문법
```xml
<!-- 시퀀스 사용 -->
<insert id="insertData" parameterType="map">
INSERT INTO table_name (id, name, reg_date)
VALUES (nextval('seq_table'), #{name}, now())
</insert>
<!-- 숫자 타입 캐스팅 -->
<update id="updateData" parameterType="map">
UPDATE table_name
SET status = #{status}
WHERE id = #{id}::numeric
</update>
<!-- 재귀 쿼리 (메뉴 트리 구조) -->
<select id="selectMenuTree" resultType="map">
WITH RECURSIVE menu_tree AS (
SELECT * FROM menu_info WHERE parent_id = 0
UNION ALL
SELECT m.* FROM menu_info m
JOIN menu_tree mt ON m.parent_id = mt.id
)
SELECT * FROM menu_tree ORDER BY path
</select>
```
## 주요 테이블 구조
### 메뉴 관리
- **MENU_INFO**: 메뉴 정보
- **MENU_AUTH_GROUP**: 메뉴 권한 그룹
- **AUTH_GROUP**: 권한 그룹 정보
### 사용자 관리
- **USER_INFO**: 사용자 정보
- **DEPT_INFO**: 부서 정보
- **USER_AUTH**: 사용자 권한
### 코드 관리
- **CODE_INFO**: 공통 코드
- **CODE_CATEGORY**: 코드 카테고리
## 데이터베이스 개발 모범 사례
### 1. 파라미터 검증
```xml
<select id="selectData" parameterType="map" resultType="map">
SELECT * FROM table_name
WHERE 1=1
<if test="id != null and id != ''">
AND id = #{id}::numeric
</if>
</select>
```
### 2. 페이징 처리
```xml
<select id="selectListWithPaging" parameterType="map" resultType="map">
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY reg_date DESC) as rnum
FROM table_name
WHERE 1=1
<!-- 검색 조건 -->
) t
WHERE rnum BETWEEN #{startRow}::numeric AND #{endRow}::numeric
</select>
```
### 3. 대소문자 처리
```xml
<!-- PostgreSQL은 대소문자를 구분하므로 주의 -->
<select id="selectData" resultType="map">
SELECT
user_id as "userId", -- 카멜케이스 변환
user_name as "userName",
UPPER(status) as "status"
FROM user_info
</select>
```
### 4. NULL 처리
```xml
<select id="selectData" resultType="map">
SELECT
COALESCE(description, '') as description,
CASE WHEN status = 'Y' THEN '활성' ELSE '비활성' END as statusName
FROM table_name
</select>
```
## 성능 최적화
### 인덱스 활용
```sql
-- 자주 검색되는 컬럼에 인덱스 생성
CREATE INDEX idx_user_dept ON user_info(dept_code);
CREATE INDEX idx_menu_parent ON menu_info(parent_id);
```
### 쿼리 최적화
```xml
<!-- EXISTS 사용으로 성능 개선 -->
<select id="selectUserWithAuth" resultType="map">
SELECT u.* FROM user_info u
WHERE EXISTS (
SELECT 1 FROM user_auth ua
WHERE ua.user_id = u.user_id
AND ua.auth_code = #{authCode}
)
</select>
```
### 배치 처리
```xml
<!-- 대량 데이터 삽입 시 배치 사용 -->
<insert id="insertBatch" parameterType="list">
INSERT INTO table_name (col1, col2, col3)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.col1}, #{item.col2}, #{item.col3})
</foreach>
</insert>
```
+118
View File
@@ -0,0 +1,118 @@
---
description:
globs:
alwaysApply: true
---
# 개발 가이드
## 개발 환경 설정
### Docker 개발 환경
```bash
# 개발 환경 실행
docker-compose -f docker-compose.dev.yml up --build -d
# 운영 환경 실행
docker-compose -f docker-compose.prod.yml up --build -d
```
### 로컬 개발 환경
1. Java 7 JDK 설치
2. Eclipse IDE 설정
3. Tomcat 7.0 설정
4. PostgreSQL 데이터베이스 설정
## 코딩 컨벤션
### Controller 개발
```java
@Controller
public class ExampleController extends BaseService {
@Autowired
ExampleService exampleService;
@RequestMapping("/example/list.do")
public String getList(HttpServletRequest request,
@RequestParam Map<String, Object> paramMap) {
// 비즈니스 로직은 Service에서 처리
List<Map<String, Object>> list = exampleService.getList(request, paramMap);
request.setAttribute("list", list);
return "/example/list";
}
}
```
### Service 개발
```java
@Service
public class ExampleService extends BaseService {
public List<Map<String, Object>> getList(HttpServletRequest request,
Map<String, Object> paramMap) {
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
try {
return sqlSession.selectList("example.selectList", paramMap);
} finally {
sqlSession.close();
}
}
}
```
### MyBatis Mapper 개발
```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="example">
<select id="selectList" parameterType="map" resultType="map">
SELECT * FROM example_table
WHERE 1=1
<if test="searchText != null and searchText != ''">
AND name LIKE '%' || #{searchText} || '%'
</if>
</select>
</mapper>
```
## 주요 유틸리티
### 공통 유틸리티
- [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java) - 공통 유틸리티 메서드
- [Constants](mdc:src/com/pms/common/utils/Constants.java) - 상수 정의
- [Message](mdc:src/com/pms/common/Message.java) - 메시지 처리
### 파일 관련
- [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java) - 파일명 변경
- 파일 업로드/다운로드 처리
## 프론트엔드 개발
### JSP 개발
- 위치: `WebContent/WEB-INF/view/`
- 공통 초기화: [init_jqGrid.jsp](mdc:WebContent/init_jqGrid.jsp)
- 스타일시트: [all.css](mdc:WebContent/css/all.css)
### JavaScript 라이브러리
- jQuery 1.11.3/2.1.4
- jqGrid 4.7.1 - 데이터 그리드
- Tabulator - 테이블 컴포넌트
- rMateChart - 차트 라이브러리
## 데이터베이스 개발
### 연결 설정
- JNDI 리소스명: `plm`
- 드라이버: PostgreSQL
- 컨텍스트 설정: [context.xml](mdc:tomcat-conf/context.xml)
### 스키마 관리
- 초기 스키마: [ilshin.pgsql](mdc:db/ilshin.pgsql)
- 역할 설정: [00-create-roles.sh](mdc:db/00-create-roles.sh)
## 빌드 및 배포
- Eclipse 기반 빌드 (Maven/Gradle 미사용)
- 컴파일된 클래스: `WebContent/WEB-INF/classes/`
- 라이브러리: `WebContent/WEB-INF/lib/`
- WAR 파일로 Tomcat 배포
+176
View File
@@ -0,0 +1,176 @@
---
description:
globs:
alwaysApply: true
---
# Next.js 마이그레이션 가이드
## 마이그레이션 개요
현재 JSP/jQuery 기반 프론트엔드를 Next.js로 전환하는 작업이 계획되어 있습니다.
자세한 내용은 [TODO.md](mdc:TODO.md)를 참조하세요.
## 현재 프론트엔드 분석
### JSP 뷰 구조
```
WebContent/WEB-INF/view/
├── admin/ # 관리자 화면
├── approval/ # 승인 관리
├── common/ # 공통 컴포넌트
├── dashboard/ # 대시보드
├── main/ # 메인 화면
└── ... # 기타 모듈별 화면
```
### 주요 JavaScript 라이브러리
- **jQuery**: 1.11.3/2.1.4 - DOM 조작 및 AJAX
- **jqGrid**: 4.7.1 - 데이터 그리드 (교체 필요)
- **Tabulator**: 테이블 컴포넌트
- **rMateChart**: 차트 라이브러리 (교체 필요)
- **CKEditor**: 텍스트 에디터
### CSS 프레임워크
- [all.css](mdc:WebContent/css/all.css) - 메인 스타일시트
- jQuery UI 테마 적용
- 반응형 디자인 미적용 (데스크톱 중심)
## API 설계 가이드
### RESTful API 변환
현재 Spring MVC는 JSP 뷰를 반환하는 구조입니다:
```java
@RequestMapping("/admin/menuMngList.do")
public String getMenuList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
// 데이터 조회
List<Map<String, Object>> menuList = adminService.getMenuList(request, paramMap);
request.setAttribute("menuList", menuList);
return "/admin/menu/menuMngList"; // JSP 뷰 반환
}
```
Next.js 연동을 위해 JSON API로 변환 필요:
```java
@RestController
@RequestMapping("/api")
public class AdminApiController {
@GetMapping("/admin/menus")
@ResponseBody
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMenuList(
@RequestParam Map<String, Object> paramMap) {
List<Map<String, Object>> menuList = adminService.getMenuList(null, paramMap);
return ResponseEntity.ok(ApiResponse.success(menuList));
}
}
```
### API 응답 표준화
```java
public class ApiResponse<T> {
private boolean success;
private T data;
private String message;
private String errorCode;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, data, null, null);
}
public static <T> ApiResponse<T> error(String message, String errorCode) {
return new ApiResponse<>(false, null, message, errorCode);
}
}
```
## 컴포넌트 매핑 가이드
### 데이터 그리드 교체
**현재**: jqGrid 4.7.1
```javascript
$("#grid").jqGrid({
url: 'menuMngList.do',
datatype: 'json',
colModel: [
{name: 'menuName', label: '메뉴명'},
{name: 'url', label: 'URL'}
]
});
```
**변환 후**: TanStack Table 또는 AG Grid
```tsx
import { useTable } from '@tanstack/react-table';
const MenuTable = () => {
const columns = [
{ accessorKey: 'menuName', header: '메뉴명' },
{ accessorKey: 'url', header: 'URL' }
];
const table = useTable({ data, columns });
// 테이블 렌더링
};
```
### 차트 라이브러리 교체
**현재**: rMateChart
**변환 후**: Recharts 또는 Chart.js
### 폼 처리 교체
**현재**: jQuery 기반 폼 처리
**변환 후**: react-hook-form 사용
## 인증/인가 처리
### 현재 세션 기반 인증
```java
// 세션에서 사용자 정보 조회
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
```
### Next.js 연동 방안
1. **세션 유지**: 쿠키 기반 세션 ID 전달
2. **JWT 토큰**: 새로운 토큰 기반 인증 도입
3. **하이브리드**: 기존 세션 + API 토큰
## 개발 단계별 접근
### Phase 1: API 개발
1. 기존 Controller 분석
2. @RestController 신규 생성
3. 기존 Service 재사용
4. CORS 설정 추가
### Phase 2: Next.js 기본 구조
1. Next.js 프로젝트 생성
2. 기본 레이아웃 구현
3. 라우팅 구조 설계
4. 공통 컴포넌트 개발
### Phase 3: 화면별 마이그레이션
1. 관리자 화면부터 시작
2. 주요 업무 화면 순차 전환
3. 대시보드 및 리포트 화면
### Phase 4: 테스트 및 최적화
1. 기능 테스트
2. 성능 최적화
3. 사용자 테스트
4. 점진적 배포
## 주의사항
### 데이터 호환성
- 기존 데이터베이스 스키마 유지
- API 응답 형식 표준화
- 날짜/시간 형식 통일
### 사용자 경험
- 기존 업무 프로세스 유지
- 화면 전환 시 혼란 최소화
- 점진적 마이그레이션 고려
### 성능 고려사항
- API 응답 속도 최적화
- 클라이언트 사이드 캐싱
- 이미지 및 정적 자원 최적화
+103
View File
@@ -0,0 +1,103 @@
---
description:
globs:
alwaysApply: true
---
# 네비게이션 가이드
## 프로젝트 구조 이해
### 루트 디렉토리
```
plm-ilshin/
├── src/ # Java 소스 코드
├── WebContent/ # 웹 리소스 (JSP, CSS, JS, 이미지)
├── db/ # 데이터베이스 스크립트
├── tomcat-conf/ # Tomcat 설정
├── docker-compose.*.yml # Docker 설정
└── 문서/ # 프로젝트 문서
```
### 주요 소스 디렉토리
```
src/com/pms/
├── controller/ # 웹 컨트롤러 (@Controller)
├── service/ # 비즈니스 로직 (@Service)
├── mapper/ # MyBatis SQL 매퍼 (XML)
├── common/ # 공통 컴포넌트
├── salesmgmt/ # 영업관리 모듈
└── ions/ # 특수 기능 모듈
```
### 웹 리소스 구조
```
WebContent/
├── WEB-INF/
│ ├── view/ # JSP 뷰 파일
│ ├── lib/ # JAR 라이브러리
│ ├── classes/ # 컴파일된 클래스
│ └── *.xml # 설정 파일
├── css/ # 스타일시트
├── js/ # JavaScript 파일
├── images/ # 이미지 리소스
└── template/ # 템플릿 파일
```
## 주요 파일 찾기
### 컨트롤러 찾기
특정 URL에 대한 컨트롤러를 찾을 때:
1. URL 패턴 확인 (예: `/admin/menuMngList.do`)
2. `src/com/pms/controller/` 에서 해당 `@RequestMapping` 검색
3. 주요 컨트롤러들:
- [AdminController.java](mdc:src/com/pms/controller/AdminController.java) - 관리자 기능
- [ApprovalController.java](mdc:src/com/pms/controller/ApprovalController.java) - 승인 관리
- [AsController.java](mdc:src/com/pms/controller/AsController.java) - AS 관리
### 서비스 찾기
비즈니스 로직을 찾을 때:
1. 컨트롤러에서 `@Autowired` 된 서비스 확인
2. `src/com/pms/service/` 디렉토리에서 해당 서비스 파일 찾기
3. 주요 서비스들:
- [AdminService.java](mdc:src/com/pms/service/AdminService.java) - 관리자 서비스
- [ApprovalService.java](mdc:src/com/pms/service/ApprovalService.java) - 승인 서비스
### SQL 쿼리 찾기
데이터베이스 쿼리를 찾을 때:
1. 서비스 코드에서 `sqlSession.selectList("namespace.queryId")` 확인
2. `src/com/pms/mapper/` 에서 해당 namespace XML 파일 찾기
3. XML 파일 내에서 queryId로 검색
### JSP 뷰 찾기
화면을 찾을 때:
1. 컨트롤러 메서드의 return 값 확인 (예: `"/admin/menu/menuMngList"`)
2. `WebContent/WEB-INF/view/` + return 값 + `.jsp` 경로로 파일 찾기
## 모듈별 주요 기능
### 관리자 모듈 (`/admin/*`)
- 메뉴 관리: [AdminController.java](mdc:src/com/pms/controller/AdminController.java)
- 사용자 관리, 권한 관리
- 코드 관리, 카테고리 관리
- 시스템 로그 관리
### 영업 관리 (`/salesmgmt/*`)
- 위치: `src/com/pms/salesmgmt/`
- 영업 관련 컨트롤러, 서비스, 매퍼 분리
### 공통 기능 (`/common/*`)
- 공통 유틸리티: [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java)
- 메시지 처리: [Message](mdc:src/com/pms/common/Message.java)
- 파일 처리: [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java)
## 개발 시 주의사항
### 파일 수정 시
1. Java 파일 수정 → Eclipse에서 자동 컴파일 → `WebContent/WEB-INF/classes/`에 반영
2. JSP/CSS/JS 수정 → 바로 반영 (서버 재시작 불필요)
3. XML 설정 파일 수정 → 서버 재시작 필요
### 데이터베이스 관련
1. 스키마 변경 시 [ilshin.pgsql](mdc:db/ilshin.pgsql) 업데이트
2. 새로운 쿼리 추가 시 해당 mapper XML 파일에 추가
3. 트랜잭션 처리는 서비스 레벨에서 관리
+32
View File
@@ -0,0 +1,32 @@
---
description:
globs:
alwaysApply: true
---
# PLM 솔루션 (ILSHIN) - 프로젝트 개요
## 프로젝트 정보
이 프로젝트는 제품 수명 주기 관리(PLM - Product Lifecycle Management) 솔루션입니다.
Spring Framework 기반의 Java 웹 애플리케이션으로, 제품 개발부터 폐기까지의 전체 생명주기를 관리합니다.
## 기술 스택
- **Backend**: Java 7, Spring Framework 3.2.4, MyBatis 3.2.3
- **Frontend**: JSP, jQuery 1.11.3/2.1.4, jqGrid 4.7.1
- **Database**: PostgreSQL
- **WAS**: Apache Tomcat 7.0
- **Build**: Eclipse IDE 기반 (Maven/Gradle 미사용)
## 주요 기능
- 제품 정보 관리
- BOM (Bill of Materials) 관리
- 설계 변경 관리 (ECO/ECR)
- 문서 관리 및 버전 제어
- 프로젝트/일정 관리
- 사용자 및 권한 관리
- 워크플로우 관리
## 주요 설정 파일
- [web.xml](mdc:WebContent/WEB-INF/web.xml) - 웹 애플리케이션 배포 설정
- [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml) - Spring MVC 설정
- [docker-compose.dev.yml](mdc:docker-compose.dev.yml) - 개발환경 Docker 설정
- [docker-compose.prod.yml](mdc:docker-compose.prod.yml) - 운영환경 Docker 설정
+248
View File
@@ -0,0 +1,248 @@
---
description:
globs:
alwaysApply: true
---
# 보안 가이드
## 인증 및 인가
### 세션 기반 인증
현재 시스템은 세션 기반 인증을 사용합니다:
```java
// 사용자 인증 정보 저장
PersonBean person = new PersonBean();
person.setUserId(userId);
person.setUserName(userName);
request.getSession().setAttribute(Constants.PERSON_BEAN, person);
// 인증 정보 조회
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
if (person == null) {
// 로그인 페이지로 리다이렉트
}
```
### 권한 관리 구조
- **AUTH_GROUP**: 권한 그룹 정의
- **MENU_AUTH_GROUP**: 메뉴별 권한 그룹 매핑
- **USER_AUTH**: 사용자별 권한 할당
### 메뉴 접근 권한 체크
```java
public HashMap<String, Object> checkUserMenuAuth(HttpServletRequest request, Map<String, Object> paramMap) {
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
String userId = person.getUserId();
paramMap.put("userId", userId);
paramMap.put("menuUrl", request.getRequestURI());
// 권한 체크 쿼리 실행
return sqlSession.selectOne("admin.checkUserMenuAuth", paramMap);
}
```
## 데이터 보안
### SQL 인젝션 방지
**올바른 방법** - 파라미터 바인딩 사용:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = #{userId} <!-- 안전한 파라미터 바인딩 -->
</select>
```
**위험한 방법** - 직접 문자열 치환:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = '${userId}' <!-- SQL 인젝션 위험 -->
</select>
```
### 패스워드 보안
```java
// 패스워드 암호화 (EncryptUtil 사용)
String encryptedPassword = EncryptUtil.encrypt(plainPassword);
// 패스워드 검증
boolean isValid = EncryptUtil.matches(plainPassword, encryptedPassword);
```
### 입력값 검증
```java
// CommonUtils를 사용한 입력값 정제
String safeInput = CommonUtils.checkNull(request.getParameter("input"));
if (StringUtils.isEmpty(safeInput)) {
throw new IllegalArgumentException("필수 입력값이 누락되었습니다.");
}
```
## 세션 보안
### 세션 설정
[web.xml](mdc:WebContent/WEB-INF/web.xml)에서 세션 타임아웃 설정:
```xml
<session-config>
<session-timeout>1440</session-timeout> <!-- 24시간 -->
</session-config>
```
### 세션 무효화
```java
// 로그아웃 시 세션 무효화
@RequestMapping("/logout.do")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/login.do";
}
```
## 파일 업로드 보안
### 파일 확장자 검증
```java
public boolean isAllowedFileType(String fileName) {
String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx"};
String extension = fileName.toLowerCase().substring(fileName.lastIndexOf("."));
return Arrays.asList(allowedExtensions).contains(extension);
}
```
### 파일 크기 제한
```java
// web.xml에서 파일 업로드 크기 제한
<multipart-config>
<max-file-size>10485760</max-file-size> <!-- 10MB -->
<max-request-size>52428800</max-request-size> <!-- 50MB -->
</multipart-config>
```
### 안전한 파일명 생성
```java
// FileRenameClass 사용하여 안전한 파일명 생성
String safeFileName = FileRenameClass.rename(originalFileName);
```
## XSS 방지
### 출력값 이스케이프
JSP에서 사용자 입력값 출력 시:
```jsp
<!-- 안전한 출력 -->
<c:out value="${userInput}" escapeXml="true"/>
<!-- 위험한 출력 -->
${userInput} <!-- XSS 공격 가능 -->
```
### JavaScript에서 데이터 처리
```javascript
// 안전한 방법
var safeData = $('<div>').text(userInput).html();
// 위험한 방법
var dangerousData = userInput; // XSS 공격 가능
```
## CSRF 방지
### 토큰 기반 CSRF 방지
```jsp
<!-- 폼에 CSRF 토큰 포함 -->
<form method="post" action="save.do">
<input type="hidden" name="csrfToken" value="${csrfToken}"/>
<!-- 기타 입력 필드 -->
</form>
```
```java
// 컨트롤러에서 CSRF 토큰 검증
@RequestMapping("/save.do")
public String save(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
String sessionToken = (String)request.getSession().getAttribute("csrfToken");
String requestToken = (String)paramMap.get("csrfToken");
if (!sessionToken.equals(requestToken)) {
throw new SecurityException("CSRF 토큰이 일치하지 않습니다.");
}
// 정상 처리
}
```
## 로깅 및 감사
### 보안 이벤트 로깅
```java
private static final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
public void logSecurityEvent(String event, String userId, String details) {
securityLogger.info("Security Event: {} | User: {} | Details: {}", event, userId, details);
}
// 사용 예시
logSecurityEvent("LOGIN_SUCCESS", userId, request.getRemoteAddr());
logSecurityEvent("ACCESS_DENIED", userId, request.getRequestURI());
```
### 민감 정보 마스킹
```java
public String maskSensitiveData(String data) {
if (data == null || data.length() < 4) {
return "****";
}
return data.substring(0, 2) + "****" + data.substring(data.length() - 2);
}
```
## 환경별 보안 설정
### 개발 환경
- 상세한 오류 메시지 표시
- 디버그 모드 활성화
- 보안 제약 완화
### 운영 환경
- 일반적인 오류 메시지만 표시
- 디버그 모드 비활성화
- 엄격한 보안 정책 적용
```java
// 환경별 설정 예시
if (Constants.IS_PRODUCTION) {
// 운영 환경 설정
response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근이 거부되었습니다.");
} else {
// 개발 환경 설정
response.sendError(HttpServletResponse.SC_FORBIDDEN, "권한 부족: " + detailedMessage);
}
```
## 보안 체크리스트
### 코드 레벨
- [ ] SQL 인젝션 방지 (#{} 파라미터 바인딩 사용)
- [ ] XSS 방지 (출력값 이스케이프)
- [ ] CSRF 방지 (토큰 검증)
- [ ] 입력값 검증 및 정제
- [ ] 패스워드 암호화 저장
### 설정 레벨
- [ ] 세션 타임아웃 설정
- [ ] 파일 업로드 제한
- [ ] 오류 페이지 설정
- [ ] HTTPS 사용 (운영 환경)
- [ ] 보안 헤더 설정
### 운영 레벨
- [ ] 정기적인 보안 점검
- [ ] 로그 모니터링
- [ ] 권한 정기 검토
- [ ] 패스워드 정책 적용
- [ ] 백업 데이터 암호화
+166
View File
@@ -0,0 +1,166 @@
---
description:
globs:
alwaysApply: true
---
# 트러블슈팅 가이드
## 일반적인 문제 해결
### 애플리케이션 시작 오류
#### 데이터베이스 연결 실패
```
원인: JNDI DataSource 설정 문제
해결:
1. tomcat-conf/context.xml 확인
2. PostgreSQL 서비스 상태 확인
3. 데이터베이스 접속 정보 확인
```
#### 클래스 로딩 오류
```
원인: 컴파일되지 않은 Java 파일
해결:
1. Eclipse에서 프로젝트 Clean & Build
2. WebContent/WEB-INF/classes/ 디렉토리 확인
3. 누락된 라이브러리 확인 (WebContent/WEB-INF/lib/)
```
### 런타임 오류
#### 404 오류 (페이지를 찾을 수 없음)
```
원인: URL 매핑 문제
해결:
1. @RequestMapping 어노테이션 확인
2. JSP 파일 경로 확인 (/WEB-INF/view/)
3. web.xml의 servlet-mapping 확인 (*.do 패턴)
```
#### 500 오류 (서버 내부 오류)
```
원인: Java 코드 실행 오류
해결:
1. 로그 파일 확인 (log4j 설정)
2. MyBatis SQL 오류 확인
3. NullPointerException 체크
4. 데이터베이스 트랜잭션 오류 확인
```
### 데이터베이스 관련 문제
#### SQL 실행 오류
```
원인: MyBatis 매퍼 설정 문제
해결:
1. mapper XML 파일의 SQL 문법 확인
2. parameterType과 resultType 확인
3. 테이블/컬럼명 대소문자 확인 (PostgreSQL)
```
#### 트랜잭션 문제
```
원인: SqlSession 관리 문제
해결:
1. SqlSession.close() 호출 확인
2. try-finally 블록에서 리소스 정리
3. 트랜잭션 커밋/롤백 처리
```
### 프론트엔드 문제
#### JavaScript 오류
```
원인: jQuery/jqGrid 라이브러리 문제
해결:
1. 브라우저 개발자 도구 콘솔 확인
2. JavaScript 파일 로딩 순서 확인
3. jQuery 버전 호환성 확인
```
#### CSS 스타일 문제
```
원인: CSS 파일 로딩 또는 경로 문제
해결:
1. CSS 파일 경로 확인
2. 브라우저 캐시 클리어
3. all.css 파일 확인
```
## 개발 환경 문제
### Docker 환경
```bash
# 컨테이너 로그 확인
docker-compose logs app
# 컨테이너 내부 접속
docker-compose exec app bash
# 데이터베이스 접속 확인
docker-compose exec db psql -U postgres -d ilshin
```
### Eclipse 환경
```
문제: 프로젝트 인식 오류
해결:
1. .project 파일 확인
2. .classpath 파일 확인
3. Project Properties > Java Build Path 확인
4. Server Runtime 설정 확인 (Tomcat 7.0)
```
## 로그 분석
### 주요 로그 위치
- 애플리케이션 로그: log4j 설정에 따름
- Tomcat 로그: `$CATALINA_HOME/logs/`
- Docker 로그: `docker-compose logs`
### 로그 설정 파일
- [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml) - 로깅 설정
- 로그 레벨 조정으로 상세 정보 확인 가능
## 성능 문제
### 데이터베이스 성능
```sql
-- 슬로우 쿼리 확인
SELECT query, mean_time, calls
FROM pg_stat_statements
ORDER BY mean_time DESC;
-- 인덱스 사용률 확인
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes;
```
### 메모리 문제
```
원인: 메모리 누수 또는 부족
해결:
1. JVM 힙 메모리 설정 확인
2. SqlSession 리소스 정리 확인
3. 대량 데이터 처리 시 페이징 적용
```
## 보안 관련
### 권한 문제
```
원인: 사용자 권한 설정 오류
해결:
1. MENU_AUTH_GROUP 테이블 확인
2. 사용자 세션 정보 확인
3. Spring Security 설정 확인 (있는 경우)
```
### SQL 인젝션 방지
```
주의사항:
1. MyBatis #{} 파라미터 바인딩 사용
2. ${} 직접 문자열 치환 지양
3. 사용자 입력값 검증
```