스프링 트랜잭션 환경에서의 데이터 가시성 문제와 해결책
데이터베이스에 데이터를 삽입한 직후, 해당 데이터를 조회하는 로직을 실행할 때 방금 삽입한 데이터를 읽지 못하는 문제가 발생할 수 있습니다. 이는 스프링의 트랜잭션 격리 수준과 전파 방식 때문에 발생합니다.
문제 발생 원인 분석
기본적으로 스프링의 트랜잭션 관리자는 데이터베이스의 기본 격리 수준을 따르며, 대부분의 RDBMS는 'Read Committed'를 사용합니다. 동일한 트랜잭션 컨텍스트 내에서 삽입(Insert) 작업이 수행되고 커밋되기 전에는, 해당 데이터를 다른 트랜잭션이나 동일한 트랜잭션 내의 특정 뷰(View)에서 조회할 수 없는 경우가 있습니다.
특히, 데이터 삽입 메서드와 데이터 조회 메서드가 동일한 트랜잭션에 묶여 있을 때, 삽입된 데이터가 아직 커밋되지 않았기 때문에 조회 메서드에서 최신 데이터를 반영하지 못하는 현상이 발생합니다.
해결 전략: 비동기 스레드와 새로운 트랜잭션 전파
이 문제를 해결하기 위해 데이터 삽입 후 별도의 스레드를 생성하고, 해당 스레드에서 새로운 트랜잭션을 시작하도록 설정합니다. 스레드에 약간의 지연(Delay)을 두어 원래의 트랜잭션이 안전하게 커밋되기를 기다린 후, 새로운 트랜잭션에서 데이터를 조회하도록 합니다.
Java 구현 예시
다음은 비동기 스레드를 활용하고 @Transactional의 전파 속성을 REQUIRES_NEW로 설정하여 데이터 가시성을 확보하는 코드입니다.
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.ArrayList;
public class DeviceService {
// 데이터 삽입 및 비동기 작업 트리거 메서드
public void registerDeviceAndTriggerQrGeneration(String deviceId, String zoneCode) {
final String operatorId = UserContext.getCurrentUserId();
final String operatorName = UserContext.getCurrentUserName();
final List<String> targetDeviceIds = new ArrayList<>();
final List<String> targetZoneCodes = new ArrayList<>();
targetDeviceIds.add(deviceId);
targetZoneCodes.add(zoneCode);
// 특정 조건에 해당하는 경우에만 비동기 작업 실행
if (!"RESTRICTED_ZONE".equals(zoneCode)) {
new Thread(() -> {
try {
// 기존 트랜잭션이 커밋될 시간을 확보하기 위해 지연
Thread.sleep(3000);
// 새로운 트랜잭션에서 실행될 메서드 호출
qrCodeService.generateDeviceQrCodes(targetDeviceIds, targetZoneCodes, operatorId, operatorName);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("스레드 대기 중 인터럽트 발생: " + e.getMessage());
} catch (Exception e) {
System.err.println("QR 코드 생성 중 오류 발생: " + e.getMessage());
}
}).start();
}
}
}
// QR 코드 생성 서비스
public class QrCodeService {
// 새로운 트랜잭션을 강제하여 커밋된 데이터만 읽도록 설정
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void generateDeviceQrCodes(List<String> deviceIds, List<String> zoneCodes, String opId, String opName) throws Exception {
// 커밋된 디바이스 정보를 조회하여 QR 코드를 생성하는 비즈니스 로직
// ...
}
}
자바스크립트에서 문자열 분할 및 배열 요소 존재 여부 확인
프론트엔드 작업 시, 쉼표로 구분된 문자열을 배열로 변환한 후 특정 요소가 포함되어 있는지 확인해야 하는 경우가 많습니다. 자바스크립트의 split과 includes 메서드를 사용하면 이를 간단하게 처리할 수 있습니다.
// 쉼표로 구분된 디바이스 코드 문자열
const deviceCodesString = 'DEV-001,DEV-002,DEV-003';
// 문자열을 배열로 변환
const deviceCodesArray = deviceCodesString.split(',');
// 특정 디바이스 코드가 배열에 포함되어 있는지 확인
const targetCode = 'DEV-002';
if (deviceCodesArray.includes(targetCode)) {
console.log(`배열에 ${targetCode} 요소가 포함되어 있습니다.`);
// 해당 디바이스에 대한 추가 로직 수행
} else {
console.log(`배열에 ${targetCode} 요소가 포함되어 있지 않습니다.`);
}