심심해서 혼자 만드는 토이 프로젝트 - 로또 번호 생성기 - 2 (RestTemplate content-type 에러)

kwak woojong·2022년 10월 28일
0

로또 번호 생성기

목록 보기
2/5
post-thumbnail

우선 단 회차 화면을 불러보도록 하자

그러기 위해 이전 글에서 적은 로또 번호 주소를 이용해야함.

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는 나중에 추가하기 위해서 일단 링크 박아둠.

근데 헤더 부분은 앵간하면 통일이라 나중에 따로 빼줄 생각이다. 일단 이렇게 ㄱ

잘 출력 된다. 타임리프 오랜만에 하니까 다 까먹어서 이리저리 검색했음... ㅋㅋㅋ

profile
https://crazyleader.notion.site/Crazykwak-36c7ffc9d32e4e83b325da26ed8d1728?pvs=4<-- 포트폴리오

0개의 댓글