만약 ajax성공했으면 status, msg,
private T body;
이거는 왜 T냐? 어떤 타입을 줄 수 있는지 모르니까
api호출하는 입장에서 collection, Object 막 다 다를 수 있다 프론트 앤드는 파싱하는게 계속 달라진다 처리하기 번거로움
로직 통일하려면
private Integer status;
private String msg;
private T body;
이거를
공통응답 DTO라고 한다
만약 이렇게 설계 안 하면
public class Resp<T>{
private Integer status;
private String msg;
private T body;
아래 생성자 만들기
}
이러면 불편하다!!
뭐가 불편했지?
유틸에
package org.example.springv3.core.util;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Resp<T> {
private Integer status;
private String msg;
private T body;
public static <B> Resp<?> ok(B body){
return new Resp<>(200, "성공", body);
}
public static Resp<?> fail(Integer status, String msg){
return new Resp<>(status, msg, null);
}
}
보드 컨트롤러에
@GetMapping("/test/v1")
public @ResponseBody Resp testV1(){
User u = new User();
u.setId(1);
u.setUsername("ssar");
u.setPassword("1234");
u.setEmail("ssar@gmail.com");
return Resp.ok(u);
}
실행 결과

ajax요청해서 바디 데이터만 뽑아서
div에 element해서 append하면 된다
2번째
//uset2명 만듬
@GetMapping("/test/v2")
public @ResponseBody Resp testV2(){
User u1 = new User();
u1.setId(1);
u1.setUsername("ssar");
u1.setPassword("1234");
u1.setEmail("ssar@gmail.com");
User u2 = new User();
u2.setId(1);
u2.setUsername("ssar");
u2.setPassword("1234");
u2.setEmail("ssar@gmail.com");
//2개를 리턴하고 싶으면 arrayList에 넣어야 한다
//new arrayList한거와 같다! 들어갈 때 내부가 T다!!
List<User> users = Arrays.asList(u1, u2);
return Resp.ok(users);
}
여기서 asList는 T 다
들어올 때 뭐가 들어갈지 모르니까!

결과

만약 Resp 안 만들었으면 return할 때 골치아프다 User로 했어야 했고
타입은 List<User>로 바껴야 하고
위에서는 Resp를 User로 변경해야 한다
3번째
터트림
//터트림
@GetMapping("/test/v3")
public @ResponseBody Resp testV3(){
throw new Exception404("유저를 찾을 수 없습니다");
}
자바 스크립트가 뜬다

이거 잘못됐음
ajax요청했는데 js로 응답해줘서
즉 스크립트가 들어간다는 것임
ajax요청 파일(View)말고 데이터 요청했을 때 Exception은 절대 데이터 그대로 응답해줘야 한다!!
변경
@GetMapping("/test/v3")
public @ResponseBody Resp testV3(){
return Resp.fail(404, "유저를 찾을 수 없습니다");
}

문제!!!

200 성공했다고 나온다! 문제임 실패했는데!

만약 키값 생각 안 나면 찾아보기
@GetMapping("/test/v4")
public @ResponseBody Resp testV4(HttpServletResponse response){
response.setStatus(404);
return Resp.fail(404, "유저를 찾을 수 없습니다");
}

되기는 하는데 귀찮음 그래서
4번째 쓰로우로 던질거임
T에 바디 데이터 넣고 HttpStatusCode에는 숫자 넣으면 안됨
→ 실수할 수 있기 때문에 404인데 4040 이렇게
실 코드
@GetMapping("/test/v5")
public ResponseEntity<?> testV5(){ // 1. ResponseBody 생략, 상태코드를 넣을 수 있따.
return new ResponseEntity<>(Resp.fail(404, "찾을 수 없습니다"), HttpStatus.NOT_FOUND);
}
결과

궁금한게 있어야 한다는데 뭐가 궁금해야하지?
5번째
서비스 요청하면 thorw하면 js로 응답한다 GlobalExceptionHandler로
이제는 핸들러 새로 또 만들어야 한다 오류뜨는!
컨트롤C하고 컨트롤 V 해서 api붙여서 만들기

Ex가서
다 복사 하고 숫자 앞에 Api 붙이기

왜 만들었냐 패치 ajax요청할 때 response 엔티티로 보낼거임
응답 분기 시키려고
글로벌 api핸들러 가서
package org.example.springv3.core.error;
import org.example.springv3.core.error.ex.*;
import org.example.springv3.core.util.Script;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalApiExceptionHandler {
// 유효성 검사 실패 (잘못된 클라이언트의 요청)
@ExceptionHandler(ExceptionApi400.class)
public String ex400(Exception e) {
return Script.back(e.getMessage());
}
// 인증 실패 (클라이언트가 인증없이 요청했거나, 인증을 하거나 실패했거나)
@ExceptionHandler(ExceptionApi401.class)
public String ex401(Exception e) {
return Script.href("인증되지 않았습니다", "/login-form");
}
// 권한 실패 (인증은 되어 있는데, 삭제하려는 게시글이 내가 적은게 아니다)
@ExceptionHandler(ExceptionApi403.class)
public String ex403(Exception e) {
return Script.back(e.getMessage());
}
// 서버에서 리소스(자원) 찾을 수 없을때
@ExceptionHandler(ExceptionApi404.class)
public String ex404(Exception e) {
return Script.back(e.getMessage());
}
// 서버에서 심각한 오류가 발생했을때 (알고 있을 때)
@ExceptionHandler(ExceptionApi500.class)
public String ex500(Exception e) {
return Script.back(e.getMessage());
}
}
왜 400도 보내고 HttpStatus 보내냐?
HttpStatus는
브라우저 한테 보냄, 아니면 프로그램에게 알려줌
400은 프론트 앤드개발자 보라고 만드는 거임 바디데이터에서 보고 해라고
안 적으면 까봐야 아니까 배려임
변경 결과!
package org.example.springv3.core.error;
import org.example.springv3.core.error.ex.*;
import org.example.springv3.core.util.Resp;
import org.example.springv3.core.util.Script;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalApiExceptionHandler {
// 유효성 검사 실패 (잘못된 클라이언트의 요청)
@ExceptionHandler(ExceptionApi400.class)
public ResponseEntity<?> ex400(Exception e) {
return new ResponseEntity<>(Resp.fail(400, e.getMessage()), HttpStatus.BAD_REQUEST);
}
// 인증 실패 (클라이언트가 인증없이 요청했거나, 인증을 하거나 실패했거나)
@ExceptionHandler(ExceptionApi401.class)
public ResponseEntity<?> ex401(Exception e) {
return new ResponseEntity<>(Resp.fail(401, e.getMessage()), HttpStatus.UNAUTHORIZED);
}
// 권한 실패 (인증은 되어 있는데, 삭제하려는 게시글이 내가 적은게 아니다)
@ExceptionHandler(ExceptionApi403.class)
public ResponseEntity<?> ex403(Exception e) {
return new ResponseEntity<>(Resp.fail(403, e.getMessage()), HttpStatus.FORBIDDEN);
}
// 서버에서 리소스(자원) 찾을 수 없을때
@ExceptionHandler(ExceptionApi404.class)
public ResponseEntity<?> ex404(Exception e) {
return new ResponseEntity<>(Resp.fail(404, e.getMessage()), HttpStatus.NOT_FOUND);
}
// 서버에서 심각한 오류가 발생했을때 (알고 있을 때)
@ExceptionHandler(ExceptionApi500.class)
public ResponseEntity<?> ex500(Exception e) {
return new ResponseEntity<>(Resp.fail(403, e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
컨트롤러
@GetMapping("/test/v6")
public ResponseEntity<?> testV6(){ // 1. ResponseBody 생략, 상태코드를 넣을 수 있따.
throw new ExceptionApi404("페이지를 찾을 수 없습니다");
}

이제 수정 삭제하자!
아직 댓글쓰기 안 했지만 더미로 넣어둠 ㄱㅊ
삭제하려면 쓰레기통이 나옴
form태그로 할 수 있는데 이거는 브라우저가 요청한거임 무조건 html을 보내줘야한다!!
js ajax로 보내면 데이터만 보낼 수 있다!!
form태그는 무조건 get요청이며 요청하면 하면 응답은 새로고침이 된다!
응답의 컨텐츠가 html이기 때문에 전체를 다시 그린다
프로토콜이다
하지만 휴대폰으로 요청했다면 새로고침이 안 된다 html이 아니니까
댓글만 부분리로딩 하고 싶다!! remove하면 된다!!
resp 데이터만 주면 된다
ssr안해줘도 된다!!
ajax로 처리하면 비용이 많이 줄어든다 하지만 문제는 모든게 ajax면 개발자가 힘들다 그래서 개발비용이 엄청 많이 든다!!
하루만에 끝날게 오래걸린다!
그래서 나온게 리액트다!! 쉽기 때문에! 내가 직접 dom찾아서 안 바꿔도 되니까!!
비동기고 나발이고 돈이 적게 듬
ajax의 좋은점!!
- 비동기 한다고 중요한 것은 아님
- 부분 리로드!!! 이게 제일 중요함
클릭 요청하면 db들어가서 치고 resp ok 200 성공 끝나면 삭제 csr하면 된다!
코드 시작
리플라이에 컨트롤러만듬
원래는 안 만듬 보드에 다 만든다 하지만 처음 만드는 것이니 만들어보자
내부적으로 있음

package org.example.springv3.reply;
import lombok.RequiredArgsConstructor;
import org.example.springv3.core.util.Resp;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
@RequiredArgsConstructor
@Controller
public class ReplyController {
//delete매핑이니까 동사를 뺄 수 잇다 post면 붙여야 한다!
@DeleteMapping("/api/reply/{id}")
public ResponseEntity<?> delete(@PathVariable("id") Integer id) {
//1. 세션 체크 인증체크 해야한다 api만 붙이면 됨 -> 주소로 처리가능
//2. 서비스 로직 호출 -> 댓글 삭제 해서
//3. 로그인한 유저, 해당 글 맞는지 권한 체크
//4. 응답
//스테틱으로 되서 new 할 필요 없다!! T body자리다
//지금은 돌려줄 바디가 필요 없다 무조건 Resp해야함
//상태코드 집어넣으려고!! ResponseEntity
return ResponseEntity.ok(Resp.ok(null));
}
}
디테일
지우기

붙여야 서브미트 되서 js 동작을 제어할 수 있게 만든다!

이렇게 고치자

여기에 댓글 아이디 넘겨줘야 한다
BoardResponse보면 ReplyDTO의 Id다 그래서 id넣으면 된다
get delete는 바디가 없다 삭제하지 뭘 주는게 아니니까!! 기억하자
await만 쓰면 계속 기다리니까 async해줘야 한다 빠져 나가야 한다
response에는 해더, 바디가 다 들어가 있다
{{>layout/header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
{{#model.isOwner}}
<div class="d-flex justify-content-end">
<a href="/api/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a>
<form action="/api/board/{{model.id}}/delete" method="post">
<button class="btn btn-danger">삭제</button>
</form>
</div>
{{/model.isOwner}}
<div class="d-flex justify-content-end">
<b>작성자</b> : {{model.username}}
</div>
<!-- 게시글내용 -->
<div>
<h2><b>{{model.title}}</b></h2>
<hr/>
<div class="m-4 p-2">
{{{model.content}}}
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
{{#model.replies}}
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div>
<div>{{comment}}</div>
</div>
{{#isOwner}}
<form>
<button onclick="deleteReply('{{id}}')" type="button" class="btn">🗑</button>
</form>
{{/isOwner}}
</div>
{{/model.replies}}
</div>
</div>
</div>
<script>
async function deleteReply(id){
let response = await fetch(`/reply/${id}`,{
method:"delete"
});
}
</script>
{{>layout/footer}}
그래서
{{>layout/header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
{{#model.isOwner}}
<div class="d-flex justify-content-end">
<a href="/api/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a>
<form action="/api/board/{{model.id}}/delete" method="post">
<button class="btn btn-danger">삭제</button>
</form>
</div>
{{/model.isOwner}}
<div class="d-flex justify-content-end">
<b>작성자</b> : {{model.username}}
</div>
<!-- 게시글내용 -->
<div>
<h2><b>{{model.title}}</b></h2>
<hr/>
<div class="m-4 p-2">
{{{model.content}}}
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
{{#model.replies}}
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div>
<div>{{comment}}</div>
</div>
{{#isOwner}}
<form>
<button onclick="deleteReply('{{id}}')" type="button" class="btn">🗑</button>
</form>
{{/isOwner}}
</div>
{{/model.replies}}
</div>
</div>
</div>
<script>
async function deleteReply(id){
let response = await fetch(`/reply/${id}`,{
method:"delete"
});
console.log(response);
let responseBody = await response.json();//파싱
console.log(responseBody);
}
</script>
{{>layout/footer}}
안에 뭐가 콘솔로 console.log(response);해야한다!! 연습해보자
지금은 응답만 되는지 ajax연습하고 하자
api잠시 빼고 하자



이런것 도 해보자
@DeleteMapping("/reply/{id}")
public ResponseEntity<?> delete(@PathVariable("id") Integer id) {
throw new ExceptionApi403("권한이 없습니다");
}
이렇게 쓰로우 던졌을 때 어떻게 되는지!!

요청은 받음 그래서 dom을 찾아야한다 id가 있어야 한다 아직 없음
그래서 댓글 뿌릴 때 부터 id가 있어야 한다!! 지금은 없으니 넣어줘야 한다
디테일 머스테치 이동

id넣기

결과

{{>layout/header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
{{#model.isOwner}}
<div class="d-flex justify-content-end">
<a href="/api/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a>
<form action="/api/board/{{model.id}}/delete" method="post">
<button class="btn btn-danger">삭제</button>
</form>
</div>
{{/model.isOwner}}
<div class="d-flex justify-content-end">
<b>작성자</b> : {{model.username}}
</div>
<!-- 게시글내용 -->
<div>
<h2><b>{{model.title}}</b></h2>
<hr/>
<div class="m-4 p-2">
{{{model.content}}}
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
{{#model.replies}}
<!-- 댓글아이템 -->
<div id="reply-{{id}}" class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div>
<div>{{comment}}</div>
</div>
{{#isOwner}}
<form>
<button onclick="deleteReply('{{id}}')" type="button" class="btn">🗑</button>
</form>
{{/isOwner}}
</div>
{{/model.replies}}
</div>
</div>
</div>
<script>
async function deleteReply(id){
let response = await fetch(`/reply/${id}`,{
method:"delete"
});
console.log(response);
let responseBody = await response.json();//파싱
console.log(responseBody);
}
</script>
{{>layout/footer}}
이제 삭제 해보자
디테일 아래쪽

이러면 끝
아직 DB에서는 날라간게 아니지만 화면에서는 삭제된다!!

이 코드는 반드시 통신 끝나고 파싱 끝나면 실행된다
이제 로직 짜자
이제 api붙이자!
디테일,


댓글 레파지토리 구현
package org.example.springv3.reply;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ReplyRepository extends JpaRepository<Reply, Integer> {
}
댓글 서비스 만들기
public void 댓글삭제(int id){
//애는 integer인지 어떻게 알았을까? 프라이머리키에 integer로 적어놔서 안다!
//jpa레파지토리 만들기 어렵지 않다 다음에 만들어보자! 궁금하다고 하면 알려주심 제내릭만 잘 알면 된다!
//삭제 전에 조회 먼저 하자
//ajax요청이니까 apiException터트려야 한다!! 받아줘야 하니까 앞에 reply에 PS 넣어줌
Reply replyPS = replyRepository.findById(id).orElseThrow(
() -> new ExceptionApi404("해당 댓글을 찾을 수 없습니다")
);
replyRepository.deleteById(id);
}
하고 권한 체크 도 해야함
삭제 최종 서비스 코드
package org.example.springv3.reply;
import lombok.RequiredArgsConstructor;
import org.example.springv3.core.error.ex.ExceptionApi403;
import org.example.springv3.core.error.ex.ExceptionApi404;
import org.example.springv3.user.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service //컴포넌트 스켄 IOC에 넣기
public class ReplyService {
private final ReplyRepository replyRepository;
@Transactional
public void 댓글삭제(int id, User sessionUser){
//애는 integer인지 어떻게 알았을까? 프라이머리키에 integer로 적어놔서 안다!
//jpa레파지토리 만들기 어렵지 않다 다음에 만들어보자! 궁금하다고 하면 알려주심 제내릭만 잘 알면 된다!
//삭제 전에 조회 먼저 하자
//ajax요청이니까 apiException터트려야 한다!! 받아줘야 하니까 앞에 reply에 PS 넣어줌
Reply replyPS = replyRepository.findById(id).orElseThrow(
() -> new ExceptionApi404("해당 댓글을 찾을 수 없습니다")
);
//지금 댓글만 조회할건데 User Board 안 내용은 비어있음 하지만 getUser.getId 포린키가 프라이머리키 들고있으니까 조회안함!!
// BoardId, User Id 는 이미 있음 래이지 로딩은 안 내용은 없지만 프라이머리키는 가지고 있다
//즉 select한번 더 할 필요 없다
// 하지만 getusername()하려면 select한번 더 해야함
//이렇게 하려면 mFindById만들어서 join하는 쿼리 만들고 1번만 select할 수 있게 만들면 된다!!
//findById하면 reply객체 만들어줌 id에 들어가고 comment들어가고 user, board에는 set id만 들어가 있고 내용은 없다!!
if(replyPS.getUser().getId() != sessionUser.getId()){
throw new ExceptionApi403("댓글 삭제 권한이 없습니다");
//이런 것 전부가 트랜잭션이다 그래서 @Transaction붙여야 한다
}
replyRepository.deleteById(id);
}
}
댓글 컨트롤러 가서 서비스 호출
2개 의존성 주입해야 한다 그래서 붙임
private final HttpSession session;
private final ReplyService replyService;
package org.example.springv3.reply;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.example.springv3.core.error.ex.ExceptionApi403;
import org.example.springv3.core.util.Resp;
import org.example.springv3.user.User;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
@RequiredArgsConstructor
@Controller
public class ReplyController {
private final HttpSession session;
private final ReplyService replyService;
//delete매핑이니까 동사를 뺄 수 잇다 post면 붙여야 한다!
@DeleteMapping("/api/reply/{id}")
public ResponseEntity<?> delete(@PathVariable("id") Integer id) {
//1. 세션 체크 인증체크 해야한다 api만 붙이면 됨 -> 주소로 처리가능 클리어
//2. 서비스 로직 호출 -> 댓글 삭제 해서
User sessionUser = (User) session.getAttribute("sessionUser");
replyService.댓글삭제(id, sessionUser);
//3. 로그인한 유저, 해당 글 맞는지 권한 체크
//4. 응답
//스테틱으로 되서 new 할 필요 없다!! T body자리다
//지금은 돌려줄 바디가 필요 없다 무조건 Resp해야함
//상태코드 집어넣으려고!! ResponseEntity
return ResponseEntity.ok(Resp.ok(null));
// throw new ExceptionApi403("권한이 없습니다");
}
}
연습할 때 강제로 댓글 6번 넣으면 터진다!! 테스트 하자!!
다 삭제하지 말고 상태코드보고 삭제할지 말지 생각해야 함!! if로 분기시켜야 한다!! 아니면 response의 status로도 할 수 있다! 둘 다 주면 배려임
response.ok하면 200이라는 뜻임!!
200 아니면 파싱할 필요 없음
아니면 alert해서 resopnseBody.msg 붙이면 된다!
왜 ajax 썼어요?
부분 리로딩해서 좋아서
비용이 엄청 감소해서!
통신의 부하가 엄청 줄어서!
요청하면 fetch가 똭 생긴다
Response에 받는다 게시글5에서는 없다!
게시글에 다 떠있으면 엄청 크기가 커진다! 비용 증가한다! 트래픽 마다 돈을 지불해야 해서!!
원래는 또 안 받게 캐싱해야 하는데 이것 보다 좋은 거는 ajax다!
디테일 머스테치
{{>layout/header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
{{#model.isOwner}}
<div class="d-flex justify-content-end">
<a href="/api/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a>
<form action="/api/board/{{model.id}}/delete" method="post">
<button class="btn btn-danger">삭제</button>
</form>
</div>
{{/model.isOwner}}
<div class="d-flex justify-content-end">
<b>작성자</b> : {{model.username}}
</div>
<!-- 게시글내용 -->
<div>
<h2><b>{{model.title}}</b></h2>
<hr/>
<div class="m-4 p-2">
{{{model.content}}}
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
{{#model.replies}}
<!-- 댓글아이템 -->
<div id="reply-{{id}}" class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div>
<div>{{comment}}</div>
</div>
{{#isOwner}}
<form>
<button onclick="deleteReply('{{id}}')" type="button" class="btn">🗑</button>
</form>
{{/isOwner}}
</div>
{{/model.replies}}
</div>
</div>
</div>
<script>
async function deleteReply(id){
let response = await fetch(`/api/reply/${id}`,{
method:"delete"
});
// console.log(response);
let responseBody = await response.json();//파싱
// console.log(responseBody);
if(response.ok){
$(`#reply-${id}`).remove();
}else {
alert(responseBody.msg);
}
}
</script>
{{>layout/footer}}
번 외! 트랜잭션 옆에 붙이는 ReadOnly=True가 궁금하다면!
서비스 위에 붙이는 트랜잭션ReadOnly= true 왜 적는지 알려면!!
유튜브 트랜잭션 고립성 공부하려면
데이터베이스고립성 치고 쉬운코드 이 사람꺼 들어가서 재생목록에 데이터 베이스에 데이터베이스 트랜잭션 isloaction, lock을 먼저 보고 트랜잭션 공부하자!!
고립성 → 내가 건드릴 때 남이 못 건들게 하는 것 이 종류가 엄청 많다!
Share article