공공부하자개발 · 영어 학습 노트
자바
실무 확장Excel · 파일 업로드 · DB 연동0/22 완료
  • 01Excel(XLSX) 구조와 순수 JDK로 읽기/쓰기
  • 02Apache POI로 Excel 업로드/다운로드
  • 03파일 업로드/다운로드 서버 (HttpServer)
  • 04JDBC 기초와 트랜잭션 (H2)
  • 05MyBatis 어노테이션 매퍼로 쿼리 연동
  • 06MyBatis XML 매퍼 · Oracle 방언 · PageHelper · Spring Boot
  • 07REST API 서버와 JSON
  • 08Vue 3 SPA 와 Java 서버 연동
  • 09@Scheduled 운영
  • 10로깅 실무: 레벨·계층, MDC 추적, 예외·성능, 마스킹, 롤링, JSON 로그
  • 11외부 API 연동
  • 12테스트 실무
  • 13암호화·개인정보 보호
  • 14인코딩·한글 실무
  • 15@Transactional 심화
  • 16긴 작업 비동기 처리와 진행률
  • 17SFTP·FTP 파일 연계
  • 18로컬 캐시와 @Cacheable
  • 19메일·알림 발송
  • 20웹 보안 체크리스트
  • 21빌드 도구와 폐쇄망 의존성 반입
  • 22성능 측정: p50·p95·p99, 측정 계층, JFR, JMH 함정, 자체 부하 테스트, 병목 순위
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 실무 확장 › 16 / 22

긴 작업 비동기 처리와 진행률

202 Accepted, 작업 상태, 폴링·SSE, 취소, 중복 방지, 완전 종료
섹션 7진행 0 / 22
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

6. 연습 문제

문제 1. 작업 결과 보관 기간이 지나면 자동 삭제하는 기능 추가

JobManager 에 완료 시각을 기록하고, 일정 시간(예: 10분)이 지난 DONE·FAILED·CANCELLED 작업을 맵에서 제거하는 정리 스레드를 추가하세요. 힌트: ScheduledExecutorService 로 주기적으로 순회합니다.

java
private final ScheduledExecutorService cleaner =
        Executors.newSingleThreadScheduledExecutor();

// 생성자나 초기화 블록에서
cleaner.scheduleAtFixedRate(() -> {
    long now = System.currentTimeMillis();
    jobs.values().removeIf(j ->
            !isActive(j.status) && now - j.finishedAt > 10 * 60 * 1000);
}, 1, 1, TimeUnit.MINUTES);

Job 에 volatile long finishedAt 필드를 추가하고, run() 에서 DONE·FAILED·CANCELLED 로 상태를 바꿀 때마다 finishedAt = System.currentTimeMillis() 를 함께 기록해야 합니다.

문제 2. 같은 작업을 재시도하는 `POST /jobs/{id}/retry` 추가

FAILED 상태인 작업을 같은 owner·total 로 다시 제출하는 엔드포인트를 만드세요. FAILED 가 아니면 409 를 반환합니다.

java
static void handleRetry(HttpExchange ex, String id) throws IOException {
    JobManager.Job old = jobManager.get(id);
    if (old == null) { sendText(ex, 404, "no such job"); return; }
    if (!"FAILED".equals(old.status)) {
        sendText(ex, 409, "FAILED 상태만 재시도 가능: " + old.status);
        return;
    }
    JobManager.Job job = jobManager.submit(old.owner, old.total);
    sendJson(ex, 202, "{\"id\":\"" + job.id + "\",\"statusUrl\":\"/jobs/" + job.id + "\"}");
}

handleJobs 의 라우팅 분기에 p.length == 4 && "POST".equals(method) && "retry".equals(p[3]) 조건을 추가해 연결합니다.

문제 3. 큐가 가득 찼을 때 몇 건이 대기 중인지 상태에 포함하기

GET /jobs (전체 목록 없이) 대신, 서버 전체 대기열 크기를 보여주는 GET /queue-status 를 추가하세요. ThreadPoolExecutor 의 메서드를 활용합니다.

java
static void handleQueueStatus(HttpExchange ex) throws IOException {
    ThreadPoolExecutor pool = jobManager.poolForMonitoring();
    String body = "{\"active\":" + pool.getActiveCount()
            + ",\"queued\":" + pool.getQueue().size()
            + ",\"completed\":" + pool.getCompletedTaskCount() + "}";
    sendJson(ex, 200, body);
}

JobManager 에 pool 필드를 반환하는 poolForMonitoring() 같은 접근자를 하나 추가해야 호출할 수 있습니다. 운영에서는 이 값을 모니터링 대시보드나 액추에이터 지표로 노출합니다.

연습 문제
    이전 섹션5 자주 하는 실수 (Tip)6 / 7다음 섹션7 정리