공공부하자개발 · 영어 학습 노트
자바
실무 확장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정리‹ 이전다음 ›

3. 코드 예제

실행 방법입니다. 외부 jar 는 필요 없습니다.

powershell
cd java-src\extension\16_async_job
javac -encoding UTF-8 *.java
java Main --demo   # 7단계 시나리오 자동 실행, 약 2초

인자 없이 java Main 만 실행하면 18086 포트에 서버만 띄우고 대기합니다.

JobManager — 상태·진행률·중복 방지·협력적 취소

java
public synchronized Job submit(String owner, int total) {
    String activeId = activeOwner.get(owner);
    if (activeId != null) {
        Job active = jobs.get(activeId);
        if (active != null && isActive(active.status)) {
            throw new IllegalStateException("이미 실행 중: " + activeId);
        }
    }
    Job job = new Job(owner, total);
    jobs.put(job.id, job);
    activeOwner.put(owner, job.id);
    pool.execute(() -> run(job)); // 큐 초과 시 RejectedExecutionException
    return job;
}

private void run(Job job) {
    job.status = "RUNNING";
    for (int i = 0; i < job.total; i++) {
        if (job.cancelRequested) {
            job.status = "CANCELLED";
            return;
        }
        // 실제로는 여기서 엑셀 한 행, 발송 한 건을 처리한다
        job.progress.incrementAndGet();
    }
    job.status = "DONE";
}

submit 은 synchronized 로 감싸 같은 owner 의 중복 확인과 등록 사이에 다른 요청이 끼어들지 못하게 합니다. run 은 매 반복마다 취소 플래그를 확인해 협력적으로 멈춥니다.

Main — 202 제출, 상태 조회, SSE, 취소, 결과 다운로드

java
static void handleSubmit(HttpExchange ex) throws IOException {
    Map<String, String> q = parseQuery(ex.getRequestURI().getRawQuery());
    try {
        JobManager.Job job = jobManager.submit(
                q.getOrDefault("owner", "anon"),
                Integer.parseInt(q.getOrDefault("total", "10")));
        sendJson(ex, 202, "{\"id\":\"" + job.id + "\",\"statusUrl\":\"/jobs/" + job.id + "\"}");
    } catch (IllegalStateException dup) {
        sendJson(ex, 409, "{\"error\":\"" + esc(dup.getMessage()) + "\"}");
    } catch (RejectedExecutionException full) {
        sendJson(ex, 503, "{\"error\":\"작업 큐가 가득 찼습니다\"}");
    }
}

SSE 엔드포인트는 sendResponseHeaders(200, 0) 으로 길이를 0 으로 줘서 청크 전송을 사용하고, 상태가 끝날 때까지 50ms 간격으로 진행률을 흘려보냅니다.

java
ex.getResponseHeaders().add("Content-Type", "text/event-stream; charset=UTF-8");
ex.getResponseHeaders().add("X-Accel-Buffering", "no");
ex.sendResponseHeaders(200, 0);
OutputStream os = ex.getResponseBody();
while (true) {
    os.write(("data: " + jobJson(job) + "\n\n").getBytes(StandardCharsets.UTF_8));
    os.flush();
    if (끝난_상태) break;
    Thread.sleep(50);
}
os.close();

데모 실행 결과

--demo 로 실행하면 서버를 띄운 뒤 HttpClient 로 7단계를 자동 실행하고 스스로 종료합니다.

text
[1] 제출 -> 202 {"id":"4f1945d4","statusUrl":"/jobs/4f1945d4"}
[2] 폴링 1 -> {"status":"RUNNING","processed":7,"total":15,...}
[3] SSE data: {"status":"DONE","processed":15,"total":15,...}   ← 마지막 이벤트
[4] 즉시 취소 후 상태 -> {"status":"CANCELLED","processed":0,...}
[5] 중복 제출 -> 409 {"error":"이미 실행 중인 작업이 있습니다: ..."}
[6] 결과 다운로드 -> OK: kim 작업 15건 처리 완료 (jobId=4f1945d4)
[7] 데모 종료, 서버 완전 종료 진행
서버 완전 종료 완료

수동으로 확인하려면 서버를 띄운 뒤 아래처럼 호출합니다.

text
curl -X POST "http://localhost:18086/jobs?owner=kim&total=20"
curl "http://localhost:18086/jobs/{id}"
curl -N "http://localhost:18086/jobs/{id}/events"

예제 직접 실행

아래 폴더를 JDK 21 로 컴파일하고 실행합니다. 외부 jar 를 쓰는 레슨은 java-src/lib 를 클래스패스에 넣습니다.

cd java-src\extension\16_async_job
javac -encoding UTF-8 *.java && java Main

:: 외부 jar 가 필요한 레슨
javac -encoding UTF-8 -cp "..\..\lib\*;." *.java && java -cp "..\..\lib\*;." Main
코드 예제
  • JobManager — 상태·진행률·중복 방지·협력적 취소
  • Main — 202 제출, 상태 조회, SSE, 취소, 결과 다운로드
  • 데모 실행 결과
이전 섹션2 핵심 원리3 / 7다음 섹션4 응용 변형 예제