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

kwak woojong·2022년 11월 29일
0

로또 번호 생성기

목록 보기
5/5
post-thumbnail

아아아

CSS는 아에 하기 싫다...

그냥 Json으로 뿌릴까? ㅠㅠㅠ

최근 알고리즘쪽에 재미 들려서 그거 하다가 이거 소홀히 하게 됨 ㅋㅋ

대충 검색 페이지 만든거...

추가 된 기능이라 하면

  1. 지금 db상 최신 당첨 회차, 날짜가 보임
  2. 서버 껐다가 키면 알아서 당첨 이력을 최신화 시키지만, 수동 버튼도 하나 만들어 봄
  3. 에러 처리
  4. 검색시 각 번호당 당첨된 횟수랑 최다빈도 최소빈도 당첨 번호를 보여준다.

1. 최신 당첨 회차, 날짜 삽입

get 요청을 할 때 아에 모델에 넣어서 보내기로 했다.

    @GetMapping("/search")
    public String searchLottoCountCondition(Model model) {

        model.addAttribute(new WinNumberSearchDto());
        model.addAttribute("leastCount", leastDrawNumberAndDateDto);

        return "searchLotto";
    }

실은 dto 클래스를 메서드를 통해 매번 생성해서 넘기는 방식이었는데, 에러 핸들을 하다 보니 그냥 메서드를 빈 등록해서 상수처럼 때리면 어떨까 싶었음.

    @Bean
    public LeastDrawNumberAndDateDto setLeastDrawNumberAndDateDto() {
        Pageable pageable = PageRequest.of(0, 1, Sort.Direction.DESC, "drawNo");
        Page<WinNumber> find = winNumberRepository.findLastIndex(pageable);
        List<WinNumber> content = find.getContent();
        WinNumber winNumber = content.get(0);
        return new LeastDrawNumberAndDateDto(winNumber.getDrawNoDate(), winNumber.getDrawNo());
    }

이제 보니까 그냥 메서드 처리 했어도 될 거 같은데, 이미 이렇게 한거 일단 냅둬보고 뭔 문제가 생기면 다시 돌리면 되겠지 싶음

  <div class="menu-name">
    <h2>로또 당첨 이력 횟수 조회</h2>
    <div th:object="${leastCount}">
      <h3>최신 당첨 이력 날짜</h3>
      <p th:text="*{drawNoDate}">최신 당첨 날짜</p>

      <h3>최신 당첨 회차</h3>
      <p th:text="${leastCount.drawNo} + '회'">최신 당첨 회차</p>
    </div>
  </div>

꾸미는거 나중에 생각하고 일딴 때려 박아줬다.


2. 당첨이력 수동 새로고침 버튼

  <div>
    <button class="refresh"> 당첨 이력 새로 고침 보턴 </button>
  </div>

버튼 만들고...

<script>
  const refresh = document.querySelector(".refresh");

  refresh.addEventListener("click", () => {
    alert("당첨 이력을 확인 하고 최신화 합니다.");
    location.href='refresh';
  })

</script>

그냥 새로고침 되면 잘 모르니까 얼럿창을 띄우기로함. 그리고 refresh 요청을 때린다.

    @GetMapping("/refresh")
    public String refreshWinNumberDB() {
        winNumberService.refreshWinNumber();
        return "redirect:search";
    }
    public void refreshWinNumber() {
        setUp.saveWinNumberForStart();
    }

굳이 이렇게 계층을 나눠야 하나 싶기도 함. 그냥 setup을 DI 받아서 쓸까...


3. 에러 처리

몇 개의 당첨 회차를 분석할 것인가! 에서 숫자를 입력받는데

이게 0이면 에러를 넣어줘야 한다.

  <form action="/search" th:object="${winNumberSearchDto}" method="post" >
    <label>최근 회차 기준 몇 회 전까지 분석합니까? (ex : 100을 입력할 경우! 최신회차-100 ~ 최신회차)</label>
    <br>
    <input type="text" th:field="*{count}" th:errorclass="field-error">
    <div class="field-error" th:errors="*{count}"> 잘못 된 데이터 </div>

    <button type="submit"> 검색 </button>
  </form>

html은 이런 식으로 해두자. 에러가 있으면 알아서 뻘개지게 해놨다.

    @PostMapping("/search")
    public String searchLottoCount(@Valid WinNumberSearchDto winNumberSearchDto,
                                   BindingResult bindingResult,
                                   Model model) {
        if (bindingResult.hasErrors()) {
            // 에러 처리
            log.error("에러 발생! = {}", bindingResult);
            model.addAttribute("leastCount", leastDrawNumberAndDateDto);
            return "searchLotto";
        }

        HistoryNumber historyNumber = winNumberService.makeHistoryNumber(winNumberSearchDto.getCount());
        model.addAttribute("result", historyNumber);

        return "winCount";
    }

bindingResult에 에러가 있다면 로그를 찍어주고 searchLotto로 넘어간다.
이 때, 최신 당첨 회차 값을 넘겨줘야 해서 모델에 Dto를 하나 넘겨줬다.


4. 최다빈도, 최소빈도 당첨 숫자 나오게 하기

    public HistoryNumber makeHistoryNumber(int count) {

        int[] countNumbers = new int[45];
        Map<Integer, Integer> mostNumbers = new TreeMap<>();
        Map<Integer, Integer> minNumbers = new TreeMap<>();

        List<WinNumber> content = setContent(count);
        setCountNumbers(content, countNumbers);
        setTreeMaps(countNumbers, mostNumbers, minNumbers);

        return new HistoryNumber(mostNumbers, minNumbers, countNumbers);
    }
    
    private List<WinNumber> setContent(int count) {
        Pageable pageable = PageRequest.of(0, count, Sort.Direction.DESC, "drawNo");
        Page<WinNumber> find = winNumberRepository.findLastIndex(pageable);
        List<WinNumber> content = find.getContent();
        return content;
    }

	private void setTreeMaps(int[] countNumbers, Map<Integer, Integer> mostNumbers, Map<Integer, Integer> minNumbers) {
        int max = Arrays.stream(countNumbers).max().getAsInt();
        int min = Arrays.stream(countNumbers).min().getAsInt();
        for (int i = 0; i < countNumbers.length; i++) {
            int countNumber = countNumbers[i];
            if (countNumber == max) {
                mostNumbers.put(i + 1, countNumber);
            } else if (countNumber == min) {
                minNumbers.put(i + 1, countNumber);
            }
        }
    }

   	private static void setCountNumbers(List<WinNumber> content, int[] countNumbers) {
        content.stream()
                .forEach(e -> {
                    int winNumber1 = e.getWinNumber1() - 1;
                    int winNumber2 = e.getWinNumber2() - 1;
                    int winNumber3 = e.getWinNumber3() - 1;
                    int winNumber4 = e.getWinNumber4() - 1;
                    int winNumber5 = e.getWinNumber5() - 1;
                    int winNumber6 = e.getWinNumber6() - 1;

                    countNumbers[winNumber1]++;
                    countNumbers[winNumber2]++;
                    countNumbers[winNumber3]++;
                    countNumbers[winNumber4]++;
                    countNumbers[winNumber5]++;
                    countNumbers[winNumber6]++;

                });
    }

이 전 방식은 int[] 인덱스가 당첨 번호를 뜻하는 거라 전체 번호를 넘기면 괜찮은데 아닐 경우 앞단에 보내기가 애매했음. 그래서 map을 활용했다.

setContent는 db에서 당첨 번호들을 불러오는 역할
setCountNumbers는 int[]를 채우는 역할이다.
setTreeMaps는 최다빈도, 최소빈도 번호들을 각각 넣어준다.

    <h3>가장 당첨 빈도가 높은 숫자</h3>
    <ul th:each="mostNumber : ${result.getMostNumbers()}">
        <li> <span th:text="${mostNumber.getKey()} + ' : ' + ${mostNumber.getValue()} + '회'"> </span> </li>
    </ul>

    <h3>가장 당첨 빈도가 낮은 숫자</h3>
    <ul th:each="minNumber : ${result.getMinNumbers()}">
        <li> <span th:text="${minNumber.getKey()} + ' : ' + ${minNumber.getValue()} + '회'"> </span> </li>
    </ul>

이런 식으로 html을 대충 후려갈겨 주자.


그럼 이런 화면이 된다.


RestApi만 만들다가 모델을 쓰려니까 에러 핸들이 좀 헷갈렸었다.

그래도 뭐 아직 어려운 부분은 하진 않고 있으니...

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

0개의 댓글

관련 채용 정보