심심해서 혼자 만드는 토이 프로젝트 - 로또 번호 생성기 - 3

kwak woojong·2022년 10월 31일
0

로또 번호 생성기

목록 보기
3/5
post-thumbnail

아무래도 매번 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주 밀린다든가 할 수 있기에 상기 코드도 장점이 있어 보이긴 함. 근데 코드가 더럽자나아아ㅏ

하여튼 잘 저장이 되는 것으로 보인다. 아주 굳

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

0개의 댓글