자바로 배우는 AI (1) — LLM API 기초

에이전트가 뭔지, MCP가 뭔지, RAG가 뭔지, 하네스가 뭔지 하나도 모르는 상태에서 시작했다. 자바 개발자라서 자바(Anthropic 공식 SDK)로 직접 만들어보며 정리한 시리즈다. 전체 코드: github.com/dadaok/ai-playground

용어부터 — 결국 REST API 연동이다

먼저 큰 그림. 외부 결제 API 연동하는 것과 구조가 같다.

용어한 줄 정의자바 개발자 비유
LLM텍스트를 넣으면 텍스트가 나오는 모델외부 서비스
API그 모델을 코드로 부르는 HTTP 창구결제사 REST API
엔드포인트실제 요청 URL (api.anthropic.com)pay.xxx.com/...
API 키“나 이 사람 맞음” 인증 문자열시크릿 키
SDKHTTP·JSON 처리를 대신 해주는 라이브러리결제사 자바 SDK
토큰글자를 잘게 쪼갠 단위. 요금·길이의 기준API 호출 건수(과금 단위)

그리고 개념 용어.

용어한 줄
프롬프트모델에게 보내는 입력 텍스트 전부
system 프롬프트모델의 역할·규칙을 정하는 고정 지시문
messages / role대화 내역 배열. user ↔ assistant 가 번갈아 쌓임
tool use모델이 “이 함수 불러줘”라고 JSON으로 요청 → 내가 실행
agent모델 + 도구 + 반복 루프
RAG답하기 전에 관련 문서를 검색해 프롬프트에 붙여넣기
MCP도구/데이터를 모델에 연결하는 표준 규격 (JDBC 같은 것)
harness모델을 감싸고 루프·컨텍스트·권한을 관리하는 실행 환경

핵심 통찰 하나만 미리: agent = LLM 호출을 while 루프에 넣고 함수(tool)를 호출시킨 것. 나머지는 다 이걸 돕는 부속품이다.

API 키는 Claude 계정과 다르다

혼동했던 부분. claude.ai 구독(Pro/Max)API 키 는 완전히 별개다.

 claude.ai 웹/앱Anthropic API
무엇사람이 브라우저에서 채팅내 코드가 HTTP로 모델 호출
인증계정 로그인x-api-key 헤더에 API 키
결제월 구독(정액)쓴 토큰만큼 과금(선불 크레딧)
발급처claude.aiconsole.anthropic.com

Pro 구독이 있어도 API 크레딧은 따로 충전해야 한다. 연습용은 $5면 충분하다.

또 하나. API 호출은 console이 아니라 api.anthropic.com으로 간다.

[처음 한 번]  console.anthropic.com(브라우저) → 키 발급 → sk-ant-... 복사
[키 보관]     export ANTHROPIC_API_KEY=sk-ant-...
[매 호출]     내 코드 ──HTTP POST──▶ api.anthropic.com/v1/messages  (헤더에 키)

console은 키를 만들 때만 간다. 그 뒤로 코드는 console을 거치지 않는다.

프로젝트 세팅 (Gradle + 공식 자바 SDK)

build.gradle.kts:

plugins { application }

repositories { mavenCentral() }

dependencies {
    implementation("com.anthropic:anthropic-java:2.34.0")
}

java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }

application { mainClass = "playground.Step1Basic" }

API 키는 환경변수로만 읽는다. 코드·git 어디에도 평문으로 두지 않는다. IntelliJ에서는 Run/Debug Configuration → Environment variables 에 ANTHROPIC_API_KEY 를 넣으면 된다.

1단계 코드 — API를 “날것으로”

package playground;

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;

public class Step1Basic {

    public static void main(String[] args) {
        // ANTHROPIC_API_KEY 환경변수를 자동으로 읽는다.
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        MessageCreateParams params = MessageCreateParams.builder()
                .model("claude-haiku-4-5")   // 가장 저렴한 모델
                .maxTokens(1024L)
                .system("너는 자바 개발자에게 AI 개념을 설명하는 친절한 튜터야. 한국어로 답해.")
                .addUserMessage("에이전트와 그냥 LLM 한 번 호출하는 것의 차이를 3문장으로 설명해줘.")
                .build();

        Message response = client.messages().create(params);

        // content 는 여러 block 으로 나뉠 수 있다(텍스트, 생각, 도구호출 등). text 만 출력.
        response.content().stream()
                .flatMap(block -> block.text().stream())
                .forEach(textBlock -> System.out.println(textBlock.text()));

        // 토큰 사용량 = 요금의 단위. 매 호출마다 확인하는 습관.
        System.out.println("input tokens : " + response.usage().inputTokens());
        System.out.println("output tokens: " + response.usage().outputTokens());
    }
}

실행하며 얻은 감(感)

  • 이건 상태 없는(stateless) HTTP 호출이다. 서버는 우리를 기억하지 않는다.
  • 대화는 messages 배열이다. role = user / assistant 가 번갈아 쌓인다.
  • 다음 턴에도 대화를 이어가려면 내가 매번 전체 messages 를 다시 보낸다. (이 사실이 뒤에서 “컨텍스트 관리” 문제로 이어진다 — 5편 참고)
  • content 가 단일 문자열이 아니라 block 리스트인 이유는, 응답에 텍스트뿐 아니라 “생각(thinking)”, “도구 호출(tool_use)” 같은 다른 종류가 섞일 수 있어서다. (2편)

정리

LLM API는 특별한 기술이 아니라 인증 붙은 stateless REST 호출이다. system으로 역할을 정하고, messages에 대화를 쌓아 보내고, 응답에서 필요한 block을 꺼낸다. 다음 편에서는 여기에 “도구(tool)”를 붙여 에이전트를 만든다.


© 2023 Lee. All rights reserved.