Skip to content

qlsdud0604/test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

next-krx-lds-bat-mst 운영자 가이드 및 교육자료

프로젝트명: KRX 배치 마스터 (next-krx-lds-bat-mst)
작성 기준일: 2026-04-01
대상 독자: 신규 개발자 / 운영 담당자


목차

  1. 프로젝트 개요
  2. 기술 스택
  3. 전체 시스템 아키텍처
  4. 레이어 아키텍처
  5. Spring Security 인증/인가 구조
  6. Redis를 활용한 세션 관리
  7. 배치 마스터(Master-Agent) 동작 구조
  8. 주요 화면 및 기능 설명
  9. 환경별 구성
  10. 운영 관리 체크리스트

1. 프로젝트 개요

1.1 무엇을 하는 시스템인가?

배치 마스터 시스템은 금융 업무에서 사용되는 배치(일괄처리) 작업들을 중앙에서 관리하는 플랫폼입니다.

쉽게 말하면: "언제, 어떤 배치 작업을, 어떻게 실행할지" 를 관리하는 관제 시스템
역할 설명
Job 관리 배치 작업 등록/수정/삭제/조회
스케줄 관리 작업을 언제 실행할지 일정 관리 (캘린더/Cron 방식)
실행 결과 조회 각 작업의 실행 이력 및 성공/실패 결과 확인
실시간 알림 작업 완료/오류 발생 시 실시간 화면 푸시

1.2 전체 시스템 위치

┌─────────────────────────────────────────────────────────────────┐
│                       KRX 전체 시스템                            │
│                                                                 │
│   ┌──────────────────┐        ┌──────────────────────────────┐  │
│   │  배치 마스터       │  Kafka │   실제 배치 작업 처리 시스템   │  │
│   │ (이 시스템)       │───────▶│  (Worker 서버들)             │  │
│   │                  │        │                              │  │
│   │  · Job 등록       │        │  · 실제 데이터 처리           │  │
│   │  · 스케줄 설정    │        │  · 파일 처리                  │  │
│   │  · 결과 조회      │        │  · DB 배치 처리               │  │
│   └──────────────────┘        └──────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 기술 스택

2.1 핵심 기술 목록

분류 기술 버전 용도
프레임워크 Spring Boot 3.5.11 전체 애플리케이션 기반
언어 Java 21 메인 개발 언어
빌드 Gradle 9.3.0 빌드/의존성 관리
보안 Spring Security 3.5.11 인증/인가 처리
인증 토큰 JWT (JJWT) 0.12.6 사용자 인증 토큰
데이터 접근 MyBatis 3.0.3 DB 쿼리 처리
데이터베이스 Oracle (AWS RDS) 23.3.0 메인 데이터 저장소
세션 저장소 Redis + Spring Session 3.5.11 분산 세션 관리
스케줄러 Quartz 3.5.11 배치 작업 일정 관리
배치 Spring Batch 3.5.11 배치 작업 실행 관리
메시징 Apache Kafka - 비동기 작업 메시지 전달
분산 락 ShedLock 4.39.0 다중 서버 중복 실행 방지
캐시 Caffeine - 메모리 캐시
화면 템플릿 Thymeleaf - 서버사이드 HTML 렌더링
API 문서 Swagger (SpringDoc) 2.8.13 API 명세 자동 생성
XSS 방어 Lucy XSS Filter 2.0.1 웹 보안 (XSS 공격 차단)

2.2 기술 스택 다이어그램

┌──────────────────────────────────────────────────────────────┐
│                     프레젠테이션 계층                          │
│  Thymeleaf (HTML/CSS/JS)  │  REST API (JSON)  │  Swagger UI  │
└─────────────────────────────────────────────────────────────-┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                      보안 계층                                 │
│         Spring Security  │  JWT  │  Redis Session             │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                     비즈니스 로직 계층                          │
│  Spring MVC (Controller/Service)  │  AOP (권한체크/실행시간)   │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                     데이터 접근 계층                            │
│            MyBatis (Mapper + XML)  │  HikariCP                │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                     인프라 계층                                 │
│  Oracle DB  │  Redis  │  Kafka  │  Caffeine Cache             │
└──────────────────────────────────────────────────────────────┘

3. 전체 시스템 아키텍처

3.1 전체 구조 다이어그램

                        ┌─────────────────┐
                        │   사용자 브라우저   │
                        └────────┬────────┘
                                 │ HTTP 요청
                                 ▼
                   ┌─────────────────────────┐
                   │    Spring Boot WAS       │
                   │  (next-krx-lds-bat-mst) │
                   │                         │
                   │  ┌────────────────────┐ │
                   │  │  Spring Security   │ │◀── 모든 요청의 첫 번째 관문
                   │  │  (보안 필터 체인)   │ │
                   │  └────────┬───────────┘ │
                   │           │             │
                   │  ┌────────▼───────────┐ │
                   │  │    Controller       │ │◀── URL 라우팅
                   │  │  (REST / Web UI)   │ │
                   │  └────────┬───────────┘ │
                   │           │             │
                   │  ┌────────▼───────────┐ │
                   │  │     Service         │ │◀── 비즈니스 로직
                   │  └────────┬───────────┘ │
                   │           │             │
                   │  ┌────────▼───────────┐ │
                   │  │  Mapper (MyBatis)   │ │◀── DB 쿼리
                   │  └────────┬───────────┘ │
                   │           │             │
                   └───────────┼─────────────┘
                               │
              ┌────────────────┼──────────────────┐
              ▼                ▼                   ▼
      ┌──────────────┐  ┌───────────┐   ┌──────────────────┐
      │  Oracle DB   │  │   Redis   │   │      Kafka        │
      │  (AWS RDS)   │  │  (세션)   │   │  (메시지 브로커)  │
      └──────────────┘  └───────────┘   └────────┬─────────┘
                                                  │
                                         ┌────────▼─────────┐
                                         │  Worker 서버들    │
                                         │  (배치 실행)     │
                                         └──────────────────┘

3.2 URL 패턴별 처리 경로

사용자 요청
    │
    ├─ /api/**           → [1순위] ApiSecurityConfig → REST Controller → JSON 응답
    ├─ /common/api/**    → [1순위] ApiSecurityConfig → REST Controller → JSON 응답
    │
    ├─ /common/auth/**   → [2순위] WebSecurityConfig → Web Controller → HTML 응답
    ├─ /job/**           → [2순위] WebSecurityConfig → Web Controller → HTML 응답
    ├─ /calendar/**      → [2순위] WebSecurityConfig → Web Controller → HTML 응답
    ├─ /result/**        → [2순위] WebSecurityConfig → Web Controller → HTML 응답
    │
    ├─ /apidoc           → 인증 없이 접근 (Swagger 문서)
    ├─ /swagger-ui/**    → 인증 없이 접근 (Swagger UI)
    └─ /resources/**     → 인증 없이 접근 (CSS, JS, 이미지)

4. 레이어 아키텍처

4.1 패키지 구조 개요

ldsframework/
│
├─ common/                          ← 전사 공통 (보안, 설정, 유틸)
│   ├─ config/security/             ← Spring Security 핵심
│   ├─ config/jwt/                  ← JWT 토큰 관리
│   ├─ config/redis/                ← Redis 세션 설정
│   ├─ config/kafka/                ← Kafka 메시징 설정
│   ├─ config/batch/                ← 스케줄러/배치 설정
│   ├─ aop/                         ← 권한 체크 AOP
│   ├─ advice/                      ← 응답 래핑, 예외 처리
│   └─ utils/                       ← 공통 유틸
│
└─ batmst/                          ← 업무 로직 (배치 마스터)
    ├─ api/job/                     ← Job 관리 API
    ├─ api/calendar/                ← 캘린더 관리 API
    ├─ api/result/                  ← 실행 결과 API
    ├─ api/scheduler/               ← Quartz 스케줄러 API
    └─ web/                         ← 화면 라우팅 (UI Controller)

4.2 Controller → Service → Mapper 계층 구조

[HTTP 요청]
    │
    ▼
┌──────────────────────────────────────────────────────────────┐
│  Controller 계층                                              │
│  - @RestController : REST API 응답 (JSON)                     │
│  - @Controller     : 웹 화면 응답 (HTML)                      │
│  - URL 매핑 처리, 요청 파라미터 수신                           │
│  - 비즈니스 로직은 없고 Service에 위임만 함                    │
└──────────────────────┬───────────────────────────────────────┘
                       │ 위임
                       ▼
┌──────────────────────────────────────────────────────────────┐
│  Service 계층                                                 │
│  - 실제 비즈니스 로직 처리 (중복 체크, 유효성 검증 등)         │
│  - 트랜잭션 관리 (@Transactional)                             │
│  - 여러 Mapper를 조합하여 복잡한 로직 처리                    │
└──────────────────────┬───────────────────────────────────────┘
                       │ DB 조회 위임
                       ▼
┌──────────────────────────────────────────────────────────────┐
│  Mapper 계층 (MyBatis)                                        │
│  - DB와의 직접 통신 담당                                      │
│  - Java 인터페이스 + XML로 쿼리 분리                          │
│  - 동적 쿼리(if, choose, foreach 등)는 XML에서 처리           │
└──────────────────────┬───────────────────────────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │   Oracle DB     │
              └─────────────────┘

4.3 공통 응답 형식

모든 REST API 응답은 CommonResponseDto로 자동 래핑됩니다.

{
  "statusCode": "200",
  "statusMsg": "성공",
  "data": {
    /* 실제 데이터 */
  }
}

참고: @NoWrap 어노테이션이 붙은 컨트롤러/메서드는 래핑에서 제외됩니다.


5. Spring Security 인증/인가 구조

5.1 이중 보안 체계 개요

이 시스템은 두 가지 보안 체계를 동시에 운영합니다.

                   ┌─────────────────────────────────────────────┐
                   │             모든 HTTP 요청                   │
                   └─────────────────┬───────────────────────────┘
                                     │
                   ┌─────────────────▼───────────────────────────┐
                   │          URL 패턴으로 보안 체계 선택          │
                   └──────────┬────────────────────┬─────────────┘
                              │                    │
              /api/**         │                    │  그 외 URL
         /common/api/**       │                    │
                              ▼                    ▼
          ┌───────────────────────┐   ┌────────────────────────┐
          │   ApiSecurityConfig   │   │   WebSecurityConfig    │
          │       [Order=1]       │   │       [Order=2]        │
          │                       │   │                        │
          │  · JWT 토큰 기반 인증  │   │  · 세션 기반 인증       │
          │  · STATELESS (무상태)  │   │  · 동시 로그인 제한(1)  │
          │  · CSRF 비활성화       │   │  · 세션 ID 고정 방지   │
          └───────────────────────┘   └────────────────────────┘
항목 API 보안 (ApiSecurityConfig) 웹 보안 (WebSecurityConfig)
적용 URL /api/**, /common/api/** 나머지 모든 URL
인증 방식 JWT 토큰 (헤더) 세션 (쿠키)
상태 관리 Stateless (상태 없음) 세션 유지
동시 로그인 제한 없음 1개만 허용
처리 순서 1순위 2순위

5.2 API 인증 흐름 (JWT 기반)

5.2.1 API 로그인 흐름

[클라이언트]
     │
     │  POST /common/api/auth/login-processing
     │  Body: { "id": "user01", "password": "pass" }
     │
     ▼
┌──────────────────────────────────────────────────────────────────┐
│  ApiJsonLoginAuthenticationFilter                                 │
│  역할: JSON Body에서 아이디/패스워드를 꺼내서 인증 요청 생성       │
└──────────────────────────────┬───────────────────────────────────┘
                               │ authenticate()
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│  AuthenticationManager (ProviderManager)                          │
│  역할: 어떤 Provider에게 검증을 맡길지 결정                        │
└──────────────────────────────┬───────────────────────────────────┘
                               │
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│  CustomUserDetailsService                                         │
│  역할: DB에서 해당 아이디의 사용자 정보(패스워드, 권한 등) 조회    │
└──────────────────────────────┬───────────────────────────────────┘
                               │
               ┌───────────────┴──────────────┐
               ▼                              ▼
         [인증 성공]                      [인증 실패]
               │                              │
               ▼                              ▼
┌──────────────────────────┐   ┌──────────────────────────────────┐
│  ApiJsonLogin            │   │  ApiJsonLogin                    │
│  AuthenticationSuccess   │   │  AuthenticationFailure           │
│  Handler                 │   │  Handler                         │
│                          │   │                                  │
│  · Access Token 발급      │   │  · 401 에러 응답                 │
│  · Refresh Token 발급     │   │  · 오류 메시지 JSON 반환          │
│  · JSON으로 토큰 응답     │   └──────────────────────────────────┘
└──────────────────────────┘
               │
               ▼
[클라이언트에게 JWT 토큰 반환]
  {
    "accessToken": "eyJ...",   ← 1시간 유효
    "refreshToken": "eyJ..."   ← 24시간 유효
  }

5.2.2 API 인증된 요청 흐름

[클라이언트]
     │
     │  GET /api/read/jobs
     │  Header: Authorization: Bearer eyJ...
     │
     ▼
┌────────────────────────────────────────────────────────────────┐
│  ApiJwtAuthenticationFilter (JWT 검증 필터)                     │
│                                                                │
│  1단계: 헤더에서 JWT 토큰 추출                                  │
│    ├─ 토큰 없음 → 다음 필터로 통과 (후에 인가 단계에서 차단)    │
│    └─ 토큰 있음 → 2단계로                                       │
│                                                                │
│  2단계: 토큰 유효성 검증                                        │
│    ├─ 만료된 토큰 → 401 응답 (토큰 만료 안내)                   │
│    ├─ 위조된 토큰 → 401 응답 (잘못된 토큰 안내)                 │
│    └─ 유효한 토큰 → SecurityContextHolder에 인증정보 저장       │
└────────────────────────────────┬───────────────────────────────┘
                                 │ 인증 성공
                                 ▼
┌────────────────────────────────────────────────────────────────┐
│  ApiAuthorizationManager (인가 처리)                            │
│                                                                │
│  · SecurityContext에서 인증 정보 꺼내기                         │
│  · JWT 토큰에서 사용자 권한(ROLE) 확인                          │
│  · 요청 URL에 대한 접근 권한 판단                               │
│    ├─ 접근 허용 → Controller로 전달                             │
│    └─ 접근 거부 → 403 응답 (ApiAccessDeniedHandler)            │
└────────────────────────────────┬───────────────────────────────┘
                                 │
                                 ▼
                         [Controller 실행]

5.3 웹 화면 인증 흐름 (세션 기반)

5.3.1 웹 로그인 흐름

[브라우저]
     │
     │  POST /common/auth/login-processing
     │  Form: id=user01&password=pass
     │
     ▼
┌──────────────────────────────────────────────────────────────────┐
│  WebIdPasswordLoginAuthenticationFilter                           │
│  역할: form 파라미터(id, password)를 꺼내서 인증 토큰 생성         │
└──────────────────────────────┬───────────────────────────────────┘
                               │ authenticate()
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│  webAuthenticationManager (ProviderManager)                       │
│  → WebIdPasswordLoginAuthenticationProvider에게 검증 위임         │
└──────────────────────────────┬───────────────────────────────────┘
                               │
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│  WebIdPasswordLoginAuthenticationProvider                         │
│                                                                   │
│  1. CustomUserDetailsService로 DB에서 사용자 조회                 │
│  2. PasswordEncoder로 입력된 패스워드와 DB 패스워드 비교          │
│  3. 계정 상태 확인 (잠김/만료/비활성화 여부)                      │
└──────────────────────────────┬───────────────────────────────────┘
                               │
               ┌───────────────┴──────────────┐
               ▼                              ▼
         [인증 성공]                      [인증 실패]
               │                              │
               ▼                              ▼
┌──────────────────────────────┐   ┌──────────────────────────────┐
│  CompositeSession            │   │  WebIdPasswordLogin          │
│  AuthenticationStrategy      │   │  AuthenticationFailure       │
│  (3단계 세션 전략 순서 실행)  │   │  Handler                     │
│                              │   │  · 로그인 페이지로 리다이렉트  │
│  1. 동시 로그인 제한 체크     │   │  · 에러 메시지 전달           │
│     (기존 세션 만료 처리)     │   └──────────────────────────────┘
│                              │
│  2. 세션 ID 교체             │
│     (세션 고정 공격 방지)    │
│                              │
│  3. 새 세션을                │
│     SessionRegistry에 등록  │
└──────────────────────┬───────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────────────┐
│  WebIdPasswordLoginAuthenticationSuccessHandler                   │
│                                                                   │
│  1. Access Token 생성  (유효기간: 1시간)                          │
│  2. Refresh Token 생성 (유효기간: 24시간)                         │
│  3. Refresh Token을 DB에 저장 (갱신용)                            │
│  4. JWT Token을 세션에 저장                                       │
│     session.setAttribute("jwtToken", jwtToken)                   │
│  5. 기본 이동 URL로 리다이렉트 → /result                          │
└──────────────────────────────────────────────────────────────────┘

5.3.2 웹 세션 인증 상태 유지

[로그인 이후 화면 요청]
     │
     │  GET /job
     │  Cookie: JSESSIONID=abc123   ← 브라우저가 자동으로 전송
     │
     ▼
┌────────────────────────────────────────────────────────────────┐
│  Session 저장소 (Redis)                                         │
│  · JSESSIONID로 세션 검색                                       │
│  · 세션에서 SecurityContext 복원                                │
│  · 세션에서 jwtToken 복원                                       │
└────────────────────────────────┬───────────────────────────────┘
                                 │ 세션 유효
                                 ▼
┌────────────────────────────────────────────────────────────────┐
│  WebAuthorizationManager (인가 처리)                            │
│  · 사용자 권한(ROLE) 확인                                       │
│  · 요청 URL 접근 가능 여부 판단                                 │
└────────────────────────────────┬───────────────────────────────┘
                                 │ 접근 허용
                                 ▼
                         [Controller 실행]

5.4 인증/인가 컴포넌트 전체 지도

┌─────────────────────────────────────────────────────────────────────┐
│                    Spring Security 컴포넌트 지도                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [설정 클래스]                                                       │
│  ┌─────────────────────┐    ┌─────────────────────────────────────┐ │
│  │  ApiSecurityConfig  │    │  WebSecurityConfig                  │ │
│  │  Order=1            │    │  Order=2                            │ │
│  │  /api/**, /common/  │    │  나머지 모든 URL                     │ │
│  │  api/**             │    │                                     │ │
│  └─────────────────────┘    └─────────────────────────────────────┘ │
│                                                                     │
│  [필터 - 인증 처리]                                                  │
│  ┌───────────────────────────────────┐                             │
│  │  ApiJwtAuthentication             │  JWT 토큰 검증               │
│  │  Filter                           │  (API 요청마다 실행)          │
│  └───────────────────────────────────┘                             │
│  ┌───────────────────────────────────┐                             │
│  │  ApiJsonLoginAuthentication       │  API 로그인 처리              │
│  │  Filter                           │  (POST /common/api/auth/...) │
│  └───────────────────────────────────┘                             │
│  ┌───────────────────────────────────┐                             │
│  │  WebIdPasswordLoginAuthentication │  웹 로그인 처리               │
│  │  Filter                           │  (POST /common/auth/...)     │
│  └───────────────────────────────────┘                             │
│                                                                     │
│  [Provider - 패스워드 검증]                                          │
│  ┌───────────────────────────────────┐                             │
│  │  WebIdPasswordLoginAuthentication │  아이디/패스워드 검증         │
│  │  Provider                         │  (웹 로그인 전용)             │
│  └───────────────────────────────────┘                             │
│                                                                     │
│  [UserDetails - 사용자 정보]                                         │
│  ┌────────────────────────────────────────────────────────────┐   │
│  │  CustomUserDetailsService  →  CustomUserDetails            │   │
│  │  (DB에서 사용자 조회)          (id, name, role, password)    │   │
│  └────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  [Handler - 성공/실패 처리]                                          │
│  ┌────────────────────┐  ┌────────────────────────────────────┐   │
│  │  API 성공 핸들러    │  │  Web 성공 핸들러                    │   │
│  │  · JWT 토큰 발급    │  │  · JWT 발급 + 세션 저장             │   │
│  │  · JSON 응답       │  │  · /result 리다이렉트               │   │
│  └────────────────────┘  └────────────────────────────────────┘   │
│  ┌────────────────────┐  ┌────────────────────────────────────┐   │
│  │  API 실패 핸들러    │  │  Web 실패 핸들러                    │   │
│  │  · 401 JSON 응답   │  │  · 로그인 페이지 리다이렉트          │   │
│  └────────────────────┘  └────────────────────────────────────┘   │
│                                                                     │
│  [Authorization - 접근 권한 판단]                                    │
│  ┌─────────────────────────┐  ┌─────────────────────────────────┐ │
│  │  ApiAuthorizationManager│  │  WebAuthorizationManager        │ │
│  │  · JWT 토큰 유효성 확인  │  │  · 세션/권한 확인               │ │
│  │  · ROLE 권한 체크        │  │  · ROLE 권한 체크               │ │
│  └─────────────────────────┘  └─────────────────────────────────┘ │
│                                                                     │
│  [EntryPoint/Handler - 예외 처리]                                   │
│  ┌──────────────────────┐  ┌────────────────────────────────────┐ │
│  │  ApiAuthentication   │  │  WebAuthentication                 │ │
│  │  EntryPoint (401)    │  │  EntryPoint (401)                  │ │
│  │  · 미인증 JSON 응답   │  │  · 로그인 페이지 리다이렉트         │ │
│  └──────────────────────┘  └────────────────────────────────────┘ │
│  ┌──────────────────────┐  ┌────────────────────────────────────┐ │
│  │  ApiAccessDenied     │  │  WebAccessDenied                   │ │
│  │  Handler (403)       │  │  Handler (403)                     │ │
│  │  · 권한없음 JSON 응답  │  │  · 403 에러 페이지                 │ │
│  └──────────────────────┘  └────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

5.5 JWT 토큰 구조 및 갱신

┌───────────────────────────────────────────────────────────────────┐
│                        JWT 토큰 구조                               │
├───────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Access Token (유효기간: 1시간)                                    │
│  ┌──────────┐   ┌────────────────────────┐   ┌─────────────────┐ │
│  │  Header  │ . │       Payload          │ . │    Signature    │ │
│  │ alg, typ │   │ id, role, exp, iat ... │   │ (비밀키로 서명)  │ │
│  └──────────┘   └────────────────────────┘   └─────────────────┘ │
│                                                                   │
│  Refresh Token (유효기간: 24시간)                                  │
│  · Access Token 만료 시 새로운 Access Token 발급에 사용            │
│  · DB에 저장되어 관리됨                                           │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

[토큰 갱신 흐름]

클라이언트                              서버
    │                                    │
    │──── API 요청 (만료된 토큰) ────────▶│
    │                                    │
    │◀─── 401 (토큰 만료) ───────────────│
    │                                    │
    │──── POST /common/api/auth/refresh──▶│
    │     Refresh Token 전송              │  · DB에서 Refresh Token 검증
    │                                    │  · 유효하면 새 Access Token 발급
    │◀─── 새 Access Token 반환 ──────────│
    │                                    │
    │──── 새 토큰으로 API 재요청 ─────────▶│

6. Redis를 활용한 세션 관리

6.1 Redis 세션이 필요한 이유

[단일 서버 환경 (Redis 없음)]
┌──────────────────┐
│     WAS 서버      │
│  메모리에 세션 저장 │◀── 사용자 로그인
│  Session: {abc}  │
└──────────────────┘
→ 서버 1대일 때는 문제없음


[다중 서버 환경 (Redis 없으면 문제 발생)]
┌─────────────┐    ┌─────────────┐
│  WAS 서버1  │    │  WAS 서버2  │
│ Session:{abc}│    │ 세션 없음!  │◀── 같은 사용자가 서버2로 연결되면
└─────────────┘    └─────────────┘    세션을 찾지 못해 로그아웃됨!


[다중 서버 환경 (Redis 사용 - 이 시스템)]
┌─────────────┐    ┌─────────────┐
│  WAS 서버1  │    │  WAS 서버2  │
└──────┬──────┘    └──────┬──────┘
       └──────────┬────────┘
                  ▼
         ┌─────────────┐
         │    Redis    │◀── 세션을 중앙 저장소에 저장
         │ Session:{abc}│    어느 서버로 연결되든 동일한 세션 사용
         └─────────────┘

6.2 Redis 세션 설정

설정 항목 설명
세션 유효 시간 6,000초 (약 100분) 마지막 활동 후 자동 만료
적용 환경 real, dr, dev, test 운영/개발 환경에서만 적용
로컬 환경 미적용 로컬 개발 시 JVM 메모리 세션 사용

6.3 세션 생명주기

┌──────────────────────────────────────────────────────────────────┐
│                      세션 생명주기                                │
└──────────────────────────────────────────────────────────────────┘

[로그인]
  브라우저 → 서버
  서버: 세션 생성 (JSESSIONID 발급)
  서버: JWT 토큰을 세션에 저장
  서버 → 브라우저: JSESSIONID 쿠키 전달

     ↓

[이후 요청들]
  브라우저 → 서버 (쿠키에 JSESSIONID 자동 포함)
  서버: Redis에서 세션 조회
  서버: 세션에서 JWT 토큰 꺼내기
  서버: 인증된 사용자로 처리

     ↓

[동시 로그인 시도]
  다른 브라우저에서 같은 계정으로 로그인
  → 기존 세션 만료 처리
  → 기존 브라우저: 다음 요청 시 /common/auth/login?expired 로 이동

     ↓

[로그아웃]
  브라우저 → POST /common/auth/logout-processing
  서버: 세션 삭제 (Redis에서 제거)
  서버: JSESSIONID 쿠키 삭제
  서버 → 브라우저: 로그인 페이지 리다이렉트

     ↓

[세션 만료 (타임아웃)]
  마지막 요청 후 6,000초 경과
  Redis에서 자동 삭제
  다음 요청 시 → 로그인 페이지 이동

6.4 동시 로그인 제어 흐름

[User A: PC에서 로그인 중]
  Session: "session_A" → Redis에 저장됨
  SessionRegistry에 "session_A" 등록됨

[User A: 모바일에서 동일 계정으로 로그인 시도]
     │
     ▼
┌──────────────────────────────────────────────────────────────────┐
│  ConcurrentSessionControlAuthenticationStrategy                   │
│  · SessionRegistry에서 현재 "user_A"의 세션 목록 확인             │
│  · 기존 세션 "session_A"가 있으므로 → 만료 표시                   │
│    (session_A.expireNow() 호출)                                   │
└──────────────────────────────────────────────────────────────────┘
     │
     ▼
┌──────────────────────────────────────────────────────────────────┐
│  SessionFixationProtectionStrategy                                │
│  · 모바일 로그인용 새 세션 ID 생성 ("session_B")                  │
│  · 세션 고정 공격 방지                                            │
└──────────────────────────────────────────────────────────────────┘
     │
     ▼
모바일: 로그인 성공, "session_B" 사용
PC: 다음 요청 시 "session_A"가 만료됨 → /common/auth/login?expired 이동

7. 배치 마스터(Master-Agent) 동작 구조

7.1 전체 Job 실행 아키텍처

┌──────────────────────────────────────────────────────────────────────┐
│                    배치 마스터 Job 실행 아키텍처                       │
└──────────────────────────────────────────────────────────────────────┘

  [운영자/관리자]
       │
       │  Job 등록 / 스케줄 설정 (화면에서)
       ▼
┌──────────────────────────────────────────┐
│           배치 마스터 시스템               │
│                                          │
│  ┌──────────────────────────────────┐   │
│  │   DB (Oracle)                    │   │
│  │   · JOB 테이블 (작업 정보)        │   │
│  │   · CALENDAR 테이블 (캘린더)     │   │
│  │   · RESULT 테이블 (실행 결과)    │   │
│  └──────────┬───────────────────────┘   │
│             │ 앱 시작 시 전체 로드        │
│             ▼                           │
│  ┌──────────────────────────────────┐   │
│  │   SchedulerService               │   │
│  │   registerAllJobsToScheduler()   │   │
│  │   · DB에서 모든 Job 조회          │   │
│  │   · 각 Job을 Quartz에 등록        │   │
│  └──────────┬───────────────────────┘   │
│             │                           │
│    ┌────────┴─────────┐                 │
│    ▼                  ▼                 │
│  ┌─────────────┐  ┌─────────────┐      │
│  │ Calendar    │  │ Cron        │      │
│  │ JobHandler  │  │ JobHandler  │      │
│  │ (특정 날짜) │  │ (반복 실행) │      │
│  └──────┬──────┘  └──────┬──────┘      │
│         └────────┬────────┘             │
│                  ▼                      │
│  ┌──────────────────────────────────┐   │
│  │   Quartz Scheduler               │   │
│  │   ThreadPool: 10개               │   │
│  │   ShedLock: 중복 실행 방지        │   │
│  └──────────────────┬───────────────┘   │
│                     │ 트리거 발동         │
│          ┌──────────┴──────────┐        │
│          ▼                     ▼        │
│  ┌──────────────┐  ┌─────────────────┐  │
│  │ CalendarJob  │  │ CronJob         │  │
│  │ Executor     │  │ Executor        │  │
│  │ execute()    │  │ execute()       │  │
│  └──────┬───────┘  └────────┬────────┘  │
│         └──────────┬─────────┘           │
│                    ▼                     │
│  ┌─────────────────────────────────┐    │
│  │   SchedulerProducer             │    │
│  │   sendJobMessage()              │    │
│  │   → Kafka 토픽으로 메시지 발행   │    │
│  └─────────────────────────────────┘    │
└──────────────────────┬───────────────────┘
                       │
                       │ Kafka 메시지
                       ▼
         ┌─────────────────────────────┐
         │  Kafka Broker               │
         │  Topic: execute-cron-job    │
         │  Topic: execute-calendar-job│
         └──────────────┬──────────────┘
                        │
                        ▼
         ┌─────────────────────────────┐
         │  Worker 서버 (Consumer)      │
         │  실제 배치 작업 수행          │
         │  (파일 처리, DB 처리 등)      │
         └──────────────┬──────────────┘
                        │ 결과 저장
                        ▼
                  ┌───────────┐
                  │  Oracle   │
                  │  RESULT   │
                  │  테이블   │
                  └───────────┘

7.2 Job 타입별 동작 방식

Calendar 타입 Job

[개념] 특정 날짜에 실행하는 Job
       예) 매월 말일, 공휴일 제외한 영업일, 특정 날짜들

[등록 방식]
  1. 캘린더에 실행할 날짜들 등록
  2. Job에 해당 캘린더 연결
  3. Quartz: CalendarIntervalTrigger 또는 CustomCalendar로 스케줄 등록

[실행 흐름]
  DB의 캘린더 날짜 목록
      ↓
  CalendarJobHandler.registerJob()
      ↓
  Quartz Scheduler 등록
      ↓
  해당 날짜 도달 시 CalendarJobExecutor.execute() 호출
      ↓
  Kafka 메시지 발행 (execute-calendar-job 토픽)

Cron 타입 Job

[개념] Cron 표현식으로 반복 실행하는 Job
       예) 매일 오전 2시, 매주 월요일 오전 9시, 매시간 정각

[Cron 표현식 형식]
  ┌────────── 초 (0-59)
  │ ┌──────── 분 (0-59)
  │ │ ┌────── 시 (0-23)
  │ │ │ ┌──── 일 (1-31)
  │ │ │ │ ┌── 월 (1-12)
  │ │ │ │ │ ┌ 요일 (0-7, 0과 7은 일요일)
  │ │ │ │ │ │
  0 2 * * * *   → 매일 오전 2시 0분 0초 실행

[실행 흐름]
  DB의 Cron 표현식
      ↓
  CronJobHandler.registerJob()
      ↓
  Quartz Scheduler에 CronTrigger 등록
      ↓
  트리거 발동 시 CronJobExecutor.execute() 호출
      ↓
  Kafka 메시지 발행 (execute-cron-job 토픽)

7.3 ShedLock - 분산 환경 중복 실행 방지

[문제 상황: ShedLock 없을 때]

  서버 A: Quartz Trigger 발동 → Job 실행 시작
  서버 B: Quartz Trigger 발동 → Job 실행 시작  ← 같은 Job이 동시에 2번 실행!

[해결: ShedLock 사용]

  서버 A: Quartz Trigger 발동
          └─ ShedLock: DB에 Lock 획득 시도
              ├─ 성공 → Job 실행 (최대 10분 Lock 유지)
              └─ 실패 → Job 건너뜀

  서버 B: Quartz Trigger 발동
          └─ ShedLock: DB에 Lock 획득 시도
              └─ 실패 (A가 이미 Lock 보유) → Job 건너뜀

  결과: 어느 환경이든 Job은 딱 1번만 실행됨

7.4 Kafka를 통한 비동기 메시지 처리

┌────────────────────────────────────────────────────────────────────┐
│                    Kafka 메시지 흐름                                │
└────────────────────────────────────────────────────────────────────┘

Producer (이 시스템)                  Consumer (Worker 서버들)
        │                                        │
        │  Topic: execute-cron-job               │
        │─── {"jobId":"J001", "params":{...}} ──▶│
        │                                        │  Job 실행
        │  Topic: execute-calendar-job           │
        │─── {"jobId":"J002", "params":{...}} ──▶│
        │                                        │
        │  Topic: file-upload                    │
        │─── {"fileId":"F001", "path":"..."} ───▶│
        │                                        │
        │  Topic: push-message                   │
        │◀── {"type":"complete", "msg":"..."} ───│  작업 완료 알림
        │                                        │
        ▼                                        │
  SSE (Server-Sent Events)                      │
  → 브라우저로 실시간 푸시                        │

[Kafka 설정]
  · 자동 커밋: OFF (수동 커밋으로 메시지 유실 방지)
  · 최대 폴링: 10건 (한 번에 최대 10개 메시지 처리)
  · 오프셋 초기화: latest (가장 최신 메시지부터)

7.5 실시간 알림 (SSE)

[SSE 연결 수립]
  브라우저 → GET /common/sse/subscribe
  서버: SSE 연결 생성, SseEmitter 등록

[이벤트 발생 시]
  Worker 서버 → Kafka push-message 토픽에 메시지 발행
       ↓
  PushMessageKafkaConsumer 메시지 수신
       ↓
  SseEmitterService.sendMessageToClient()
       ↓
  브라우저: 실시간으로 알림 수신 (페이지 새로고침 없이)

8. 주요 화면 및 기능 설명

8.1 화면 구성도

┌──────────────────────────────────────────────┐
│                   화면 구조                   │
│                                              │
│  ┌────────────────────────────────────────┐  │
│  │           상단 네비게이션 (topbar)       │  │
│  └────────────────────────────────────────┘  │
│  ┌───────────┐  ┌──────────────────────────┐ │
│  │           │  │                          │ │
│  │  사이드바  │  │     메인 컨텐츠 영역       │ │
│  │ (sidebar) │  │                          │ │
│  │           │  │  · Job 목록              │ │
│  │  · 결과   │  │  · 검색 필터              │ │
│  │  · Job    │  │  · 데이터 테이블          │ │
│  │  · 캘린더  │  │  · 모달 창               │ │
│  │           │  │                          │ │
│  └───────────┘  └──────────────────────────┘ │
│  ┌────────────────────────────────────────┐  │
│  │               footer                   │  │
│  └────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘

8.2 주요 화면별 URL 및 기능

화면 URL 주요 기능
로그인 /common/auth/login 아이디/패스워드 로그인
메인 / 시스템 메인 화면
결과 조회 /result 배치 실행 결과 목록/상세
Job 관리 /job Job 등록/수정/삭제/조회
캘린더 관리 /calendar 실행 날짜 캘린더 관리

8.3 Job 관리 화면 기능

[Job 관리 화면 (/job)]

┌────────────────────────────────────────────────────────────────┐
│  검색 조건                                                      │
│  [Job명] [상태] [스케줄타입] [검색]                              │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│  Job 목록                                              [+ 등록] │
│  ┌────────┬───────────┬──────────┬────────┬──────────────────┐ │
│  │ Job ID │  Job명    │ 스케줄   │ 상태   │  작업            │ │
│  ├────────┼───────────┼──────────┼────────┼──────────────────┤ │
│  │ J001   │ 일일정산  │ CRON     │ 활성   │ [상세] [수정] [삭제] │
│  │ J002   │ 월말정산  │ CALENDAR │ 활성   │ [상세] [수정] [삭제] │
│  └────────┴───────────┴──────────┴────────┴──────────────────┘ │
└────────────────────────────────────────────────────────────────┘

[Job 등록 모달]
  · Job ID, Job명 입력
  · 스케줄 타입 선택 (CRON / CALENDAR)
  · CRON 선택 시: Cron 표현식 입력 (가이드 모달 제공)
  · CALENDAR 선택 시: 캘린더 선택
  · 실행 경로, 파라미터 설정

9. 환경별 구성

9.1 환경 종류

환경 Profile 용도 Redis 포트
로컬 local 개발자 로컬 개발 미사용 (JVM 세션) 80
개발 dev 통합 개발 환경 사용 -
테스트 test QA/테스트 환경 사용 -
DR dr 재해복구 서버 사용 -
운영 real 실제 운영 환경 사용 -

9.2 환경별 설정 파일 구조

src/main/resources/
├── application.yml                 ← 모든 환경 공통 설정
├── application-local.yml           ← 로컬 전용 설정
├── application-dev.yml             ← 개발 전용 설정
├── application-dr.yml              ← DR 전용 설정
├── application-real.yml            ← 운영 전용 설정
├── application-test.yml            ← 테스트 전용 설정
│
├── config-local/datasource.yml     ← 로컬 DB 접속 정보
├── config-dev/datasource.yml       ← 개발 DB 접속 정보
├── config-dr/datasource.yml        ← DR DB 접속 정보
├── config-real/datasource.yml      ← 운영 DB 접속 정보
└── config-test/datasource.yml      ← 테스트 DB 접속 정보

9.3 애플리케이션 실행 방법

# 로컬 환경 실행
./gradlew bootRun --args='--spring.profiles.active=local'

# 개발 환경 실행
java -jar app.jar --spring.profiles.active=dev

# 운영 환경 실행
java -jar app.jar --spring.profiles.active=real

9.4 주요 공통 설정 (application.yml)

설정 항목 설명
JWT Access Token 유효기간 3,600,000ms (1시간) API 인증 토큰
JWT Refresh Token 유효기간 86,400,000ms (24시간) 토큰 갱신용
Swagger UI 경로 /apidoc API 문서 접근 URL
MyBatis 쿼리 타임아웃 10초 DB 쿼리 최대 실행 시간
Kafka 최대 폴링 수 10건 1회에 처리할 메시지 수
ShedLock 최대 잠금 시간 10분 분산 락 최대 유지 시간

9.5 DB 커넥션 풀 설정 (HikariCP)

설정 항목 설명
최대 커넥션 수 60 동시 사용 가능한 최대 DB 연결
최소 유지 커넥션 20 항상 유지하는 최소 연결 수
커넥션 대기 시간 30초 커넥션 획득 최대 대기
커넥션 최대 수명 900초 (15분) 커넥션 재생성 주기
유휴 커넥션 만료 300초 (5분) 미사용 커넥션 반환 시간

10. 운영 관리 체크리스트

10.1 일상 운영 점검 항목

[ ] 1. 전일 배치 작업 실행 결과 확인
       → /result 화면에서 성공/실패 여부 확인
       → 실패한 Job이 있으면 원인 파악 및 재실행

[ ] 2. 스케줄러 동작 상태 확인
       → Quartz Scheduler 정상 구동 여부
       → 등록된 Job 수와 DB Job 수 일치 여부

[ ] 3. 로그 모니터링
       → 애플리케이션 로그 파일 확인
         (./log/krx-nlds-bat-mst.log)
       → ERROR 레벨 로그 발생 여부

[ ] 4. 서버 자원 모니터링
       → /actuator/health 엔드포인트 확인
       → DB 커넥션 풀 사용량
       → Kafka Consumer lag 확인

10.2 주요 API 엔드포인트 (Swagger)

개발/테스트 환경에서 /apidoc 접근 시 전체 API 목록 확인 가능

분류 주요 API 경로 설명
인증 POST /common/api/auth/login-processing API 로그인
인증 POST /common/api/auth/refresh 토큰 갱신
Job POST /api/create/job Job 등록
Job GET /api/read/jobs Job 목록 조회
Job POST /api/update/job Job 수정
Job POST /api/delete/job Job 삭제
캘린더 POST /api/create/calendar 캘린더 등록
캘린더 GET /api/read/calendars 캘린더 목록
결과 GET /api/read/results 실행 결과 조회
스케줄러 POST /api/scheduler/register 스케줄 수동 등록

10.3 보안 관련 주의사항

⚠️  운영 환경에서 반드시 확인할 사항

1. PERMIT_ALL_LIST에 "/**" 가 있으면 안 됨 (개발 편의용 설정)
   → ApiSecurityConfig.PERMIT_ALL_LIST 확인
   → WebSecurityConfig.PERMIT_ALL_LIST 확인

2. Swagger UI는 운영 환경에서 접근 제한 필요
   → /apidoc, /swagger-ui/**, /v3/api-docs/** 경로 차단 검토

3. JWT 비밀키는 환경별로 다르게 설정해야 함
   → application-*.yml의 jwt.secret 값 확인

4. HTTPS 적용 시 WebSecurityConfig의 HSTS 설정 활성화 필요
   → http.headers(headers -> headers
         .httpStrictTransportSecurity(hsts -> hsts
             .maxAgeInSeconds(31536000)
             .includeSubDomains(true)))

10.4 트러블슈팅 가이드

증상 원인 해결 방법
로그인 후 세션 유지 안 됨 Redis 연결 문제 Redis 서버 상태 및 접속 정보 확인
JWT 토큰 만료 오류 반복 Access Token 유효시간 Refresh Token으로 갱신 API 호출
Job이 두 번 실행됨 ShedLock DB 테이블 문제 ShedLock 테이블 상태 확인
특정 Job이 실행 안 됨 Quartz 스케줄 미등록 스케줄러 재시작 또는 수동 등록 API 호출
화면은 열리는데 API 오류 권한 설정 문제 사용자 ROLE 및 URL 권한 테이블 확인
Kafka 메시지 처리 지연 Consumer lag 증가 Consumer 서버 상태 및 토픽 파티션 확인

부록

A. 주요 클래스 파일 위치

클래스명 파일 경로
ApiSecurityConfig ldsframework/common/config/security/ApiSecurityConfig.java
WebSecurityConfig ldsframework/common/config/security/WebSecurityConfig.java
ApiJwtAuthenticationFilter ldsframework/common/config/security/filter/ApiJwtAuthenticationFilter.java
JwtTokenProvider ldsframework/common/config/jwt/JwtTokenProvider.java
RedisConfig ldsframework/common/config/redis/RedisConfig.java
SchedulerConfig ldsframework/common/config/batch/SchedulerConfig.java
KafkaConfig ldsframework/common/config/kafka/KafkaConfig.java
CustomUserDetails ldsframework/common/config/security/principal/CustomUserDetails.java

B. 개발 규칙 요약

규칙 내용
REST API URL 패턴 /api/{action}/{resource} (예: /api/create/job)
응답 형식 CommonResponseDto 자동 래핑 (예외: @NoWrap)
권한 체크 @RequirePermission(type = PermissionType.READ) AOP
쿼리 위치 단순 쿼리: @Mapper 어노테이션 / 동적 쿼리: XML 파일
검증 그룹 ValidGroups.Insert, ValidGroups.Update, ValidGroups.Delete
공통 예외 CustomRuntimeException 사용

╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ 배치 작업 등록 → 스케줄러 등록 → Agent 실행 지시 → 배치 실행 전체 흐름 ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PHASE 1 웹 화면을 통한 배치 작업(Job) 등록
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[운영자 브라우저] [jobCreateModal.js] [Spring Security] │ │ │ │ ① /job 화면 접속 │ │ │──────────────────────────────────────────────────────────────────▶│ │ │ │ WebSecurityConfig │ ② 작업 목록 화면 렌더링 │ │ 세션 인증 통과 │◀──────────────────────────────────────────────────────────────────│ │ │ │ ③ [작업 등록] 버튼 클릭 │ │──────────────────────────────────▶│ $('#jobCreateModal').modal('show') │ │ │ ④ 등록 모달 창 열림 │ │◀──────────────────────────────────│ │ │ ⑤ 폼 입력 (작업ID, 작업명, 시스템, 캘린더 유형, Cron 표현식 / 캘린더ID, 실행시간, 서버 등) │ │ ⑥ [저장] 버튼 클릭 │ │──────────────────────────────────▶│ │ │ payload 조립 │ │ { jobId, jobNm, jobSysId, grpNm, │ │ jobSchdlTpNm, jobCalndId (또는 cronExpress), │ │ batProgmExecuteTm, svrNm, │ │ jobExecutePath, strtDd, endDd, ... } │ │ │ │ ⑦ $.ajax POST /api/create/job │ │ Header: Authorization: Bearer {JWT} │ │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ │ API 보안 필터 체인 (ApiSecurityConfig) │ │ │ │ │ │ ⑧ ApiJwtAuthenticationFilter │ │ │ · 헤더에서 JWT 추출 │ │ │ · jwtTokenProvider.validateToken() → 서명/만료 검증 │ │ │ · jwtTokenProvider.getAuthentication() → 인증 정보 생성 │ │ │ · SecurityContextHolder에 저장 │ │ │ │ │ │ ⑨ ApiAuthorizationManager │ │ │ · SecurityContext에서 사용자 정보 조회 │ │ │ · JWT에서 ROLE(권한) 확인 → 접근 허용 판단 │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ 인증/인가 통과 │ ▼ │ [JobController] │ ⑩ POST /api/create/job │ │ │ ▼ │ [JobService.createJob()] │ ⑪ jobMapper.selectJobCountById(jobId) │ └─ 중복 ID 체크 │ │ │ ⑫ jobMapper.insertJob(jobDto) │ │ │ ▼ │ [Oracle DB - JOB 테이블] │ ⑬ INSERT INTO JOB (...) VALUES (...) │ │ │ │ 저장 성공 │ ◀─────┘ │ 성공 응답 반환 (200 OK) │◀──────────────────────────────────│ │ ⑭ alert("배치 작업이 정상적으로 │ │ 저장되었습니다.") │ │ 모달 닫기 │ │ JobSearch.reload() → 목록 새로고침 │

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ PHASE 2 Quartz Scheduler 등록 (POST /api/scheduler/register 호출 또는 애플리케이션 재기동 시 자동 실행) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[운영자 / 시스템] │ │ ① POST /api/scheduler/register │ ▼ [SchedulerController] │ ▼ [SchedulerService.registerAllJobsToScheduler()] │ ├──────────────────────────────────────────────────────────────────────────┐ │ CAL 타입 처리 │ CRON 타입 처리 ▼ ▼ [CalendarJobHandler.registerJob()] [CronJobHandler.registerJob()] │ │ │ ② jobMapper.selectExecutableJobs() ③ jobMapper.selectJobs(전체) │ → CAL 타입 Job 전체 조회 → 전체 Job 조회 │ │ │ ④ 등록 조건 4가지 동시 검사 ⑤ 등록 조건 2가지 검사 │ ┌─────────────────────────────┐ ┌──────────────────────────┐ │ │ isCalendarJob() │ │ isCronJob() │ │ │ jobSchdlTpNm == "CAL" │ │ jobSchdlTpNm == "CRON" │ │ ├─────────────────────────────┤ ├──────────────────────────┤ │ │ isUsableJob() │ │ isUsableJob() │ │ │ 오늘 >= strtDd │ │ 오늘 >= strtDd │ │ │ 오늘 <= endDd │ │ 오늘 <= endDd │ │ ├─────────────────────────────┤ └──────────────────────────┘ │ │ !isFollowJob() │ 모두 통과 시만 등록 │ │ 후행 Job이 아닌 경우 │ │ │ (대표 그룹 Job만 등록) │ │ ├─────────────────────────────┤ │ │ isRunTodayJob() │ │ │ 캘린더 SQL 실행 │ │ │ → 오늘 날짜가 결과에 포함 │ │ │ 되는지 DB 조회로 판별 │ │ └─────────────────────────────┘ │ 모두 통과 시만 등록 │ │ ⑥ JobDetail 생성 ⑥ JobDetail 생성 │ JobBuilder.newJob(CalendarJobExecutor.class) JobBuilder.newJob(CronJobExecutor.class) │ .withIdentity("[등록일시][실행일시][Job ID]", grpNm) .withIdentity("[등록일시][CRON][Job ID]", grpNm) │ .usingJobData("jobId", ...) .usingJobData("jobId", ...) │ .usingJobData("jobExecutePath", ...) .usingJobData("jobExecutePath", ...) │ .usingJobData("follwJobId", ...) .usingJobData("follwJobId", ...) │ .usingJobData("svrNm", ...) .usingJobData("svrNm", ...) │ │ ⑦ Trigger 생성 ⑦ Trigger 생성 │ TriggerBuilder TriggerBuilder │ .startAt(DateBuilder.todayAt(HH, mm, ss)) .withSchedule( │ → 오늘 특정 시각 1회 실행 CronScheduleBuilder.cronSchedule("0 2 * * *")) │ → 반복 일정으로 실행 │ │ ⑧ scheduler.scheduleJob(jobDetail, trigger) ⑧ scheduler.scheduleJob(jobDetail, trigger) │ → Quartz에 등록 완료 → Quartz에 등록 완료 │ → 다음 실행 시각(Date) 반환 → 다음 실행 시각(Date) 반환 │ │ ⑨ 오늘 실행 예정이면 ⑨ 오늘 실행 예정이면 │ schedulerMapper.insertJobExecutionInfo(...) schedulerMapper.insertJobExecutionInfo(...) │ → EXECUTION_INFO 테이블에 실행 예정 정보 저장 → EXECUTION_INFO 테이블에 실행 예정 정보 저장 │ └──────────────────────────────────────────────────────────────────────────┘ │ [Quartz Scheduler] 등록된 Job 대기 중 (Thread Pool: 10개)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ PHASE 3 스케줄 트리거 발동 → Agent에 실행 지시 (Kafka 메시지 발행) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Quartz Scheduler - 지정된 시각 도달] │ │ ① Trigger 발동 (지정 시각 또는 Cron 조건 충족) │ · ShedLock: DB Lock 획득 시도 │ ├─ 성공 → 계속 진행 (다른 서버에서는 Lock 획득 실패 → 실행 건너뜀) │ └─ 실패 → 이 서버에서 실행 중단 (중복 실행 방지) │ ├──────────────────────────────────────────────┐ │ CAL 타입 │ CRON 타입 ▼ ▼ [CalendarJobExecutor.execute()] [CronJobExecutor.execute()] │ │ │ ② context.getJobDetail() │ ② context.getJobDetail() │ → 등록 시 저장했던 데이터 꺼내기 │ → 등록 시 저장했던 데이터 꺼내기 │ │ │ ③ message 조립 │ ③ message 조립 │ { │ { │ key : jobDetail.key, │ key : jobDetail.key, │ jobId : "J001", │ jobId : "J001", │ jobExecutePath : "/batch/run/daily.sh", │ jobExecutePath : "/batch/run/daily.sh", │ follwJobId : "J002", ← 후행 Job ID │ follwJobId : "J002", │ svrNm : "REAL01" ← 실행 대상 서버 │ svrNm : "REAL01" │ } │ } │ │ └──────────────────────┬───────────────────────┘ │ ▼ [SchedulerProducer.sendJobMessage(topic, message)] │ │ ④ new ProducerRecord<>(topic, message) │ │ ⑤ kafkaTemplate.send(record) ← jsonKafkaTemplate 사용 (HashMap → JSON 직렬화) │ ├──────────────────────────────────────────────┐ │ CAL 타입 │ CRON 타입 ▼ ▼ Topic: "execute-calendar-job" Topic: "execute-cron-job" │ │ └──────────────────┬───────────────────────────┘ │ ▼ [Apache Kafka Broker] 메시지 저장 및 전달 대기 │ │ ⑥ 전송 성공/실패 콜백 │ whenComplete((result, ex) -> { │ 성공: log "offset=N" │ 실패: log "전송 실패" │ })

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ PHASE 4 Agent(Worker 서버)가 메시지 수신 → 배치 실행 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Apache Kafka Broker] │ │ ① Consumer가 토픽 폴링 (최대 10건씩, 수동 커밋) ▼ [Agent / Worker 서버 - Kafka Consumer] │ │ ② 메시지 역직렬화 (JSON → HashMap) │ { │ jobId : "J001", │ jobExecutePath : "/batch/run/daily.sh", │ follwJobId : "J002", │ svrNm : "REAL01" │ } │ │ ③ svrNm 기반으로 실행 대상 서버 식별 │ │ ④ jobExecutePath 기반으로 배치 프로그램 실행 │ ├──────────────────────────────────────────────────────────────────────┐ │ 선행 Job 성공 시 │ 실행 완료 후 ▼ ▼ [follwJobId 확인] [Oracle DB - RESULT 테이블] follwJobId != null 실행 결과 저장 → 후행 Job이 존재함 · jobId → 후행 Job을 이어서 실행 지시 · 실행 시작/종료 시각 (동일 토픽으로 후행 Job 메시지 발행) · 성공/실패 여부 · 오류 메시지 (실패 시) │ │ ⑤ (선택) Kafka push-message 토픽 │ → PushMessageKafkaConsumer │ → SseEmitterService │ → 브라우저 실시간 알림

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 전체 흐름 요약 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

운영자 │ 화면에서 Job 입력 → [저장] │ ▼ JWT 인증 → JobController → JobService → Oracle DB INSERT │ Job 데이터 저장 │ 스케줄러 등록 명령 (수동 or 재기동) │ │ ▼ ▼ Oracle DB에서 Job 목록 조회 SchedulerService │ │ 조건 검사 ├─ CAL Job → CalendarJobHandler (타입 / 유효기간 / 후행여부 / 오늘실행여부) │ → Quartz에 "오늘 HH:MM" 1회 트리거 등록 │ └─ CRON Job → CronJobHandler → Quartz에 "Cron 표현식" 반복 트리거 등록 │ 지정 시각 / Cron 조건 충족 │ ▼ ShedLock → DB Lock 획득 (중복 방지) │ ▼ CalendarJobExecutor / CronJobExecutor JobDetail 데이터 꺼내기 (jobId, jobExecutePath, svrNm, follwJobId) │ ▼ SchedulerProducer Kafka 메시지 발행 │ ┌──────────────────┴──────────────────┐ ▼ ▼ execute-calendar-job execute-cron-job └──────────────────┬──────────────────┘ │ ▼ Kafka Broker │ ▼ Agent (Worker 서버) 메시지 수신 → 배치 실행 후행 Job 있으면 연쇄 실행 │ ▼ 결과 DB 저장 + 실시간 알림 (SSE)

이 문서는 next-krx-lds-bat-mst 프로젝트의 아키텍처 기준으로 작성되었습니다.

About

adsdas

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors