ERP 연동을 해본 사람은 안다. 기술적으로 어려운 게 아니라, 사람이 어렵다는 걸. SAP팀은 '우리 데이터 포맷에 맞춰라'고 하고, 우리 팀은 'REST API 표준을 따라야 한다'고 하고. 3개월간의 기술 개발보다 6개월간의 회의가 더 힘들었던, ERP 연동의 현실을 적어본다.

연동이 필요한 이유

ERP와 그룹웨어 연동이 필요한 대표적인 시나리오:

아키텍처 설계

미들웨어 방식 vs 직접 연동

직접 연동의 문제점:

따라서 미들웨어 방식을 채택했습니다:

ERP System ←→ Integration Middleware ←→ Groupware
                      ↓
              Staging Database
              (변환/검증/로깅)

배치 vs 실시간

데이터 특성에 따라 처리 방식을 구분합니다:

핵심 설계 패턴

1. Staging 테이블 활용

-- 원본 데이터 수신
CREATE TABLE stg_employee_sync (
    sync_id BIGINT PRIMARY KEY,
    emp_no VARCHAR(20),
    emp_name NVARCHAR(100),
    dept_code VARCHAR(20),
    position_code VARCHAR(20),
    raw_data NVARCHAR(MAX),  -- 원본 JSON 보관
    status VARCHAR(20),       -- RECEIVED, VALIDATED, PROCESSED, ERROR
    error_message NVARCHAR(500),
    created_at DATETIME2,
    processed_at DATETIME2
);

Staging 테이블의 장점:

2. 멱등성 보장

네트워크 장애로 재전송이 발생해도 중복 처리되지 않도록 합니다:

@Transactional
public void processEmployeeSync(EmployeeSyncDto dto) {
    // 이미 처리된 건인지 확인
    if (syncRepository.existsByTransactionId(dto.getTransactionId())) {
        log.info("Already processed: {}", dto.getTransactionId());
        return;
    }
    
    // 처리 로직
    Employee emp = employeeMapper.toEntity(dto);
    employeeRepository.upsert(emp);
    
    // 처리 완료 기록
    syncRepository.markAsProcessed(dto.getTransactionId());
}

3. 에러 처리 및 재시도

@Retryable(
    value = {TransientDataAccessException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 1000, multiplier = 2)
)
public void syncToGroupware(EmployeeDto dto) {
    groupwareClient.updateEmployee(dto);
}

@Recover
public void recoverSync(TransientDataAccessException e, EmployeeDto dto) {
    // Dead Letter Queue에 저장
    errorQueueService.enqueue(dto, e.getMessage());
    alertService.notify("Employee sync failed: " + dto.getEmpNo());
}

데이터 변환 전략

코드 매핑 테이블

ERP와 그룹웨어의 코드 체계가 다른 경우:

CREATE TABLE code_mapping (
    source_system VARCHAR(20),
    source_code VARCHAR(50),
    target_system VARCHAR(20),
    target_code VARCHAR(50),
    code_type VARCHAR(50),  -- DEPT, POSITION, etc.
    PRIMARY KEY (source_system, source_code, code_type)
);

데이터 검증

public class EmployeeValidator {
    public ValidationResult validate(EmployeeSyncDto dto) {
        List<String> errors = new ArrayList<>();
        
        if (StringUtils.isBlank(dto.getEmpNo())) {
            errors.add("사원번호 누락");
        }
        
        if (!deptRepository.existsByCode(dto.getDeptCode())) {
            errors.add("존재하지 않는 부서코드: " + dto.getDeptCode());
        }
        
        // 퇴직일이 입사일보다 빠른 경우
        if (dto.getResignDate() != null && 
            dto.getResignDate().isBefore(dto.getJoinDate())) {
            errors.add("퇴직일이 입사일보다 빠름");
        }
        
        return new ValidationResult(errors.isEmpty(), errors);
    }
}
현장에서 느낀 것: 그룹사 ERP 연동 프로젝트에 참여했을 때, 기술적인 구현보다 부서 간 데이터 표준을 맞추는 과정이 훨씬 오래 걸렸습니다. 부서코드 체계가 시스템마다 달라서 매핑 작업에 시간이 많이 소요되었고, 결국 ERP 연동의 진짜 난이도는 코드가 아니라 사람과 프로세스에 있다는 것을 느꼈습니다.

운영 고려사항

모니터링

장애 대응

ERP 연동, 결국 사람의 문제였다

시스템 연동은 기술적 구현보다 업무 흐름의 이해가 더 중요합니다. 현업 담당자와 긴밀하게 소통하고, 예외 상황에 대한 처리 방안을 사전에 정의하는 것이 성공의 핵심입니다.

Jaeseong
Jaeseong

10년차 풀스택 개발자. Spring Boot, Flutter, AI 등 실무 경험을 기록합니다.

GitHub →

💬 댓글