Light Blue Pointer
본문 바로가기
개발일지

2024-01-18, Today I Learned

by 개발바닥곰발바닥!!! 2024. 1. 18.

오늘 한 일

💡글 상세 조회 페이지 사용자의 역할별로 버튼 다르게 붙임

🚩 문제 : 작성자로 로그인하면 상세글 페이지가 출력되지 않는 문제 해결

🚩문제 : 받아온 데이터가 화면에 출력되지 않는 문제 해결

🚩문제 : 버튼이 안 보이는 문제 해결

🚩문제 : 모집마감이 되지 않는 문제 해결

💡메뉴페이지 만듦

🚩문제 : Bean Validation에서 걸리는 문제 해결

🚩문제 : 메뉴가 안 뜨는 문제 해결

🚩문제 : Get할때 JSON 형식으로 Object를 전송하면 url에 이상하게 들어가는 문제 해결

💡메뉴 추가하는 부분 접었다 폈다 하는 기능 추가해봄

💡자동 새로고침 기능 추가해봄

 

💡글 상세 조회 페이지 사용자의 역할별로 버튼 다르게 붙임

글 상세조회 페이지 post.html 새로 만드는데

글 주인이냐 참가자냐 아니냐에 따라서 버튼을 다르게 보여줘야 해서

DetailedPostResponse에 UserPostRole 추가해봄

그리고 postId가 있어야 해서 추가해봄

@Builder
public record DetailedPostResponse(
    
    Long id,
    String address,
    Double latitude,
    Double longitude,
    String store,
    Integer minPrice,
    Integer deliveryCost,
    List<NickMenusResponse> menus,
    Integer sumPrice,
    LocalDateTime deadline,
    UserPostRole role
) {

}
@Override
    public DetailedPostResponse getPost(Long postId, User user) {
        Post post = getPostById(postId);
        List<UserPost> userPosts = getUserPostsByPost(post);

        return DetailedPostResponse.builder()
            .id(post.getId())
            .longitude(post.getLongitude())
            .latitude(post.getLatitude())
            .address(post.getAddress())
            .store(post.getStore())
            .minPrice(post.getMinPrice())
            .deliveryCost(post.getDeliveryCost())
            .menus(getNickMenus(userPosts))
            .sumPrice(getSumPrice(userPosts, post))
            .deadline(getDeadline(post))
            .role(getUserPostByUserIfParticipant(user,userPosts).getRole())
            .build();
    }

지금보니 참가자인지 판별하는 로직이 안 들어있어서 바꿔봄

 

내가 이미 이 함수를 짜둠 하지만 나는 해당 Post의 List<UserPost> 가 이미 있기때문에 오버로딩해봄

private UserPost getUserPostIfParticipant(User user, Post post) {
        return userPostRepository.findByPostAndUserAndRoleEquals(post, user,
            UserPostRole.PARTICIPANT).orElseThrow(() ->
            new GlobalException(PostErrorCode.FORBIDDEN_ACCESS_PARTICIPANT)
        );
    }

이라고 생각하고 위를 보니 과거의 내가 똑같이 생각하고 오버로딩을 해놨다 ㅋㅋㅋㅋㅋㅋㅋ

private UserPost getUserPostByUserIfParticipant(User user, List<UserPost> userPosts) {
        for (UserPost userPost : userPosts) {
            if (userPost.getRole().equals(UserPostRole.HOST)) {
                continue;
            }
            if (user.getId().equals(userPost.getUser().getId())) {
                return userPost;
            }
        }
        throw new GlobalException(PostErrorCode.FORBIDDEN_ACCESS_PARTICIPANT);
    }

    private UserPost getUserPostIfParticipant(User user, Post post) {
        return userPostRepository.findByPostAndUserAndRoleEquals(post, user,
            UserPostRole.PARTICIPANT).orElseThrow(() ->
            new GlobalException(PostErrorCode.FORBIDDEN_ACCESS_PARTICIPANT)
        );
    }

그래서 이렇게 됐다

@Override
    public DetailedPostResponse getPost(Long postId, User user) {
        Post post = getPostById(postId);
        List<UserPost> userPosts = getUserPostsByPost(post);

        return DetailedPostResponse.builder()
            .longitude(post.getLongitude())
            .latitude(post.getLatitude())
            .address(post.getAddress())
            .store(post.getStore())
            .minPrice(post.getMinPrice())
            .deliveryCost(post.getDeliveryCost())
            .menus(getNickMenus(userPosts))
            .sumPrice(getSumPrice(userPosts, post))
            .deadline(getDeadline(post))
            .role(getUserPostByUserIfParticipant(user,userPosts).getRole())-> 이부분이 추가됨
            .build();
    }

html내부에서 role에 따라 구분해서 버튼을 달아봄

<script th:inline="javascript">

  $(document).ready(function () {
    getData();
  });

  function getData() {

    $.ajax({
      type: 'GET',
      url: `/api/v1/posts/[[${postId}]]`,
      dataType: "json",
      contentType: 'application/json',
      data: {},
      success: function (response) {
        console.log('Success:', response);
        let data = response.data;
        latitude = data.latitude;
        longitude = data.longitude

        deadline = data.deadline;
        deliveryCost = data.deliveryCost;
        minPrice = data.minPrice;
        store = data.store;
        sumPrice = data.sumPrice;
        nickmenus = data.menus;

        drawMap(latitude, longitude);

        drawField(store,sumPrice,minPrice,deliveryCost,minPrice,deadline);

        drawMenus(nickmenus);

/**이 부분!**/
        if(data.role == "HOST"){
          drawHostButtons();
        }else if(data.role == "Participant"){
          drawParticipantButtons();
        }else{
          drawAnyoneButtons();
        }
      },

그리고 각각 버튼 다르게 달아봄

 

글 주인용 상세페이지

<div id = "buttons" style="padding:10px;width:1000px;height:min-content;">
</div>

button을 달 element를 일단 만들어봄

function drawHostButtons(){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" name="btnradio" onclick="etc()" id="getOffers" autocomplete="off" checked>
      <label class="btn btn-outline-secondary" for="else">요청 조회</label>
      <input type="radio" class="btn-check" name="btnradio" onclick="burger()" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="burger">메뉴 수정</label>
      <input type="radio" class="btn-check" name="btnradio" onclick="chicken()" id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="chicken">모집 마감</label>
      <input type="radio" class="btn-check" name="btnradio" onclick="korean()" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="korean">모집 취소</label>
      <input type="radio" class="btn-check" name="btnradio" onclick="western()" id="completeOrder" autocomplete="off">
      <label class="btn btn-outline-secondary" for="western">주문 완료</label>
    </div>
            `
    )
  }

postId 연결해줌

if(data.role == "HOST"){
          drawHostButtons(data.id);
        }else if(data.role == "PARTICIPANT"){
          drawParticipantButtons(data.id);
        }else{
          drawAnyoneButtons(data.id);
        }

버튼에도 postId달아줌

function drawHostButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="getOffers(this)" name="btnradio" id="getOffers" autocomplete="off" checked>
      <label class="btn btn-outline-secondary" for="getOffers">요청 조회</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="closeApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="closeApplication">모집 마감</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="cancelRecruitment(this)" name="btnradio" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelRecruitment">모집 취소</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="completeOrder(this)" name="btnradio" id="completeOrder" autocomplete="off">
      <label class="btn btn-outline-secondary" for="completeOrder">주문 완료</label>
    </div>
            `
    )
  }

아 근데 저렇게 안 하고 그냥 전역변수에 postId 넣어버려도 될 듯함

이건 어차피 script내부에서 동작하는거라…

package com.moayo.moayoeats.backend.domain.post.controller;

import com.moayo.moayoeats.backend.domain.post.dto.request.PostCategoryRequest;
import com.moayo.moayoeats.backend.domain.post.dto.request.PostIdRequest;
import com.moayo.moayoeats.backend.domain.post.dto.request.PostRequest;
import com.moayo.moayoeats.backend.domain.post.dto.request.PostSearchRequest;
import com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse;
import com.moayo.moayoeats.backend.domain.post.dto.response.DetailedPostResponse;
import com.moayo.moayoeats.backend.domain.post.service.PostService;
import com.moayo.moayoeats.backend.global.dto.ApiResponse;
import com.moayo.moayoeats.backend.global.security.UserDetailsImpl;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1")
public class PostController {

    private final PostService postService;

    // 글 생성하기
    @PostMapping("/posts")
    public ApiResponse<Void> createPost(
        @Valid @RequestBody PostRequest postReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        postService.createPost(postReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.CREATED.value(), "글을 생성했습니다.");
    }

    // 인증정보 없이 모든 글 조회하기
    @GetMapping("/readonly/posts")
    public ApiResponse<List<BriefPostResponse>> getPostsForAnyone() {
        return new ApiResponse<>(HttpStatus.OK.value(), "모든 글 조회에 성공했습니다.",
            postService.getPostsForAnyone());
    }

    // 모든 글 조회하기
    @GetMapping("/posts")
    public ApiResponse<List<BriefPostResponse>> getPosts(
        @AuthenticationPrincipal UserDetailsImpl userDetails) {
        return new ApiResponse<>(HttpStatus.OK.value(), "모든 글 조회에 성공했습니다.",
            postService.getPosts(userDetails.getUser()));
    }

    //글 단독 조회, 글 상세페이지
    @GetMapping("/posts/{postId}")
    public ApiResponse<DetailedPostResponse> getPost(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @PathVariable(name = "postId") Long postId
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "글 상세페이지 조회에 성공했습니다.",
            postService.getPost(postId, userDetails.getUser()));
    }

    //글 카테고리별 조회
    @GetMapping("/posts/category")
    public ApiResponse<List<BriefPostResponse>> getPostsByCategory(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostCategoryRequest postCategorySearchReq
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "글 카테고리별 조회에 성공했습니다.",
            postService.getPostsByCategory(postCategorySearchReq, userDetails.getUser()));
    }

    //글 검색하기
    @GetMapping("/posts/search")
    public ApiResponse<List<BriefPostResponse>> searchPost(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostSearchRequest postSearchReq
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "검색 결과",
            postService.searchPost(postSearchReq, userDetails.getUser()));
    }

    //모집 취소하기
    @DeleteMapping("/posts")
    public ApiResponse<Void> deletePost(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.deletePost(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "모집 취소에 성공했습니다.");
    }

    //모집 마감
    @PatchMapping("/posts/close")
    public ApiResponse<Void> closeApplication(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.closeApplication(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "모집이 마감되었습니다.");
    }

    //주문 완료
    @PatchMapping("/posts/complete-order")
    public ApiResponse<Void> completeOrder(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.completeOrder(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "주문완료 처리가 되었습니다.");
    }

    //나가기 기능
    @DeleteMapping("/posts/exit")
    public ApiResponse<Void> exit(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.exit(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "글에서 나가기 되었습니다.");
    }

    //수령 완료
    @DeleteMapping("/posts/received")
    public ApiResponse<Void> receiveOrder(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.receiveOrder(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "수령완료 처리가 되었습니다.");
    }

    //Test
    @GetMapping("/test/posts/{postId}")
    public ApiResponse<DetailedPostResponse> getPostTest(
        @PathVariable(name = "postId") Long postId
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "글 상세페이지 조회에 성공했습니다.",
            postService.getPostTest(postId));
    }

}

모집마감 달아봄

function closeApplication(button){
    var postId = $(button).attr("post");
    $.ajax({
      type: 'POST',
      url: "/api/v1/posts/close",
      dataType: "json",
      contentType: 'application/json',
      data: JSON.stringify({
        postId: postId
      }),
      success: function (response) {
        console.log('Success:', response);
        alert(response.message);
      },
      error: function (error,response) {
        alert(response.message);
        console.error('Error:', error);
      }
    });
  }

모집취소 달아봄

function cancelRecruitment(button){
    var postId = $(button).attr("post");
    $.ajax({
      type: 'DELETE',
      url: "/api/v1/posts",
      dataType: "json",
      contentType: 'application/json',
      data: JSON.stringify({
        postId: postId
      }),
      success: function (response) {
        console.log('Success:', response);
        alert(response.message);
      },
      error: function (error,response) {
        alert(response.message);
        console.error('Error:', error);
      }
    });
  }

주문완료 달아봄

function completeOrder(button){
    var postId = $(button).attr("post");
    $.ajax({
      type: 'PATCH',
      url: "/api/v1/posts/complete-order",
      dataType: "json",
      contentType: 'application/json',
      data: JSON.stringify({
        postId: postId
      }),
      success: function (response) {
        console.log('Success:', response);
        alert(response.message);
      },
      error: function (error,response) {
        alert(response.message);
        console.error('Error:', error);
      }
    });
  }

 

 

🚩 문제 : 작성자로 로그인하면 상세글 페이지가 출력되지 않는다

작성자로 로그인해도 글 상세 페이지가 보이지 않았음

 

범인이 전역변수인가 싶어서 값 전달을 파라미터로 다시 해봄

그래도 상황이 변하지 않았다

 

UserPost에 관계가 멀쩡하게 들어있는데 왜 참가자가 아니라고 뜰까?

 

⛳ 해결 : getRole에서 HOST 걸러내던거 거르지 않게 변경함

@Override
    public DetailedPostResponse getPost(Long postId, User user) {
        Post post = getPostById(postId);
        List<UserPost> userPosts = getUserPostsByPost(post);

        return DetailedPostResponse.builder()
            .id(post.getId())
            .longitude(post.getLongitude())
            .latitude(post.getLatitude())
            .address(post.getAddress())
            .store(post.getStore())
            .minPrice(post.getMinPrice())
            .deliveryCost(post.getDeliveryCost())
            .menus(getNickMenus(userPosts))
            .sumPrice(getSumPrice(userPosts, post))
            .deadline(getDeadline(post))
            .role(getUserPostByUserIfParticipant(user,userPosts).getRole())
            .build();
    }

아까 여기서

.role(getUserPostByUserIfParticipant(user,userPosts).getRole())

이거 추가한거 로직이 잘못된듯

private UserPost getUserPostByUserIfParticipant(User user, List<UserPost> userPosts) {
        for (UserPost userPost : userPosts) {
            if (userPost.getRole().equals(UserPostRole.HOST)) {
                continue;
            }
            if (user.getId().equals(userPost.getUser().getId())) {
                return userPost;
            }
        }
        throw new GlobalException(PostErrorCode.FORBIDDEN_ACCESS_PARTICIPANT);
    }

호스트는 걸러버리고 있음

메서드 재사용이 안 될거 같아서 하는수없이 새로 만듦

private UserPostRole getRoleByUserAndUserPosts(User user, List<UserPost> userPosts) {
        for (UserPost userPost : userPosts) {
            if (userPost.getRole().equals(UserPostRole.HOST)) {
                return UserPostRole.HOST;
            }
            if (user.getId().equals(userPost.getUser().getId())) {
                return UserPostRole.PARTICIPANT;
            }
        }
        return null;
    }
@Override
    public DetailedPostResponse getPost(Long postId, User user) {
        Post post = getPostById(postId);
        List<UserPost> userPosts = getUserPostsByPost(post);

        return DetailedPostResponse.builder()
            .id(post.getId())
            .longitude(post.getLongitude())
            .latitude(post.getLatitude())
            .address(post.getAddress())
            .store(post.getStore())
            .minPrice(post.getMinPrice())
            .deliveryCost(post.getDeliveryCost())
            .menus(getNickMenus(userPosts))
            .sumPrice(getSumPrice(userPosts, post))
            .deadline(getDeadline(post))
            .role(getRoleByUserAndUserPosts(user,userPosts)) -> 수정해봄!!!
            .build();
    }

다시 테스트해봄

 

데이터는 이제 잘 받아와진다

 

🚩문제 : 받아온 데이터가 화면에 출력되지 않음

⛳ 해결 : data.id보내는거 response.data.id로 고쳐줌

화면이 잘 뜬다

 

🚩문제 : 버튼이 안 보임

버튼이 안 보여서

if(data.role == "HOST"){
          drawHostButtons(response.data.id);
        }else if(data.role == "PARTICIPANT"){
          drawParticipantButtons(response.data.id);
        }else{
          drawAnyoneButtons(response.data.id);
        }

이부분 ==을 ===으로 수정해봄

if(data.role === "HOST"){
          drawHostButtons(response.data.id);
        }else if(data.role === "PARTICIPANT"){
          drawParticipantButtons(response.data.id);
        }else{
          drawAnyoneButtons(response.data.id);
        }

여전히 뜨지 않는다

role이 HOST로 잘 왔는데 왜 안 뜰까?

저 drawHostButtons가 실행이 안 돼서 안 뜨겠지

function drawHostButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="getOffers(this)" name="btnradio" id="getOffers" autocomplete="off" checked>
      <label class="btn btn-outline-secondary" for="getOffers">요청 조회</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="closeApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="closeApplication">모집 마감</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="cancelRecruitment(this)" name="btnradio" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelRecruitment">모집 취소</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="completeOrder(this)" name="btnradio" id="completeOrder" autocomplete="off">
      <label class="btn btn-outline-secondary" for="completeOrder">주문 완료</label>
    </div>
            `
    )
  }

if else가 안 되나 싶어서 밖으로 빼 봄

drawHostButtons(response.data.id);
        if(data.role === "HOST"){
          
        }else if(data.role === "PARTICIPANT"){
          drawParticipantButtons(response.data.id);
        }else{
          drawAnyoneButtons(response.data.id);
        }

 

됐다!

 

부트스트랩 추가하고 지도 사이즈 줄여봄

<link crossorigin="anonymous"
        href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
        rel="stylesheet">
  <link href="./css/sticky-footer-navbar.css" rel="stylesheet">
  <link href="./css/style.css" rel="stylesheet">

 

 

 

🚩문제 : 모집마감이 되지 않음

모집마감 눌러봄

 

⛳ 해결 : ajax의 POST를 PATCH로 바꿈

 

@PatchMapping인데다가

type: ‘POST’ 로 보내고 있었어서

‘PATCH’로 바꾸고 한번 더 해봄

해결되었다!

 

이제 참가자용 버튼 만들어봄

function drawParticipantButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="cancelApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelApplication">참가 취소</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="receiveOrder(this)" name="btnradio" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="receiveOrder">수령 완료</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="chatRoom(this)" name="btnradio" id="chatRoom" autocomplete="off">
      <label class="btn btn-outline-secondary" for="chatRoom">채팅방 참여</label>
    </div>
            `
    )
  }

  function drawAnyoneButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="applyApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelApplication">참가 신청</label>
    </div>
            `
    )
  }

 

 

나가기 버튼 연결

//나가기 기능
    @DeleteMapping("/posts/exit")
    public ApiResponse<Void> exit(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.exit(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "글에서 나가기 되었습니다.");
    }
function exit(button){
    var postId = $(button).attr("post");
    $.ajax({
      type: 'PATCH',
      url: "/api/v1/posts/exit",
      dataType: "json",
      contentType: 'application/json',
      data: JSON.stringify({
        postId: postId
      }),
      success: function (response) {
        console.log('Success:', response);
        alert(response.message);
      },
      error: function (error,response) {
        alert(response.message);
        console.error('Error:', error);
      }
    });
  }

 

 

수령 완료 버튼 연결

//수령 완료
    @DeleteMapping("/posts/received")
    public ApiResponse<Void> receiveOrder(
        @AuthenticationPrincipal UserDetailsImpl userDetails,
        @Valid @RequestBody PostIdRequest postIdReq
    ) {
        postService.receiveOrder(postIdReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "수령완료 처리가 되었습니다.");
    }
function receiveOrder(button){
    var postId = $(button).attr("post");
    $.ajax({
      type: 'DELETE',
      url: "/api/v1/posts/received",
      dataType: "json",
      contentType: 'application/json',
      data: JSON.stringify({
        postId: postId
      }),
      success: function (response) {
        console.log('Success:', response);
        alert(response.message);
      },
      error: function (error,response) {
        alert(response.message);
        console.error('Error:', error);
      }
    });
  }

 

 

참가신청 버튼 연결

@PostMapping()
    public ApiResponse<Void> applyParticipation(
        @RequestBody OfferRelatedPostRequest offerRelatedPostReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {

        offerService.applyParticipation(offerRelatedPostReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "참가신청이 완료되었습니다.");
    }
@DeleteMapping()
    public ApiResponse<Void> cancelParticipation(
        @RequestBody OfferRequest offerReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {

        offerService.cancelParticipation(offerReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "참가취소가 완료되었습니다.");
    }

→ OfferId는 글에서 알 수 없는 부분이라 와이어프레임에서 참가 취소를 요청 조회 페이지에서 하도록 수정함

 

function drawHostButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="getOffers(this)" name="btnradio" id="getOffers" autocomplete="off" checked>
      <label class="btn btn-outline-secondary" for="getOffers">요청 조회</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="closeApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="closeApplication">모집 마감</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="cancelRecruitment(this)" name="btnradio" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelRecruitment">모집 취소</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="completeOrder(this)" name="btnradio" id="completeOrder" autocomplete="off">
      <label class="btn btn-outline-secondary" for="completeOrder">주문 완료</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="chatRoom(this)" name="btnradio" id="chatRoom" autocomplete="off">
      <label class="btn btn-outline-secondary" for="chatRoom">채팅방 참여</label>
    </div>
            `
    )
  }

  function drawParticipantButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="exit(this)" name="btnradio"  id="exit" autocomplete="off">
      <label class="btn btn-outline-secondary" for="exit">나가기</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="receiveOrder(this)" name="btnradio" id="cancelRecruitment" autocomplete="off">
      <label class="btn btn-outline-secondary" for="receiveOrder">수령 완료</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="chatRoom(this)" name="btnradio" id="chatRoom" autocomplete="off">
      <label class="btn btn-outline-secondary" for="chatRoom">채팅방 참여</label>
    </div>
            `
    )
  }

  function drawAnyoneButtons(postId){
    $('#buttons').append(
        `
     <div class="btn-group" role="group" aria-label="Basic radio toggle button group">
      <input type="radio" class="btn-check" post = "${postId}" onclick="getOffers(this)" name="btnradio" id="getOffers" autocomplete="off" checked>
      <label class="btn btn-outline-secondary" for="getOffers">요청 조회</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="editmenus(this)" name="btnradio" id="editmenus" autocomplete="off">
      <label class="btn btn-outline-secondary" for="editmenus">메뉴 수정</label>
      <input type="radio" class="btn-check" post = "${postId}" onclick="applyApplication(this)" name="btnradio"  id="closeApplication" autocomplete="off">
      <label class="btn btn-outline-secondary" for="cancelApplication">참가 신청</label>
    </div>
            `
    )
  }

 

💡메뉴페이지 만듦

본인 메뉴 조회를 하고 메뉴 삭제/생성 요청을 보내야 하는 페이지임

//자신의 메뉴 조회
    @GetMapping("/menus")
    public ApiResponse<List<MenuResponse>> getMenus(
        @Valid @RequestBody MenuReadRequest menuReadReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "나의 메뉴 조회",
            menuService.getMenus(menuReadReq, userDetails.getUser()));
    }
public record MenuResponse(
    String menuname,
    Integer price
) {

}

id가 안 달려있어서 id추가해줌

public record MenuResponse(
    Long id,
    String menuname,
    Integer price
) {

}

service단 함수에서도 수정함

@Override
    public List<MenuResponse> getMenus(MenuReadRequest menuReadReq, User user) {
        Post post = findPostById(menuReadReq.postId());
        List<Menu> menus = menuRepository.findAllByUserAndPost(user, post);
        return menus.stream().map(menu -> new MenuResponse(menu.getId() ,menu.getMenuname(), menu.getPrice()))
            .toList();
    }
// 글에 본인이 주문할 Menu 삭제하기
    @DeleteMapping("/menus")
    public ApiResponse<Void> deleteMenu(
        @Valid @RequestBody MenuDeleteRequest menuDeleteReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        menuService.deleteMenu(menuDeleteReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "메뉴를 삭제했습니다.");
    }
// 글에 본인이 주문할 Menu 추가하기
    @PostMapping("/menus")
    public ApiResponse<Void> createMenu(
        @Valid @RequestBody MenuRequest menuReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        menuService.createMenu(menuReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.CREATED.value(), "메뉴를 추가했습니다.");
    }

menu.html 만드는데 메뉴 추가를 누르면 메뉴 이름이랑 메뉴 가격 정해지고 추가될 수 있는거 하고싶음

function drawMenus(data) {
    data.forEach((menu) =>
        $('#menus').append(
            `
  <div>
    <span>${menu.menuname}</span>
    <span>${menu.price}</span>
    <button class="btn btn-outline-secondary" menuId=${menu.id} onclick="deleteMenu(this)"
            type="button">삭제
    </button>
  </div>
  `
        ))

    $('#menus').append(
        `
  <div id = "addmenu">
           <button class="btn btn-outline-secondary" menuId=${menu.id} onclick="dropAddMenu()"
       type="button">추가
    </button>
  </div>
  `
    )
  }

  function dropAddMenu() {
    $('#addmenu').append(
        `<div id = "dropaddmenu">
        <div className="input-group">
          <span className="input-group-text">메뉴 이름</span>
          <input aria-label="메뉴 이름" placeholder="메뉴 이름" className="form-control" id="menuname"
                 type="text">
          <span className="input-group-text">메뉴 가격</span>
            <input aria-label="메뉴 가격" placeholder="메뉴 가격" className="form-control" id="menuprice" type="text">
        </div>
        <button class="btn btn-outline-secondary" onclick="addMenu()"
       type="button">추가
    </button>
    </div>
        `
    )
  }

이렇게 짜고 있었는데

삭제하는거 몰라서 찾아봄

div.remove();

 

아 근데 삭제 대신 숨겼다 다시 꺼내고 싶어짐

https://hianna.tistory.com/484

<div id="dropaddmenu" visibility="hidden">
      <div className="input-group">
        <span className="input-group-text">메뉴 이름</span>
        <input aria-label="메뉴 이름" placeholder="메뉴 이름" className="form-control" id="menuname"
               type="text">
        <span className="input-group-text">메뉴 가격</span>
        <input aria-label="메뉴 가격" placeholder="메뉴 가격" className="form-control" id="menuprice" type="text">
      </div>
      <button className="btn btn-outline-secondary" onClick="addMenu()"
              type="button">추가
      </button>
    </div>
function dropAddMenu() {

      if ($('#dropaddmenu').style.visibility === 'hidden') {
        $('#dropaddmenu').style.visibility = 'visible'
      } else {
        $('#dropaddmenu').style.visibility = 'hidden'
      }
    }

그리고 메뉴 추가했을때 페이지 새로고침 하고싶음

 

메뉴를 추가해봄

public record MenuRequest(
    @NotNull
    Long postId,
    @NotBlank
    String name,
    @NotNull
    Integer price
) {

}
// 글에 본인이 주문할 Menu 추가하기
    @PostMapping("/menus")
    public ApiResponse<Void> createMenu(
        @Valid @RequestBody MenuRequest menuReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        menuService.createMenu(menuReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.CREATED.value(), "메뉴를 추가했습니다.");
    }
function addMenu() {

      let menuname = $('#menuname').val();
      let price = $('#menuprice').val();
      let postId = [[${postId}]];

      $.ajax({
        type: 'POST',
        url: "/api/v1/menus",
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: postId,
          menuname: menuname,
          price: price
        }),
        success: function (response) {
          console.log('Success:', response);
          alert(response.message);
        },
        error: function (error, response) {
          alert(response.message);
          console.error('Error:', error);
        }
      });

    }

메뉴 삭제함

public record MenuDeleteRequest(
    @NotNull
    Long menuId
){

}
// 글에 본인이 주문할 Menu 삭제하기
    @DeleteMapping("/menus")
    public ApiResponse<Void> deleteMenu(
        @Valid @RequestBody MenuDeleteRequest menuDeleteReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        menuService.deleteMenu(menuDeleteReq, userDetails.getUser());
        return new ApiResponse<>(HttpStatus.OK.value(), "메뉴를 삭제했습니다.");
    }
<div>
    <span>${menu.menuname}</span>
    <span>${menu.price}</span>
    <button class="btn btn-outline-secondary" menuId=${menu.id} onclick="deleteMenu(this)"
            type="button">삭제
    </button>
  </div>
function deleteMenu(button) {
      
      let menuId = $(button).attr("menuId");
      
      $.ajax({
        type: 'DELETE',
        url: "/api/v1/menus",
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          menuId: menuId
        }),
        success: function (response) {
          console.log('Success:', response);
          alert(response.message);
        },
        error: function (error, response) {
          alert(response.message);
          console.error('Error:', error);
        }
      });

    }
function editmenus(button){
    var postId = $(button).attr("post");
    window.location.replace(host + `/menus/${postId}`);
  }

Post 페이지에 이렇게 달아줌

432:183 Uncaught ReferenceError: host is not defined at editmenus (32:183:29) at HTMLInputElement.onclick (32:1:1)

테스트해봄

function editmenus(button){
    var postId = $(button).attr("post");
    const host = 'http://' + window.location.host;
    window.location.replace(host + `/menu/${postId}`);
  }

못생기게 뜨는데다가 뭔가 잘못됨

Bean Validation에서 걸러지면 저 오류 뜨던데

🚩문제 : Bean Validation에서 걸림

 

 

💡원인 : 그냥 name인데 menuname으로 읽고있었음

function addMenu() {

      let name = $('#menuname').val();
      let price = $('#menuprice').val();
      let postId = [[${postId}]];

      $.ajax({
        type: 'POST',
        url: "/api/v1/menus",
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: postId,
          name: name,
          price: price
        }),
        success: function (response) {
          console.log('Success:', response);
          alert(response.message);
        },
        error: function (error, response) {
          alert(response.message);
          console.error('Error:', error);
        }
      });

    }

⛳해결 : 그냥 name으로 추가해봄

잘 된다

메뉴 테이블에도 잘 들어가있음

🚩문제 : 메뉴가 안 뜸

 

get이 안된다고 함

function getData() {

      $.ajax({
        type: 'GET',
        url: `/api/v1/menus`,
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: [[${postId}]]
        }),

function getData() {

      let postId = [[${postId}]]

      $.ajax({
        type: 'GET',
        url: `/api/v1/menus`,
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: postId
        }),

이렇게 분리시켜봄

안 됨

전체글 조회에서는 어떻게 했지?

그건 그냥 href

@GetMapping("/menu/{postId}")
    public String menuPage(@PathVariable(name = "postId") Long postId) {
        ModelAndView mav = new ModelAndView("postId");
        mav.addObject("postId", postId);
        return "domain/menu/menu";
    }
}

@GetMapping("/menu/{postId}")
    public String menuPage(
        @PathVariable(name = "postId") Long postId,
        Model model
    ) {
        Map<String, Long> map = new HashMap<>();
        map.put("postId", postId);
        model.addAttribute("postId", postId);
        model.mergeAttributes(map);
        return "domain/menu/menu";
    }

근데 여기서 안 받아와져서 그냥 요소에다 만들어서 붙이고 그거 읽어옴

function getData() {

      let postId = ${postId}

또 쌍따옴표 이슈인가

let postId = "${postId}";

응 아니었음

 

걍 요소에 붙이고 그거가지고 할래

<div style="padding:10px;width:1000px; height:max-content">
  <div id = "post" post = "${postId}" >id<span th:text="${postId}"></span></div>

 

function getData() {

      let postId = $('#post').attr("post");

      $.ajax({
        type: 'GET',
        url: `/api/v1/menus`,
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: postId
        }),
        success: function (response) {
          console.log('Success:', response);
          alert(response.message);

          drawMenus(response.data);

        },
        error: function (error) {
          console.error('Error:', error);
        }
      });
    }

아니 주소가 왜 이렇게 됨?

 

🚩문제 : Get할때 JSON 형식으로 Object를 전송하면 url에 이상하게 들어감

Invalid character found in the request target [/api/v1/menus?{%22postId%22:%22${postId}%22} ]. The valid characters are defined in RFC 7230 and RFC 3986

 

function getData() {

      //let postId = $('#post').attr("post");

      $.ajax({
        type: 'GET',
        url: `/api/v1/menus`,
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: [[${postId}]]
        }),
        success: function (response) {
          console.log('Success:', response);
          alert(response.message);

          drawMenus(response.data);

        },
        error: function (error) {
          console.error('Error:', error);
        }
      });
    }

다시 원래 방식대로 회귀함

let idForPost = [[${postId}]]

java.lang.IllegalArgumentException: Invalid character found in the request target [/api/v1/menus?{%22postId%22:32} ]. The valid characters are defined in RFC 7230 and RFC 3986

아 map이라 저런가 val로 읽어줘야되나

let idForPost = postId.val();

      $.ajax({
        type: 'GET',
        url: `/api/v1/menus`,
        dataType: "json",
        contentType: 'application/json',
        data: JSON.stringify({
          postId: idForPost
        }),

이렇게 한번 해봄

이제 ajax가 날아가지도 않는다 ㅋㅋㅋㅋ

 

JavaScript Syntax:

Instead of $('#dropaddmenu').style.visibility, use $('#dropaddmenu').css('visibility') to get and set the visibility.
The same applies to $('#dropaddmenu').style.visibility = 'hidden', replace it with $('#dropaddmenu').css('visibility', 'hidden').
Variable Type Handling:

In the getData function, it seems you're trying to get the value of [[${postId}]].val();. The correct syntax is let idForPost = [[${postId}]];.
@GetMapping("/menu/{postId}")
    public String menuPage(
        @PathVariable(name = "postId") Long postId,
        Model model
    ) {
        model.addAttribute("postId", postId);
        return "domain/menu/menu";
    }

이렇게 해봄

function getData() {

      let idForPost = [[${postId}]];

      $.ajax({

이렇게 보내면

/api/v1/menus?{%22postId%22:32}

이렇게 요청이 가는데

/api/v1/menus

와 함께 32 객체를 보내고 싶어

{postId:32} 이렇게

data: {
          postId: idForPost
        },

이렇게 한번 해봄

 

이렇게 날아감

 

⛳해결 : GET요청을 할 때 RequestBody 대신 RequestParam을 씀

GET요청을 할때 RequestBody가 안 먹힘

//자신의 메뉴 조회
    @GetMapping("/menus")
    public ApiResponse<List<MenuResponse>> getMenus(
        @Valid @RequestBody MenuReadRequest menuReadReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "나의 메뉴 조회",
            menuService.getMenus(menuReadReq, userDetails.getUser()));
    }

this code can’t read /api/v1/menus?{%22postId%22:32}

//자신의 메뉴 조회
    @GetMapping("/menus")
    public ApiResponse<List<MenuResponse>> getMenus(
        @Valid @RequestBody MenuReadRequest menuReadReq,
        @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        return new ApiResponse<>(HttpStatus.OK.value(), "나의 메뉴 조회",
            menuService.getMenus(menuReadReq, userDetails.getUser()));
    }

 

이제 메뉴 추가도 잘 되고

삭제도 잘 된다

 

 

💡메뉴 추가하는 부분 접었다 폈다 하는 기능 추가해봄

function dropAddMenu() {
      var dropAddMenuElement = $('#dropaddmenu');

      if (dropAddMenuElement.css('visibility') === 'hidden') {
        dropAddMenuElement.css('visibility', 'visible');
      } else {
        dropAddMenuElement.css('visibility', 'hidden');
      }
    }

 

💡자동 새로고침 기능 추가해봄

이제 자동 새로고침만 해봄

function refresh(){
    const host = 'http://' + window.location.host;
    window.location.replace(host + `/menu/${postId}`);
  }

이걸 넣어봄

그냥 새로고침해야 불러와진다 ㅋㅋ refresh는 안 움직임

refresh →

location.reload();

새로고침 될때마다 나의 메뉴 조회 뜨는거 짜증나서 케이스분류함

if(response.status !== 200){
          alert(response.message);
        }

잘 된다!

 

 

 

 

'개발일지' 카테고리의 다른 글

2024-01-19,Today I Learned  (0) 2024.01.20
2024-01-17, Today I Learned  (0) 2024.01.17
2024-01-16, Today I Learned  (0) 2024.01.16
2024-01-15, Today I Learned  (0) 2024.01.15
2024-01-14, Today I Learned  (0) 2024.01.15