HTTP
https://developer.mozilla.org/ko/docs/Web/HTTP/Messages
HTTP 메시지는 3개로 구분이 가능(empty line은 제외) : start-line, HTTP headers, body
ResponseEntity
HttpEntity를 상속받는 결과 데이터와 HTTP 상태 코드를 제어할 수 있는 클래스
HttpRequest에 대한 응답 데이터 포함
구조
HttpStatus (상태 확인)
HttpHeaders ()
HttpBody (resource 자원)
스프링 프레임워크에서 제공하는 HTTP 통신을 간편하게 처리하기 위한 클래스
Http 프로토콜의 메서드에 맞는 여러 메서드 제공
RESTful 형식을 갖춘 템플릿
HTTP 요청 후 JSON,XML,문자열 등의 다영한 형식으로 응답받을수 있음
블로킹 기반의 동기 방식 사용
다른 API 호출할 때 HTTP 헤더에 다양한 값 설정 가능
<get,post,put,delete>ForEntity 메소드를 이용
우리의 서버에서 다른 서버로 요청, 응답을 쉽게 보낼 수 있음
Jackson 라이브러리에서 제공하는 클래스로, 자바 객체와 JSON 데이터 간의 변환을 처리하는 데 사용됨
JSON <-> Java 변환시 사용하는 Jackson 라이브러리의 클래스
대표적인 메소드
server.port=8081
server.servlet.context-path=/
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#Naver Search API-Book (보안 상의 이유로 참조해서 사용함)
naver.search.api.book.client.id=Test
naver.search.api.book.client.secret=Test
Book, Post, User, NaverResult DTO는 생략함 (특이 사항 없음)
기본 생성자로 query 필드를 제외한 나머지 필드의 기본값을 지정함 (유동적인 쿼리스트링 활용을 위해서)
package naver.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class NaverRequest {
private String query;
private Integer display;
private Integer start;
private String sort;
public NaverRequest() {
this.display = 10;
this.start = 1;
this.sort = "sim";
}
}
@Value 어노테이션을 이용해서 OpenAPI의 properties의 id, secret을 참조함
URI, UriComponentsBuilder를 이용해서 url을 대체함
getForEntity 대신 exchange 메소드를 이용함 (RequestEntity를 사용하기 위해서)
getForEntity는 RequestEntity를 파라미터로 받을 수 없음
package naver.controller;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import naver.dto.Book;
import naver.dto.NaverRequest;
import naver.dto.NaverResult;
import naver.dto.Post;
import naver.dto.User;
@Controller
public class BookController {
// Naver Search API-Book
@Value("${naver.search.api.book.client.id}")
private String clientId;
@Value("${naver.search.api.book.client.secret}")
private String clientSecret;
@GetMapping(value = "main")
public String main() {
return "main";
}
@GetMapping("/rest-template")
public void getRestTemplate() {
// https://jsonplaceholder.typicode.com/posts
String url = "https://jsonplaceholder.typicode.com/posts";
User user = new User("dev", 27);
// RestTemplate 객체 생성
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
// ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// writeValue : java -> json
// writeValue(결과 파일, java객체)
// readValue : json -> java
// readValue(대상 파일, java객체)
try {
String jsonObject = "{\"name\":\"dev\",\"age\":27}";
String jsonArrayObject = "[{\"name\":\"dev\",\"age\":27}, {\"name\":\"devops\",\"age\":30}]";
// java -> json
// objectMapper.writeValue(new File("user.json"), user);
// 문자열 -> java
User convertedUser = objectMapper.readValue(jsonObject, User.class);
// json -> java
// User convertedUser = objectMapper.readValue(new File("user.json"), User.class);
// System.out.println(convertedUser);
// jsonArray -> java
List<User> userList = objectMapper.readValue(jsonArrayObject, new TypeReference<List<User>>() {});
// System.out.println(userList);
// responseEntity.getBody() -> java
List<Post> bookList = objectMapper.readValue(responseEntity.getBody(), new TypeReference<List<Post>>() {});
// System.out.println(bookList);
} catch (StreamWriteException e) {
e.printStackTrace();
} catch (DatabindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/api/search/book")
public String naverSearchBookAPI(@ModelAttribute NaverRequest naverRequest,
Model model) {
// String url = "https://openapi.naver.com/v1/search/book.json?query=" + query;
// URI
URI uri = UriComponentsBuilder
.fromUriString("https://openapi.naver.com")
.path("/v1/search/book.json")
.queryParam("query", naverRequest.getQuery())
.queryParam("display", naverRequest.getDisplay())
.queryParam("start", naverRequest.getStart())
.queryParam("sort", naverRequest.getSort())
.encode()
.build()
.toUri();
// requestEntity : header에 id, secret 지정하여 요청을 보내야 하기 때문!
RequestEntity<Void> requestEntity = RequestEntity
.get(uri)
.header("X-Naver-Client-Id", clientId)
.header("X-Naver-Client-Secret", clientSecret)
.build();
// restTemplate
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class);
// ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
NaverResult naverResult = null;
try {
naverResult = objectMapper.readValue(responseEntity.getBody(), NaverResult.class);
}catch(JsonMappingException e) {
e.printStackTrace();
}catch(JsonProcessingException e) {
e.printStackTrace();
}
List<Book> bookList = naverResult.getItems();
model.addAttribute("bookList", bookList);
return "book";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Book List</title>
</head>
<body>
<h1>Naver Search API : Book</h1>
<table border="1">
<tr>
<th>ISBN</td>
<th>이미지</td>
<th>도서명</td>
<th>저자/출판사</td>
<th>출판일</td>
</tr>
<c:forEach var="book" items="${bookList}">
<tr>
<td>${book.isbn}</td>
<td><img src="${book.image}" alt="${book.title}" width="80"></td>
<td><a href="${book.link}">${book.title}</a></td>
<td>${book.author} / ${book.publisher}</td>
<td>${book.pubdate}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
문제 발생 1. :내 서버에서 naverAPI url로 요청을 보내면 id 미존재 예외가 발생함 (인증실패)
해결 : RequestEntity를 통해 header에 id, scrit를 담아서 보내야함
문제 발생 2. : 쿼리스트링에 특정 값의 유무에 따라 url이 달라지므로 이를 처리하는 과정을 합치거나 분할 시켜야 함
해결 : URI, RequestDTO (@ModelAttribute)를 사용해서 유동적인 url을 만들 수 있음
Builder 패턴을 점점 더 많이 사용하는 것 같음
클라이언트 --> 서버
서버 ---> 서버
위처럼 요청과 응답의 대상과 피대상을 잘 이해해야할 것으로 예상됨