14 KiB
14 KiB
ElinkTransactionContext 아키텍처
개요
ElinkTransactionContext는 트랜잭션(거래) 범위에서 컨텍스트 변수를 관리하기 위한 ThreadLocal 기반 유틸리티입니다. JEP 함수 등 EAIMessage나 Properties에 직접 접근할 수 없는 클래스에서 트랜잭션 범위 변수에 접근할 수 있도록 합니다.
핵심 설계 원칙
- ThreadLocal이 직접 저장소: EAIMessage의 Map을 참조하지 않고 ThreadLocal에 직접 저장
- Lazy Initialization: 최초 put 시 Map 자동 생성 (명시적 초기화 불필요)
- clear()는 필수: 스레드 풀 환경에서 메모리 누수 방지
- 비동기 전달용 필드 분리:
transactionContextTransfer는 JMS 전달 전용
구성 요소
1. ElinkTransactionContext (저장소)
// ElinkTransactionContext.java (elink-online-core)
public class ElinkTransactionContext {
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>();
// 컨텍스트 정리 (필수)
public static void clear();
// 비동기 전달용
public static Map<String, Object> getAll(); // 복사본 반환
public static void restore(Map<String, Object> ctx); // 복원
// 명시적 접근자 (Lazy init - 최초 put 시 Map 생성)
public static void setSeedSalt(String seedSalt);
public static String getSeedSalt();
public static void setEncryptionKey(String encryptionKey);
public static String getEncryptionKey();
}
- 위치:
elink-online-core - 역할: ThreadLocal 기반 트랜잭션 변수 저장소
- 특징:
- 명시적 초기화 불필요 (Lazy init)
clear()는 필수 (finally 블록에서 호출)
2. EAIMessage.transactionContextTransfer (전달용)
// EAIMessage.java (elink-online-common)
/**
* [주의] 비동기 JMS 전달 전용 - 직접 사용 금지!
*/
private Map<String, Object> transactionContextTransfer;
public Map<String, Object> getTransactionContextTransfer();
public void setTransactionContextTransfer(Map<String, Object> ctx);
- 위치:
elink-online-common - 역할: 비동기 거래 시 JMS를 통한 컨텍스트 전달
- 특징:
Serializable- ObjectMessage로 직렬화- 직접 데이터 저장 용도로 사용 금지
- 복원 후 즉시 null로 초기화
처리 흐름
동기 (Sync) 처리
┌─────────────────────────────────────────────────────────────────────────┐
│ ApiAdapterController.callApi() │
│ │ │
│ ├─ try { │
│ │ service.callApi() │
│ │ └─ RequestProcessor.hookup() │
│ │ │ │
│ │ ├─ [컨텍스트 사용 가능 - Lazy Init] ───────────────── │
│ │ │ ElinkTransactionContext.setSeedSalt("value") │
│ │ │ └─ 최초 호출 시 Map 자동 생성 │
│ │ │ │
│ │ ├─ FlowRouter.process() │
│ │ │ └─ ElinkESBProcessProxy → Process 호출 │
│ │ │ │
│ │ ├─ [반환 후에도 컨텍스트 사용 가능] ✓ │
│ │ │ │
│ │ └─ 응답 반환 │
│ │ } │
│ └─ finally { │
│ ElinkTransactionContext.clear() ← 최상위 진입점에서 해제 │
│ } │
└─────────────────────────────────────────────────────────────────────────┘
비동기 (Async) 처리 - Queue 사용
┌─────────────────────────────────────────────────────────────────────────┐
│ [송신 스레드] │
│ │
│ ApiAdapterController.callApi() │
│ │ │
│ ├─ try { │
│ │ RequestProcessor.hookup() │
│ │ ├─ ElinkTransactionContext.setSeedSalt("value") │
│ │ │ └─ ThreadLocal에 직접 저장 │
│ │ │ │
│ │ └─ FlowRouter.process() │
│ │ ├─ [컨텍스트 복사] ───────────────────────────────────── │
│ │ │ eaiMessage.setTransactionContextTransfer( │
│ │ │ ElinkTransactionContext.getAll() │
│ │ │ ) │
│ │ └─ JMSSender.sendToQueue(eaiMessage, ...) │
│ │ └─ EAIMessage 직렬화 (transactionContextTransfer 포함)│
│ │ } │
│ └─ finally { ElinkTransactionContext.clear() } │
│ │ │
└──────────────────────────┼──────────────────────────────────────────────┘
│
[JMS Queue]
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ [수신 스레드 - Consumer] │
│ │
│ FCQueueReceiver / DefaultQueueConsumer │
│ │ │
│ ├─ EAIMessage 역직렬화 (transactionContextTransfer 복원) │
│ │ │
│ └─ ElinkESBProcessProxy.callElinkESBProcess(BaseFCProcess, ...) │
│ │ │
│ ├─ [조건 충족: ASYNC && !outbound] │
│ │ ├─ ElinkTransactionContext.restore( │
│ │ │ eaiMessage.getTransactionContextTransfer() │
│ │ │ ) │
│ │ └─ eaiMessage.setTransactionContextTransfer(null) ← 즉시 초기화
│ │ │
│ ├─ BaseFCProcess.callService() │
│ │ └─ ElinkESBProcessProxy(..., RESTProcess, ...) │
│ │ └─ [조건 미충족: outbound] → 복원 안함 │
│ │ └─ RESTProcess 호출 │
│ │ └─ ElinkTransactionContext.getSeedSalt() │
│ │ └─ 송신 스레드에서 설정한 값 조회 ✓ │
│ │ │
│ └─ finally { ElinkTransactionContext.clear() } │
└─────────────────────────────────────────────────────────────────────────┘
초기화/해제 위치
| 위치 | 역할 | 비고 |
|---|---|---|
ApiAdapterController.callApi() |
해제 (finally) | 동기 - 최상위 진입점 |
RequestProcessorSupport.execute() |
해제 (finally) | 동기 - 안전망 (이중 해제) |
FlowRouter.process() |
컨텍스트 복사 | 비동기 송신 전 |
ElinkESBProcessProxy.callElinkESBProcess() |
복원/해제 (조건부) | 비동기 수신 시 |
ElinkESBProcessProxy 복원 조건
boolean shouldManageContext = eaiMessage != null
&& EAIMessageKeys.ASYNC_SVC.equals(eaiMessage.getSvcTsmtUsgTp()) // ASYNC 거래
&& (serviceURI == null || !serviceURI.startsWith("com.eactive.eai.outbound")); // outbound가 아닐 때
if (shouldManageContext) {
ElinkTransactionContext.restore(eaiMessage.getTransactionContextTransfer());
eaiMessage.setTransactionContextTransfer(null); // 복원 후 즉시 초기화
}
- ASYNC 거래: 동기 거래는 Lazy init으로 자동 처리
- outbound가 아닐 때: BaseFCProcess 호출 시에만 복원, RESTProcess 호출 시에는 이미 복원된 상태
Note
:
clear()는 멱등(idempotent)하므로 여러 번 호출되어도 안전합니다.
사용 예시
값 설정 (RequestProcessor, DefaultProcess 등)
// SeedSalt 설정 (최초 호출 시 Map 자동 생성)
ElinkTransactionContext.setSeedSalt("o123y498172");
// EncryptionKey 설정
ElinkTransactionContext.setEncryptionKey("myKey123");
값 조회 (JEP 함수, Transform 등)
// SeedSalt 조회
String seedSalt = ElinkTransactionContext.getSeedSalt();
// EncryptionKey 조회
String encryptionKey = ElinkTransactionContext.getEncryptionKey();
변수 추가 방법
새로운 트랜잭션 변수가 필요한 경우 ElinkTransactionContext에 접근자 메소드 추가:
// ElinkTransactionContext.java
// 1. Key 상수 추가
private static final String KEY_NEW_VARIABLE = "NEW_VARIABLE";
// 2. Setter 추가 (private put 헬퍼 사용)
public static void setNewVariable(String value) {
put(KEY_NEW_VARIABLE, value);
}
// 3. Getter 추가 (private get 헬퍼 사용)
public static String getNewVariable() {
return get(KEY_NEW_VARIABLE);
}
주의사항
Thread Pool 환경
ApiAdapterController.callApi()finally에서clear()호출ElinkESBProcessProxy.callElinkESBProcess()finally에서 조건부clear()호출- Thread 재사용 시 이전 트랜잭션 데이터 누수 방지
비동기 Queue 처리
EAIMessage.transactionContextTransfer는 전달 전용 (직접 접근 금지)FlowRouter에서 송신 전getAll()로 복사ElinkESBProcessProxy에서restore()후 즉시 null 초기화
transactionContextTransfer 직접 사용 금지
// ❌ 잘못된 사용
eaiMessage.getTransactionContextTransfer().put("key", "value");
// ✓ 올바른 사용
ElinkTransactionContext.setSeedSalt("value");
관련 파일
| 파일 | 위치 | 역할 |
|---|---|---|
ElinkTransactionContext.java |
elink-online-core |
ThreadLocal 컨텍스트 저장소 |
EAIMessage.java |
elink-online-common |
transactionContextTransfer (전달용) |
ApiAdapterController.java |
eapim-online |
해제 (동기 최상위 진입점) |
FlowRouter.java |
elink-online-common |
비동기 송신 전 컨텍스트 복사 |
ElinkESBProcessProxy.java |
elink-online-common |
복원/해제 (비동기 - 조건부) |
RequestProcessorSupport.java |
elink-online-common |
해제 (동기 안전망) |
클래스 다이어그램
┌─────────────────────────────────────┐
│ elink-online-core │
├─────────────────────────────────────┤
│ ElinkTransactionContext │
│ ┌───────────────────────────────┐ │
│ │ ThreadLocal<Map<String,Object>>│◄── 직접 저장소 (Lazy Init)
│ └───────────────────────────────┘ │
│ + clear() │
│ + getAll() → Map 복사본 │
│ + restore(Map) │
│ + setSeedSalt() / getSeedSalt() │
│ + setEncryptionKey() / ... │
└───────────────┬─────────────────────┘
│ 비동기 전달 시
│ getAll() → 복사
▼
┌─────────────────────────────────────┐
│ elink-online-common │
├─────────────────────────────────────┤
│ EAIMessage │
│ - transactionContextTransfer │◄── JMS 전달 전용 (직접 사용 금지!)
│ : Map<String,Object> │ Serializable
│ │
│ [송신] FlowRouter에서 설정 │
│ [수신] ElinkESBProcessProxy에서 │
│ restore() 후 null 초기화 │
└─────────────────────────────────────┘