Java SDK
Java SDK
Anthropic Java SDK는 Java로 작성된 애플리케이션에서 Claude API에 편리하게 접근할 수 있게 해줘요. 요청을 만들 때 빌더 패턴을 사용하고 동기·비동기 연산을 모두 지원해요. 이 페이지에서는 설치, 클라이언트 설정, 동기·비동기 사용법, 스트리밍, 구조화된 출력, 도구 사용, 에러 처리, 재시도, 타임아웃, 페이지네이션, 타입 시스템 등을 다뤄요.
API 기능 문서와 코드 예시는 API 참조를 보세요. 이 페이지는 Java 특정 SDK 기능과 설정을 다뤄요.
출처: 문서
본문
설치 (Installation)
<Gradle 빌드 도구>
implementation("com.anthropic:anthropic-java:2.65.0")
<Maven 빌드 도구>
<dependency>
<groupId>com.anthropic</groupId>
<artifactId>anthropic-java</artifactId>
<version>2.65.0</version>
</dependency>
요구사항 (Requirements)
이 라이브러리는 Java 8 이상이 필요해요.
참고: SDK는 Java 8 이상을 지원해요. 이 문서의 코드 예시는 JDK 25 컴팩트 소스 파일로 작성되었으며, 간단한
void main()진입점과IO.println()출력을 사용해요. API 호출 자체는 지원되는 모든 JDK에서 동일해요. 예시를 이전 버전에서 컴파일하려면IO.println(...)을System.out.println(...)으로 바꾸고 본문을 클래스 안의public static void main(String[] args)안에 넣으세요.
빠른 시작 (Quick start)
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
Message message = client.messages().create(params);
클라이언트 설정 (Client configuration)
API 키 설정 (API key setup)
시스템 프로퍼티나 환경 변수로 클라이언트를 설정하세요:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
또는 직접 설정:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.apiKey("my-anthropic-api-key")
.build();
또는 두 가지를 조합:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
// Configures using system properties or environment variables
.fromEnv()
.apiKey("my-anthropic-api-key")
.build();
인증 옵션(Workload Identity Federation 포함)은 인증을 보세요. API 키가 개인 또는 서비스 계정 키로서 여러 워크스페이스에 접근할 수 있다면 anthropic-workspace-id 요청 헤더에 워크스페이스 ID를 설정하세요. 워크스페이스 선택에서 이 SDK의 요청별 옵션을 보여줘요.
설정 옵션 (Configuration options)
| Setter | System property | Environment variable | Required | Default value |
|---|---|---|---|---|
apiKey |
anthropic.apiKey |
ANTHROPIC_API_KEY |
false | - |
authToken |
anthropic.authToken |
ANTHROPIC_AUTH_TOKEN |
false | - |
baseUrl |
anthropic.baseUrl |
ANTHROPIC_BASE_URL |
true | "https://api.anthropic.com" |
시스템 프로퍼티가 환경 변수보다 우선해요.
팁: 같은 애플리케이션에서 클라이언트를 두 개 이상 만들지 마세요. 각 클라이언트에는 연결 풀과 스레드 풀이 있고, 요청 사이에서 공유하는 것이 더 효율적이에요.
설정 수정 (Modifying configuration)
같은 연결과 스레드 풀을 재사용하면서 임시로 수정된 클라이언트 설정을 쓰려면, 클라이언트나 서비스에서 withOptions()를 호출하세요:
import com.anthropic.client.AnthropicClient;
AnthropicClient clientWithOptions = client.withOptions(optionsBuilder -> {
optionsBuilder.baseUrl("https://example.com");
optionsBuilder.maxRetries(42);
});
withOptions() 메서드는 원래 클라이언트나 서비스에는 영향을 주지 않아요.
비동기 사용법 (Async usage)
기본 클라이언트는 동기적이에요. 비동기 실행으로 전환하려면 async() 메서드를 호출하세요:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
CompletableFuture<Message> message = client.async().messages().create(params);
또는 처음부터 비동기 클라이언트를 만들 수 있어요:
import com.anthropic.client.AnthropicClientAsync;
import com.anthropic.client.okhttp.AnthropicOkHttpClientAsync;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
AnthropicClientAsync client = AnthropicOkHttpClientAsync.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
CompletableFuture<Message> message = client.messages().create(params);
비동기 클라이언트는 대부분의 메서드가 CompletableFuture를 반환한다는 점을 제외하고 동기 클라이언트와 같은 옵션을 지원해요.
스트리밍 (Streaming)
SDK는 응답 "청크" 스트림을 반환하는 메서드를 정의해요. 각 청크는 전체 응답을 기다리는 대신 도착하는 즉시 개별적으로 처리할 수 있어요.
동기 스트리밍 (Synchronous streaming)
이런 스트리밍 메서드는 동기 클라이언트에서 StreamResponse를 반환해요:
import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;
try (StreamResponse<RawMessageStreamEvent> streamResponse = client.messages().createStreaming(params)) {
streamResponse.stream().forEach(chunk -> {
IO.println(chunk);
});
IO.println("No more chunks!");
}
비동기 스트리밍 (Asynchronous streaming)
비동기 클라이언트에서는 메서드가 AsyncStreamResponse를 반환해요:
import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;
client.async().messages().createStreaming(params).subscribe(chunk -> {
IO.println(chunk);
});
// If you need to handle errors or completion of the stream
client.async().messages().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
@Override
public void onNext(RawMessageStreamEvent chunk) {
IO.println(chunk);
}
@Override
public void onComplete(Optional<Throwable> error) {
if (error.isPresent()) {
IO.println("Something went wrong!");
throw new RuntimeException(error.get());
} else {
IO.println("No more chunks!");
}
}
});
// Or use futures
client.async().messages().createStreaming(params)
.subscribe(chunk -> {
IO.println(chunk);
})
.onCompleteFuture()
.whenComplete((unused, error) -> {
if (error != null) {
IO.println("Something went wrong!");
throw new RuntimeException(error);
} else {
IO.println("No more chunks!");
}
});
비동기 스트리밍은 전용 per-client 캐시 스레드 풀 Executor를 사용해 현재 스레드를 막지 않고 스트리밍해요. 다른 Executor를 쓰려면:
Executor executor = Executors.newFixedThreadPool(4);
client.async().messages().createStreaming(params).subscribe(
chunk -> IO.println(chunk), executor
);
또는 streamHandlerExecutor 메서드로 클라이언트를 전역적으로 설정:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.streamHandlerExecutor(Executors.newFixedThreadPool(4))
.build();
메시지 누적기와 함께 스트리밍 (Streaming with message accumulator)
MessageAccumulator는 처리되는 응답의 이벤트 스트림을 기록해서, non-streaming API가 반환했을 것과 유사한 Message 객체를 누적할 수 있어요.
동기 응답에서는 스트림 파이프라인에 Stream.peek() 호출을 추가해 각 이벤트를 누적하세요:
import com.anthropic.core.http.StreamResponse;
import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.RawMessageStreamEvent;
MessageAccumulator messageAccumulator = MessageAccumulator.create();
try (StreamResponse<RawMessageStreamEvent> streamResponse =
client.messages().createStreaming(createParams)) {
streamResponse.stream()
.peek(messageAccumulator::accumulate)
.flatMap(event -> event.contentBlockDelta().stream())
.flatMap(deltaEvent -> deltaEvent.delta().text().stream())
.forEach(textDelta -> IO.print(textDelta.text()));
}
Message message = messageAccumulator.message();
비동기 응답에서는 subscribe() 호출에 MessageAccumulator를 추가하세요:
import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;
MessageAccumulator messageAccumulator = MessageAccumulator.create();
client.async().messages()
.createStreaming(createParams)
.subscribe(event -> messageAccumulator.accumulate(event).contentBlockDelta().stream()
.flatMap(deltaEvent -> deltaEvent.delta().text().stream())
.forEach(textDelta -> IO.print(textDelta.text())))
.onCompleteFuture()
.join();
Message message = messageAccumulator.message();
BetaMessage 객체 누적용 BetaMessageAccumulator도 사용할 수 있어요. MessageAccumulator와 같은 방식으로 쓰면 돼요.
구조화된 출력 (Structured outputs)
Java 예시를 포함한 완전한 구조화된 출력 문서는 구조화된 출력을 보세요.
도구 사용 (Tool use)
Claude와 함께하는 도구 사용은 AI 모델의 응답에 외부 도구와 함수를 직접 통합하게 해줘요. 평문을 만드는 대신 모델은 적절할 때 도구나 함수를 호출하기 위한 지시(파라미터 포함)를 출력할 수 있어요. 도구에 대한 JSON 스키마를 정의하면 모델이 스키마를 사용해 언제, 어떻게 도구를 쓸지 결정해요.
도구 사용 기능은 AI 모델의 JSON 출력이 입력 파라미터에서 제공한 JSON 스키마를 준수하도록 보장하는 "strict" 모드를 지원해요.
SDK는 임의의 Java 클래스 구조에서 도구와 그 파라미터를 자동으로 유도할 수 있어요: 클래스 이름(snake case로 변환)이 도구 이름을, 클래스 필드가 도구의 파라미터를 정의해요.
참고: 도구 클래스는 최상위 클래스나
static중첩 클래스로 선언하세요. 이 요구사항은 SDK가 도구 입력을 클래스 인스턴스로 역직렬화할 때 사용하는 Jackson Databind 라이브러리(com.fasterxml.jackson.databind)에서 나온 것인데, non-static 내부 클래스는 인스턴스화할 수 없어요.
애너테이션으로 도구 정의하기 (Defining tools with annotations)
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
enum Unit {
CELSIUS,
FAHRENHEIT;
public String toString() {
return switch (this) {
case CELSIUS -> "C";
case FAHRENHEIT -> "F";
};
}
public double fromKelvin(double temperatureK) {
return switch (this) {
case CELSIUS -> temperatureK - 273.15;
case FAHRENHEIT -> (temperatureK - 273.15) * 1.8 + 32.0;
};
}
}
@JsonClassDescription("Get the weather in a given location")
static class GetWeather {
@JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
public String location;
@JsonPropertyDescription("The unit of temperature")
public Unit unit;
public Weather execute() {
double temperatureK = switch (location) {
case "San Francisco, CA" -> 300.0;
case "New York, NY" -> 310.0;
case "Dallas, TX" -> 305.0;
default -> 295;
};
return new Weather(String.format("%.0f%s", unit.fromKelvin(temperatureK), unit));
}
}
static class Weather {
public String temperature;
public Weather(String temperature) {
this.temperature = temperature;
}
}
도구 호출 (Calling tools)
도구 클래스를 정의했으면 MessageCreateParams.Builder.addTool(Class<T>)로 메시지 파라미터에 추가하고, AI 모델 응답에서 요청하면 호출하세요. BetaToolUseBlock.input(Class<T>)로 도구의 JSON 형태 파라미터를 도구 정의 클래스 인스턴스로 파싱할 수 있어요.
도구를 호출한 후에는 BetaToolResultBlockParam.Builder.contentAsJson(Object)로 도구 결과를 AI 모델에 다시 전달하세요:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.*;
import com.anthropic.models.messages.Model;
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(2048)
.addTool(GetWeather.class)
.addUserMessage("What's the temperature in New York?");
client.beta().messages().create(createParamsBuilder.build()).content().stream()
.flatMap(contentBlock -> contentBlock.toolUse().stream())
.forEach(toolUseBlock -> createParamsBuilder
// Add a message indicating that the tool use was requested.
.addAssistantMessageOfBetaContentBlockParams(
List.of(BetaContentBlockParam.ofToolUse(BetaToolUseBlockParam.builder()
.name(toolUseBlock.name())
.id(toolUseBlock.id())
.input(toolUseBlock._input())
.build())))
// Add a message with the result of the requested tool use.
.addUserMessageOfBetaContentBlockParams(
List.of(BetaContentBlockParam.ofToolResult(BetaToolResultBlockParam.builder()
.toolUseId(toolUseBlock.id())
.contentAsJson(callTool(toolUseBlock))
.build()))));
client.beta().messages().create(createParamsBuilder.build()).content().stream()
.flatMap(contentBlock -> contentBlock.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
private static Object callTool(BetaToolUseBlock toolUseBlock) {
if (!"get_weather".equals(toolUseBlock.name())) {
throw new IllegalArgumentException("Unknown tool: " + toolUseBlock.name());
}
GetWeather tool = toolUseBlock.input(GetWeather.class);
return tool != null ? tool.execute() : new Weather("unknown");
}
도구 이름 변환 (Tool name conversion)
도구 이름은 camel case 도구 클래스 이름(예: GetWeather)에서 유도되어 snake case(예: get_weather)로 변환돼요. 단어 경계는 현재 문자가 첫 문자가 아니면서 대문자이고, 앞 문자가 소문자이거나 뒤 문자가 소문자인 지점에서 시작돼요. 예를 들어 MyJSONParser는 my_json_parser가 되고 ParseJSON은 parse_json이 돼요. 이 변환은 @JsonTypeName 애너테이션으로 덮어쓸 수 있어요.
로컬 도구 JSON 스키마 검증 (Local tool JSON schema validation)
도구 클래스에서 유도된 JSON 스키마가 Anthropic의 제약을 존중하는지 로컬 검증을 수행할 수 있어요. 로컬 검증은 기본적으로 켜져 있지만 끌 수 있어요:
MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(2048)
.addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
.addUserMessage("What's the temperature in New York?");
도구 클래스 애너테이션 (Annotating tool classes)
애너테이션으로 JSON 스키마에 도구에 대한 추가 정보를 넣을 수 있어요:
@JsonClassDescription- 도구 클래스에 그 도구를 언제, 어떻게 쓰는지 상세히 설명하는 설명 추가.@JsonTypeName- snake case로 변환한 클래스의 단순 이름 대신 다른 도구 이름 설정.@JsonPropertyDescription- 도구 파라미터에 상세한 설명 추가.@JsonIgnore- 생성된 JSON 스키마에서public필드나 getter 메서드 제외.@JsonProperty- 생성된 JSON 스키마에 non-public필드나 getter 메서드 포함.
메시지 배치 (Message batches)
SDK는 client.messages().batches() 네임스페이스 아래에서 배치 처리를 지원해요. 배치를 나열하고 페이지를 넘기는 방법은 페이지네이션을 보세요.
파일 업로드 (File uploads)
SDK는 MultipartField 클래스로 파일을 받는 메서드를 정의해요:
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(Files.newInputStream(Paths.get("/path/to/file.pdf")))
.contentType("application/pdf")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
또는 InputStream에서:
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(URI.create("https://example.com/path/to/file").toURL().openStream())
.filename("document.pdf")
.contentType("application/pdf")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
또는 메모리 내 바이트에서:
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(new ByteArrayInputStream("content".getBytes()))
.filename("document.txt")
.contentType("text/plain")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
바이너리 응답 (Binary responses)
SDK는 반드시 JSON으로 파싱할 필요가 없는 API 응답에 대해 바이너리 응답을 반환하는 메서드를 정의해요:
import com.anthropic.core.http.HttpResponse;
HttpResponse response = client.files().download("file_abc123");
응답 내용을 파일에 저장하려면:
import com.anthropic.core.http.HttpResponse;
try (HttpResponse response = client.files().download(params)) {
Files.copy(
response.body(),
Paths.get(path),
StandardCopyOption.REPLACE_EXISTING
);
} catch (Exception e) {
IO.println("Something went wrong!");
throw new RuntimeException(e);
}
또는 응답 내용을 어떤 OutputStream으로 전송:
import com.anthropic.core.http.HttpResponse;
try (HttpResponse response = client.files().download(params)) {
response.body().transferTo(Files.newOutputStream(Paths.get(path)));
} catch (Exception e) {
IO.println("Something went wrong!");
throw new RuntimeException(e);
}
에러 처리 (Error handling)
SDK는 커스텀 unchecked 예외 타입을 던져요:
AnthropicServiceException- HTTP 오류의 기본 클래스.AnthropicIoException- I/O 네트워킹 오류.AnthropicRetryableException- 재시도할 수 있는 실패를 나타내는 일반 오류.AnthropicInvalidDataException- 성공적으로 파싱된 데이터를 해석하지 못한 경우(예: 필수여야 하는 프로퍼티에 접근했는데 API가 예상치 못하게 생략한 경우).AnthropicException- 모든 예외의 기본 클래스.
상태 코드 매핑 (Status code mapping)
| Status | Exception |
|---|---|
| 400 | BadRequestException |
| 401 | UnauthorizedException |
| 403 | PermissionDeniedException |
| 404 | NotFoundException |
| 422 | UnprocessableEntityException |
| 429 | RateLimitException |
| 5xx | InternalServerException |
| others | UnexpectedStatusCodeException |
SseException은 성공적인 초기 HTTP 응답 후 SSE 스트리밍 중 발생한 오류에 대해 던져져요.
import com.anthropic.errors.*;
try {
Message message = client.messages().create(params);
} catch (RateLimitException e) {
IO.println("Rate limited, retry after: " + e.headers());
} catch (UnauthorizedException e) {
IO.println("Invalid API key");
} catch (AnthropicServiceException e) {
IO.println("API error: " + e.statusCode());
} catch (AnthropicIoException e) {
IO.println("Network error: " + e.getMessage());
}
요청 ID (Request IDs)
원시 응답을 사용할 때 requestId() 메서드로 request-id 응답 헤더에 접근할 수 있어요:
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);
Optional<String> requestId = message.requestId();
이것으로 실패한 요청을 빠르게 로그로 남기고 Anthropic에 보고할 수 있어요. 요청 디버깅에 대한 자세한 내용은 요청 ID를 보세요.
재시도 (Retries)
SDK는 기본적으로 2번 자동 재시도하며, 요청 사이에 짧은 지수 백오프를 사용해요. 다음 오류 타입만 재시도돼요:
- 연결 오류(예: 네트워크 연결 문제)
- 408 Request Timeout
- 409 Conflict
- 429 Rate Limit
- 5xx Internal
API가 SDK에 요청을 재시도하라고 명시적으로 지시할 수도 있어요. 커스텀 재시도 횟수를 설정하려면 maxRetries 메서드로 클라이언트를 설정하세요:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder().fromEnv().maxRetries(4).build();
타임아웃 (Timeouts)
요청은 기본적으로 10분 후에 타임아웃돼요.
하지만 maxTokens를 받는 메서드에서 스트리밍하면서 큰 maxTokens 값을 지정하면 기본 타임아웃이 이 공식으로 동적으로 계산돼요:
Duration.ofSeconds(
Math.min(
60 * 60, // 1 hour max
Math.max(
10 * 60, // 10 minute minimum
60 * 60 * maxTokens / 128_000
)
)
)
이로 인해 덮어쓰지 않으면 maxTokens 파라미터에 따라 최대 60분까지 타임아웃이 조정돼요. non-streaming 요청에서는 동적 타임아웃이 maxTokens에 따라 최소 30초에서 최대 10분까지 조정돼요.
요청별 커스텀 타임아웃을 설정하려면:
import com.anthropic.models.messages.Message;
Message message = client
.messages()
.create(params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build());
또는 클라이언트 레벨에서 모든 메서드 호출의 기본값을 설정:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.timeout(Duration.ofSeconds(30))
.build();
긴 요청 (Long requests)
주의: 더 긴 실행 요청에는 스트리밍을 사용하는 것을 고려하세요.
스트리밍 없이 큰 maxTokens 값을 설정하는 것은 피하세요. 일부 네트워크는 일정 시간 후 유휴 연결을 끊을 수 있어 Anthropic으로부터 응답을 받지 못하고 요청이 실패하거나 타임아웃될 수 있기 때문이에요. SDK는 주기적으로 API에 핑을 보내 연결을 유지하고 이런 네트워크의 영향을 줄여요.
SDK는 non-streaming 요청이 10분보다 오래 걸릴 것으로 예상되면 에러를 던져요. 스트리밍 메서드를 쓰거나 클라이언트/요청 레벨에서 타임아웃을 덮어쓰면 이 에러가 비활성화돼요.
페이지네이션 (Pagination)
SDK는 페이지가 매겨진 결과를 한 번에 한 페이지씩 또는 모든 페이지에 걸쳐 항목별로 접근하는 편리한 방법을 제공해요.
자동 페이지네이션 (Auto-pagination)
모든 페이지의 모든 결과를 순회하려면 필요에 따라 자동으로 더 많은 페이지를 가져오는 autoPager() 메서드를 쓰세요.
import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;
BatchListPage page = client.messages().batches().list();
// Process as an Iterable
for (MessageBatch batch : page.autoPager()) {
IO.println(batch);
}
// Process as a Stream
page.autoPager()
.stream()
.limit(50)
.forEach(batch -> IO.println(batch));
비동기 클라이언트를 사용할 때 메서드는 AsyncStreamResponse를 반환해요:
import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.batches.BatchListPageAsync;
import com.anthropic.models.messages.batches.MessageBatch;
CompletableFuture<BatchListPageAsync> pageFuture = client.async().messages().batches().list();
pageFuture.thenAccept(page -> page.autoPager().subscribe(batch -> {
IO.println(batch);
}));
// If you need to handle errors or completion of the stream
pageFuture.thenAccept(page -> page.autoPager().subscribe(new AsyncStreamResponse.Handler<>() {
@Override
public void onNext(MessageBatch batch) {
IO.println(batch);
}
@Override
public void onComplete(Optional<Throwable> error) {
if (error.isPresent()) {
IO.println("Something went wrong!");
throw new RuntimeException(error.get());
} else {
IO.println("No more!");
}
}
}));
// Or use futures
pageFuture.thenAccept(page -> page.autoPager()
.subscribe(batch -> {
IO.println(batch);
})
.onCompleteFuture()
.whenComplete((unused, error) -> {
if (error != null) {
IO.println("Something went wrong!");
throw new RuntimeException(error);
} else {
IO.println("No more!");
}
}));
수동 페이지네이션 (Manual pagination)
개별 페이지 항목에 접근하고 다음 페이지를 수동으로 요청하려면:
import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;
BatchListPage page = client.messages().batches().list();
while (true) {
for (MessageBatch batch : page.items()) {
IO.println(batch);
}
if (!page.hasNextPage()) {
break;
}
page = page.nextPage();
}
타입 시스템 (Type system)
불변성과 빌더 (Immutability and builders)
SDK의 각 클래스는 만들기 위한 연관 빌더가 있어요. 각 클래스는 한 번 만들면 불변(immutable)이에요. 연관 빌더가 있으면 toBuilder() 메서드가 있어서 수정된 복사본을 만들기 위해 빌더로 다시 변환할 수 있어요.
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
// Create a modified copy using toBuilder()
MessageCreateParams modified = params.toBuilder().maxTokens(2048L).build();
각 클래스는 불변이므로 빌더 수정은 이미 만든 클래스 인스턴스에 영향을 주지 않아요.
요청과 응답 (Requests and responses)
Claude API에 요청을 보내려면 어떤 Params 클래스의 인스턴스를 만들고 해당 클라이언트 메서드에 전달하세요. 응답을 받으면 Java 클래스 인스턴스로 역직렬화돼요.
예를 들어 client.messages().create(...)는 MessageCreateParams 인스턴스와 함께 호출해야 하고, Message 인스턴스를 반환해요.
문서화되지 않은 파라미터 (Undocumented parameters)
문서화되지 않은 파라미터를 설정하려면 어떤 Params 클래스에서든 putAdditionalHeader, putAdditionalQueryParam, putAdditionalBodyProperty 메서드를 호출하세요:
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
MessageCreateParams params = MessageCreateParams.builder()
.putAdditionalHeader("Secret-Header", "42")
.putAdditionalQueryParam("secret_query_param", "42")
.putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
.build();
이것들은 나중에 빌드된 객체에서 _additionalHeaders(), _additionalQueryParams(), _additionalBodyProperties() 메서드로 접근할 수 있어요.
주의: 이 메서드에 전달된 값은 이전 메서드에 전달된 값을 덮어써요. 보안상의 이유로 이 메서드는 신뢰할 수 있는 입력 데이터에만 사용하세요.
중첩 헤더, 쿼리 파라미터, 본문 클래스에 문서화되지 않은 파라미터를 설정하려면:
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Metadata;
MessageCreateParams params = MessageCreateParams.builder()
.metadata(
Metadata.builder().putAdditionalProperty("secretProperty", JsonValue.from("42")).build()
)
.build();
이 프로퍼티들은 나중에 중첩 빌드 객체에서 _additionalProperties() 메서드로 접근할 수 있어요.
문서화된 파라미터나 프로퍼티를 문서화되지 않았거나 아직 지원되지 않는 값으로 설정하려면 setter에 JsonValue 객체를 전달하세요:
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(JsonValue.from(3.14))
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
JsonValue 만들기 (JsonValue creation)
JsonValue를 만드는 가장 간단한 방법은 from(...) 메서드를 쓰는 거예요:
import com.anthropic.core.JsonValue;
// Create primitive JSON values
JsonValue nullValue = JsonValue.from(null);
JsonValue booleanValue = JsonValue.from(true);
JsonValue numberValue = JsonValue.from(42);
JsonValue stringValue = JsonValue.from("Hello World!");
// Create a JSON array value equivalent to `["Hello", "World"]`
JsonValue arrayValue = JsonValue.from(List.of("Hello", "World"));
// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
JsonValue objectValue = JsonValue.from(Map.of("a", 1, "b", 2));
// Create an arbitrarily nested JSON equivalent to:
// { "a": [1, 2], "b": [3, 4] }
JsonValue complexValue = JsonValue.from(Map.of("a", List.of(1, 2), "b", List.of(3, 4)));
필수 파라미터 강제 생략 (Forcibly omitting required parameters)
보통 Builder 클래스의 build 메서드는 필수 파라미터나 프로퍼티가 설정되지 않으면 IllegalStateException을 던져요. 필수 파라미터나 프로퍼티를 강제로 생략하려면 JsonMissing을 전달하세요:
import com.anthropic.core.JsonMissing;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.addUserMessage("Hello, world")
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(JsonMissing.of())
.build();
응답 프로퍼티 (Response properties)
문서화되지 않은 응답 프로퍼티에 접근하려면 _additionalProperties() 메서드를 호출하세요:
import com.anthropic.core.JsonValue;
Map<String, JsonValue> additionalProperties = client
.messages()
.create(params)
._additionalProperties();
JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
@Override
public String visitNull() {
return "It's null!";
}
@Override
public String visitBoolean(boolean value) {
return "It's a boolean!";
}
@Override
public String visitNumber(Number value) {
return "It's a number!";
}
// Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
// The default implementation of each unimplemented method delegates to `visitDefault`,
// which throws by default, but can also be overridden
});
프로퍼티의 원시 JSON 값에 접근하려면 _ 접두사 메서드를 호출하세요:
import com.anthropic.core.JsonField;
import com.anthropic.models.messages.StopReason;
JsonField<StopReason> stopReason = client.messages().create(params)._stopReason();
if (stopReason.isMissing()) {
// The property is absent from the JSON response
} else if (stopReason.isNull()) {
// The property was set to literal null
} else {
// Check if value was provided as a string
// Other methods include `asNumber()`, `asBoolean()`, etc.
Optional<String> jsonString = stopReason.asString();
// Try to deserialize into a custom type
MyClass myObject = stopReason.asUnknown().orElseThrow().convert(MyClass.class);
}
응답 검증 (Response validation)
기본적으로 SDK는 API가 예상 타입과 일치하지 않는 응답을 반환할 때 예외를 던지지 않아요. 프로퍼티에 직접 접근할 때만 AnthropicInvalidDataException을 던져요.
응답이 완전히 잘 타입화되어 있는지 미리 확인하려면 validate()를 호출하세요:
import com.anthropic.models.messages.Message;
Message message = client.messages().create(params).validate();
또는 요청별로 설정:
import com.anthropic.models.messages.Message;
Message message = client
.messages()
.create(params, RequestOptions.builder().responseValidation(true).build());
또는 클라이언트 레벨에서 모든 메서드 호출의 기본값을 설정:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.responseValidation(true)
.build();
HTTP 클라이언트 커스터마이징 (HTTP client customization)
프록시 설정 (Proxy configuration)
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import java.net.Proxy;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("https://example.com", 8080)))
.build();
HTTPS / SSL 설정
참고: 대부분의 애플리케이션은 이 메서드를 호출하지 말고 시스템 기본값을 사용해야 해요. 기본값에는 구현을 수정하면 잃을 수 있는 특수 최적화가 포함돼요.
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.sslSocketFactory(yourSSLSocketFactory)
.trustManager(yourTrustManager)
.hostnameVerifier(yourHostnameVerifier)
.build();
커스텀 HTTP 클라이언트 (Custom HTTP client)
SDK는 세 개의 아티팩트로 구성돼요:
anthropic-java-core- 핵심 SDK 로직 포함, OkHttp에 의존하지 않아요.AnthropicClient,AnthropicClientAsync와 그 구현 클래스를 노출하며, 모두 어떤 HTTP 클라이언트와도 작동할 수 있어요.anthropic-java-client-okhttp- OkHttp에 의존.AnthropicOkHttpClient와AnthropicOkHttpClientAsync를 노출.anthropic-java-anthropic-java-core와anthropic-java-client-okhttp의 API 모두에 의존하고 노출. 자체 로직은 없어요.
이 구조는 불필요한 의존성을 끌어들이지 않고 SDK의 기본 HTTP 클라이언트를 교체할 수 있게 해줘요.
커스터마이즈된 OkHttpClient (Customized OkHttpClient)
팁: 기본 클라이언트를 교체하기 전에 사용 가능한 네트워크 옵션을 시도해 보세요.
커스터마이즈된 OkHttpClient를 쓰려면:
anthropic-java의존성을anthropic-java-core로 교체하세요.anthropic-java-client-okhttp의OkHttpClient클래스를 코드에 복사하고 커스터마이즈하세요.- 커스터마이즈된 클라이언트로
AnthropicClientImpl또는AnthropicClientAsyncImpl을 구성하세요.
완전히 커스텀 HTTP 클라이언트 (Completely custom HTTP client)
완전히 커스텀 HTTP 클라이언트를 쓰려면:
anthropic-java의존성을anthropic-java-core로 교체하세요.HttpClient인터페이스를 구현하는 클래스를 작성하세요.- 새 클라이언트 클래스로
AnthropicClientImpl또는AnthropicClientAsyncImpl을 구성하세요.
플랫폼 통합 (Platform integrations)
참고: 코드 예시가 포함된 상세한 플랫폼 설정 가이드는 다음을 보세요:
Java SDK는 플랫폼별 Backend 구현을 제공하는 별도의 의존성을 통해 다음 플랫폼을 지원해요:
- Agent Platform:
com.anthropic:anthropic-java-vertex:VertexBackend.fromEnv()또는VertexBackend.builder()사용. - Bedrock:
com.anthropic:anthropic-java-bedrock: Messages-API Bedrock 엔드포인트에는BedrockMantleBackend.fromEnv()/BedrockMantleBackend.builder()를, (bedrock-runtime경로)에는BedrockBackend.fromEnv()/BedrockBackend.builder()를 사용. - Claude Platform on AWS:
com.anthropic:anthropic-java-aws:AwsBackend.fromEnv()(ANTHROPIC_AWS_WORKSPACE_ID와 AWS 기본 리전/자격 증명 체인을 읽음) 또는AwsBackend.builder()사용. 베타에서 사용 가능해요. - Foundry:
com.anthropic:anthropic-java-foundry:FoundryBackend.fromEnv()또는FoundryBackend.builder()사용.
새 프로젝트에는 BedrockMantleBackend를, Bedrock InvokeModel API를 쓰는 기존 애플리케이션에는 BedrockBackend을 쓰세요.
플랫폼 아티팩트는 AnthropicOkHttpClient를 제공하는 기본 com.anthropic:anthropic-java 의존성의 애드온이므로 둘 다 설치해야 해요. 각 Backend 구현은 AnthropicOkHttpClient.builder()에서 .backend()로 클라이언트에 전달돼요. 각 클라우드 백엔드는 각자의 클라우드 플랫폼 SDK 클래스를 전이적 의존성으로 끌어들여요.
고급 사용법 (Advanced usage)
원시 응답 접근 (Raw response access)
HTTP 헤더, 상태 코드, 원시 응답 본문에 접근하려면 어떤 HTTP 메서드 호출에든 withRawResponse()를 붙이세요:
import com.anthropic.core.http.Headers;
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);
int statusCode = message.statusCode();
Headers headers = message.headers();
필요하다면 응답을 여전히 Java 클래스 인스턴스로 역직렬화할 수 있어요:
import com.anthropic.models.messages.Message;
Message parsedMessage = message.parse();
로깅 (Logging)
SDK는 표준 OkHttp 로깅 인터셉터를 사용해요. ANTHROPIC_LOG 환경 변수를 info로 설정해 로깅을 켜세요:
export ANTHROPIC_LOG=info
또는 더 자세한 로깅을 위해 debug로:
export ANTHROPIC_LOG=debug
<Jackson 호환성> SDK는 JSON 직렬화/역직렬화에 Jackson을 의존해요. 2.13.4 이상과 호환되지만 기본적으로 2.19.4에 의존해요. SDK는 런타임에 호환되지 않는 Jackson 버전을 감지하면(예: Maven이나 Gradle 구성에서 기본 버전을 덮어쓴 경우) 예외를 던져요. SDK가 예외를 던졌지만 버전이 호환된다고 확신한다면 AnthropicOkHttpClient 또는 AnthropicOkHttpClientAsync에서 checkJacksonVersionCompatibility로 버전 검사를 비활성화하세요.
주의: Jackson 버전 검사가 비활성화되면 SDK가 올바르게 작동한다는 보장이 없어요.
또한 오래된 Jackson 버전에는 SDK에 영향을 줄 수 있는 버그가 있어요. SDK는 모든 Jackson 버그를 우회하지 않으며 그러한 경우 사용자가 Jackson을 업그레이드할 것으로 기대해요. </Jackson 호환성>
<ProGuard/R8 구성> SDK가 리플렉션을 사용하지만 anthropic-java-core가 keep 규칙을 담은 구성 파일과 함께 배포되므로 ProGuard와 R8에서 여전히 사용할 수 있어요. ProGuard와 R8은 게시된 규칙을 자동으로 감지하고 사용해야 하지만, 필요하다면 keep 규칙을 수동으로 복사할 수도 있어요.
</ProGuard/R8 구성>
문서화되지 않은 API 기능 (Undocumented API functionality)
SDK는 문서화된 API를 편리하게 사용하도록 타입화되어 있어요. 하지만 문서화되지 않았거나 아직 지원되지 않는 API 부분을 작업하는 것도 지원해요.
문서화되지 않은 요청 파라미터 (Undocumented request parameters)
문서화되지 않은 파라미터에 설명된 대로 putAdditionalHeader, putAdditionalQueryParam, putAdditionalBodyProperty 메서드를 사용해 문서화되지 않은 요청 파라미터를 설정하세요.
문서화되지 않은 응답 프로퍼티 (Undocumented response properties)
응답 프로퍼티에 설명된 대로 _additionalProperties() 메서드로 문서화되지 않은 응답 프로퍼티에 접근하세요.
새롭거나 미출시된 enum 값 (New or unreleased enum values)
SDK의 Model, AnthropicBeta 같은 enum 유사 클래스는 닫힌 Java enum 타입이 아니에요. 각각 어떤 문자열이든 받는 of(String) 팩토리 메서드를 제공하므로 SDK에 아직 추가되지 않은 값(예: SDK 버전 이후 출시된 모델이나 beta 헤더)을 사용할 수 있어요:
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.messages.Model;
Model model = Model.of("some-new-model");
AnthropicBeta beta = AnthropicBeta.of("some-new-beta-2026-01-01");
이런 타입을 받는 빌더 메서드에는 종종 of(...)를 대신 호출하는 String 오버로드도 있어요:
import com.anthropic.models.messages.MessageCreateParams;
MessageCreateParams params = MessageCreateParams.builder()
.model("some-new-model") // same as .model(Model.of("some-new-model"))
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.build();
자동완성과 deprecation 경고를 얻으려면 잘 타입화된 상수(예: Model.CLAUDE_OPUS_5)를 선호하세요. String 오버로드와 of(...)는 주로 그것을 포함한 SDK 릴리스를 기다리는 동안 문서화되지 않았거나 아직 지원되지 않는 값으로 필드를 설정할 때 써요.
베타 기능 (Beta features)
베타 기능은 일반 릴리스 전에 제공되어 조기 피드백을 받고 새 기능을 테스트해요. Claude의 모든 역량과 도구의 사용 가능 여부는 build with Claude 개요에서 확인할 수 있어요. 대부분의 베타 API 기능은 클라이언트의 beta() 메서드로 접근할 수 있어요. 특정 베타 기능을 활성화하려면 메시지 params를 만들 때 .addBeta()로 적절한 베타 헤더를 추가하세요.
예를 들어 컨텍스트 편집을 활성화하려면:
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaMessage message = client.beta().messages().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addBeta(AnthropicBeta.CONTEXT_MANAGEMENT_2025_06_27)
.addUserMessage("Hello, Claude")
.build());
}
자주 묻는 질문 (Frequently asked questions)
<SDK가 일반 enum 클래스를 사용하지 않는 이유는?>
Java enum 클래스는 순방향으로 쉽게 호환되지 않아요. SDK에서 쓰면 API가 새 enum 값으로 응답하도록 업데이트될 때 런타임 예외가 발생할 수 있어요.
이 클래스들은 열려 있으므로 of(String) 팩토리 메서드로 어떤 문자열 값이든 만들 수도 있어요. SDK 버전에 아직 없는 값을 사용해야 한다면 새롭거나 미출시된 enum 값을 보세요.
</SDK가 일반 enum 클래스를 사용하지 않는 이유는?>
<필드가 일반 T 대신 JsonFieldJsonField<T>를 사용하면 몇 가지 기능이 가능해져요:
- 문서화되지 않은 API 기능 사용 허용
- 예상 형태에 대해 API 응답을 지연 검증
- 없는 값과 명시적 null 값 표현
</필드가 일반 T 대신 JsonField
로 표현되는 이유는?>
<SDK가 데이터 클래스를 사용하지 않는 이유는?> 데이터 클래스에 새 필드를 추가하는 것은 하위 호환이 되지 않아서, SDK는 클래스에 필드가 추가될 때마다 깨지는 변경을 도입하는 것을 피해요. </SDK가 데이터 클래스를 사용하지 않는 이유는?>
<SDK가 checked 예외를 사용하지 않는 이유는?> Checked 예외는 Java 프로그래밍 언어에서 널리 실수로 여겨져요. 실제로 Kotlin에서는 이런 이유로 생략되었어요. Checked 예외는:
- 처리하기에 장황해요
- 오류에 대해 아무것도 할 수 없는 잘못된 추상화 수준에서 오류 처리를 조장해요
- 함수 컬러링 문제 때문에 전파하기에 지루해요
- 람다와 잘 어울리지 않아요(또한 함수 컬러링 문제 때문에) </SDK가 checked 예외를 사용하지 않는 이유는?>
시맨틱 버저닝 (Semantic versioning)
이 패키지는 일반적으로 SemVer 규칙을 따르지만, 일부 하위 호환성이 깨지는 변경은 마이너 버전으로 릴리스될 수 있어요:
- 기술적으로 공개되어 있지만 외부 사용을 의도하거나 문서화하지 않은 라이브러리 내부의 변경.
- 실제로 대다수 사용자에게 영향을 주지 않을 것으로 예상되는 변경.