본문으로 바로가기
반응형

public/todo.js

 ....

$(function() {
    var todoListItem = $('.todo-list');
    var todoListInput = $('.todo-list-input');
    $('.todo-list-add-btn').on("click", function(event) {
        event.preventDefault();

        var item = $(this).prevAll('.todo-list-input').val();

        if (item) {
            todoListItem.append("<li><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
            todoListInput.val("");
        }
    });

   ...

저번에 이어서 여기에서 '.todo-list-add-btn'을 클릭했을 때 서버에 add를 요청할 수 있게 만들어 봅시다!

이 코드를 보면은

 ....

$(function() {
    var todoListItem = $('.todo-list');
    var todoListInput = $('.todo-list-input');
    $('.todo-list-add-btn').on("click", function(event) {
        event.preventDefault();

        var item = $(this).prevAll('.todo-list-input').val();

        if (item) {
            $.post("/todos", {name:item}, function(e){ // 1
                addItem({name:item, completed:false); 
            // todoListItem.append("<li><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
            todoListInput.val("");
        }
    });

   ...

1 : todo-list-add-btn을 클릭했을 때 todo-list-input의 값을 읽어서 어떤 것이 추가 되었는지 확인해서 그것을 item에 넣고 바로 add하는 식인데 그 data를 서버로 POST로 보내줍니다.

그 후 처리가 되면 응답이 올텐데 addItem을 호출해주면 됩니다. 그리고 item을 넣을 때 값으로 completed와 name이 있기 때문에
item을 바로 넣는게 아니라 JSON으로 만들어서 넣어줍니다.

이제 POST로 Todo를 보낸 다음에 그 응답이 왔을 때 add하기 때문에 바로 보낼 필요가 없어서 todoListItem.append부분을 주석처리 시켜 줍니다.

 

그 후 Todos POST핸들러를 추가해줍시다!

app/app.go

  func addTodoHandler(w http.ResponseWriter, r *http.Request) { // 1
    name := r.FormValue("name") // 2
    id := len(todoMap) + 1 // 2
    todo := &Todo{id, name, false, time.Now()} // 3
    todoMap[id] = todo // 4
    rd.JSON(w, http.StatusOK, todo) // 5
  }

  func MakeHandler() http.Handler {
    todoMap = make(map[int]*Todo)
    addTestTodos()

    rd = render.New()
    r := mux.NewRouter()

    r.HandleFunc("/todos", getTodoListHandler).Methods("GET")
    r.HandleFunc("/todos", addTodoHandler).Methods("POST") // 1
    r.HandleFunc("/", indexHandler)

    return r
  }

1 : addTodoHandler 함수입니다.
2 : addTodoHandler를 추가 해줍니다.

여기에서 TodoList를 add해주는데 js에서 보낼 때 POST로 todo로 보낼 때 name에 item오브젝트를 넣어서 보내서 이것을 읽으려면

2-1 : FormValue()를 사용하여 key를 넣어주면 JS에서 보낸 inputvalue가 나오게 되는데 이것을 name에 넣고, 그것을 todoMap에 추가해주어야 하는데

2-2 : id가 없으므로 id를 새로 발급해야 하는데 그 맵의 갯수가 id값이 되도록 한다. 0부터 시작하므로 +1을 해줍니다.
2-3 : todo 인스턴스를 만들고
2-4 : todoMap에 등록해서
2-5 : 그 정보를 JSON형태로 반환 하여 클라이언트에게 알려줍니다.

 

이제 저장 후에 실행을 해봅시다!

아무거나 입력해서 등록하면 잘 들어가는 것을 확인 할 수 있습니다.

다시 소스로 돌아와서
public/todo.js

 ....

    var item = $(this).prevAll('.todo-list-input').val();

        if (item) {
            $.post("/todos", {name:item}, function(e){
                addItem({name:item, completed:false); 
            // todoListItem.append("<li><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
            todoListInput.val("");
        }
    });

....

이 곳을 보면 post로 todos를 보내는데 응답을 받은 상태에서 서버가 보내준 응답이 e에 들어있는데 이것을 사용하지 않고

기존에 있던 item변수에서 가져오는데 이렇게 하면 서버에서 보내준 id값을 읽어 올 수 없기 때문에 서버의 data를 그대로 사용 할 수 있도록 수정 하고, 서버가 JSON object로 반환해주기 때문에 그 값을 그대로 사용 할 수 있습니다.

 ....

    var item = $(this).prevAll('.todo-list-input').val();

        if (item) {
            $.post("/todos", {name:item}, additem); 
            // todoListItem.append("<li><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
            todoListInput.val("");
        }
    });

     var addItem = function(item) {
        if (item.completed) {
            todoListItem.append("<li class='completed'"+ " id='" + item.id + "'><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' checked='checked' />" + item.name + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
        } else {
            todoListItem.append("<li "+ " id='" + item.id + "'><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item.name + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
        }
    };

....

그래서 그냥 additem을 추가해주면 서버 응답이 될 때 이 함수가 호출이 되고, addItem함수에 item값에 서버의 응답값이 들어오게 되서 item에 해당하는 값을 사용할 수 있게 됩니다.

그리고 todoListItem.append부분에 id='" + item.id + "'를 넣어서 서버가 보내준 id를 추가 해줍니다.

이렇게 하면 나중에 remove할 때 id값을 사용할 수 있게 됩니다.

이제 다시 실행 시켜서 값을 추가 시켜준 뒤 개발자 도구에 'Elements'로 들어가서 코드를 확인해보면 각 항목에 id값이 추가 되었다는 것을 확인 할 수 있습니다.

지금까지 add버튼을 클릭했을 때 그 항목을 읽어서 서버로 todos Post로 보내서 응답이 오면 additem함수를 호출해서 additem함수에서 그 서버가 알려준 항목을 추가할 수 있게 했습니다.

 

이제 Delete할 수 있게 만들어 보죠! 서버로 요청을 날린다음에 요청이 처리된 다음에 지울 수 있게 만들어 보겠습니다.

public/todo.js

  todoListItem.on('click', '.remove', function() { // 1
          // url: todos/id method: DELETE
          var id = $(this).closest("li").attr('id');  // 1
          var $self = $(this); // 4
          $.ajax({ // 2
              url: "todos/" + id,
              type: "DELETE",
              success: function() {
                      $self.parent().remove(); // 3
                  }
              }
          })
          //$(this).parent().remove();
  });

1 : delete가 클릭 되었을 때 todos/id URL로 메소드는 DELETE로 보낼 것입니다.
그러기위해서는 id를 알아야하는데, todolist item의 remove 항목이 클릭되면 function()이 출력되는데

1-1 : 이 때 ($this)는 remove버튼이 됩니다.

그래서 this에서 가장 가까운 li항목을 찾아와서 그것의 Attribute id를 가져오면 remove될 항목의 id값을 알 수 있습니다.
1-2 : ajax request object 생성.
1-3 : 성공했을 때 이벤트. $(this).parent().remove(); 값을 넣어주는데, this가 바뀔 수 있기 때문에
1-4 : self라는 변수를 만들어 this를 저장해 줍니다.
그 이유는 success: function(data) 불릴 때 this가 되어버리기 때문에 바깥쪽의 this와 다르기 때문에 바깥쪽의 this를 저장해서 사용합니다.

이제 저장 후에 제대로 동작하는지 확인해보죠!

404 에러가 뜨면서 todos/7이 온 것을 확인 할 수 있습니다.

제대로 동작하므로 delete핸들러를 만들어줍시다!

app/app.go

  type Success struct { // 3
    Success bool `json:"success"`
  }

  func removeTodoHandler(w http.ResponseWriter, r *http.Request) { // 1
    vars := mux.Vars(r) // 2
    id, _ := strconv.Atoi(vars["id"]) // 2
    if _, ok := todoMap[id]; ok { // 3
      delete(todoMap, id) // 4
      rd.JSON(w, http.StatusOK, Success{true}) // 5
    } else {
      rd.JSON(w, http.StatusOK, Success{false}) // 6
    }
  }

  func MakeHandler() http.Handler {
    todoMap = make(map[int]*Todo)
    addTestTodos()

    rd = render.New()
    r := mux.NewRouter()

    r.HandleFunc("/todos", getTodoListHandler).Methods("GET")
    r.HandleFunc("/todos", addTodoHandler).Methods("POST")
    r.HandleFunc("/todos/{id:[0-9]+}", removeTodoHandler).Methods("DELETE") // 1
    r.HandleFunc("/", indexHandler)

    return r
  }

1 : removeTodoHandler 함수.

2 : removeHandler 추가.

2-1 : id값을 가져오기 위해 사용했습니다..
2-2 : vars의 id값을 문자열을 숫자로 바꾸어준다.
2-3 : Map에 id값에 해당하는 data가 있는지 확인한다.
2-4 : 있으면 지워주고,
2-5, 2-6 : 그 후 응답 결과를 return을 해줍니다.

3 : 응답 결과를 알려주기 위한 struct입니다.

 

이 후 서버를 재실행 했을 때 정상적으로 삭제되는 것을 알 수 있습니다.

 

 

이제 DELETE했을 때 성공여부에 따라서 data가 success일 때만 지워 줄 수 있도록 코드를 수정해줍시다.

public/todo.js

  todoListItem.on('click', '.remove', function() {
          // url: todos/id method: DELETE
          var id = $(this).closest("li").attr('id'); 
          var $self = $(this);
          $.ajax({
              url: "todos/" + id,
              type: "DELETE",
              success: function() {
                  if (data.success) { // 추가.
                      $self.parent().remove(); 
                  }
              }
          })
          //$(this).parent().remove();
  });

이제 지우는 것은 끝이났고, 마지막으로 Complete toggle처리해주는 것을 합시다.

이 부분은 여기에 있는데 이 부분을 수정해보죠.
public/todo.js

  todoListItem.on('change', '.checkbox', function() {
      if ($(this).attr('checked')) {
          $(this).removeAttr('checked');
      } else {
          $(this).attr('checked', 'checked');
      }

      $(this).closest("li").toggleClass('completed');
  });

public/todo.js

  todoListItem.on('change', '.checkbox', function() {
    var id = $(this).closest("li").attr('id'); // 2
    var $self = $(this); // 3
    var complete = true; // 6
    if ($(this).attr('checked')) { // 5
      complete = false;
    }

    $.get("complete-todo/"+id+"?complete="+complete, function(data){ // 1
       if ($self.attr('checked')) {
          $self.removeAttr('checked');
        } else {
          $self.attr('checked', 'checked');
        }

        $self.closest("li").toggleClass('completed');
    });

1 : 서버로 요청하게 수정해 줍니다.
어떤 id를 complete 시켜줄 것인지 알려주기 위해 id를 받아와야하는데

2 ~ 3 : 방금 delete 부분 수정했을 때 코드를 그대로 가져와 줍니다.
1 : 그렇게 되면 change가 클릭한 항목들이 나오게 될텐데, 그게 온 다음에 complete-todo/에 id를 붙인 URL에 요청을 해주게 되고,
4 : 그 뒤 응답이 오면 아래의 코드가 실행 되도록 해줍니다.

5 : toggle되는 것을 알려주어야 하기 때문에 사용되는 if문 (checked인지 아닌지)
6 : checked인 상태는 change가 온 상태이므로 완료상태에서 완료 상태가 아닌 과정을 만드는 것이므로 기본값을 true로 바꾸어줍니다.

 

재실행해서 정상적으로 값이 들어오는지 확인해보죠!

체크되어있는 상태에서 체크를 해제하면 아래와 같은 오류창이 뜨는 것을 확인 할 수 있는데 정상적인 URL을 호출하는 것을 확인 할 수 있습니다.

이제 이것을 요청하는 핸들러를 만들어보죠!
app/app.go

  func completeTodoHandler(w http.ResponseWriter, r *http.Request) { // 1
    vars := mux.Vars(r) // 2
    id, _ := strconv.Atoi(vars["id"]) // 2
    complete := r.FormValue("complete") == "true" // 3
    if todo, ok := todoMap[id]; ok { // 4
      todo.Completed = complete
      rd.JSON(w, http.StatusOK, Success{true})
    } else {
      rd.JSON(w, http.StatusOK, Success{false})
    }
  }

  func MakeHandler() http.Handler {
    todoMap = make(map[int]*Todo)
    addTestTodos()

    rd = render.New()
    r := mux.NewRouter()

    r.HandleFunc("/todos", getTodoListHandler).Methods("GET")
    r.HandleFunc("/todos", addTodoHandler).Methods("POST")
    r.HandleFunc("/todos/{id:[0-9]+}", removeTodoHandler).Methods("DELETE")
    r.HandleFunc("/complete-todo/{id:[0-9]+}", completeTodoHandler).Methods("GET") // 1
    r.HandleFunc("/", indexHandler)

    return r
  }

1 : completeTodoHandler 함수를 생성해 줍니다.

2 : complte핸들러를 추가 해줍니다.
2-1 ~ 2-2 : remove코드를 가져옵니다.
2-3 : complete여부는 FormValue에 들어있는데 true,false로 값이 옵니다.
2-4 : 그 값이 map에 있는지 확인 후 , 있는경우 값을 가져와서 complete의 값을 변경시켜주고, todo값을 반환시켜 줍니다.

없는 경우 Success{false}로 반환해준다. 이 부분은 remove부분과 비슷합니다.

 

다시 public/todo.js로 돌아와서

  todoListItem.on('change', '.checkbox', function() {
    var id = $(this).closest("li").attr('id'); 
    var $self = $(this); 
    var complete = true; 
    if ($(this).attr('checked')) {
      complete = false;
    }

    $.get("complete-todo/"+id+"?complete="+complete, function(data){
       if (complete) { // 1
            $self.attr('checked', 'checked');
        } else {
            $self.removeAttr('checked');
        }

        $self.closest("li").toggleClass('completed');
    })
  });

1 : complete완료를 요청하는 경우에 check를 넣어주고, 완료 해제를 하는 경우엔 check를 remove를 할 수 있게 바꾸어줍니다.

 

그 후 재실행을 해보면 체크, 체크 해제가 정상적으로 동작하는 것을 확인할 수 있습니다.

 

풀소스 : github.com/ckdqja135/Typescript-restful-starter/tree/master/mdfile/Go/Go%20-%20Web%20Source/Go%20-%20Todo(Add%2C%20Delete%2C%20Complete)

 

반응형

'프로그래밍(Web) > Golang' 카테고리의 다른 글

[바미] Go - sqlite3  (0) 2020.12.17
[바미] Go - Refactoring  (0) 2020.12.17
[바미] Go - OAuth 2.0  (0) 2020.12.17
[바미] Go - Template  (0) 2020.12.17
[바미] Go - SQL query(CRUD)  (0) 2020.12.17

댓글을 달아 주세요