# XIT Framework 개발 가이드
## 목차
- [1. 프로젝트 개요](#1-프로젝트-개요)
- [2. 기술 스택 및 라이브러리 버전](#2-기술-스택-및-라이브러리-버전)
- [2.1 핵심 기술 스택](#tech-stack)
- [2.2 주요 라이브러리](#main-libraries)
- [3. 프로젝트 구조](#3-프로젝트-구조)
- [3.1 디렉토리 구조](#directory-structure)
- [3.2 패키지 구조](#package-structure)
- [4. 주요 설정 파일](#4-주요-설정-파일)
- [4.1 application.yml](#application-yml)
- [4.2 application-local.yml](#application-local-yml)
- [4.3 application-dev.yml](#application-dev-yml)
- [4.4 application-prd.yml](#application-prd-yml)
- [4.5 build.gradle](#build-gradle)
- [4.6 logback-spring.xml](#logback-spring-xml)
- [4.7 mybatis-config.xml](#mybatis-config-xml)
- [4.8 데이터베이스 스키마](#database-schema)
- [5. 주요 기능 및 사용법](#5-주요-기능-및-사용법)
- [5.1 프로젝트 구조 패턴](#project-structure-pattern)
- [5.2 사용자 관리 기능 예시](#user-management-example)
- [5.2.1 Controller](#controller)
- [5.2.2 Service](#service)
- [5.2.3 Mapper](#mapper)
- [5.3 로그인 기능](#login-function)
- [5.3.1 Controller](#login-controller)
- [5.4 공통 유틸리티](#common-utilities)
- [5.4.1 API 응답 유틸리티](#api-response-util)
- [5.5 레이아웃 구성](#layout-configuration)
- [5.6 보안 기능](#security-features)
- [5.6.1 XSS 필터](#xss-filter)
- [5.6.2 리퍼러 체크](#referer-check)
- [5.6.3 권한 관리](#permission-management)
- [5.6.4 사용자 > 그룹 > 역할 > 메뉴 구조](#user-group-role-menu-structure)
- [5.7 배치 작업 관리](#batch-management)
- [6. 개발 가이드라인](#6-개발-가이드라인)
- [6.1 코드 작성 규칙](#code-writing-rules)
- [6.2 디렉토리 구조 가이드](#directory-structure-guide)
- [6.3 UI 컴포넌트 가이드](#ui-component-guide)
- [7. 배포 가이드](#7-배포-가이드)
- [7.1 빌드 방법](#build-method)
- [7.1.1 WAR 파일 빌드](#war-file-build)
- [7.1.2 bootWar 파일 빌드](#bootwar-file-build)
- [7.2 프로필 설정](#profile-settings)
- [7.2.1 기본 프로필](#default-profile)
- [7.2.2 프로필 활성화 방법](#profile-activation)
- [7.3 외부 WAS를 이용한 배포](#external-was-deployment)
- [7.3.1 Tomcat에 WAR 파일 배포](#tomcat-war-deployment)
- [7.3.2 외부 WAS에서 프로필 설정](#external-was-profile-settings)
- [7.4 WAR로 배포 및 실행](#war-deployment-execution)
- [7.4.1 bootWar 파일 실행](#bootwar-execution)
- [7.4.2 외부 WAS에 배포하여 실행](#external-was-execution)
- [7.4.3 백그라운드 실행 (Linux/macOS)](#background-execution)
- [7.4.4 Windows 서비스로 등록](#windows-service-registration)
- [7.5 배포 환경 설정](#deployment-environment-settings)
- [7.6 배포 체크리스트](#deployment-checklist)
- [8. 참고 자료](#8-참고-자료)
## 1. 프로젝트 개요
XIT Framework는 Spring Boot 기반의 웹 애플리케이션 프레임워크로, 전자정부 프레임워크를 확장하여 개발된 프로젝트입니다. 이 문서는 XIT Framework의 구조, 기술 스택, 주요 기능 및 사용법에 대한 종합적인 가이드를 제공합니다.
## 2. 기술 스택 및 라이브러리 버전
2.1 핵심 기술 스택
| 기술 | 버전 | 설명 |
|------|------|------|
| Java | 개발: 1.8, 배포: 1.8 | 자바 개발 및 실행 환경 |
| Spring Boot | 2.7.18 | 스프링 기반 애플리케이션 개발 프레임워크 |
| 전자정부 프레임워크 | 4.3.0 | 한국 정부 표준 웹 개발 프레임워크 |
| Servlet | 3.1 | 웹 애플리케이션 표준 |
| Gradle | - | 빌드 및 의존성 관리 도구 |
| MariaDB | - | 관계형 데이터베이스 |
2.2 주요 라이브러리
| 라이브러리 | 버전 | 설명 |
|------------|------|------|
| MyBatis | 2.3.1 | SQL 매핑 프레임워크 |
| Apache Tiles | 3.0.8 | 레이아웃 템플릿 엔진 |
| TOAST UI Grid | 4.19.2 | 자바스크립트 그리드 라이브러리 |
| Lombok | - | 자바 코드 생성 라이브러리 |
| Apache Commons Text | 1.10.0 | 텍스트 처리 유틸리티 |
| Apache POI | 5.3.0 | 엑셀 파일 처리 라이브러리 |
## 3. 프로젝트 구조
3.1 디렉토리 구조
```
xit-framework/
├── DB-DDL/ # 데이터베이스 스크립트
│ └── maria/ # MariaDB 스크립트
│ ├── ddl/ # 테이블 정의 스크립트
│ └── dml/ # 샘플 데이터 스크립트
├── src/
│ ├── main/
│ │ ├── java/ # 자바 소스 코드
│ │ │ ├── egovframework/ # 전자정부 프레임워크 확장 코드
│ │ │ └── go/kr/project/ # 프로젝트 소스 코드
│ │ ├── resources/ # 리소스 파일
│ │ │ ├── mybatis/ # MyBatis 설정 및 매퍼
│ │ │ └── application.yml # 애플리케이션 설정 파일
│ │ └── webapp/ # 웹 리소스
│ │ ├── resources/ # 정적 리소스 (CSS, JS, 이미지 등)
│ │ └── WEB-INF/views/ # JSP 뷰 파일
│ └── test/ # 테스트 코드
└── build.gradle # Gradle 빌드 스크립트
```
3.2 패키지 구조
```
go.kr.project/
├── common/ # 공통 컴포넌트
├── login/ # 로그인 관련 기능
│ ├── controller/ # 컨트롤러 클래스
│ ├── mapper/ # MyBatis 매퍼 인터페이스
│ ├── model/ # 데이터 모델 클래스
│ └── service/ # 서비스 클래스
└── system/ # 시스템 관리 기능
├── auth/ # 권한 관리
├── code/ # 코드 관리
├── group/ # 그룹 관리
├── menu/ # 메뉴 관리
├── role/ # 역할 관리
└── user/ # 사용자 관리
├── controller/ # 컨트롤러 클래스
├── mapper/ # MyBatis 매퍼 인터페이스
├── model/ # 데이터 모델 클래스
└── service/ # 서비스 클래스
egovframework/
├── config/ # 프레임워크 설정
├── exception/ # 예외 처리
├── filter/ # 필터
├── interceptor/ # 인터셉터
└── util/ # 유틸리티 클래스
```
## 4. 주요 설정 파일
주요 테이블 구조는 `DB-DDL/maria/all_ddl.sql`에 정의되어 있습니다:
- **tb_user**: 사용자 정보
- **tb_group**: 그룹 정보
- **tb_role**: 역할 정보
- **tb_menu**: 메뉴 정보
- **tb_group_role**: 그룹-역할 매핑
- **tb_role_menu**: 역할-메뉴 매핑
- **tb_code_group**: 코드 그룹
- **tb_code_detail**: 코드 상세
- **tb_bbs_config**: 게시판 설정
- **tb_bbs_post**: 게시물
- **tb_bbs_file**: 게시판 파일
- **tb_user_session**: 사용자 세션
- **tb_login_log**: 로그인 로그
MyBatis 매퍼 파일은 `src/main/resources/mybatis/mapper/` 디렉토리에 모듈별로 구성되어 있습니다.
## 5. 주요 기능 및 사용법
5.1 프로젝트 구조 패턴
XIT Framework는 MVC(Model-View-Controller) 패턴을 기반으로 하며, 각 기능별로 다음과 같은 계층 구조를 가집니다:
1. **Controller**: 클라이언트 요청을 처리하고 응답을 반환합니다.
2. **Service**: 비즈니스 로직을 처리합니다.
3. **Mapper**: 데이터베이스 접근을 담당합니다.
4. **Model**: 데이터 구조를 정의합니다.
5.2 사용자 관리 기능 예시
사용자 관리 기능은 다음과 같은 구조로 구현되어 있습니다:
5.2.1 Controller
```java
@Controller
@RequestMapping("/system/user")
public class UserController {
@Resource(name = "UserService")
private UserService userService;
// 사용자 목록 페이지
@RequestMapping("/list.do")
public String userList(SystemUserVO paramVO, Model model) {
return "system/user/list.base";
}
// 사용자 목록 조회 AJAX
@PostMapping("/list.ajax")
public ResponseEntity> getUserListAjax(@ModelAttribute SystemUserVO paramVO) {
int totalCount = userService.selectUserListTotalCount(paramVO);
paramVO.setTotalCount(totalCount);
paramVO.setPagingYn("Y");
List userList = userService.selectUserList(paramVO);
return ApiResponseUtil.successWithGrid(userList, paramVO);
}
// 사용자 등록 페이지
@GetMapping("/register.do")
public String registerUser(Model model) {
model.addAttribute("user", new SystemUserVO());
return "system/user/form.base";
}
// 사용자 등록 처리 AJAX
@PostMapping("/register.ajax")
public ResponseEntity> registerUserAjax(@ModelAttribute SystemUserVO userVO) {
// 사용자 ID 자동 생성
String userId = userService.generateUserId();
userVO.setUserId(userId);
// 기본 비밀번호 설정
String defaultPassword = env.getProperty("Globals.DefaultPassword", "xitpassword");
userVO.setPasswd(EgovFileScrty.encryptPassword(defaultPassword, userId));
int result = userService.insertUser(userVO);
if (result > 0) {
return ApiResponseUtil.success("사용자가 성공적으로 등록되었습니다.");
} else {
return ApiResponseUtil.error("사용자 등록에 실패했습니다.");
}
}
}
```
5.2.2 Service
```java
@Service("UserService")
public interface UserService {
// 사용자 정보 조회
SystemUserVO selectUser(String userId);
// 사용자 목록 조회
List selectUserList(SystemUserVO vo);
// 사용자 목록 총 개수 조회
int selectUserListTotalCount(SystemUserVO vo);
// 사용자 ID 생성
String generateUserId();
// 사용자 등록
int insertUser(SystemUserVO vo);
// 사용자 수정
int updateUser(SystemUserVO vo);
}
```
5.2.3 Mapper
```java
@Mapper
public interface UserMapper {
// 사용자 정보 조회
SystemUserVO selectUser(String userId);
// 사용자 목록 조회
List selectUserList(SystemUserVO vo);
// 사용자 목록 총 개수 조회
int selectUserListTotalCount(SystemUserVO vo);
// 사용자 ID 생성
String generateUserId();
// 사용자 등록
int insertUser(SystemUserVO vo);
// 사용자 수정
int updateUser(SystemUserVO vo);
}
```
5.3 로그인 기능
로그인 기능은 다음과 같은 구조로 구현되어 있습니다:
5.3.1 Controller
```java
@Controller
@RequestMapping("/login")
public class LoginController {
@Autowired
private LoginService loginService;
// 로그인 페이지
@RequestMapping(value = "/login.do", method = RequestMethod.GET)
public String loginPage(Model model, HttpServletRequest request) {
// 쿠키에서 저장된 아이디 가져오기
Cookie[] cookies = request.getCookies();
String savedUserId = "";
boolean isSaveId = false;
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("savedUserId".equals(cookie.getName())) {
savedUserId = cookie.getValue();
isSaveId = true;
break;
}
}
}
model.addAttribute("savedUserId", savedUserId);
model.addAttribute("isSaveId", isSaveId);
return "login/login.login";
}
// 로그인 처리 AJAX
@PostMapping("/login.ajax")
public ResponseEntity> loginAjax(
@RequestParam("userAcnt") String userAcnt,
@RequestParam("passwd") String passwd,
@RequestParam(value = "saveId", required = false) String saveId,
HttpServletRequest request,
HttpServletResponse response) {
try {
// 로그인 처리
SessionVO sessionVO = loginService.login(userAcnt, passwd, request, response);
// 아이디 저장 처리
if ("Y".equals(saveId)) {
Cookie cookie = new Cookie("savedUserId", userAcnt);
cookie.setMaxAge(60 * 60 * 24 * 7);
cookie.setPath("/");
response.addCookie(cookie);
} else {
Cookie cookie = new Cookie("savedUserId", "");
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
if (sessionVO != null) {
// 로그인 성공
Map data = new HashMap<>();
data.put("redirectUrl", "/main.do");
return ApiResponseUtil.success(data, "로그인에 성공하였습니다.");
} else {
// 로그인 실패
return ApiResponseUtil.error("아이디 또는 비밀번호가 일치하지 않습니다.");
}
} catch (Exception e) {
return ApiResponseUtil.error("로그인 처리 중 오류가 발생했습니다.");
}
}
// 로그아웃 처리
@GetMapping("/logout.do")
public String logout(HttpServletRequest request, HttpServletResponse response) {
try {
loginService.logout(request, response);
} catch (Exception e) {
log.error("로그아웃 처리 중 오류 발생", e);
}
return "redirect:" + loginProperties.getUrl();
}
}
```
5.4 공통 유틸리티
5.4.1 API 응답 유틸리티
```java
public class ApiResponseUtil {
// 성공 응답
public static ResponseEntity> success(Object data, String message) {
ApiResponseEntity response = new ApiResponseEntity();
response.setResult(true);
response.setMessage(message);
response.setData(data);
return ResponseEntity.ok(response);
}
// 성공 응답 (메시지만)
public static ResponseEntity> success(String message) {
return success(null, message);
}
// 그리드 데이터 응답
public static ResponseEntity> successWithGrid(List> data, PagingVO pagingVO) {
Map gridData = new HashMap<>();
gridData.put("contents", data);
gridData.put("pagination", pagingVO);
return success(gridData, "조회가 완료되었습니다.");
}
// 오류 응답
public static ResponseEntity> error(String message) {
ApiResponseEntity response = new ApiResponseEntity();
response.setResult(false);
response.setMessage(message);
return ResponseEntity.ok(response);
}
}
```
```
### 레이아웃 구성 요소
1. **Sidebar (사이드바)**
- `menu_header`: 메뉴 헤더 영역
- `menu`: 메뉴 네비게이션 영역
2. **Main (메인 영역)**
- `main_header`: 메인 헤더 영역
- `main`: 실제 콘텐츠 영역
### 주요 CSS/JS 라이브러리
- **Bootstrap**: 반응형 UI 프레임워크
- **Material Design Icons**: 아이콘 라이브러리
- **FontAwesome**: 아이콘 라이브러리
- **DataTables**: 데이터 테이블 플러그인
- **TOAST UI Grid**: 고성능 그리드 컴포넌트
- **SimpleBar**: 커스텀 스크롤바
- **Crypto-JS**: 암호화 라이브러리
- **InputMask**: 입력 마스킹
- **Moment.js**: 날짜/시간 처리
### XIT 커스텀 파일
- **xit-common.css/js**: 공통 스타일 및 유틸리티
- **xit-tui-grid.css/js**: TOAST UI Grid 커스터마이징
- **xit-validation.js**: 폼 유효성 검사
- **menu-path.js**: 메뉴 경로 처리
- **common_util.js**: 공통 유틸리티 함수
- **datatables_util.js**: DataTables 유틸리티
- **xit-multi-fileupload.css**: 다중 파일 업로드 스타일
### 캐시 제어
브라우저 캐시를 방지하기 위해 메타 태그와 HTTP 헤더를 설정하여 항상 최신 콘텐츠를 제공합니다.
5.6 보안 기능
XIT Framework는 다양한 보안 기능을 제공하여 애플리케이션의 안전성을 강화합니다.
5.6.1 XSS 필터
XSS(Cross-Site Scripting) 공격을 방지하기 위한 필터를 제공합니다. 이 필터는 모든 요청 파라미터를 검사하고 잠재적인 XSS 공격 코드를 제거합니다.
```java
// XSS 필터 설정 클래스
@Configuration
public class XssFilterConfig {
private final XssUtil xssUtil;
public XssFilterConfig(XssUtil xssUtil) {
this.xssUtil = xssUtil;
}
@Bean
public FilterRegistrationBean xssFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new XssFilter(xssUtil));
registrationBean.addUrlPatterns("/*"); // 모든 URL에 적용
registrationBean.setName("xssFilter");
registrationBean.setOrder(1); // 필터 순서 (낮은 숫자가 먼저 실행)
return registrationBean;
}
}
```
XSS 필터는 다음과 같은 기능을 제공합니다:
1. **HTML 태그 이스케이프**: 일반 텍스트 필드에서 HTML 태그를 이스케이프 처리합니다.
2. **HTML 에디터 내용 정화**: HTML 에디터 내용에서는 허용된 태그만 남기고 위험한 스크립트를 제거합니다.
3. **파일명 정화**: 파일명에서 위험한 문자를 제거합니다.
4. **보안 헤더 설정**: XSS 방지를 위한 HTTP 헤더를 설정합니다.
```java
// XSS 필터 구현
public class XssFilter extends OncePerRequestFilter {
private final XssUtil xssUtil;
public XssFilter(XssUtil xssUtil) {
this.xssUtil = xssUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// XSS 방지를 위한 헤더 설정
// X-XSS-Protection: 브라우저의 XSS 필터를 켜고, 공격이 감지되면 페이지를 차단.
response.setHeader("X-XSS-Protection", "1; mode=block");
// X-Content-Type-Options: 브라우저가 서버가 지정한 MIME 타입만 사용하게 하여, 잘못된 타입 해석으로 인한 보안 문제를 방지.
response.setHeader("X-Content-Type-Options", "nosniff");
// 요청을 래핑하여 XSS 필터링 적용
XssRequestWrapper xssRequestWrapper = new XssRequestWrapper(request, xssUtil);
// 필터 체인 실행
filterChain.doFilter(xssRequestWrapper, response);
}
}
```
5.6.2 리퍼러 체크
리퍼러(Referer) 헤더를 검사하여 직접 URL을 입력하거나 외부 사이트에서의 접근을 제한합니다. 이를 통해 CSRF(Cross-Site Request Forgery) 공격을 방지하고 애플리케이션의 보안을 강화합니다.
```java
// AuthInterceptor 클래스의 preHandle 메서드 내부
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 리퍼러 체크 로직
if (!isRefererCheckExcluded(requestURI)) {
String referer = request.getHeader("Referer");
if (referer == null || referer.isEmpty()) {
// Referer 헤더가 없는 경우 (직접 URL 입력 또는 북마크 등)
log.warn("Referer 헤더 없음: {}", requestURI);
// AJAX 요청인 경우 JSON 응답 반환
if (HttpServletUtil.isAjaxRequest(request) || HttpServletUtil.isRealAjaxRequest(request)) {
handleRefererMissing(response);
return false;
}
// 일반 요청인 경우 로그인 페이지로 리다이렉트
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("");
return false;
}
}
// 나머지 로직...
return true;
}
```
리퍼러 체크는 다음과 같은 특징을 가집니다:
1. **예외 URL 설정**: 로그인 페이지, 정적 리소스 등 일부 URL은 리퍼러 체크에서 제외됩니다.
2. **AJAX 요청 처리**: AJAX 요청의 경우 JSON 형식으로 오류 응답을 반환합니다.
3. **일반 요청 처리**: 일반 요청의 경우 경고 메시지와 함께 로그인 페이지로 리다이렉트합니다.
5.6.3 권한 관리
사용자의 권한에 따라 접근 가능한 기능과 메뉴를 제한하는 권한 관리 시스템을 제공합니다. 권한 관리는 인터셉터를 통해 구현되며, 사용자의 세션 정보와 요청 URL을 기반으로 접근 권한을 검사합니다.
```java
// 권한 관리 인터셉터
@Slf4j
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final LoginService loginService;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
private final XssUtil xssUtil = new XssUtil();
@Autowired
private InterceptorProperties interceptorProperties;
@Autowired
private LoginProperties loginProperties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
// Referer 헤더 검사 로직...
try {
// 세션 정보 조회
SessionVO sessionVO = loginService.getSessionInfo(request);
// 세션이 없거나 로그인 상태가 아닌 경우
if (sessionVO == null || !sessionVO.isLogin()) {
// 방문자 권한 확인
if (sessionVO != null && sessionVO.isVisitor()) {
// 방문자 권한으로 접근 가능한지 확인
if (hasAccess(sessionVO, requestURI)) {
return true;
}
}
// 세션 만료 처리...
return false;
}
// 로그인 상태인 경우 접근 권한 확인
if (hasAccess(sessionVO, requestURI)) {
return true;
}
// 접근 권한이 없는 경우
log.warn("접근 권한 없음: {} - {}", sessionVO.getUser().getUserAcnt(), requestURI);
response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근 권한이 없습니다.");
return false;
} catch (Exception e) {
// 예외 처리...
return false;
}
}
// 접근 권한 확인 메서드
private boolean hasAccess(SessionVO sessionVO, String requestURI) {
List menus = flattenMenuTree(sessionVO.getMenus());
if (menus.isEmpty()) {
return false;
}
// 메뉴의 URL 패턴과 일치하는지 확인
for (MenuVO menu : menus) {
if (menu.getUrlPattern() != null && !menu.getUrlPattern().isEmpty()) {
String[] patterns = xssUtil.unescape(menu.getUrlPattern()).split(",");
for (String pattern : patterns) {
if (pathMatcher.match(pattern.trim(), requestURI)) {
return true;
}
}
}
}
return false;
}
}
```
5.6.4 사용자 > 그룹 > 역할 > 메뉴 구조
XIT Framework는 사용자, 그룹, 역할, 메뉴를 연결하는 권한 관리 구조를 제공합니다. 이 구조는 데이터베이스 테이블 간의 관계를 통해 구현되며, 접근 제어가 가능합니다.
1. **데이터 구조 개요**
XIT Framework의 권한 관리 시스템은 다음과 같은 주요 엔티티로 구성됩니다:
- **사용자(User)**: 시스템을 사용하는 개인 계정
- **그룹(Group)**: 사용자들의 집합
- **역할(Role)**: 특정 기능에 대한 권한 집합
- **메뉴(Menu)**: 시스템의 기능 단위와 UI 구성 요소
- **URL 패턴**: 각 메뉴에 연결된 실제 접근 경로
2. **엔티티 간 관계**
- **사용자-그룹 관계**: 사용자는 하나의 그룹에 소속됩니다. 그룹은 여러 사용자를 포함할 수 있습니다. (N:1 관계)
- **그룹-역할 관계**: 그룹은 여러 역할을 가질 수 있으며, 역할은 여러 그룹에 할당될 수 있습니다. (N:M 관계)
- **역할-메뉴 관계**: 역할은 여러 메뉴에 대한 접근 권한을 가지며, 메뉴는 여러 역할에 의해 접근될 수 있습니다. (N:M 관계)
- **메뉴-URL 패턴 관계**: 메뉴는 하나 이상의 URL 패턴과 연결됩니다. (1:N 관계)
3. **데이터베이스 테이블 구조**
- **TB_USER**: 사용자 정보 저장 (USER_ID, USER_ACNT, USER_NM, PASSWD, GRP_ID 등)
- **TB_GROUP**: 그룹 정보 저장 (GRP_ID, GRP_NM, GRP_DC 등)
- **TB_ROLE**: 역할 정보 저장 (ROLE_ID, ROLE_NM, ROLE_DC 등)
- **TB_GROUP_ROLE**: 그룹-역할 매핑 정보 (GRP_ID, ROLE_ID)
- **TB_MENU**: 메뉴 정보 저장 (MENU_ID, MENU_NM, MENU_URL, URL_PATTERN, UPPER_MENU_ID 등)
- **TB_ROLE_MENU**: 역할-메뉴 매핑 정보 (ROLE_ID, MENU_ID)
4. **권한 확인 프로세스**
사용자가 특정 URL에 접근하려고 할 때, 시스템은 다음과 같은 과정으로 권한을 확인합니다:
1. 사용자 식별: 로그인한 사용자의 USER_ID 확인
2. 그룹 확인: 사용자가 속한 그룹(GRP_ID) 조회
3. 역할 확인: 그룹에 할당된 역할(ROLE_ID) 목록 조회
4. 메뉴 확인: 역할에 연결된 메뉴(MENU_ID) 목록 조회
5. URL 패턴 확인: 메뉴에 연결된 URL 패턴과 요청 URL 비교
6. 접근 허용/거부: 일치하는 URL 패턴이 있으면 접근 허용, 없으면 거부
5. **권한 관리의 장점**
이러한 계층적 권한 구조는 다음과 같은 이점을 제공합니다:
- **효율적인 권한 관리**: 그룹과 역할을 통해 다수의 사용자에게 일괄적으로 권한 부여 가능
- **세밀한 접근 제어**: URL 패턴 기반으로 세밀한 접근 제어 가능
- **유연한 권한 설계**: 사용자-그룹-역할-메뉴의 다단계 구조로 다양한 권한 정책 구현 가능
- **동적 메뉴 구성**: 사용자의 권한에 따라 UI 메뉴를 동적으로 구성 가능
- **관리 용이성**: 관리자 화면에서 그룹, 역할, 메뉴, 권한을 통합적으로 관리 가능
5.7 배치 작업 관리
XIT Framework는 Quartz 기반의 배치 스케줄링 시스템을 내장하고 있습니다. 배치 작업은 등록, 실행, 일시정지, 재개, 삭제, 실행 이력 및 로그 관리 등 다양한 기능을 제공합니다.
### 5.7.1 전체 구조
- **Controller**: `BatchJobController` - 배치 작업의 웹/REST API 제공
- **Service**: `BatchJobService` - 배치 작업의 비즈니스 로직 처리
- **Job**: `SampleBatchJob`, `SampleBatchJob2`, `SampleBatchJob3` 등 - 실제 실행되는 배치 잡 구현체
- **Config**: `QuartzConfig`, `BatchJobInitializer`, `QuartzJobListener`, `QuartzListenerConfig` - 스케줄러 및 리스너 설정, 초기화
- **Mapper**: `BatchJobMapper` - 배치 작업/실행/로그 DB 연동
- **Model**: `BatchJobInfoVO`, `BatchJobExecutionVO`, `BatchJobLogVO` - 배치 정보/실행/로그 VO
- **Util**: `BatchJobLogUtil`, `ServerInfoUtil` - 배치 로그, 서버 정보 등 유틸리티
### 5.7.2 주요 기능 및 API
- **배치 작업 목록 조회**: `/batch/list.do`, `/batch/list.ajax`
- **배치 작업 즉시 실행**: `/batch/trigger.ajax` (POST)
- **배치 작업 일시정지/재개/삭제**: `/batch/pause.ajax`, `/batch/resume.ajax`, `/batch/delete.ajax` (POST)
- **실행 이력/로그 조회**: `/batch/execution.do`, `/batch/execution.ajax`, `/batch/log.do`, `/batch/log.ajax`
- **잡 등록/수정/삭제**: `/batch/register.ajax` 등
#### 예시: 배치 작업 즉시 실행
```http
POST /batch/trigger.ajax
Content-Type: application/x-www-form-urlencoded
jobId=...&jobName=SampleBatchJob&jobGroup=DEFAULT
```
### 5.7.3 배치 잡 예시
```java
@Component
@DisallowConcurrentExecution
public class SampleBatchJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 실제 배치 로직 구현
}
}
```
### 5.7.4 DB 연동 및 로그 관리
- **잡 정보/실행/로그**는 각각 TB_BATCH_JOB_INFO, TB_BATCH_JOB_EXECUTION, TB_BATCH_JOB_LOG 테이블에 저장
- `BatchJobMapper`를 통해 CRUD 및 이력/로그 관리
- `BatchJobLogUtil`로 실행 중 로그를 DB에 저장 가능
### 5.7.5 배치 스케줄러 및 리스너 설정
- `QuartzConfig`, `QuartzListenerConfig`에서 Quartz 스케줄러 및 JobListener 등록
- `BatchJobInitializer`에서 DB에 등록된 잡을 애플리케이션 시작 시 자동 등록
### 5.7.6 확장 및 커스터마이징
- 새로운 배치 잡은 `Job` 인터페이스 구현 후 등록
- 잡 등록/수정/삭제, 스케줄 변경 등은 Controller/Service/DB를 통해 관리
- 실행 이력, 평균 소요시간, 최근 실패 등 다양한 통계 제공
### 5.7.7 참고 VO 구조
- **BatchJobInfoVO**: 잡ID, 이름, 그룹, 클래스, 크론, 상태, 설명 등
- **BatchJobExecutionVO**: 실행ID, 잡이름/그룹, 시작/종료시간, 상태, 종료코드/메시지, 서버정보 등
- **BatchJobLogVO**: 로그ID, 실행ID, 로그레벨, 메시지, 시간 등
## 6. 개발 가이드라인
6.1 코드 작성 규칙
1. **Java 개발 및 수정**
- `src/main/java/go/kr/project/system` 하위에 있는 프로그램을 참조하여 구조, 패턴, 스타일을 사용합니다.
- 클래스, 메소드, 변수에 대한 주석을 작성합니다.
2. **JSP 개발 및 수정**
- `src/main/webapp/WEB-INF/views/system` 하위에 있는 프로그램을 참조하여 구조, 패턴, 스타일을 사용합니다.
- JSP 페이지에서 태그의 id 속성은 중복되지 않도록 합니다.
- 모든 URL은 `` 태그로 감싸줍니다.
3. **데이터베이스 관련**
- 신규로 생성되는 테이블은 `DB-DDL/maria/ddl.sql`에 등록합니다.
- `ON DELETE SET NULL`, `ON DELETE CASCADE` 등의 ON 구문은 사용하지 않고, 프로그램에서 직접 구현합니다.
4. **Ajax 처리**
- Ajax 요청 시 `src/main/java/egovframework/util/ApiResponseUtil.java`와 `src/main/java/egovframework/util/ApiResponseEntity.java`를 참조하여 응답을 처리합니다.
- Ajax 응답을 처리할 때는 `response.success` 대신 `response.result`를 사용합니다.
```bash
# Gradle 빌드 (테스트 포함)
./gradlew clean build
# Gradle 빌드 (테스트 제외)
./gradlew clean build -x test
# WAR 파일만 생성
./gradlew war
```
7.1.2 bootWar 파일 빌드
Spring Boot 애플리케이션은 실행 가능한 WAR 파일로 빌드할 수 있습니다.
```bash
# 실행 가능한 WAR 파일 생성
./gradlew bootWar
```
7.2 프로필 설정
XIT Framework는 다양한 환경에 맞게 설정할 수 있는 프로필 시스템을 제공합니다.
7.2.1 기본 프로필
프로젝트는 다음과 같은 프로필을 제공합니다:
| 프로필 | 설명 | 주요 설정 |
|--------|------|-----------|
| local | 로컬 개발 환경 | 개발 도구 활성화, 로컬 경로 사용 |
| dev | 개발 서버 환경 | 개발 서버 설정, 세션 타임아웃 30분 |
| prd | 운영 서버 환경 | 운영 서버 설정, 리눅스 경로 사용 |
7.2.2 프로필 활성화 방법
애플리케이션 속성 파일 설정
`application.yml` 파일에서 기본 프로필을 설정할 수 있습니다:
```yaml
spring:
profiles:
active: local # local, dev, prd 중 선택
```
명령행 인수로 프로필 설정
애플리케이션 실행 시 명령행 인수로 프로필을 지정할 수 있습니다:
```bash
# JAR 파일 실행 시
java -jar xit-framework.war --spring.profiles.active=dev
# Gradle로 실행 시
./gradlew bootRun --args='--spring.profiles.active=dev'
```
환경 변수로 프로필 설정
환경 변수를 통해 프로필을 설정할 수 있습니다:
```bash
# Windows
set SPRING_PROFILES_ACTIVE=dev
java -jar xit-framework.war
# Linux/macOS
export SPRING_PROFILES_ACTIVE=dev
java -jar xit-framework.war
```
7.3 외부 WAS를 이용한 배포
7.3.1 Tomcat에 WAR 파일 배포
1. `build/libs` 디렉토리에 생성된 `xit-framework.war` 파일을 Tomcat의 `webapps` 디렉토리에 복사합니다.
2. Tomcat을 재시작합니다.
7.3.2 외부 WAS에서 프로필 설정
Tomcat에서 프로필 설정
Tomcat의 `setenv.bat`(Windows) 또는 `setenv.sh`(Linux/macOS) 파일에 다음 내용을 추가합니다:
**Windows (setenv.bat)**:
```batch
set "JAVA_OPTS=%JAVA_OPTS% -Dspring.profiles.active=dev"
```
**Linux/macOS (setenv.sh)**:
```bash
export JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=dev"
```
WAS의 JVM 옵션으로 설정
대부분의 WAS에서는 JVM 옵션을 통해 시스템 속성을 설정할 수 있습니다:
```
-Dspring.profiles.active=dev
```
web.xml에 시스템 속성 설정
`web.xml` 파일에 다음과 같이 시스템 속성을 설정할 수 있습니다:
```xml
spring.profiles.activedev
```
7.4 WAR로 배포 및 실행
Spring Boot 애플리케이션은 bootWar로 빌드된 실행 가능한 WAR 파일로 배포할 수 있습니다.
7.4.1 bootWar 파일 실행
bootWar로 빌드된 WAR 파일은 내장 서버를 포함하고 있어 JAR 파일처럼 직접 실행할 수 있습니다:
```bash
# 기본 프로필로 실행
java -jar build/libs/xit-framework-boot.war
# 특정 프로필로 실행
java -jar build/libs/xit-framework-boot.war --spring.profiles.active=dev
# 포트 변경하여 실행
java -jar build/libs/xit-framework-boot.war --server.port=9090
# 메모리 설정을 추가하여 실행
java -Xms512m -Xmx1024m -jar build/libs/xit-framework-boot.war
# 로컬 테스트
java -jar build/libs/xit-framework-boot.war --spring.profiles.active=local --server.port=9090
```
7.4.2 외부 WAS에 배포하여 실행
bootWar로 빌드된 WAR 파일은 외부 WAS(Tomcat, JBoss, WebLogic 등)에 배포하여 실행할 수도 있습니다:
1. `build/libs` 디렉토리에 생성된 `xit-framework.war` 파일을 WAS의 배포 디렉토리에 복사합니다.
- Tomcat: `webapps` 디렉토리
- JBoss/WildFly: `deployments` 디렉토리
- WebLogic: 관리 콘솔을 통해 배포
2. WAS를 시작하거나 재시작합니다.
7.4.3 백그라운드 실행 (Linux/macOS)
내장 서버로 실행할 경우, 백그라운드에서 실행할 수 있습니다:
```bash
# nohup을 사용하여 백그라운드 실행
nohup java -jar build/libs/xit-framework.war > app.log 2>&1 &
# 실행 중인 프로세스 확인
ps -ef | grep java
```
7.4.4 Windows 서비스로 등록
Windows에서는 [winsw](https://github.com/winsw/winsw)와 같은 도구를 사용하여 Spring Boot 애플리케이션을 Windows 서비스로 등록할 수 있습니다.
7.5 배포 환경 설정
1. **Java 버전**: 배포 환경에서는 Java 1.8을 사용합니다.
2. **서버**:
- 내장 서버(bootWar 직접 실행): 내장된 Tomcat 9.0.78 사용
- 외부 서버(bootWar 배포): Tomcat 9.0.78 이상 권장
3. **데이터베이스**: MariaDB를 사용합니다.
4. **메모리 설정**: 애플리케이션 크기와 사용자 수에 따라 적절한 JVM 메모리 설정이 필요합니다.
7.6 배포 체크리스트
1. **프로필 설정 확인**: 배포 환경에 맞는 프로필이 활성화되었는지 확인합니다.
2. **데이터베이스 연결 확인**: 데이터베이스 연결 정보가 올바르게 설정되었는지 확인합니다.
3. **파일 업로드 경로 확인**: 파일 업로드 경로가 존재하고 쓰기 권한이 있는지 확인합니다.
4. **로그 경로 확인**: 로그 파일 경로가 존재하고 쓰기 권한이 있는지 확인합니다.
5. **메모리 설정 확인**: JVM 메모리 설정이 적절한지 확인합니다.
6. **포트 충돌 확인**: 사용할 포트가 다른 애플리케이션과 충돌하지 않는지 확인합니다.
## 8. 참고 자료
- [Spring Boot 공식 문서](https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/)
- [전자정부 프레임워크 가이드](https://www.egovframe.go.kr/wiki/doku.php)
- [MyBatis 공식 문서](https://mybatis.org/mybatis-3/ko/index.html)
- [TOAST UI Grid 공식 문서](https://ui.toast.com/tui-grid)
- [TOAST UI Editor 공식 문서](https://ui.toast.com/tui-editor)