728x90
320x100
🎯 오늘의 목표
- 책(Book) - Spring WebClient로 외부 API 연동: KakaoBookClient 구현
⚙️ 진행한 작업
- 책(Book) - Spring WebClient로 외부 API 연동: KakaoBookClient 구현
📌 build.gradle : 의존성 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}
- spring-boot-starter-web만으로는 WebClient가 포함되지 않아서, WebClient를 쓰려면 반드시 spring-boot-starter-webflux 의존성을 추가해야 함.
✅ Spring WebClient?
- WebClient는 Spring에서 제공하는 최신 HTTP 클라이언트
- 내 서버에서 카카오 API 같은 외부 서버로 비동기 요청을 보낼 때 사용
- RestTemplate의 후계자이자 고성능 서비스를 위한 선택
- 예:
- 내 서버 → Kakao API로 GET 요청 → 응답 받아오기
- 내 서버 → 다른 마이크로서비스로 POST 요청 → 결과 처리
WebClient | RestTemplate | |
동기/비동기 | 동기 (blocking) | 비동기 (non-blocking) |
스레드 효율 | 요청당 스레드 필요 | 요청당 스레드 불필요, 효율적 |
성능 | 단순 앱은 OK, 대규모에서 부하 큼 | 대규모, 고성능 서비스에 적합 |
지원 상태 | Spring 5 이후 Deprecated 예정 | Spring 5 이후 공식 후계자 |
✅ WebClient를 선택하게 된 이유
외부 API 요청을 백엔드에서 직접 처리하려고 WebClient를 선택한 거고, RestTemplate보다 최신 방식이라 WebClient로 간거임.
📌 도서 API 고르기 : 카카오 도서 API 선택
예전에 다른 사이드 프로젝트에서도 도서 API가 필요해서 카카오 도서 API를 선택했던적이 있고 그 과정을 아래의 포스팅에 정리해두었음.
[개발일지#014] 도서 API 적용하기 (Feat. 카카오 도서 API)
tmi.정말 오랜만에 다시 시작한 사이드 프로젝트작년 9월에 Ver1을 마무리 지었으니 5개월만에 다시 꺼내본다...!5개월만에 코드를 다시보니 너무나 새로워...!Ver1을 마치면서 앞으로 추가될 기능으
ddururiiiiiii.tistory.com
추가로 API 키 받는건 이 포스팅에서는 생략하였음.
📌 KakaoBookClient 생성
package ddururi.bookbookclub.global.external.kakao;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
@Component
@RequiredArgsConstructor
public class KakaoBookClient {
private final WebClient webClient = WebClient.builder()
.baseUrl("https://dapi.kakao.com")
.defaultHeader("Authorization", "KakaoAK {REST_API_KEY}") // REST_API_KEY 자리에 실제 키 넣기
.build(); // REST_API_KEY 자리에 실제 키 넣기
public KakaoBookSearchResponse searchBooks(String query) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/v3/search/book")
.queryParam("query", query)
.build())
.retrieve()
.bodyToMono(KakaoBookSearchResponse.class)
.block(); // 비동기 → 동기로 받음
}
}
📌 KakaoBookClient를 따로 만든 이유
1️⃣ 역할 분리
- KakaoBookClient는 백엔드에서 카카오 책 API만 전담해서 호출하는 컴포넌트.
- 컨트롤러, 서비스 로직에서 외부 API 호출을 직접 다루지 않고 → 별도의 클라이언트로 캡슐화.
- 코드 재사용성 높이고, 유지보수 쉽게 만들기 위해 분리.
2️⃣ 서버-서버 통신 필요
- 일부 서비스에서는 프론트에서 외부 API 호출이 아니라, 백엔드에서 외부 API로 직접 요청하고 → 가공된 데이터를 프론트에 내려주는 방식이 필요.
- 예: 프론트에서 /api/books/search 호출 → 백엔드에서 KakaoBookClient가 카카오 API 호출 → 결과를 가공해 프론트로 반환.
3️⃣ API 키 보안
- 카카오 API 키는 노출되면 안 되는 민감 정보.
- 프론트에서 직접 카카오 API를 호출하면 키가 노출되기 때문에, 백엔드에서 대신 호출하면 보안을 강화할 수 있음.
📌 카카오 응답 DTO 생성, 수정
@Getter
public class KakaoBookSearchResponse {
private List<KakaoBookDocument> documents;
}
package ddururi.bookbookclub.global.external.kakao;
import lombok.Getter;
import java.util.List;
@Getter
public class KakaoBookDocument {
private String title;
private List<String> authors;
private String publisher;
private String isbn;
private String thumbnail;
}
/**
* 책 등록 요청 DTO
*/
@Getter
@NoArgsConstructor
public class BookRequest {
@NotBlank(message = "제목은 비어 있을 수 없습니다.")
private String title;
private List<String> authors; //수정
private String publisher;
private String isbn;
private String thumbnailUrl;
}
package ddururi.bookbookclub.domain.book.service;
import ddururi.bookbookclub.domain.book.dto.BookRequest;
import ddururi.bookbookclub.domain.book.dto.BookResponse;
import ddururi.bookbookclub.domain.book.entity.Book;
import ddururi.bookbookclub.domain.book.repository.BookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 책 관련 비즈니스 로직 처리
*/
@Service
@RequiredArgsConstructor
@Transactional
public class BookService {
private final BookRepository bookRepository;
/**
* 책 등록
* @param request 책 등록 요청 데이터
* @return 등록된 책 정보
*/
public BookResponse createBook(BookRequest request) {
String author = (request.getAuthors() != null && !request.getAuthors().isEmpty())
? String.join(", ", request.getAuthors())
: "알 수 없음"; //추가
Book book = Book.create(
request.getTitle(),
author, //수정
request.getPublisher(),
request.getIsbn(),
request.getThumbnailUrl()
);
Book savedBook = bookRepository.save(book);
return new BookResponse(savedBook);
}
}
📌 BookController 수정
package ddururi.bookbookclub.domain.book.controller;
* 책 관련 API 컨트롤러
* - 등록
*/
@RestController
@RequestMapping("/api/books")
@RequiredArgsConstructor
public class BookController {
private final BookService bookService;
private final KakaoBookClient kakaoBookClient;
@GetMapping("/search")
public ResponseEntity<ApiResponse<KakaoBookSearchResponse>> searchBooksFromKakao(
@RequestParam String query
) {
KakaoBookSearchResponse response = kakaoBookClient.searchBooks(query);
return ResponseEntity.ok(ApiResponse.success(response));
}
//코드생략//
}
[포스트맨으로 테스트하기]
GET http://localhost:8080/api/books/search?query=클린코드
[테스트 결과 : 성공]
728x90
320x100
'💻 뚝딱뚝딱 > 북북클럽' 카테고리의 다른 글
[JPA/QueryDSL] 적용하기 (0) | 2025.05.02 |
---|---|
[개발일지 #030] 좋아요(Like) 랭킹에서 피드 상세정보 함께 내려주기 (0) | 2025.05.02 |
[개발일지 #028] 좋아요 순 피드 조회 (주간/월간/연간/누적) (0) | 2025.05.01 |
[개발일지 #027] 신고(Report) 누적 시, 블라인드 처리 (0) | 2025.05.01 |
[개발일지 #026] 신고(Report) 도메인 구현 및 단위테스트 (0) | 2025.05.01 |