지난 이야기다음엔 백업 테이블 구성을 진행해보고자 합니다.
다음을 기대해주세요ㅎㅎ
시스템 유효성 문제
원래는 Bulk Insert 백업 테이블을 먼저 구성하려 했습니다.. ㅎㅎ
하지만 어느날 사업소에서 제가 수정한 기능의 데이터가 제대로 들어오지 않는다는 소리를 들었습니다..
그래서 어떤 것이 문제가 있는지를 확인해보았습니다.
시스템 구성도
원인
담당자는 SAP에 차량 데이터를 입력해 저장한 뒤, 2차 작업을 위해 WEB에서 작업 직전 차량을 수신하게되는데 이때 차량 수신을 하는 과정에서 문제가 발생한 건입니다.
로그 분석
문제를 해결하기위해 로그를 분석하여 보았습니다.
그랬더니 ORA-00001: unique constraint primary key violated라는 에러가 나왔습니다.
흠.. SAP에 있는 데이터를 그대로 수신받는 것에도 불구하고 에러가 발생한 것을 보면 수신 받는 데이터들이 문제가 있다고 판단했습니다.
테스트 진행
로그 분석 내용을 토대로 테스트를 진행했습니다.
테스트 진행은 JUnit4에 SAP Connection을 직접 연결한 뒤 RFC로 전송 받은 결과 중 WEB DB의 PK에 해당하는 데이터를 Map<String,Boolean>으로 받아 중복된 결과가 있는지를 확인했습니다.
이외에 필수 제약 조건으로 설정된 컬럼들 중 일부가 누락되었음을 테스트를 통해 찾을 수 있었습니다.
@Test(expected=CustomException.class)
public void SAP연결후_중복유효성검사(){
Map<String,Object> param = new Parameter();
Map<String,Boolean> 중복된차량 = new HashMap<>();
...
// SAP Function get
JCoFunction function = ...;
if(function != null) {
// SAP RFC return table
JCoTable table = ...;
for (int i=0; i < retTableRowCnt; i++, retTable.nextRow()) {
String pk컬럼 = table.getString("pk컬럼");
validatePrimaryKey(중복된차량, pk컬럼);
}
}
}
public void validatePrimaryKey(Map<String,Boolean> 중복된차량, String 차량){
if(중복된차량.get(차량) == null){
중복된차량.put(차량, true);
return;
}
Boolean 중복됐나요 = 중복된차량.get(차량);
if(중복됐나요){
throw new CustomException(오류종류);
}
}
@Test(expected=CustomException.class)
public void SAP연결후_빈값검사(){
Map<String,Object> param = new Parameter();
...
// SAP Function get
JCoFunction function = ...;
if(function != null) {
// SAP RFC return table
JCoTable table = ...;
for (int i=0; i < retTableRowCnt; i++, retTable.nextRow()) {
String 필수제약컬럼 = table.getString("필수제약컬럼");
processException(필수제약컬럼, 오류종류);
}
}
}
public void processException(String 데이터, ... 오류종류){
if(StringUtils.isBlank(데이터)){
throw new CustomException(오류종류);
}
}
원인 파악
결과적으로는 중복된 데이터가 빠져 있다는 것과 필수 컬럼들이 누락되었음을 확인했습니다.
이러한 저의 판단이 맞는지 확인하기 위해 SAP 담당자에게 WEB DB의 PK와 제약조건에 해당하는 컬럼들의 리스트를 만들어 공유해 확인했고, 결론적으로는 해당 데이터들이 문제가 발생되고 있음을 확신할 수 있었습니다.
시스템 개선 방향
입력할 데이터의 유효성 검증은 Controller에서 진행하였으며, 진행 후 문제가 발생하면 CustomException을 발생시켜 @ControllerAdvice에서 에러 내용을 페이지로 전달했습니다.
@ControllerAdvice 사용 이유
조치 전 문제가 발생하면 항상 오류 페이지로 전환돼 에러가 발생했습니다.
오류 페이지로만 전환이 되다보니 개발자들은 어떤 부분에서 에러가 발생하는지 명확히 알 수 없다는 문제가 있었습니다.
이에 좀 더 명확한 오류와 문제 원인을 표현할 필요가 있다 싶어 CustomException을 만들었고, Exception의 내용을 처리할 수 있는 @ControllerAdvice를 만들게 되었습니다.
개선된 코드
CustomException
public class CustomException extends RuntimeException{
public CustomException(ErrorType message){
super(message.getValue());
}
}
@ControllerAdvice
@ControllerAdvice
public class CustomExceptionHandler{
private final Map<String, Object> errorResponse = new Parameter();
@ExceptionHandler(CustomException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Map<String, Object> handleValidationException(CustomException exception) {
errorResponse.put("message", exception.getMessage());
return errorResponse;
}
}
아쉬운 점
1. List를 이용해 stream distinct로 중복 검사를 확인하고 싶었지만 jdk-1.7이여서 사용할 수 없었습니다..^^;;
2. 코드의 일관성을 위해 에러 처리를 Map으로 return하는 방법을 사용했지만 DTO를 활용했다면 명확한 컬럼 정의 및 문법 검사가 가능했을 것 같습니다.
3. 이외의 아쉬운 점이 있다면 알려주시면 감사하겠습니다.
다음 이야기
다음엔 이전에 하지 못한 백업 테이블 구성을 하려합니다.
다음을 기대해주세요^^
'Project&기능개선 > Spring&MyBatis' 카테고리의 다른 글
Bulk Insert를 통한 기능 개선 사례 #1 (0) | 2023.07.25 |
---|