아무래도 매번 API를 불러대면 속도가 느릴거 같았음.
그래서 차라리 DB에 다 저장해두면 어떨까 싶었다.
{"totSellamnt":118628811000,
"returnValue":"success",
"drwNoDate":"2022-01-29",
"firstWinamnt":1246819620,
"drwtNo6":42,"drwtNo4":22,
"firstPrzwnerCo":22,
"drwtNo5":32,
"bnusNo":39,
"firstAccumamnt":27430031640,
"drwNo":1000,
"drwtNo2":8,
"drwtNo3":19,
"drwtNo1":2}
이게 API를 성공적으로 불렀을 때 모습이고, (1039회차)
{"returnValue":"fail"}
이게 회차가 없을 때 모습이다. (1100회차)
즉 returnValue 값을 받아서 최신 회차까지 확인할 수 있을 것 같았음.
해서 처음 서버를 가동할 때, @PostConstruct를 이용하면, DB에 오늘 날짜 최신회차까지 저장 가능할꺼라 생각했다.
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class WinNumberService {
private final WinNumberRepository winNumberRepository;
private final RestTemplate restTemplate;
private final WinNumberMapper mapper;
@PostConstruct
public List<WinNumber> saveWinNumberForStart() {
log.info("최신 당첨 회차 조회 시작");
Pageable pageable = PageRequest.of(0, 1, Sort.Direction.DESC, "drawNo");
Page<WinNumber> page = winNumberRepository.findLastIndex(pageable);
List<WinNumber> content = page.getContent();
List<WinNumber> total = new ArrayList<>();
Spring Data JPA 메서드 이름으로 쿼리 만드는게 익숙치 않아서 그냥 Pageable를 사용했다.
findLastIndex()는 지금 보니까 메서드 이름을 다른 걸로 할걸 그랬다. 이따 고쳐야징
하튼 저렇게 page로 불러온 후 Content를 뺐음.
그리고 saveAll()을 이용해서 저장하기 위해 빈 리스트를 선언해뒀다.
long drawNo = 0L;
if (content.size() == 1) {
drawNo = content.get(0).getDrawNo();
}
log.info("DB에 저장되어 있는 최신 회차 = {}", drawNo);
while (drawNo <= 10000) {
drawNo++;
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);
log.info("새로 검색된 회차 = {}", winNumberDto.getDrawNo());
if (winNumberDto.getReturnValue().equals("fail")) {
break;
}
total.add(mapper.WinNumberDtoToWinNumber(winNumberDto));
}
그 후 content에서 뽑은 DB에 남아 있는 마지막 회차를 뽑는다. drawNo가 그것.
while문이 좀 깔끔하지 않다. 조건을 뭔가 신박한거로 했음 좋겠는데 flag 같은 거로 하는게 더 좋을라나...
그냥 true 박기엔 내가 예상치 못한 부분에서 무한호출 날까봐 일단 10000회차 이하로 잡아줬다.
이렇게 되면 total에 하나하나 회차가 쌓이게 됨.
log.info("검색 종료 DB 저장 시작");
List<WinNumber> winNumbers = winNumberRepository.saveAll(total);
log.info("저장된 회차 갯수 = {}", winNumbers.size());
return winNumbers;
saveAll을 이용해서 저장해주자. 아마 통짜로 저장하는 더 좋은 방법이 있을거 같기도 한데...
하튼 이러면 최신회차까지 저장이 된다.
통으로 적으면 대충 이렇다.
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class WinNumberService {
private final WinNumberRepository winNumberRepository;
private final RestTemplate restTemplate;
private final WinNumberMapper mapper;
@PostConstruct
public List<WinNumber> saveWinNumberForStart() {
log.info("최신 당첨 회차 조회 시작");
Pageable pageable = PageRequest.of(0, 1, Sort.Direction.DESC, "drawNo");
Page<WinNumber> page = winNumberRepository.findLastIndex(pageable);
List<WinNumber> content = page.getContent();
List<WinNumber> total = new ArrayList<>();
long drawNo = 0L;
if (content.size() == 1) {
drawNo = content.get(0).getDrawNo();
}
log.info("DB에 저장되어 있는 최신 회차 = {}", drawNo);
while (drawNo <= 10000) {
drawNo++;
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);
log.info("새로 검색된 회차 = {}", winNumberDto.getDrawNo());
if (winNumberDto.getReturnValue().equals("fail")) {
break;
}
total.add(mapper.WinNumberDtoToWinNumber(winNumberDto));
}
log.info("검색 종료 DB 저장 시작");
List<WinNumber> winNumbers = winNumberRepository.saveAll(total);
log.info("저장된 회차 갯수 = {}", winNumbers.size());
return winNumbers;
}
}
보기 흉한 부분을 좀 정리하면, uri 생성 부분을 메서드로 빼도 괜찮을 듯.
그리고 현재 WinNumberService에 들어 있는데 차라리 SetUp 같은 클래스를 만들어서 거기로 넘기는게 더 객체 지향적 설계로 보인다.
그리고 최신회차 확인 방법이 그다지 똑똑하지 않았음.
그래서 테스트 부분에선 다른 방법을 써봤는데
@SpringBootTest
@Transactional
class WinNumberServiceTest {
Logger log = (Logger) LoggerFactory.getLogger(WinNumberServiceTest.class);
@Autowired
WinNumberService winNumberService;
@Autowired
WinNumberRepository winNumberRepository;
@Test
void saveWinNumberForStart() {
String baseDateString = "2022-10-29";
LocalDate baseDate = LocalDate.parse(baseDateString);
long baseDrawNo = 1039L;
LocalDate today = LocalDate.now();
long days = baseDate.until(today, ChronoUnit.DAYS);
long latestDrawNo = baseDrawNo + (days / 7);
List<WinNumber> beforeRepositorySize = winNumberRepository.findAll();
int before = beforeRepositorySize.size();
winNumberService.saveWinNumberForStart();
List<WinNumber> afterRepositorySize = winNumberRepository.findAll();
int after = afterRepositorySize.size();
assertThat(after).isEqualTo(latestDrawNo);
assertThat(after).isGreaterThan(before);
}
}
저기선 1039회차를 잡아놨지만, 1회차를 기준으로 계산한다면 굳이 while 없이 가능할 것으로 보인다.
DB 저장된 회차 부르는 건 그대로 하고, 해당 날짜를 기준, 오늘이랑 비교해서 최신 회차가 몇 회인지 계산이 가능함.
차라리 그렇게 하는게 속도도 좀 더 빠를 것 같음. 물론 천재지변이 일어나서 로또 회차가 1주 밀린다든가 할 수 있기에 상기 코드도 장점이 있어 보이긴 함. 근데 코드가 더럽자나아아ㅏ
하여튼 잘 저장이 되는 것으로 보인다. 아주 굳