Skip to content

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

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors