코딩하는 털보

11 to 9, Day 2 본문

Diary/Eleven to Nine

11 to 9, Day 2

이정인 2021. 2. 19. 20:52

Today, ToDoList

  • Toy Project - NGMA

    • 로그인 redirect /error 확인
    • Schedule List 페이지 data load
    • 새로운 Schedule 추가 구현
    • Schedule 제거/수정 버튼 추가

Login redirect "/error"

어제 애플리케이션 실행 후 최초 로그인 시 /error로 리다이렉트 되던 문제가 있었다.

몇 차례 문제를 검토하면서 알게된 것은 chrome devtools를 이용했을때만 이런 문제가 발생하는것.

기본적으로 로그인 성공시 root page로 이동하도록 해놓았는데, root page에서 아래 사진과 같이 콘솔 화면에 2개의 에러 메시지가 발생하는 것을 확인했다.

이 에러를 없애면 되지 않을까라는 생각이 들어서 bootstrap의 css파일 및 js 파일에서 아래 문장을 제거하였다.

//# sourceMappingURL=bootstrap.bundle.min.js.map

/*# sourceMappingURL=bootstrap.min.css.map */

이로써 devtools를 켜도 이전과 같이 /error로 리다이렉트 되는 문제는 발생하지 않았고 정상적으로 root page로 이동한다. 하지만 명확히 어떤 원인에 의해 발생했던것인지는 아직도 의문이다..

추가로 로그인 했을 때 아래 사진처럼 본적 없던 경고메시지가 나왔었는데, 크롬 브라우저에서 제공하는 보안 서비스라고 한다. 실제로 패스워드가 유출되었을 수 도 있지만 그냥 너무 쉬운 비밀번호를 사용해도 이런 메세지를 받는다고...

https://zdnet.co.kr/view/?no=20191211151642

그래서 비밀번호를 어렵게 바꾸었더니 경고메시지는 발생하지 않았다.


Schedule List data load

두 개의 함수를 만들었다.

  1. ajax로 데이터(json) 불러오기.
  2. json 데이터를 html 테이블에 행 추가하기.
  function loadSchedules() {
    $.ajax({
      url: "http://localhost:8080/schedules",
      type: "GET",
      dataType: "json",
      success: function(data) {
        addRow(data);
      },
      error: function(e) {
        alert("값을 가져오지 못했습니다.");
      }
    });
  }

  function addRow(schedules) {
    $.each(schedules, function (i, item) {
      let tr = $("tbody#schedule").append("<tr></tr>");
      $(tr).append("<td>"+item.dateTime.substr(0,10)+"</td>");
      $(tr).append("<td>"+item.place+"</td>");
      $(tr).append("<td>"+item.title+"</td>");
      if (item.personal == "true") {
        $(tr).append("<td>Alone</td>");
      } else {
        $(tr).append("<td>Together</td>");
      }
    });
  }

문제는... 새로운 일정을 추가하는 템플릿도 만들어야 하지만, 추가를 한 다음에 redirect를 이 일정리스트로 돌아왔으면 좋겠는데 그 방법을 모르겠다. 그리고 가장 가까운 시점의 일정부터 보이도록 쿼리를 변경해야겠다는 생각이 들었다.

그냥 ajax 비동기 통신 안하고 새로운 템플릿 로드하면 쉬울텐데 ajax 쓰면서 여러가지로 어려운거 같다..

일정 추가는 Modal을 띄워서 ajax로 POST 요청하도록 했다.

    <div class="modal fade" id="newScheduleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">일정 등록</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <!-- <form action="/schedule" method="post" th:action="@{/schedule}" th:object="${schedule}"> -->
          <form id="scheduleForm">
            <div class="modal-body">
              <form class="needs-validation" novalidate>
                <div class="col-sm-6">
                  <label for="title" class="form-label">제목</label>
                  <input type="text" class="form-control" id="title" name="title" placeholder="" value="" required>
                  <div class="invalid-feedback">
                    제목을 입력해 주세요.
                  </div>
                </div>

                <div class="col-sm-6">
                  <label for="date" class="form-label">날짜</label>
                  <input type="datetime-local" class="form-control" id="date" name="dateTime" placeholder="" value="" required>
                  <div class="invalid-feedback">
                    날짜를 입력해 주세요.
                  </div>
                </div>

                <div class="col-12">
                  <label for="place" class="form-label">장소</label>
                  <div class="input-group">
                    <input type="text" class="form-control" id="place" name="place" placeholder="" required>
                    <div class="invalid-feedback">
                      장소를 입력해 주세요.
                    </div>
                  </div>
                </div>

                <div class="form-check">
                  <input class="form-check-input" id="personal" name="personal" type="checkbox" value="true">
                  <label class="form-check-label" for="personal">
                    Is Personal?
                  </label>
                </div>
              </form>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">취소</button>
              <a type="submit" class="btn btn-primary" id="scheduleSubmit">등록</a>
            </div>
          </form>
        </div>
      </div>
    </div>

Check Box는 form에 value 값을 포함시켜준다. 만약 체크되어있지않으면 아무것도 넘겨주지 않는다.

$(document).ready(function () {
    $(document).on("click","#scheduleSubmit",function (event) {
        submitForm();
        return false;
    });
});

function submitForm(){
    $.ajax({
        type: "POST",
        url: "http://localhost:8080/schedule",
        cache:false,
        data: $("form#scheduleForm").serialize(),
        success: function(response){
            $("#newScheduleModal").modal('hide');
            loadSchedules();
        },
        error: function(){
            alert("Error");
        }
    });
}

submit 한 뒤에 Modal을 닫고 현재 페이지의 테이블에 추가한 일정을 바로 그려주는데, 이게 또 원래 있던 일정 리스트를 중복해서 가져오는 문제가 있었다. 그래서 기존에 있던 테이블을 비우고 다시 추가하도록 비우는 로직을 추가했다.

그리고 <tr></tr>을 추가하는 부분에서 append() 가 리턴하는게 새로 생성된 엘리먼트가 아니라는것을 알게되어 순서를 바꿔 appendTo()로 변경했다.

그리고 조건문에서 "===" 의 의미를 알게되었다.

"==" : 자동 형변환 기능 있음 (ex 0 -> false)

"===" : 자동 형변환 안함

function addRow(schedules) {
    var tableBody = $("tbody#scheduleTable");
    $(tableBody).children("tr").remove();
    $.each(schedules, function (i, item) {
        var newRow = $("<tr></tr>").appendTo(tableBody);
        $(newRow).append("<td>"+item.dateTime.substr(0,10)+"</td>");
        $(newRow).append("<td>"+item.place+"</td>");
        $(newRow).append("<td>"+item.title+"</td>");
        if (item.personal === true) {
            $(newRow).append("<td>Alone</td>");
        } else {
            $(newRow).append("<td>Together</td>");
        }
    });
}

일정 변경 또는 제거를 위한 이미지를 구해서 불러왔다.

CheckedBox & Trash

          <thead>
          <tr>
            <th scope="col"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check2-square" viewBox="0 0 16 16">
              <path d="M3 14.5A1.5 1.5 0 0 1 1.5 13V3A1.5 1.5 0 0 1 3 1.5h8a.5.5 0 0 1 0 1H3a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h10a.5.5 0 0 0 .5-.5V8a.5.5 0 0 1 1 0v5a1.5 1.5 0 0 1-1.5 1.5H3z"/>
              <path d="M8.354 10.354l7-7a.5.5 0 0 0-.708-.708L8 9.293 5.354 6.646a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0z"/>
            </svg></th>
            <th scope="col">When</th>
            <th scope="col">Where</th>
            <th scope="col">What</th>
            <th scope="col">Together or Alone</th>
            <th scope="col"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
              <path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
              <path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
              </svg></th>
          </tr>
          </thead>

CheckBox & Pencil

function addRow(schedules) {
    var tableBody = $("tbody#scheduleTable");
    $(tableBody).children("tr").remove();
    $.each(schedules, function (i, item) {
        var newRow = $("<tr></tr>").appendTo(tableBody);
        $(newRow).append('<td><input class="form-check-input" id="checkedSchedule" name="checkedSchedule" type="checkbox" value="checked"></td>');
        $(newRow).append("<td>"+item.dateTime.substr(0,10)+"</td>");
        $(newRow).append("<td>"+item.place+"</td>");
        $(newRow).append("<td>"+item.title+"</td>");
        if (item.personal === true) {
            $(newRow).append("<td>Alone</td>");
        } else {
            $(newRow).append("<td>Together</td>");
        }
        $(newRow).append('<td><a type="button" onclick="loadSchedules();"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">\n' +
            '            <path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>\n' +
            '        </svg></td>');
    });
}

마지막으로 일정의 날짜가 가장 가까운 일시를 위로 올라오도록 쿼리 변경

    @Query("select s from Schedule s where s.owner.id = :accountId " +
            "or s.owner.id = (select a.lover.id from Account a where a.id = :accountId) order by s.dateTime")
    List<Schedule> findAllByCouple(Long accountId);

오늘은 여기까디~~

Comments