Coding 공부/IntelliJ

[IntelliJ_Springboot_MariaDB] Reply CRUD(Service, Controller)

CBJH 2024. 5. 24.
728x90
반응형

1. 동기와 비동기 차이점

1.1 동기(Synchronous)

  1. 작업의 순차적 실행:
    • 동기 방식에서는 한 작업이 완료될 때까지 다음 작업이 시작되지 않습니다. 즉, 작업이 순차적으로 실행됩니다.
  2. 블로킹(Blocking):
    • 동기 방식에서는 현재 작업이 완료될 때까지 시스템 자원을 차단(블로킹)합니다. 이로 인해 다른 작업이 대기 상태에 놓입니다.
  3. 단순한 흐름:
    • 작업이 순차적으로 이루어지기 때문에 프로그램의 흐름을 이해하고 디버깅하기 쉽습니다.
  4. 예시:
    • 함수 호출: result = functionA(); resultB = functionB(result); 여기서 functionB는 functionA가 완료된 후에 실행됩니다.
    • 파일 읽기: readFileSync (동기 방식의 파일 읽기 함수로, 파일을 다 읽을 때까지 다음 코드가 실행되지 않습니다).

1.2 비동기(Asynchronous)

  1. 작업의 병렬적 실행:
    • 비동기 방식에서는 한 작업이 진행되는 동안 다른 작업을 동시에 실행할 수 있습니다. 작업이 병렬적으로 실행됩니다.
  2. 논블로킹(Non-blocking):
    • 비동기 방식에서는 현재 작업이 진행되는 동안 다른 작업이 차단되지 않습니다. 시스템 자원을 효율적으로 사용하여 여러 작업을 동시에 처리할 수 있습니다.
  3. 복잡한 흐름 관리:
    • 비동기 작업의 흐름은 동기 작업보다 복잡할 수 있습니다. 콜백(callback), 프라미스(Promise), async/await와 같은 패턴을 사용하여 작업을 관리해야 합니다.
  4. 예시:
    • 콜백 함수: functionA(result => { functionB(resultB => { /* ... */ }) }) 여기서 functionB는 functionA의 결과가 준비되면 실행됩니다.
    • 파일 읽기: readFile (비동기 방식의 파일 읽기 함수로, 파일을 읽는 동안 다음 코드가 계속 실행됩니다).

1.3 동기와 비동기의 비교

  1. 작업 처리 순서:
    • 동기: 작업이 순차적으로 처리됨.
    • 비동기: 작업이 병렬적으로 처리될 수 있음.
  2. 자원 차단:
    • 동기: 현재 작업이 완료될 때까지 시스템 자원이 차단됨.
    • 비동기: 현재 작업이 진행되는 동안 다른 작업이 차단되지 않음.
  3. 코드의 복잡성:
    • 동기: 코드가 단순하고 이해하기 쉬움.
    • 비동기: 코드가 복잡할 수 있으며, 흐름 관리를 위해 추가적인 패턴이 필요함.
  4. 응답 시간:
    • 동기: 작업 완료까지 기다려야 하기 때문에 응답 시간이 길어질 수 있음.
    • 비동기: 대기 시간이 줄어들어 응답 시간이 단축될 수 있음.
    • 동기: 순차적 작업, 블로킹, 단순한 흐름.
    • 비동기: 병렬적 작업, 논블로킹, 복잡한 흐름 관리.요약

 

 

2. Reply CRUD(Service)

public interface ReplyService {
    Long register(ReplyDTO replyDTO);
    ReplyDTO read(Long rno);
    void modify(ReplyDTO replyDTO);
    void remove(Long rno);
    PageResponseDTO getListOfBoard(Long bno, PageRequestDTO PageRequestDTO);
}
@Service  // 해당 클래스가 서비스 클래스임을 나타냅니다.
@RequiredArgsConstructor  // 생성자를 자동으로 생성해주는 Lombok 어노테이션입니다.
@Log4j2  // 로그를 사용하기 위한 Log4j2 어노테이션입니다.
public class ReplyServiceImpl implements ReplyService{

    private final ReplyRepository replyRepository;  // 댓글 데이터를 처리하는 리포지토리입니다.
    private final ModelMapper modelMapper;  // DTO와 엔티티 간 매핑을 처리하는 ModelMapper입니다.

    @Override
    public Long register(ReplyDTO replyDTO){
        // ReplyDTO를 Reply 엔티티로 변환합니다.
        Reply reply = modelMapper.map(replyDTO, Reply.class);
        // 엔티티를 저장하고 생성된 댓글의 번호(rno)를 반환합니다.
        Long rno = replyRepository.save(reply).getRno();
        return rno;
    }

    @Override
    public ReplyDTO read(Long rno){
        // 댓글 번호로 Reply 엔티티를 조회합니다.
        Optional<Reply> result = replyRepository.findById(rno);
        // 결과가 없으면 예외를 발생시킵니다.
        Reply reply = result.orElseThrow();
        // 조회된 엔티티를 ReplyDTO로 변환하여 반환합니다.
        return modelMapper.map(reply, ReplyDTO.class);
    }

    @Override
    public void modify(ReplyDTO replyDTO){
        // 수정할 댓글을 댓글 번호로 조회합니다.
        Optional<Reply> result = replyRepository.findById(replyDTO.getRno());
        // 결과가 없으면 예외를 발생시킵니다.
        Reply reply = result.orElseThrow();
        // 댓글 내용을 수정합니다.
        reply.changeText(replyDTO.getReplyText());
        // 수정된 댓글을 저장합니다.
        replyRepository.save(reply);
    }

    @Override
    public void remove(Long rno){
        // 댓글 번호로 댓글을 삭제합니다.
        replyRepository.deleteById(rno);
    }
    
    // bno가 일치하는 댓글들을 페이지 요청에 맞춰서 읽어오는 메서드입니다.
    @Override
    public PageResponseDTO getListOfBoard(Long bno, PageRequestDTO pageRequestDTO){
        // 페이지 요청을 설정합니다.
        Pageable pageable = PageRequest.of(
                pageRequestDTO.getPage()<=0 ? 0 : pageRequestDTO.getPage()-1,  // 페이지 번호는 0부터 시작합니다.
                pageRequestDTO.getSize(),  // 한 페이지에 보여줄 댓글 수입니다.
                Sort.by("rno").ascending());  // rno를 기준으로 오름차순 정렬합니다.
        
        // bno가 일치하는 댓글들을 페이지 요청에 맞춰서 조회합니다.
        Page<Reply> result =  replyRepository.listOfBoard(bno, pageable);
        
        // 조회된 댓글 엔티티들을 ReplyDTO 리스트로 변환합니다.
        List<ReplyDTO> dtoList = result.getContent().stream()
                .map(reply -> modelMapper.map(reply, ReplyDTO.class))
                .collect(Collectors.toList());
        
        // 페이지 응답 DTO를 생성하여 반환합니다.
        return PageResponseDTO.<ReplyDTO>withAll()
                .pageRequestDTO(pageRequestDTO)  // 페이지 요청 정보를 설정합니다.
                .dtoList(dtoList)  // 댓글 DTO 리스트를 설정합니다.
                .total((int)result.getTotalElements())  // 전체 댓글 수를 설정합니다.
                .build();
    }
}
  • 댓글 서비스 구현 코드입니다. 댓글 등록, 조회, 수정, 삭제 기능을 제공합니다. 또한 특정 게시글에 달린 댓글 목록을 페이지 요청에 맞춰 조회할 수 있습니다.

자세한 설명:

이 클래스는 댓글 서비스 구현체로서 다음과 같은 기능을 제공합니다:

  • register(ReplyDTO replyDTO): 댓글을 등록하고, 생성된 댓글 번호를 반환합니다.
  • read(Long rno): 댓글 번호로 댓글을 조회하고, 댓글 정보를 DTO로 반환합니다.
  • modify(ReplyDTO replyDTO): 댓글을 수정합니다. 댓글 번호로 댓글을 조회한 후 내용을 수정하여 저장합니다.
  • remove(Long rno): 댓글 번호로 댓글을 삭제합니다.
  • getListOfBoard(Long bno, PageRequestDTO pageRequestDTO): 특정 게시글에 달린 댓글 목록을 페이지 요청에 맞춰 조회하고, 페이지 응답 DTO를 반환합니다.
  • 이 클래스는 ReplyRepository를 사용하여 데이터베이스와 상호작용하고, ModelMapper를 사용하여 DTO와 엔티티 간 매핑을 처리합니다.

 

3. ReplyCotroller

@RestController  // 이 클래스가 RESTful 웹 서비스의 컨트롤러임을 나타냅니다.
@RequestMapping("/replies")  // 이 클래스의 기본 요청 경로를 "/replies"로 설정합니다.
@Log4j2  // 로그를 사용하기 위한 Log4j2 어노테이션입니다.
@RequiredArgsConstructor  // 생성자를 자동으로 생성해주는 Lombok 어노테이션입니다.
public class ReplyController {
    private final ReplyService replyService;  // ReplyService 객체를 주입받습니다.

    @PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE)
    // 댓글 등록 요청을 처리하는 메서드입니다. JSON 형식의 데이터를 소비합니다.
    public Map<String, Long> register(@Valid @RequestBody ReplyDTO replyDTO,
                                      BindingResult bindingResult) throws BindException {
        log.info(replyDTO);  // replyDTO 객체의 내용을 로그에 출력합니다.
        if (bindingResult.hasErrors()) {  // 유효성 검사에서 오류가 발생하면
            throw new BindException(bindingResult);  // BindException을 발생시킵니다.
        }
        Map<String, Long> resultMap = new HashMap<>();
        Long rno = replyService.register(replyDTO);  // 댓글을 등록하고 댓글 번호를 반환받습니다.
        resultMap.put("rno", rno);  // 결과 맵에 댓글 번호를 저장합니다.

        return resultMap;  // 결과 맵을 반환합니다.
    }

    @GetMapping(value = "/list/{bno}")
    // 특정 게시글에 달린 댓글 목록을 조회하는 메서드입니다.
    public PageResponseDTO<ReplyDTO> getList(@PathVariable("bno") Long bno, PageRequestDTO pageRequestDTO) {
        PageResponseDTO<ReplyDTO> responseDTO = replyService.getListOfBoard(bno, pageRequestDTO);
        // replyService를 통해 댓글 목록을 조회하고, PageResponseDTO 객체를 반환받습니다.
        return responseDTO;  // 조회 결과를 반환합니다.
    }

    @GetMapping(value = "/{rno}")
    // 특정 댓글을 조회하는 메서드입니다.
    public ReplyDTO getReplyDTO(@PathVariable("rno") Long rno) {
        ReplyDTO replyDTO = replyService.read(rno);  // 댓글 번호로 댓글을 조회하고 ReplyDTO 객체를 반환받습니다.
        return replyDTO;  // 조회된 댓글 정보를 반환합니다.
    }

    @PutMapping(value = "/{rno}", consumes = MediaType.APPLICATION_JSON_VALUE)
    // 특정 댓글을 수정하는 메서드입니다. JSON 형식의 데이터를 소비합니다.
    public Map<String, Long> modify(@Valid @RequestBody ReplyDTO replyDTO,
                                    @PathVariable("rno") Long rno,
                                    BindingResult bindingResult) throws BindException {
        if (bindingResult.hasErrors()) {  // 유효성 검사에서 오류가 발생하면
            throw new BindException(bindingResult);  // BindException을 발생시킵니다.
        }
//        replyDTO.setRno(rno);  // 주석 처리된 부분입니다. 필요시 사용할 수 있습니다.
        replyService.modify(replyDTO);  // 댓글을 수정합니다.
        Map<String, Long> resultMap = new HashMap<>();
        resultMap.put("rno", rno);  // 결과 맵에 댓글 번호를 저장합니다.
        return resultMap;  // 결과 맵을 반환합니다.
    }

    @DeleteMapping("/{rno}")
    // 특정 댓글을 삭제하는 메서드입니다.
    public Map<String, Long> remove(@PathVariable("rno") Long rno) {
        replyService.remove(rno);  // 댓글을 삭제합니다.
        Map<String, Long> resultMap = new HashMap<>();
        resultMap.put("rno", rno);  // 결과 맵에 댓글 번호를 저장합니다.
        return resultMap;  // 결과 맵을 반환합니다.
    }
}
  • 댓글 컨트롤러입니다. 댓글 등록, 조회, 수정, 삭제 기능을 제공하며, 특정 게시글에 달린 댓글 목록을 조회할 수 있습니다.

자세한 설명:

이 클래스는 댓글 관리를 위한 RESTful 컨트롤러로서 다음과 같은 기능을 제공합니다:

  • register(ReplyDTO replyDTO, BindingResult bindingResult): 댓글을 등록하고, 댓글 번호를 반환합니다. 유효성 검사에서 오류가 발생하면 BindException을 발생시킵니다.
  • getList(Long bno, PageRequestDTO pageRequestDTO): 특정 게시글에 달린 댓글 목록을 페이지 요청에 맞춰 조회하고, PageResponseDTO 객체를 반환합니다.
  • getReplyDTO(Long rno): 댓글 번호로 댓글을 조회하고, 댓글 정보를 ReplyDTO 객체로 반환합니다.
  • modify(ReplyDTO replyDTO, Long rno, BindingResult bindingResult): 댓글을 수정하고, 수정된 댓글 번호를 반환합니다. 유효성 검사에서 오류가 발생하면 BindException을 발생시킵니다.
  • remove(Long rno): 댓글 번호로 댓글을 삭제하고, 삭제된 댓글 번호를 반환합니다.

이 컨트롤러는 ReplyService를 통해 비즈니스 로직을 처리하며, @Valid 어노테이션을 사용하여 입력 데이터를 검증합니다.

 

4. 부트스트랩 예제 코드(버튼 추가)

<div class="d-flex justify-content-end align-items-center">
    <a href="/board/register" class="btn btn-outline-primary me-4">Register</a>
    <ul class="pagination flex-wrap mb-0">
        <li class="page-item" th:if="${responseDTO.prev}">
            <a class="page-link" th:data-num="${responseDTO.start -1}">Previous</a>
        </li>
        <th:block th:each="i: ${#numbers.sequence(responseDTO.start, responseDTO.end)}">
            <li th:class="${responseDTO.page == i}?'page-item active':'page-item'">
                <a class="page-link" th:data-num="${i}">[[${i}]]</a>
            </li>
        </th:block>
        <li class="page-item" th:if="${responseDTO.next}">
            <a class="page-link" th:data-num="${responseDTO.end +1}">Next</a>
        </li>
    </ul>
</div>
  • 페이지네이션(글 목록 페이지 보이는 곳) 왼쪽에 Register 버튼을 추가한다.
  • d-flex: 요소를 플렉스 컨테이너로 만듭니다.
  • justify-content-between: 플렉스 항목을 컨테이너의 양 끝에 정렬합니다.
  • align-items-center: 플렉스 항목을 세로축(중앙)에 정렬합니다.
  • mb-0: 하단 여백을 제거합니다. 페이지네이션이 다른 요소와 겹치지 않도록 합니다.
  • me-4 : 오른쪽 여백을 추가합니다. 인텔리제이에 me-를 입력하면 여러가지 옵션이 있어 여백의 크기를 조절할 수 있습니다.

 

댓글