본문 바로가기
Spring Boot Project/Plming

[Plming] 게시판 기능 코드 리뷰 3편 (신청 기능)

by slchoi 2022. 4. 14.
728x90
SMALL

저번 글에서는 게시판 CRUD 기능과 관련된 코드를 리뷰해보았다. 이번 글에서는 게시글 신청, 검색 기능과 관련된 코드를 살펴보며 개발을 진행하면서 궁금했던 점과 공부해야 할 부분들을 정리해볼 것이다.

 

1. 게시글 신청

사용자는 게시글에 참여 신청을 할 수 있고, 게시글을 작성한 사용자는 참여를 신청한 사용자를 승인, 거절할 수 있다. 또한, 본인의 게시글에 들어갈 경우 게시글 상세 정보와 함께 현재 신청한 사용자와 참여가 승인된 참여자 목록을 보여줄 것으로 계획했기 때문에 이와 관련된 API도 필요했다. 만약 본인이 작성한 게시글이 아닌 경우에는 게시글 상세 정보와 참여가 승인된 참여자 목록만 보이도록 할 예정이다.

이제 기능별 코드를 하나씩 살펴볼 것이다.

1.1. 게시글 신청하기

게시글을 신청하려는 경우 예외가 발생하는 경우는 아래와 같다.

  1. 이미 신청한 게시글에 중복 신청하려는 경우
  2. 참여 승인이 거절된 게시글에 반복 신청하려는 경우
  3. 현재 참여 승인된 인원 수와 최대 참여 인원수가 같아진 경우

예외가 발생할 경우 406 코드와 response body에 예외가 발생한 원인에 따른 메시지가 함께 전달된다.

BoardApiController

/**
 * 게시글 신청
 */
@PostMapping("/{id}/application")
public ResponseEntity<Object> apply(@PathVariable final Long id, @CookieValue final String token) {

    Long userId = jwtTokenProvider.getUserId(token);
    String appliedStatus = boardService.apply(id, userId);
    CustomException e = new CustomException(ErrorCode.NOT_ACCEPTABLE);

    if (appliedStatus.equals("거절")) {
        return ResponseEntity.status(e.getErrorCode().getStatus().value())
                .body(new ErrorResponse(e.getErrorCode(), "참여 승인이 거절된 게시글입니다."));
    }
    if (appliedStatus.equals("신청")) {
        return ResponseEntity.status(e.getErrorCode().getStatus().value())
                .body(new ErrorResponse(e.getErrorCode(), "이미 신청한 게시글입니다."));
    }
    if (appliedStatus.equals("마감")) {
        return ResponseEntity.status(e.getErrorCode().getStatus().value())
                .body(new ErrorResponse(e.getErrorCode(), "신청 마감된 게시글입니다."));
    }

    return ResponseEntity.status(201).body(appliedStatus);
}
  • POST "posts/{id}/application" 요청이 들어오면 게시글 신청 로직이 수행된다.
  • URI의 path에서 게시글 id를 가져오고, Cookie에서 token 값을 가져온다.
  • 토큰으로부터 사용자 id를 가져와 게시글 id와 함께 BoardService의 apply 메서드의 매개변수로 전달하고, apply 메서드를 호출한다.
    • apply 메서드는 참여 신청에 성공하면 신청한 게시글 id를 반환한다.
    • 예외가 발생하면, 예외 1번의 경우 "신청", 예외 2번의 경우 "거절", 예외 3번의 경우 "마감"을 반환한다.
  • 신청이 성공하면 201 코드와 response body에 신청한 게시글 id를, 신청에 실패하면 403 코드와 response body에 신청에 실패한 원인을 담아 반환한다. 

 

BoardService

/**
 * 게시글 신청 하기
 */
@Transactional
public String apply(final Long boardId, final Long userId) {

    return applicationService.save(boardId, userId);
}
  • 매개변수로 게시글 id와 사용자 id를 받아와 ApplicationService의 save 메서드에 전달한다.
  • ApplicationService의 save 메서드의 반환 값을 그대로 반환한다.

 

ApplicationService

save, isMaxNum, isStatusTrue

더보기
@Transactional
public String save(final Long boardId, final Long userId) {

    if (findApplication(boardId, userId).size() == 0 && isMaxNum(boardId) && isStatusTrue(boardId, userId)) {
        Application application = Application.builder()
                .board(boardRepository.getById(boardId))
                .user(userRepository.getById(userId))
                .status("대기")
                .build();
        applicationRepository.save(application);

        return applicationRepository.getById(application.getId()).getBoard().getId().toString();
    }

    /**
     * 신청 인원이 가득 찼을 경우 자동으로 모집 완료 상태로 변경
     */
    if(!isMaxNum(boardId)) {
        Board board = boardRepository.getById(boardId);
        board.updateStatus("모집 완료");
    }

    if ((findApplication(boardId, userId).size() == 0 && !isMaxNum(boardId))) {
        return "마감";
    } else if (!isStatusTrue(boardId, userId)) {
        return "거절";
    } else {
        return "신청";
    }
}

/**
 * 게시글 참여 인원이 최대 인원인지 확인
 * 최대 인원이 아닌 경우 true 반환
 */
public boolean isMaxNum(final Long boardId) {

    Board board = boardRepository.findById(boardId).orElseThrow(() -> new CustomException(ErrorCode.POSTS_NOT_FOUND));
    Integer participantMax = board.getParticipantMax();
    Integer participantNum = countParticipantNum(boardId);

    return (participantNum == 0) || (participantMax > participantNum) ? true : false;
}

/**
 * 신청 상태가 거절인지 확인
 * 신청 상태가 거절이 아닌 경우 true 반환
 */
public boolean isStatusTrue(final Long boardId, final Long userId) {
    if (findApplication(boardId, userId).size() == 0) {
        return true;
    }

    return !findApplication(boardId, userId).get(0).getStatus().equals("거절") ? true : false;
}

 

save 예외 1, 2, 3번에 모두 해당되지 않는 경우 새로운 Application 객체를 생성하고, ApplicationRepository의 save 메서드의 매개변수로 넣어준다. save 메서드를 호출해 게시글에 참여를 신청한다. 신청에 성공하면 참여 신청한 게시글 id를 반환한다.

신청 인원이 가득 찼을 경우 매개변수로 받아온 게시글 id를 사용해 Board 객체를 가져오고 Board Entity 내부에 구현한 updateStatus 메서드를 사용해 게시글 모집 상태를 "모집 완료"로 변경한다.

사용자가 게시글을 신청한 적이 없지만, 게시글의 모집 상태가 모집 완료인 경우 "모집 완료"를 반환하고, 신청이 거절된 게시글에 다시 신청한 경우 "거절"을, 참여 신청 후 승인 대기 중인 게시글에 다시 신청한 경우 "신청"을 반환한다.
isMaxNum 현재 참여 승인된 인원 수가 최대 참여 인원 수보다 작은지 확인하는 메서드이다. 최대 참여 인원 미만일 경우 true를 반환하고, 최대 참여 인원 이상일 경우 false를 반환한다.
isStatusTrue 사용자의 게시글 신청 상태를 확인하는 메서드이다. 만약 사용자가 게시글에 참여 신청한 적이 없거나 신청 상태가 거절이 아닐 경우 true를 반환하고, 신청 상태가 거절일 경우 false를 반환한다.

findApplication

/**
 * 게시글 신청 조회
 */
private List<Application> findApplication(final Long boardId, final Long userId) {

    return applicationRepository.findApplication(boardId, userId);
}
  • 매개변수로 게시글 id와 사용자 id가 들어오면 ApplicationRepository의 findApplication 메서드의 매개변수로 전달해주고, findApplication을 실행해 반환해주는 값을 그대로 반환한다.

 

ApplicationRepository

@Override
public List<Application> findApplication(Long boardId, Long userId) {

    return jpaQueryFactory.selectFrom(application)
            .where(application.board.id.eq(boardId), application.user.id.eq(userId))
            .fetch();
}
  • application 테이블에서 board.id가 매개변수로 들어온 boardId와 같고, user.id가 매개변수로 들어온 userId와 같은 Application 객체를 반환한다.
🏭 Refactoring
사용자는 동일한 게시글에 한 번만 신청 가능하기 때문에 findApplication의 결과는 Application 객체 하나 이거나 null이다. 이럴 경우 fetch( )를 사용하는 것보다 fetchOne( )을 사용하는 게 더 나아 보이므로 리팩터링 할 때 이 부분도 함께 수정해주어야겠다. 이 부분을 수정하면 ApplicationService의 save 메서드에서 findApplication 메서드의 반환 값의 크기를 0과 비교해주는 것이 아니라 null과 비교하도록 변경해주어야 한다.

 

1.2. 신청한 게시글 리스트 조회

사용자가 본인의 마이페이지에 들어갈 경우 본인이 신청한 게시글 리스트를 확인할 수 있도록 계획했기 때문에 이와 관련된 기능을 구현한 API이다.

BoardApiController

/**
 * 신청 게시글 리스트 조회 - 사용자 ID 기준
 */
@GetMapping("/application")
public Page<BoardListResponseDto> findAppliedBoardByUserID(@CookieValue final String token, Pageable pageable) {
    return boardService.findAppliedBoardByUserId(jwtTokenProvider.getUserId(token), pageable);
}
  • GET "posts/application" 요청이 들어오면 사용자 신청 게시글 리스트 로직이 실행된다.
  • Cookie에서 token 값을 가져오고, 페이징을 적용하기 위해 Pageable 정보도 가져온다.
  • BoardService의 findAppliedBoardByUserId 메서드에 token에서 추출한 사용자 id와 Pageable 정보를 전달하고, findAppliedBoardByUserId가 반환하는 값을 그대로 반환한다.

 

BoardService

findAppliedBoardByUserId

/**
 * 신청 게시글 리스트 조회 - (사용자 ID 기준)
 */
public Page<BoardListResponseDto> findAppliedBoardByUserId(final Long userId, final Pageable pageable) {

    Page<Board> appliedBoards = applicationService.findAppliedBoardByUserId(userId, pageable);

    return getBoardListResponseFromPage(appliedBoards);
}
  • 매개변수로 사용자 id와 pageable 정보가 들어오면 ApplicationService의 findAppliedBoardByUserId 메서드의 매개변수로 전달하면서, 이 함수를 호출하고 반환받은 결과를 appliedBoards 변수에 저장한다.
  • 같은 클래스 내의 getBoardListResponseFromPage의 매개변수로 appliedBoards 값을 넣어주고, 이 함수를 호출해 반환받은 값을 그대로 반환한다.

getBoardListResponseFromPage

/**
 * 각 게시글의 태그 이름 조회 후 BoardListResponseDto 반환
 */
public Page<BoardListResponseDto> getBoardListResponseFromPage(Page<Board> list) {

    List<BoardListResponseDto> result = new ArrayList<BoardListResponseDto>();
    List<Board> boards = list.getContent();
    for (Board post : boards) {
        Integer participantNum = applicationService.countParticipantNum(post.getId());
        result.add(new BoardListResponseDto(post, participantNum));
    }

    return new PageImpl<>(result);
}
  • 매개변수로 받은 페이징이 적용된 Board 객체 리스트를 페이징이 적용된 BoardListResponseDto 객체로 변환해주는 메서드이다.
  • 먼저, 결과를 담은 result 변수를 생성하고, 페이징이 적용된 Board 객체 리스트에서 Board 객체 리스트만 빼와 boards 변수에 저장한다.
  • boards 변수에 저장된 Board 객체를 하나씩 가져와 해당 게시글에 현재 참여가 승인된 인원수를 계산해 Board 객체와 함께 BoardListResponseDto 생성자에 매개변수로 넣어주어 BoardListResponseDto 객체를 생성해준다.
  • 생성된 BoardListResponseDto 객체를 result 변수에 추가해준 뒤, boards 안의 모든 Board 객체에 대해 처리가 완료되면 result 변수를 content 값으로 가지는 새로운 Page 객체를 생성해 반환한다.

 

ApplicationService

/**
 * 신청한 게시글 리스트 조회
 */
public Page<Board> findAppliedBoardByUserId(final Long userId, final Pageable pageable) {

    return applicationRepository.findAppliedBoardByUserId(userId, pageable);
}
  • 매개변수로 사용자 id와 Pageable 정보를 받아와 ApplicationRepository의 findAppliedBoardByUserId 메서드의 매개변수로 넣어주고, 이 메서드를 호출해 반환받은 결과를 그대로 반환한다.

 

ApplicationRepository

/**
 * 신청 게시글 리스트 조회 - (사용자 Id 기준)
 */
@Override
public Page<Board> findAppliedBoardByUserId(Long userId, Pageable pageable) {

    // content를 가져오는 쿼리
    List<Board> query = jpaQueryFactory
            .select(application.board).from(application)
            .where(application.user.id.eq(userId))
            .orderBy(board.id.desc(), board.createDate.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

    // count만 가져오는 쿼리
    JPQLQuery<Board> count = jpaQueryFactory.selectFrom(application.board)
            .where(application.user.id.eq(userId));

    return PageableExecutionUtils.getPage(query, pageable, () -> count.fetchCount());
}
  • 매개변수로 사용자 id와 Pageable 정보를 받아온다.
  • application 테이블에서 user.id와 매개변수로 받아온 userId가 일치하는 board 정보를 board.id와 createDate를 기준으로 내림차순으로 정렬해 가져온다.
    • 페이징 적용을 위해 offset과 limit을 지정한다.
  • content를 가져오는 코드와 count를 가져오는 쿼리를 합쳐서 구현할 수 있지만, 나눠서 구현하는 것이 성능 측면에서 더 나은 선택이라고 한다.
공부해야 할 부분 (Spring)
Pageable이 제공하는 기능

게시판 리스트를 보여줄 경우 페이징 처리가 필요해 구글링 해서 페이징을 적용할 수 있는 방법을 찾은 것이 바로 Pageble이었다. Pageable에 대해 자세히 공부해본 것이 아니기 때문에 Pageable, Page, PageImpl 등 Spring에서 제공하는 페이징 관련 기능들에 대해 공부해봐야겠다.

 

1.3. 게시글 신청자 리스트 + 참여자 리스트 조회

본인이 작성한 게시글에 들어갈 경우 신청자 리스트를, 본인이 작성하지 않은 게시글에 들어갈 경우 참여자(참여가 승인된 사용자) 리스트를 전달하는 API이다.

BoardApiController

/**
 * 게시글 신청자 리스트 + 참여자 리스트 통합
 */

@GetMapping("/{id}/application/users")
public ResponseEntity<Object> findAppliedUserByBoardId(@PathVariable final Long id, @CookieValue final String token) {

    return ResponseEntity.status(200).body(boardService.findAppliedUsers(id, jwtTokenProvider.getUserId(token)));
}
  • GET "posts/{id}/application/users" 요청이 들어오면 게시글 신청자 리스트 + 참여자 리스트 조회 로직이 실행된다.
  • URI에서 게시글 id를 가져오고, Cookie에서 token 값을 가져온 뒤 사용자 id를 추출해 게시글 id와 함께 BoardService의 findAppliedUsers 메서드의 매개변수로 넣어주고, 이 메서드를 실행시켜 반환받은 값을 response body에 담고 200 코드로 응답한다.

 

BoardService

findAppliedUsers

/**
 * 신청 사용자 리스트 조회 + 참여자 리스트 조회
 */
public Object findAppliedUsers(final Long boardId, final Long userId) {

    Board board = boardRepository.findById(boardId).orElseThrow(() -> new CustomException(ErrorCode.POSTS_NOT_FOUND));

    if(board.getUser().getId().equals(userId)) {
        return findAppliedUserByBoardId(boardId);
    } else {
        return findParticipantUserByBoardId(boardId);
    }
}
  • 매개변수로 전달받은 게시글 id를 BoardRepository의 findById 메서드의 매개변수로 전달하고, 이 메서드를 호출해 해당 게시글 정보를 불러온다. 만약 게시글 id에 해당하는 게시글이 없다면 예외가 발생한다.
    • findById 메서드는 JPA Repository에서 기본으로 제공해주는 메서드이다.
  • Board 객체의 사용자 id와 매개변수로 받아온 userId가 일치하는 경우 findAppliedUserByBoardId 메서드를 호출하고, 일치하지 않는 경우 findParticipantUserByBoardId 메서드를 호출해 반환받은 값을 그대로 반환한다.

findAppliedUserByBoardId

/**
 * 신청 사용자 리스트 조회 - (게시글 ID 기준)
 */
public List<Map<String, Object>> findAppliedUserByBoardId(final Long boardId) {

    List<Application> applicationList = applicationService.findAppliedUserByBoardId(boardId);

    List<Map<String, Object>> result = new ArrayList<>();

    applicationList.forEach(application -> {
        Map<String, Object> map = new HashMap<>(2);
        map.put("user", userService.getUserList(application.getUser().getId()));
        map.put("status", application.getStatus());
        result.add(map);
    });

    return result;
}
  • 프론트엔드 파트에서 신청한 사용자 리스트를 전달할 때 사용자 정보와 신청 상태를 함께 전달해주었으면 좋겠다고 하셔서 Map 형태로 "user" 키에는 사용자 정보를, "status" 키에는 신청 상태를 전달하기로 했다.
  • 먼저, 매개변수로 받아온 게시글 id를 ApplicationService의 findApplicationBoardId 메서드의 매개변수로 넣어주고 이 메서드를 실행해 현재 게시글의 신청 정보 리스트를 받아온다.
  • 받아온 Application 리스트에 들어있는 모든 Application 객체에 대해 "user" 정보와 "status" 정보를 저장한 뒤 result 리스트에 추가하고, 모든 Application에 대해 수행이 끝나면 result 리스트를 반환한다.

findParticipantUserByBoardId

/**
 * 참여 사용자 리스트 조회 - (게시글 ID 기준)
 */
public List<UserListResponseDto> findParticipantUserByBoardId(final Long boardId) {

    List<User> participatedUsers = applicationService.findParticipantUserByBoardId(boardId);

    return participatedUsers.stream().map(User::getId).map(userService::getUserList).collect(Collectors.toList());
}
  • 매개변수로 받아온 게시글 id를 ApplicationService의 findParticipantUserByBoardId 메서드의 매개변수로 넣어주고, 이 메서드를 호출해 해당 게시글에 참여가 승인된 사용자 정보 리스트를 받아온다.
  • 받아온 User 객체 리스트에 들어있는 모든 User 객체에 대해 UserService의 getUsetList를 호출해 UserListResponseDto를 생성하고 생성된 UserListResponseDto 객체들을 리스트화해 반환한다.

 

ApplicationService

/**
 * 게시글 신청자 리스트 조회
 */
public List<Application> findAppliedUserByBoardId(final Long boardId) {

    return applicationRepository.findAppliedUserByBoardId(boardId);
}

/**
 * 게시글 참여자 리스트 조회
 */
public List<User> findParticipantUserByBoardId(final Long boardId) {

    return applicationRepository.findParticipantByBoardId(boardId);
}
  • findAppliedUserByBoardId 메서드는 매개변수로 게시글 Id가 주어지면 ApplicationRepository의 findAppliedUserByBoardId 메서드의 매개변수로 게시글 Id를 전달하고, 이 메서드를 호출해 반환받은 결괏값을 그대로 반환한다.
  • findParticipantUserByBoardId 메서드는 매개변수로 게시글 id가 주어지면 ApplicationRepository의 findParticipantByBoardId의 매개변수로 게시글 id를 전달하고, 이 메서드를 호출해 반환받은 결괏값을 그대로 반환한다.
🏭 Refactoring
findParticipantUserByBoadId 메서드 이름을 findParticipantByBoardId로 수정해야겠다.

 

ApplicationRepository

/**
 * 신청 사용자 리스트 조회 - (게시글 ID 기준)
 */
@Override
public List<Application> findAppliedUserByBoardId(Long boardId) {

    return jpaQueryFactory.selectFrom(application)
            .where(application.board.id.eq(boardId))
            .fetch();
}

/**
 * 참가자 리스트 조회 - (게시글 ID 기준)
 */
@Override
public List<User> findParticipantByBoardId(Long boardId) {

    return jpaQueryFactory.select(application.user).from(application)
            .where(application.board.id.eq(boardId), application.status.eq("승인"))
            .fetch();
}
  • findAppliedUserByBoardId 메서드는 게시글 id를 매개변수로 받아와 application 테이블에서 board.id 값과 매개변수로 받아온 게시글의 id 값이 일치하는 Application 객체 리스트를 반환한다.
    • 신청 사용자 같은 경우 user 정보와 status 정보가 모두 필요하므로 selectFrom을 사용해 Application 객체의 모든 정보를 반환하도록 구현했다.
  • findParticipantByBoardId 메서드는 게시글 id를 매개변수로 받아와 application 테이블에서 board.id 값과 매개변수로 받아온 게시글 id 값이 일치하고, status 값이 "승인"인 Application 객체 정보 중 사용자 관련 정보만 반환한다.
    • 참가자 같은 경우 user 정보만 필요하기 때문에 select문과 from문을 구분해서 사용자 정보만 조회해오도록 구현했다.
🏭 Refactoring
findAppliedUserByBoardId 부분도 select문과 from문을 구분해서 Application 객체 정보 중에서 board 정보는 제외하고 가져오도록 수정해야겠다.

 

1.4. 게시글 신청 상태 업데이트

BoardApiController

/**
 * 게시글 신청 상태 업데이트
 */
@PatchMapping("/{id}/application")
public String updateAppliedStatus(@PathVariable final Long id, @CookieValue final String token,
                                  @RequestParam final String status, @RequestParam final String nickname) {

    ApplicationStatusRequestDto body = ApplicationStatusRequestDto.builder().status(status).nickname(nickname).build();
    String result = boardService.updateAppliedStatus(id, jwtTokenProvider.getUserId(token), body.getNickname(), body.getStatus());

    return result;
}
  • PATCH "/posts/{id}/application" 요청이 들어오면 게시글 신청 상태 업데이트 로직이 실행된다.
  • URI에서 게시글 id를, Cookie에서 token 값을, request body로 수정할 신청 상태와 신청 상태를 수정해줄 사용자의 nickname을 받아온다.
  • request body를 통해 받아온 정보를 사용해 ApplicationStatusRequestDto 객체를 생성한다.
  • BoardService의 updateAppliedStatus 메서드의 매개변수로 게시글 id, 사용자 id, 신청 상태를 수정해줄 사용자 닉네임, 수정할 신청 상태 값을 넣어주고, 이 메서드를 호출해 반환받은 값을 그대로 반환한다.
🏭 Refactoring

코드 리뷰를 하면서 코드를 다시 보니 ApplicationStatusRequestDto 클래스는 없어도 될 것 같다는 생각이 들어 이 부분은 request body로 받아온 값을 바로 updateAppliedStatus 메서드의 매개변수로 전달해주도록 수정하고, ApplicationStatusRequestDto 클래스는 삭제해야겠다.

 

BoardService

/**
 * 게시글 신청 정보 업데이트
 */
@Transactional
public String updateAppliedStatus(final Long boardId, final Long userId, final String nickname, final String status) {

    if(boardRepository.getById(boardId).getUser().getId().equals(userId)) {
        Application application = applicationService.updateAppliedStatus(boardId, nickname, status);

        return application.getStatus();
    }
    else {
        throw new CustomException(ErrorCode.FORBIDDEN);
    }
}
  • 매개변수로 게시글 id, 사용자 id, 신청 상태를 수정해줄 사용자 닉네임, 수정할 신청 상태를 받아온다.
  • 매개변수로 받아온 게시글 id를 BoardRepositort의 getById 메서드의 매개변수로 넣어 이 메서드를 호출해 Board 정보를 가져온다. 가져온 Board 정보 중 사용자 id와 매개변수로 받아온 사용자 id가 일치하는지 확인한다.
    • 사용자 id가 일치하는 경우 ApplicationService의 updateAppliedStatus 메서드의 매개변수로 게시글 id, 사용자 닉네임, 신청 상태를 넣어주고, 이 메서드를 호출해 신청 상태를 업데이트해준 뒤, 업데이트해준 게시글 신청 상태를 반환한다.
    • 사용자 id가 일치하지 않는 경우 예외가 발생한다.

 

ApplicationService

/**
 * 게시글 신청 상태 업데이트
 */
public Application updateAppliedStatus(final Long boardId, final String nickname, final String status) {

    return applicationRepository.updateAppliedStatus(boardId, nickname, status);
}
  • 매개변수로 게시글 id, 사용자 닉네임, 변경할 신청 상태를 받아온다.
  • 매개변수로 받아온 정보들을 ApplicationRepository의 updateAppliedStatus 메서드의 매개변수로 넣어주고, 이 메서드를 호출해 반환받은 값을 그대로 반환한다.

 

ApplicationRepository

@Override
@Transactional
public Application updateAppliedStatus(Long boardId, String nickname, String status) {

    Long userId = jpaQueryFactory.select(user.id).from(user).where(user.nickname.eq(nickname)).fetchOne();

    jpaQueryFactory.update(application)
            .set(application.status, status)
            .where(application.board.id.eq(boardId), application.user.id.eq(userId))
            .execute();

    return jpaQueryFactory.selectFrom(application)
            .where(application.board.id.eq(boardId), application.user.nickname.eq(nickname))
            .fetchOne();
}
  • user 테이블에서 매개변수로 받아온 nickname과 일치하는 사용자의 id를 찾아 userId 변수에 저장한다.
  • application 테이블에서 board.id가 매개변수로 받아온 boardId와 일치하고, user.id가 userId 변수 값과 일치하는 Application 객체를 찾아 그 객체의 status 값을 매개변수로 받아온 status 값으로 변경한다.
  • board.id가 boardId와 일치하고, user.id가 userId와 일치하는 Application 객체를 찾아 반환한다.
🏭 Refactoring
ApplicationRepository의 updateAppliedStatus 메서드가 Application 객체를 반환하는 것이 아니라 board.id가 boardId와 일치하고, user.id가 userId와 일치하는 Application 객체 정보 중에서 status 값만 반환하도록 한다면, DB에서 가져올 정보도 적어지고 BoardService의 updateAppiedStatus 메서드도 아주 조금이지만 더 간결해질 것 같다는 생각이 들어 이렇게 코드를 수정해야겠다.

 

1.5. 게시글 신청 취소하기

BoardApiController

@DeleteMapping("/{id}/application")
public void canceledApply(@PathVariable final Long id, @CookieValue final String token) {
    boardService.cancelApplied(id, jwtTokenProvider.getUserId(token));
}
  • DELETE "/posts/{id}/application" 요청이 들어오면 게시글 신청을 취소하는 로직이 실행된다.
  • URI에서 게시글 id를 가져오고, Cookie에서 token 값을 가져온다.
  • token 값에서 추출한 사용자 id와 URI에서 가져온 게시글 id를 BoardService의 cancelApplied 메서드의 매개변수로 전달한다.

 

BoardService

/**
 * 지원 취소하기 - Application에서 데이터 삭제
 */
public void cancelApplied(final Long boardId, final Long userId) {

    applicationService.cancelApplied(boardId, userId);
}
  • 매개변수로 게시글 id와 사용자 id가 들어오면 ApplicationService의 cancelAppied 메서드의 매개변수로 전달하고, 이 메서드를 호출한다.

 

ApplicationService

/**
 * 게시글 신청 취소
 */
@Transactional
public void cancelApplied(final Long boardId, final Long userId) {

    Application application = applicationRepository.findApplication(boardId, userId);
    if (application == null) {
        throw new CustomException(ErrorCode.BAD_REQUEST);
    } else {
        applicationRepository.cancelApplied(boardId, userId);
    }
}
  • 매개변수로 게시글 id와 사용자 id를 받아와 ApplicationRepository의 findApplication 메서드의 매개변수로 전달하고, 이 함수를 호출해 Application 객체 정보를 받아와 application 변수에 저장한다.
  • application 객체가 null인 경우, 사용자는 이 게시글에 참여 신청을 한 적이 없었다는 의미이므로 예외가 발생한다.
  • application 객체가 null이 아닌 경우, ApplicationRepository의 cancelApplied 메서드에 게시글 id와 사용자 id를 매개변수로 전달하고 이 메서드를 호출한다.

 

ApplicationRepository

@Override
public void cancelApplied(Long boardId, Long userId) {
    jpaQueryFactory.delete(application)
            .where(application.board.id.eq(boardId), application.user.id.eq(userId))
            .execute();
}
  • application 테이블에서 board.id가 매개변수로 받아온 boardId와 일치하고, user.id가 매개변수로 받아온 userId와 일치하는 Application 정보를 삭제한다.
728x90
LIST

댓글