inblog logo
|
sxias
    REST API

    [REST API] 4. 주소 제거

    문정준's avatar
    문정준
    May 08, 2025
    [REST API] 4. 주소 제거
    Contents
    ✅ 기능별 구조 전환🧠 기타 정리: ByteBuddyInterceptor 관련

    📌 REST API 구조 전환 핵심 요약

    항목
    변경 전
    변경 후
    URL 스타일
    /board/save, /reply/{id}/delete 등 동사 포함 주소
    /board, /reply/{id} 등 RESTful 주소
    요청 타입
    application/x-www-form-urlencoded
    application/json
    HTTP 메서드
    GET, POST만 사용
    GET, POST, PUT, DELETE 구분
    데이터 전달 방식
    request.setAttribute()로 모델 전달
    DTO 객체를 JSON으로 직렬화
    응답 구조
    뷰 리턴 (redirect:)
    @ResponseBody + Resp<?> JSON 구조
    엔티티 직렬화
    Lazy 로딩 충돌 발생
    DTO로 변환 후 안전 직렬화

     

    ✅ 주요 변화 상세 정리

    1. 주소에서 동사 제거 + HTTP 메서드 분리

    • 기존에는 URL 자체가 기능을 표현함 (/reply/save, /reply/{id}/delete)
    • → REST API에서는 자원(명사)을 주소로, 동작은 HTTP 메서드로
    기능
    변경 전
    변경 후
    댓글 저장
    POST /reply/save
    POST /reply
    댓글 삭제
    POST /reply/{id}/delete
    DELETE /reply/{id}
    게시글 저장
    POST /board/save
    POST /board
    게시글 목록
    GET /board/list (뷰 반환)
    GET /board/list (JSON 응답)
    게시글 상세
    GET /board/{id} (뷰 반환)
    GET /board/{id}/detail (JSON 응답)
    회원 가입
    POST /join (뷰 or redirect)
    POST /join (JSON 응답)
    회원 수정
    POST /user/update 등
    PUT /user
    뷰 모델 세팅
    request.setAttribute("model", dto)
    ❌ 사용 안 함 (클라이언트 렌더링 or API 전용)
     

    ✅ 기능별 구조 전환

    1. 글 목록 보기 (GET)

    @GetMapping("/board/list") public @ResponseBody Resp<?> getList(@RequestParam(defaultValue = "") String keyword, ...) { BoardResponse.ListDTO listDTO = boardService.글목록보기(keyword, ...); return Resp.ok(listDTO); }
    • /board/list로 조회
    • Entity → DTO 변환 후 응답
    • Lazy 로딩 방지 위해 내부에서 .map(BoardResponse.DTO::new) 사용
    📦 JSON 응답 예시:
    { "status": 200, "msg": "성공", "body": { "boards": [ { "id": 20, "title": "제목20", "content": "내용20", "isPublic": true, "userId": 1, "createdAt": "2025-05-07 11:27:51.027589" }, { "id": 19, "title": "제목19", "content": "내용19", "isPublic": true, "userId": 1, "createdAt": "2025-05-07 11:27:51.02659" }, { "id": 18, "title": "제목18", "content": "내용18", "isPublic": true, "userId": 1, "createdAt": "2025-05-07 11:27:51.02659" } ], "prev": -1, "next": 1, "current": null, "size": 3, "totalCount": 19, "totalPage": 7, "isFirst": true, "isLast": false, "numbers": [ 0, 1, 2, 3, 4 ], "keyword": "" } }

    2. 글 보기(업데이트 글보기) (GET)

    @GetMapping("/board/{id}") public @ResponseBody Resp<?> getBoardOne(@PathVariable("id") int id) { User sessionUser = (User) session.getAttribute("sessionUser"); BoardResponse.DTO respDTO = boardService.글보기(id, sessionUser.getId()); return Resp.ok(respDTO); }
    • 로그인한 유저의 ID를 기반으로 게시글을 조회함
    • 예: 본인이 쓴 글인 경우, 수정/삭제 버튼 활성화 등을 컨트롤 가능
    • JSON으로 응답 → 프론트엔드 분리 구조에 적합
    • @ResponseBody + Resp.ok() 조합으로 표준화된 응답 구조 사용
    📦 JSON 응답 예시:
    { "status": 200, "msg": "성공", "body": { "id": 4, "title": "제목4", "content": "내용4", "isPublic": true, "userId": 3, "createdAt": "2025-05-07 10:34:26.612766" } }

     

    3. 글 상세 보기 (GET)

    @GetMapping("/board/{id}/detail") public @ResponseBody Resp<?> getBoardDetail(@PathVariable Integer id) { BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id); return Resp.ok(detailDTO); }
    • /board/{id}/detail로 API 요청
    • 기존 request.setAttribute() 제거
    • 댓글 리스트까지 포함해 JSON으로 응답
    📦 JSON 응답 예시:
    { "status": 200, "msg": "성공", "body": { "id": 4, "title": "제목4", "content": "내용4", "isPublic": true, "isOwner": true, "isLove": false, "loveCount": 2, "username": "love", "createdAt": "2025-05-07T01:34:26.612+00:00", "loveId": null, "replies": [ { "id": 3, "content": "댓글3", "username": "ssar", "isOwner": false }, { "id": 2, "content": "댓글2", "username": "cos", "isOwner": false }, { "id": 1, "content": "댓글1", "username": "ssar", "isOwner": false } ] } }

    4. 글 쓰기 (POST)

    @PostMapping("/board") public @ResponseBody Resp<?> save(@Valid @RequestBody BoardRequest.SaveDTO reqDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); BoardResponse.DTO respDTO = boardService.글쓰기(reqDTO, sessionUser); return Resp.ok(respDTO); }
    • @RequestBody를 통해 JSON으로 데이터 받음
    • 뷰 리턴 대신 DTO로 응답

    5. 글 수정 (PUT)

    @PutMapping("/board/{id}") public @ResponseBody Resp<?> update(@PathVariable Integer id, @Valid @RequestBody BoardRequest.UpdateDTO updateDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); BoardResponse.DTO respDTO = boardService.글수정(id, updateDTO, sessionUser); return Resp.ok(respDTO); }
    • HTTP 메서드 PUT 사용으로 의미 명확화
    • JSON 기반 DTO 전달

    6. 댓글 쓰기 (POST)

    @PostMapping("/reply") public @ResponseBody Resp<?> save(@Valid @RequestBody ReplyRequest.SaveDTO reqDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); ReplyResponse.DTO respDTO = replyService.댓글쓰기(reqDTO, sessionUser); return Resp.ok(respDTO); }

    7. 댓글 삭제 (DELETE)

    @DeleteMapping("/reply/{id}") public @ResponseBody Resp<?> delete(@PathVariable Integer id) { User sessionUser = (User) session.getAttribute("sessionUser"); replyService.댓글삭제(id, sessionUser.getId()); return Resp.ok(null); }
    • DELETE 메서드 사용으로 URL에 동사 제거
    • 댓글 삭제 시에도 JSON 응답 유지
    📦 JSON 응답 예시:
    { "status": 200, "msg": "성공", "body": null }

    8. 회원가입 (POST)

    @PostMapping("/join") public @ResponseBody Resp<?> join(@Valid @RequestBody UserRequest.JoinDTO reqDTO, Errors errors) { UserResponse.DTO respDTO = userService.회원가입(reqDTO); return Resp.ok(respDTO); }
    • 기존 @ModelAttribute 방식 제거
    • JSON 기반 요청으로 전환

    9. 회원정보 수정 (PUT)

    @PutMapping("/user") public @ResponseBody Resp<?> update(@Valid @RequestBody UserRequest.UpdateDTO updateDTO, Errors errors) { User sessionUser = (User) session.getAttribute("sessionUser"); User userPS = userService.회원정보수정(updateDTO, sessionUser.getId()); session.setAttribute("sessionUser", userPS); return Resp.ok(null); }
    • PUT 메서드로 REST 설계에 부합
    • 수정 후 세션 갱신 처리

    🧠 기타 정리: ByteBuddyInterceptor 관련

    Lazy 로딩 중 JSON 직렬화 과정에서 연관 객체를 접근하면 발생하는 예외
    • com.fasterxml.jackson.databind.JsonMappingException: ByteBuddyInterceptor 예외 발생
    • 이유: 엔티티를 JSON으로 직렬화할 때 연관 객체에 접근 → 세션 종료로 인한 Lazy 로딩 실패
    해결
    → 반드시 Entity가 아닌 DTO로 변환해서 JSON 직렬화할 것
    this.boards = boards.stream() .map(board -> new DTO(board)) .toList();
     
    Share article
    Contents
    ✅ 기능별 구조 전환🧠 기타 정리: ByteBuddyInterceptor 관련

    Sxias ㆍ a32176740@gmail.com

    RSS·Powered by Inblog