우선 단 회차 화면을 불러보도록 하자
그러기 위해 이전 글에서 적은 로또 번호 주소를 이용해야함.
https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo=1000
상기 주소로 접속하면 Json으로 정보를 준다.
@Configuration
@Slf4j
@RequiredArgsConstructor
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
RestTemplate restTemplate = restTemplateBuilder
.requestFactory(() -> new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.setConnectTimeout(Duration.ofMillis(5000))
.setReadTimeout(Duration.ofMillis(5000))
.additionalMessageConverters(new StringHttpMessageConverter(Charset.forName("UTF-8")))
.errorHandler(new RestTemplateResponseErrorHandler())
.build();
restTemplate.getInterceptors().add(((request, body, execution) -> {
ClientHttpResponse response = execution.execute(request,body);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response;
}));
return restTemplate;
}
}
RestTemplate를 이용해서 API 요청을 해보자. 저 친구는 이제 안쓰는 친구로 알고 있다. 근데 난 WebFlux로 뒷단 제작하는게 익숙치 않아서 최근 바뀐 애 말고 일단 이 친구로 써보기로 함.
최근엔 WebFlux를 사용하는 WebClient를 쓴다고 함. 그거 알고 싶으면 이 벨로그 참고 ㄱ
https://tecoble.techcourse.co.kr/post/2021-07-25-resttemplate-webclient/
하튼 상기 코드에서 인터셉터 추가를 안하니까 response가 Application/json으로 오는게 아니라 다른 거로 와서 파싱이 안되더라.
인터셉터를 통해 response를 강제로 application/json으로 변경해준다.
에러 핸들도 일단 가볍게 쓰고 나중에 더 만지도록 하자.
@Slf4j
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus.Series series = response.getStatusCode().series();
return series == HttpStatus.Series.CLIENT_ERROR
|| series == HttpStatus.Series.SERVER_ERROR;
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus.Series series = response.getStatusCode().series();
if (series == HttpStatus.Series.SERVER_ERROR) {
log.error("server error = {}", response);
return;
}
if (series == HttpStatus.Series.CLIENT_ERROR) {
log.error("client error = {}", response);
return;
}
}
}
일단 400, 500 에러를 제외하고는 터지지 않는다.
이렇게 RestTemplate을 Bean으로 등록 시켜 놓고 컨트롤러에서 DI받아서 api 호출을 해보자
@Controller
@RequiredArgsConstructor
@RequestMapping
@Slf4j
public class DhLotteryApiController {
private final RestTemplate restTemplate;
@GetMapping("/win-number/{drawNo}")
public String getLottoNumber(@PathVariable String drawNo,
Model model) {
URI uri = UriComponentsBuilder.fromUriString("https://www.dhlottery.co.kr/common.do")
.queryParam("method","getLottoNumber")
.queryParam("drwNo", drawNo)
.build()
.toUri();
WinNumberDto winNumberDto = restTemplate.getForObject(uri, WinNumberDto.class);
model.addAttribute("result", winNumberDto);
return "winNumber";
}
uri 주소를 만들어주자. 그리고 호출해서 받아온 데이터를 model에 추가.
만약 상기 RestTemplate에서 인터셉터를 추가하여 response의 Content-type를 강제로 바꾸지 않으면 여기서 에러가 터진다. 타입이 안맞아서 파싱을 못 함.
Dto는 JsonProperty를 써서 내가 원하는 변수명으로 바꿔줬다.
여기서 문제가 발생하는데 내가 타임리프를 드럽게 오랜만에 쓰기 때문에 일단 잘 작동 하는지 확인하기 위해서 테스트 코드랑 타임리프 활용을 둘 다 해봤음.
@SpringBootTest
class DhLotteryApiControllerTest {
@Autowired
DhLotteryApiController dhLotteryApiController;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(dhLotteryApiController).build();
}
@Test
void getLottoNumber() throws Exception {
//given
WinNumberDto winNumberDto = WinNumberDto.builder()
.totalSellAmount(118628811000L)
.firstWinAmount(1246819620L)
.drawNoDate(LocalDate.parse("2022-01-29", DateTimeFormatter.ISO_DATE))
.drawNo(1000)
.winNumber1(2)
.winNumber2(8)
.winNumber3(19)
.winNumber4(22)
.winNumber5(32)
.winNumber6(42)
.bonusNumber(39).build();
ResultActions actions = mockMvc.perform(
get("/win-number/1000")
);
actions
.andExpect(status().isOk())
.andExpect(model().attribute("result", winNumberDto));
}
테스트 코드는 일단 잘 작동한다. 다행히
이제 타임리프를 위한 html을 조금 만져보자. css는 아직 적용하지 않았음.
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Lotto Number Maker</title>
<!-- Custom styles for this template -->
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container" th:object="${result}">
<div class="menu-name">
<h2>로또 단일 회차 당첨 번호 조회</h2>
</div>
<ul>
<li> 총 판매 금액 : <span th:text="*{totalSellAmount}" > 100 </span> </li>
<li> 1등 당청금 : <span th:text="*{firstWinAmount}"> 100 </span> </li>
<li> 날짜 : <span th:text="*{drawNoDate}"> 2022-10-28 </span> </li>
<li> 회차 : <span th:text="*{drawNo}"></span> </li>
<li> 당첨번호 1 : <span th:text="*{winNumber1}"></span> </li>
<li> 당첨번호 2 : <span th:text="*{winNumber2}"></span> </li>
<li> 당첨번호 3 : <span th:text="*{winNumber3}"></span></li>
<li> 당첨번호 4 : <span th:text="*{winNumber4}"></span></li>
<li> 당첨번호 5 : <span th:text="*{winNumber5}"></span></li>
<li> 당첨번호 6 : <span th:text="*{winNumber6}"></span></li>
<li> 보너스 번호 : <span th:text="*{bonusNumber}"></span></li>
</ul>
</div>
</body>
</html>
우선 간단하게 이렇게만 해주자. css는 나중에 추가하기 위해서 일단 링크 박아둠.
근데 헤더 부분은 앵간하면 통일이라 나중에 따로 빼줄 생각이다. 일단 이렇게 ㄱ
잘 출력 된다. 타임리프 오랜만에 하니까 다 까먹어서 이리저리 검색했음... ㅋㅋㅋ