ai

spring boot에 claude API 연동하는 방법

pobii 2024. 11. 6. 16:45

 


목차

Claude API 키 발급받기
Claude api <-> 사용자 간 주고받는 형식 소개
Spring boot 작성
   - DTO 작성
   - api 키 저장하기
   - Claude Config 구현   
   - Service 구현
   - Controller 구현
사용해보기
   - 정상 답변 받아보기
   - 오류 답변 받아보기
   - error 구별 방법

Claude API 키 발급받기

claude api를 사용하기 위해서는 claude api 키가 필요하다. 키를 발급받는 방법은 아래 링크를 참고하자.

 

claude api 키 발급받는 법 / 금액 충전하는 법

claude api를 사용하기 위해서는 키를 받아야 한다. 홈페이지를 통해 api 키를 발급해보자. 키 발급하기1. 아래 링크를 통해 claude 회원가입/로그인을 진행하자.https://console.anthropic.com/login?returnTo=%2F%

writeguidekr.com


Claude api <-> 사용자 간 주고받는 형식 소개

claude api 사용시 모두 json 형식으로 주고받는다. 아래 세 코드는 사용자와 claude api 간의 질문/답변 형식이다.

아래 형식들을 토대로 spring 코드를 작성할 것이다.

 

claude api 요청 형식 예시

curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \		//api 키값
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-3-5-sonnet-20241022",		//모델 명
    "max_tokens": 1000,					//최대 사용 가능한 토큰 수
    "messages": [
        {"role": "user", "content": "Hello, world"}	//질문 내용
    ]
}'

 

정상 작동시 답변 형식 예시

{
  "content": [				//답변 내용
    {
      "text": "Hi! My name is Claude.",
      "type": "text"
    }
  ],
  "id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",		//답변의 고유 id
  "model": "claude-3-5-sonnet-20241022",	//사용한 모델명
  "role": "assistant",		
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "type": "message",
  "usage": {
    "input_tokens": 2095,		//사용한 input 토큰 수
    "output_tokens": 503		//사용한 output 토큰 수 
  }
}

 

오류 발생시 답변 형식 예시

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",		//오류 내용
    "message": "<string>"
  }
}

 


Spring boot 작성

- DTO 작성

위 형식들을 그대로 가져오는 DTO를 작성한다. 즉, 위의 변수명과 값들을 모두 그대로 서버에 가져오게 될것이다.

 

요청 DTO

import lombok.Builder;
import lombok.Data;
import java.util.List;

@Builder
@Data      
public class ClaudeRequestApiDto {
    private String model;
    private List<Message> messages;
    private int max_tokens;

    @Data
    @Builder
    public static class Message {
        private String role;
        private String content;
    }
}

 

답변 DTO (오류 DTO도 포함)

import lombok.*;
import java.util.List;

@Data   
public class ClaudeResponseApiDto {
    private List<Content> content; 
    private String id;          // 고유아이디
    private String model;       // 모델명
    private String role;        // 보통의 경우 모두 "assistant"
    private String stop_reason;     // (답변문장이 중간에 끊긴 경우 원인이 들어감) ["end_turn"(응답완료) / "max_tokens"(최대 토큰 수에 도달) / "stop_sequence"(stop_sequence 발현)] or null
    private String stop_sequence;   // [null / request에서 설정했던 stop_sequence값 ] 
    private String type;        // "message" or "error"
    private Usage usage;       
    
    @Data
    public static class Content {
        public String text;     // 답변 내용
        public String type;     // "text" or error 정보
    }

    @Data
    public static class Usage {
        public int input_tokens;    //input 토큰 수
        public int output_tokens;   //output 토큰 수
    }

   //error 답변인 경우 해당 함수 호출
    public static ClaudeResponseApiDto getClaudeErrorDto(String errorMessage) {
        ClaudeResponseApiDto claudeResponseApiDto = new ClaudeResponseApiDto();
        Content content = new Content();
        content.setText(errorMessage);
        claudeResponseApiDto.setType("error");
        claudeResponseApiDto.setContent(List.of(content));
        return claudeResponseApiDto;
    }
}

 

 


 

- api 키 저장하기

위에서 발급받은 자신의 api키를 application.yml에 넣는다.

application.yml

claude-api:
   api-key: 여기에 키값을 넣어주세요
   base-url: https://api.anthropic.com/v1/messages		#기본 claude api url, 변경필요x

 

api 키는 공개되지 않도록 주의해야 하는데, 특히 프로젝트를  github에 저장할 때 공개되기 쉽다. 이를 방지하기 위해 키값을  application.yml에 넣어 숨긴다.


- Claude Config 구현

요청 보낼때의 header 부분을 설정한다.

application.yml 에서 가져온 본인 key값과 claude에서 정한 기본 값들을 넣어준다.

ClaudeConfig

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class ClaudeConfig {
    @Getter
    private WebClient webClient;

    public ClaudeConfig(@Value("${claude-api.api-key}") String secretKey,
                        @Value("${claude-api.base-url}") String baseUrl) {
        this.webClient = WebClient.builder()
                .baseUrl(baseUrl)					//claude가 정한 기본값
                .defaultHeader("x-api-key", secretKey)			//본인 키값
                .defaultHeader("anthropic-version", "2023-06-01")	//claude가 정한 기본값
                .defaultHeader("content-type", "application/json")	//claude가 정한 기본값
                .build();
    }
}

 


- Service 구현

이제 위 DTO를 이용하여 api와 연동하는 함수를 만들면 된다.

이때 Mono를 사용하는데, 이는 api 호출을 비동기적으로 처리하기 위함이다. Claude api 호출은 네트워크 요청이라 시간이 걸리기 때문에 비동기적으로 처리해야 한다.

또한 Mono는 에러 핸들링 연산자를 제공하여 예외 상황에 의해 프로그램이 중단되지 않게 한다.

 

Service

@Service
public interface ClaudeService {
    Mono<ClaudeResponseApiDto> sendApiRequest(String prompt);
}

ServiceImpl

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ClaudeServiceImpl implements ClaudeService {
    private final ClaudeConfig claudeConfig;

    private String modelVersion = "claude-3-haiku-20240307";	//사용할 모델명
    private int maxTokens = 1000;					//최대 사용 가능한 토큰 수

    /*
     * 입력 매개변수 : "질문내용"
     * 출력 : api의 답변
     * */
    public Mono<ClaudeResponseApiDto> sendApiRequest(String prompt) {
        ClaudeRequestApiDto request = ClaudeRequestApiDto.builder()		//요청 DTO 생성하기
                .model(modelVersion)
                .messages(List.of(ClaudeRequestApiDto.Message.builder()
                        .role("user")
                        .content(prompt)		
                        .build()))
                .max_tokens(maxTokens)
                .build();

        return claudeConfig.getWebClient().post()		//POST 요청하고 답변받기
                .bodyValue(request)
                .retrieve()
                .bodyToMono(ClaudeResponseApiDto.class)
                .onErrorResume(error -> {				//에러날 시 error 답변 받기
                    System.out.println("Claude Api Error: "+ error.getMessage());
                    return Mono.just(ClaudeResponseApiDto.getClaudeErrorDto(error.getMessage()));
                });
    }


}

- Controller 구현

controller

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

@RestController          
@RequiredArgsConstructor
@RequestMapping("/api/claude")
public class ClaudeController {
    private final ClaudeService claudeService;

    @PostMapping("/")
    public Mono<ClaudeResponseApiDto> apiRequest(@RequestBody String prompt) {
        return claudeService.sendApiRequest(prompt);
    }
}

 

 


사용해보기

Postman을 이용하여 요청을 보내봤다.

 

- 정상 답변 받아보기

body에 질문 내용인 "hi claude!"를 적어 요청을 보냈다.

claude 로부터 온 답변이다.

max_token을 50으로 작게 설정했더니 답변 내용이 중간에 끊겼다. 그래서 "stop_reason" 값이 "max_tokens"로 나왔다. (보통은 "end_turn"이라 뜬다)

 

 

- 오류 답변 받아보기

api 키값을 다른 값으로 변경하여 요청을 보냈다.

 

그랬더니 "type"값이 "error"로 뜨고 "content"의 "text"값이 "401 Unauthorized from POST https://api.anthropic.com/v1/messages" 로 변경되었다.

 

- error 구별 방법

error 답변을 통해 에러의 원인을 확인할 수 있다.

위의 경우 오류코드가 401이므로 api 키에 문제가 있다는 걸 짐작할 수 있다.