사용자와 상호작용을 위한 controller 레이어를 만들었다. 기본적으로, 이 프로젝트에 구현한 대부분의 컨트롤러는 이렇게 crud하는 구조를 가진다. (물론, 특수한 로직을 사용해야하는 경우 추가로 다른 구조로 작성되었다.)
가장 자주 사용되는 게시글 api를 살펴보자.
Board controller
BoardController.java
@RestController
@RequestMapping("/boards")
@Tag(name = "board", description = "게시글 관련 api")
public class BoardController {
private BoardService boardService;
private static final Logger log = LoggerFactory.getLogger(BoardController.class);
@Autowired
public BoardController(BoardService boardService){
this.boardService = boardService;
}
@GetMapping("/{board_id}")
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 조회", description = "게시글 하나를 조회합니다.")
public ResponseEntity<ResponseBoardDto> getBoard(@PathVariable(name = "board_id") Long board_id){
ResponseBoardDto responseBoardDto = boardService.getBoard(board_id);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDto);
}
@GetMapping()
@Operation(summary = "게시글 리스트 조회", description = "게시글 리스트를 페이지 단위로 가져옵니다.")
public ResponseEntity<List<ResponseBoardListDto>> getBoards(@PageableDefault(size = 8) Pageable pageable,
RequestSearchBoardDto requestSearchBoardDto){
List<ResponseBoardListDto> responseBoardDtoList = boardService.getBoardList(pageable, requestSearchBoardDto);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDtoList);
}
@PostMapping()
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER) //swagger에서 헤더로 토큰을 전송하기 위해 입력창을 만든다.
@Operation(summary = "게시글 생성", description = "게시글 하나를 작성합니다.")
public ResponseEntity<ResponseBoardDto> createBoard(@RequestBody RequestCreateBoardDto requestCreateBoardDto, HttpServletRequest request){
ResponseBoardDto responseBoardDto = boardService.createBoard(requestCreateBoardDto, request);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDto);
}
@PutMapping("/{board_id}")
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER)
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 수정", description = "게시글 하나를 업데이트 합니다.")
public ResponseEntity<ResponseBoardDto> updateBoard(@PathVariable(name = "board_id") Long board_id,
@RequestBody RequestUpdateBoardDto requestUpdateBoardDto,
HttpServletRequest request
){
ResponseBoardDto responseBoardDto = boardService.updateBoard(board_id, requestUpdateBoardDto, request);
return null;
}
@DeleteMapping("/{board_id}")
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER)
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 삭제", description = "게시글 하나를 삭제합니다.")
public ResponseEntity<String> deleteBoard(@PathVariable(name = "board_id") Long board_id, HttpServletRequest request){
log.info("BoardController deleteBoard ==> header : " + request.getRequestURI());
boardService.deleteBoard(board_id, request);
return ResponseEntity.status(HttpStatus.OK).body("성공적으로 게시글 삭제");
}
}
게시글 controller이다. boardService에 만든 로직들을 주입받아 사용한다. 코드가 길어 하나씩 뜯어보자.
@GetMapping("/{board_id}")
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 조회", description = "게시글 하나를 조회합니다.")
public ResponseEntity<ResponseBoardDto> getBoard(@PathVariable(name = "board_id") Long board_id){
ResponseBoardDto responseBoardDto = boardService.getBoard(board_id);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDto);
}
먼저 게시글 하나를 get하는 api이다. 이것이 동작하는 순간은 게시글 하나를 클릭해서 자세히 볼 때다. boardService의 getBoard를 실행하고, 반환값을 리턴한다.
여기서 responseBoardDto는 게시글에 대한 자세한 정보들(작성자, 작성일자, 게시글 내용, 달린 댓글들 등등..)이 담겨있다.
@PostMapping()
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER) //swagger에서 헤더로 토큰을 전송하기 위해 입력창을 만든다.
@Operation(summary = "게시글 생성", description = "게시글 하나를 작성합니다.", responses = {
@ApiResponse(responseCode = "200", description = "게시글 생성 성공", content = {
@Content(mediaType = "application/json", schema =
@Schema(implementation = ResponseBoardDto.class))
})
})
public ResponseEntity<ResponseBoardDto> createBoard(@RequestBody RequestCreateBoardDto requestCreateBoardDto, HttpServletRequest request) throws CustomException{
ResponseBoardDto responseBoardDto = boardService.createBoard(requestCreateBoardDto, request);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDto);
}
그 다음은 게시글 생성 api이다. http message의 body 부분에 json형태로 데이터를 담아 전송하는 post api이다. body는 requestCreateBoardDto로 , 게시글을 생성하는 데 필요한 정보들이 들어있다.
여기서 사용된 HttpServletRequest는 이 http message를 보낸 게시글 작성자에 대한 정보를 담고 있다. (작성자의 ip 주소라던가, 인증 정보라던가..) 이것을 사용한 이유는 로그인한 사용자만 게시글을 작성하도록 하기 위해서이다. 로그인 여부는 이 request의 header에 담긴 토큰으로 구별한다. jwt에 대한 자세한 내용은 이 글에 작성했다.
https://yoonsys.tistory.com/18
spring security : jwt를 이용한 로그인 구현[1]
프로젝트를 하면서, 로그인을 구현해야하는 단계에 왔을 때, sns로그인을 이용해 간단하게 넘길지 아니면 직접 구현할지 팀원과 의논했다. spring security는 강의에서도 생각보다 자세하게 다루지
yoonsys.tistory.com
@PutMapping("/{board_id}")
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER)
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 수정", description = "게시글 하나를 업데이트 합니다.", responses = {
@ApiResponse(responseCode = "200", description = "게시글 수정 성공", content = {
@Content(mediaType = "application/json", schema =
@Schema(implementation = ResponseBoardDto.class))
})
})
public ResponseEntity<ResponseBoardDto> updateBoard(@PathVariable(name = "board_id") Long board_id,
@RequestBody RequestUpdateBoardDto requestUpdateBoardDto,
HttpServletRequest request
){
try{
ResponseBoardDto responseBoardDto = boardService.updateBoard(board_id, requestUpdateBoardDto, request);
return ResponseEntity.status(HttpStatus.OK).body(responseBoardDto);
}catch(CustomException e){
throw e;
}
}
그 다음으로 게시글 수정 api이다. 게시글 수정도 토큰을 이용한 권한을 체크하는데, 이는 게시글 작성자만 게시글을 수정하도록 하기 위함이다. 역시나 boardService를 사용하는 모습이다.
@DeleteMapping("/{board_id}")
@Parameter(name = "X-AUTH-TOKEN", description = "토큰을 전송합니다.", in = ParameterIn.HEADER)
@Parameter(name = "board_id", description = "게시글 ID", in = ParameterIn.PATH, schema = @Schema(type = "integer", format = "int64"))
@Operation(summary = "게시글 삭제", description = "게시글 하나를 삭제합니다.", responses = {
@ApiResponse(responseCode = "200", description = "게시글 삭제 성공", content = {
@Content(mediaType = "application/json", schema =
@Schema(implementation = String.class))
})
})
public ResponseEntity<String> deleteBoard(@PathVariable(name = "board_id") Long board_id, HttpServletRequest request){
try{
log.info("BoardController deleteBoard ==> header : " + request.getRequestURI());
boardService.deleteBoard(board_id, request);
return ResponseEntity.status(HttpStatus.OK).body("성공적으로 게시글 삭제");
}catch(CustomException e){
throw e;
}
}
마지막으로 게시글 삭제 api이다. 여기선 수정과 달리 게시글 작성자 뿐 아니라 관리자도 삭제가 가능하도록 했다. 자세한 내용은 service작성글에 나와있다.
여기서 사용된 LoggerFactory를 이용해 로그를 출력할 수 있다.
공통적으로 보이는 @Operation, @Parameter는 swagger를 위한 어노테이션이다. 없어도 오류는 발생하지 않고, 용도는 swagger 문서를 디테일하게 작성하기 위함이다.
board 를 get, create, update, delete하는데, 추가로 board를 여러개 get하는 메서드도 있다. 페이징을 사용한 부분으로, 자세한 내용은 아래 링크로 가면 알 수 있다.
spring boot JPA : 페이징(paging)
보통 게시판 화면을 보면 게시글이 특정 개수 단위로만 띄워진다. 모든 게시글을 한눈에 보이도록 제공하지는 않는다. 그렇게 하면 서버 부하도 심할 것이고, 보는 사용자 입장에서도 가시성이
yoonsys.tistory.com
페이지 번호를 통해 몇 페이지의 게시글들을 볼 지 정할 수 있다.
'Project > 9uin' 카테고리의 다른 글
spring boot 사이드 프로젝트: DockerFile, docker-compose.yml 작성 및 클라우드 세팅(GCP) 1 (0) | 2023.10.30 |
---|---|
spring boot 사이드 프로젝트 : spring boot + github action을 통해 CI/CD구축 (0) | 2023.10.29 |
spring boot 사이드 프로젝트[8] : 게시글 태그 필터링 (querydsl join) (0) | 2023.06.14 |
spring boot 사이드 프로젝트[7] : 게시글 검색 기능 구현, 동적 쿼리 적용 (0) | 2023.06.13 |
spring boot 사이드 프로젝트[6] : querydsl 설정 및 작성 (0) | 2023.05.26 |