국비/Project - 1 게시판

2023.05.19 80일차 Project

춘핑이 2023. 5. 19. 12:03

80일차

오전 댓 오후 플젝?
댓글 기능 추가
게시물을 보고 페이지 로딩 없이 댓글 crud를 할 것이다.

91. 댓글

HTTP로 요청 보낼때 크게 GET과 POST방식이 있엇다.
근데 ajax방식으로 CRUD할때 값을 얻어올때 R(GET) C(POST) U(PUT, PATCH) D(DELETE)
GET POST말고도 여러 메소드가 있다.

GET/ POST
HEAD
PUT
DELETE
VONNECT
OPTION
PATCH

ajax로 요청보낼때 ajax("url", { method : "put"})또는 delete가 들어갈 것이다.
받을때도 put방식으로 왓을때 delte방식일때 등등 차이가 생기게 된다.

뭘완성하던 화면 컨트롤러 서비스 javaBean등등을 수정해야한다.

댓글은 어떤 정보들이 나와야하는가?
댓글 내용 : 누가 : 언제

댓글테이블을 만들어보자.

91.1 테이블

CREATE TABLE Comment (
    id INT PRIMARY KEY AUTO_INCREMENT,
    boardId INT NOT NULL,
    content VARCHAR(500) NOT NULL,
    memberId VARCHAR(20) NOT NULL,
    memberId DATETIME NOT NULL DEFAULT NOW(),
    FOREIGN KEY (boardId) REFERENCES Board(id),
    FOREIGN KEY (memberId) REFERENCES Member(id)
);

91.2 view

const boardId = $("#boardIdText").text().trim();
$.ajax("/comment/list/?board=" + boardId, {
    method: "get", //생략 가능
    success: function(data) {
        console.log(data);
    }
})

91.3 컨트롤러

일단 콘솔에서 문자열을 받는 것으로 하자.

@Controller
@RequestMapping("comment")
public class CommentController {

    @GetMapping("list")
    @ResponseBody
    public List<String> list(@RequestParam("board") Integer boardId) {
        return List.of("댓1", "댓2", "댓3");
    }
}

91.4 서비스

서비스가 리턴하는 것은 댓글 정보를 가지고 있는 객체를 리턴하는 것이다.

@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService{

    private final CommentMapper mapper;

    public List<Comment> list(Integer boardId) {
        // TODO Auto-generated method stub
        return mapper.selectAllByBoardId(boardId);
    }
}

91.5 mapper

@Mapper
public interface CommentMapper {

    @Select("""
            SELECT * 
            FROM Comment
            WHERE boardId = #{boardId}
            """)
    List<Comment> selectAllByBoardId(Integer boardId);
}

91.6 ajax

가져온 데이터를 java처럼 for문으로 받아오거나
java의 forEach처럼 데이터를 하나씩 꺼내서 볼 수 있다.
이렇게 꺼내서 commentContainer에 하나씩 넣으면된다.

const boardId = $("#boardIdText").text().trim();
$.ajax("/comment/list?board=" + boardId, {
    method: "get", // 생략 가능
    success: function(comments) {
        for (const comment of comments) {
            $("#commnetListContainer").append(`
                <div>
                    ${comment.content} 
                    : ${comment.memberId} 
                    : ${comment.inserted}
                </div>
            `)
        }
    }
});

댓글 여러개 가져오기 끝났다.
하나 가져오기는 업데이트 할때

92. 새댓글쓰기

버튼이 눌리면 textarea에 있는게 보내지면된다.

$("#sendCommentBtn").click(function() {
    const boardId = $("#boardIdText").text().trim();
    const content = $("#commentTextArea").val();
    const data = { boardId, content };

    $.ajax("/comment/add", {
        method: "post",
        contentType: "application/json",
        data: JSON.stringify(data)
    });
})

92.2 컨트롤러

@PostMapping("add")
@ResponseBody
public String add(@RequestBody Comment comment) {
    service.add(comment);
    return "ok";
}

92.3 서비스

트랜잭션처리도 해주자. 대부분 서비스에서 이루어지니 여기서해준다.

public void add(Comment comment) {
    comment.setMemberId("admin0");
    mapper.insert(comment);
}

92.4 매퍼

@Insert("""
        INSERT INTO Comment (boardId, content, memberId) 
        VALUES (#{boardId}, #{content}, #{memberId});
        """)
Integer insert(Comment comment);

93. 새댓글 입력후 리스트 다시 불러오기

send댓글 한 후 다시 댓글 리스트를 불러와야한다.
다시 코드를 작성하는게 아니라 list를 불러올때 썻던 ajax를 함수로 만들어서 실행하면된다.
그런데 함수를 정의만햇으니 첫 로딩시 함수를 실행시켜야한다.

listComment();

$("#sendCommentBtn").click(function() {
    const boardId = $("#boardIdText").text().trim();
    const content = $("#commentTextArea").val();
    const data = {boardId, content};

    $.ajax("/comment/add", {
        method: "post",
        contentType: "application/json",
        data: JSON.stringify(data),
        complete: function() {
            listComment();
        }
    });
})

function listComment() {
    const boardId = $("#boardIdText").text().trim();
    $.ajax("/comment/list?board=" + boardId, {
        method: "get", // 생략 가능
        success: function(comments) {
            // console.log(data);
            $("#commentListContainer").empty();
            for (const comment of comments) {
                // console.log(comment);
                $("#commentListContainer").append(`
                    <div>
                        ${comment.content} 
                        : ${comment.memberId} 
                        : ${comment.inserted}
                    </div>
                `);
            }
        }
    });
}

94. 댓글삭제

실제 존재하는 attribute가아니라 data-comment-id라는 가상의 속성을 만들어서
버튼을 누르면 data-comment-id이 id값을 가지고 날라가게 될 것이다.
method: "delete" delete요청을 날라가게 할 것이다

<button id="commentDeleteBtn${comment.id}" 
class="commentDeleteButton"
data-comment-id="${comment.id}">삭제</button>

$(".commentDeleteButton").click(function(){
    const commentId = $(this).attr("data-comment-id");
    $.ajax("/comment/id/" + commentId, {
        method: "delete"
    })
})

94.1 컨트롤러

@RequestMapping(path = "id/{id}", method = RequestMethod.DELETE)
로 받아야하는데 getMapping이나 postMapping처럼 @DeleteMapping으로 받으면된다.

@DeleteMapping("id/{id}")
@ResponseBody
public String remove(@PathVariable("id") Integer id) {
    service.remove(id);
    return "ok";
}

94.2 서비스

@Override
public void remove(Integer id) {
    mapper.deleteById(id);
}

94.3 mapper

@Delete("""
        DELETE FROM Comment
        WHERE id = #{id}
        """)
Integer deleteById(Integer id);

95 댓글 수정 전 조회

수정버튼을 누르면 바로 삭제가 날라가는게 아니라 수정할수있는 창을띄워줘야한다.
일단은 input박스를 만들어서 받는다.
버튼을 누르면 textarea에 나타나게 해줘야한다.
조회후 수정의 규칙을 지켜야한다.

<div id="updateCommentContainer">
    <h6>수정</h6>
    <textarea id="commentUpdateTextArea"></textarea>
    <button id="updateCommentBtn">수정</button>
</div>

<button id="commentUpdateBtn${comment.id}" 
class="commentUpdateButton"
data-comment-id="${comment.id}">수정</button>

$(".commentUpdateButton").click(function() {
        const id = $(this).attr("data-comment-id");
        $.ajax("/comment/id/" + id, {
            method: "get",
            success: function(data) {
                $("#commentUpdateTextArea").val(data.content);
            }
        });
    })

95.1 컨트롤러

@GetMapping("id/{id}")
@ResponseBody
public Comment get(@PathVariable("id") Integer id) {
    return service.get(id);
}

95.2 서비스

@Override
public Comment get(Integer id) {
    return mapper.selectById(id);
}

95.3 mapper

@Select("""
        SELECT *
        FROM Comment
        WHERE id = #{id}
        """)
Comment selectById(Integer id);

96. 수정하기

버튼이 눌리면 수정이 되게 하면된다.
댓글의 id와 내용이 보내지면된다.
업데이트에는 put방식이나 patch방식으로 보내주면된다.
put은 target리소스전체를 바꿀때사용된다. patch는 일부분을 바꾼다고 되어있다.
엄격하면 후자 아니면 그냥 전자를 사용해도된다.

<div id="updateCommentContainer">
    <h6>수정</h6>
    <input type="hidden" id="commentUpdateIdInput" />
    <textarea id="commentUpdateTextArea"></textarea>
    <button id="updateCommentBtn">수정</button>
</div>

$(".commentUpdateButton").click(function() {
    const id = $(this).attr("data-comment-id");
    $.ajax("/comment/id/" + id, {
        method: "get",
        success: function(data) {
            $("#commentUpdateIdInput").val(data.id);
            $("#commentUpdateTextArea").val(data.content);
        }
    });
})

$("#updateCommentBtn").click(function() {
    const content = $("#commentUpdateTextArea").val();
    const commentId = $("#commentUpdateIdInput").val(data.id);
    const data = {
        id: commentId,
        content: content
    };

    $.ajax("/comment/update", {
        method: "put",
        contentType: "application/json",
        data: JSON.stringify(data),
        complete: function() {
            listComment();
        }
    });
})

96.1 컨트롤러

@PutMapping("update")
@ResponseBody
public String get(@RequestBody Comment comment) {
    service.update(comment);
    return "ok";
}

96.2 서비스

@Override
public void update(Comment comment) {
    mapper.update(comment);
}

96.3 mapper

@Update("""
        UPDATE Comment SET
        content = #{content}
        WHERE id = #{id}
        """)
Integer update(Comment comment);

97 댓글 수정되엇음을 알리는 메세지

부트스트랩 toast를 사용해서 메세지로 업데이트 를 알려보자.

알람을 위해 메시지를 받아야하니 Map으로 변경해준다.

@PostMapping("add")
@ResponseBody
public ResponseEntity<Map<String, Object> > add(@RequestBody Comment comment) {
    Map<String, Object> res = service.add(comment);
    return ResponseEntity.ok().body(res) ;
}

@Override
public Map<String, Object> add(Comment comment) {
    comment.setMemberId("admin0");

    Map<String, Object> res = new HashMap<>();

    int cnt = mapper.insert(comment);
    if (cnt == 1) {
        res.put("message", "댓글이 등록되었습니다.");
    } else {
        res.put("message", "댓글이 등록되지 않았습니다.");
    }
    return res;
}

이 message를 토스트로 받아서 보여주면된다.

$("#sendCommentBtn").click(function() {
    const boardId = $("#boardIdText").text().trim();
    const content = $("#commentTextArea").val();
    const data = { boardId, content };

    $.ajax("/comment/add", {
        method: "post",
        contentType: "application/json",
        data: JSON.stringify(data),
        complete: function(jqXHR) {
            listComment();
            $(".toast-body").text(jqXHR.responseJSON.message);
            toast.show();
        }
    });
})