이전 게시글에서 기본 설정과 개념을 살펴보았다. 이번에는 POST 요청을 해보도록 하자.
구조는 다음과 같다. 이전에 언급한대로 Controller, dto, entity, repository, service가 보인다.
먼저 entity를 살펴보자. 참고로 자바의 기본적인 내용과 겹치는건 간단하게 넘어가겠다.
@Entity
@Getter @Setter
@NoArgsConstructor
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long boardId;
private String title;
private String content;
}
코드를 보면 @Entity @Getter @Setter 등 많은 어노테이션이 보이는데 설명하면
1) @Entity = 이 클래스를 Entity로 선언한다는 말이다. JPA의 기능이다.
2) @Getter @Setter 우리가 Lombok을 추가한게 기억이 나는가? 원래는
public Long getBoardId() {
return boardId;
}
public void setBoardId(Long boardId) {
this.boardId = boardId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
이걸 넣어줘서 처리를 했는데 어노테이션을 추가해서 깔끔하게 생략 되었다.
3) @NoArgsConstructor 기본 생성자를 만들어 주는 녀석이다. JPA에서는 구현체들을 기본생성자를 통해 초기화 하고 생성하는데 어노테이션 하나로 이 과정을 쉽게 만들수 있다.
4) @Id 해당 변수를 ID값으로 선언한다는 뜻이다. 나중에 DB보면 이해간다.
5) @GeneratedValue(strategy = GenerationType.IDENTITY) 이건 기본키를 지정하는건대 DB를 식별하는데 쓰이는 대표요소 라고 생각하자.
4번 5번이 특히 이해가 안갈수 있다. 게시글 5개가 올라오면 1번 게시글부터 5번까지 있지 않겠는가? 그 순서를 매긴다고 생각하자.
그리고
private Long boardId;
private String title;
private String content;
이건 내가 DB에서 사용할 녀석들 이다. 게시글 보면 제목이 있고 내용이 있지 않겠는가? boardId는 위의 4,5번에 해당한다.
여기까지 Entity 설명이였고 dto를 보자
@Getter @Setter
public class BoardPostDto {
@NotEmpty
private String title;
@NotEmpty
private String content;
}
Post 기능을 쓸때 사용되는 녀석들인데 여기도 설명가겠다.(중복 개념은 설명안함)
1) @NotEmpty 여기에 빈값이 들어가면 에러를 나오게 한다.
2) Post에서는 title과 content 두개만 들고 왔다. boardId도 들고와도 되는데 자동 생성해줘서 굳이 우리가 입력할필요가 없어서 제거했다.
아래에서 보겠지만 dto가 service 에서 사용될꺼다.
@Repository
public interface BoardRepository extends JpaRepository<Board,Long> {
}
1) @Repository는 여기 인터페이스가 Repository 임을 선언하는 어노테이션 이다.
2) JpaRepository를 상속받아서 아무것도 없는 BoardRepository 기능을 확장 시켜버린다. 검색, 저장 및 삭제 같은 기능들이 추가되게 된다.
3) <Board,Long> Repository 사용될 엔티티와 타입을 지정해 주는거다.
이렇게 보니깐 정말 별거 아니지 않은가? 다음으로 Service를 보자.
@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;
public Long createBoard(BoardPostDto boardPostDto) {
Board board = new Board();
board.setTitle(boardPostDto.getTitle());
board.setContent(boardPostDto.getContent());
return boardRepository.save(board).getBoardId();
}
}
이젠 감이 올것이다.
1) @Service는 이 클래스가 Service 임을 선언하는 거다.
2) @RequiredArgsConstructor 아마 다들 생성자가 뭔지 알것이다. 그 중에 final과 같은 필수 인자를 생성하는데 사용된다. 원래는 아래처럼 써줘야 한다.
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
그런데 @RequiredArgsConstructor 넣어주면 저 과정을 생략해 준다 실제로 지금 내가 게시판 하나 만들어서 그렇지 기능들 추가하다보면 final 여러개 붙는데 엄청 지저분 해진다.
3) 왜 final 쓰냐고? 한번 값이 확정 되어 들어가면 중간에 값이 절대 못변하게 하기 위함이다 설명이 더 필요하겠는가 ㅎㅎ.또한 이건 변하지 않는 값인걸 알기에 유지보수할때 편해진다.
4)
public Long createBoard(BoardPostDto boardPostDto) {
Board board = new Board();
board.setTitle(boardPostDto.getTitle());
board.setContent(boardPostDto.getContent());
return boardRepository.save(board).getBoardId();
}
4-1) (BoardPostDto boardPostDto)가 인자인걸 알겠지? 모른다면 BoardPostDto의 값들을 가지고 오게 되는거다. 아까 기억하는가 title 이랑 content가 들어 있던걸.
4-2) Board board = new Board(); 여기서 엔티티도 들고 온다.
4-3) board.setTitle(boardPostDto.getTitle());
board.setContent(boardPostDto.getContent());
여기는 board 엔티티의 title과 content에 boardPostDto에 있는 title과 content를 집어 넣는거다. dto 무시하고 엔티티에 값을 바로 넣으면 엔티티가 기능 에서도 사용될텐데 충돌나게 될수 있다. 그래서 dto 쓰는거다.
생각해보자 예를들어 "이달의 추천 게시글" 이라는 기능을 새로 만들때 이 게시판의 Entity를 끌어쓸꺼 아니냐 물론 서비스단에다 계속 끝없이 몸집을 키울수도 있겠지만 그건 회사마다 다르겠지 하지만 나는 다른 기능은 다 분리시킨다.
그래서 다른 기능에서는 BoardPostDto를 안가져갈꺼니깐. 게시글이 필요하면 Entity의 boardId를 가져가겠지.설명이 어려우면 코드를 보자.
@Entity
@Getter @Setter
@NoArgsConstructor
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long boardId;
private String title;
private String content;
협업하는데 어떤 사람이 title을 object 혹은 뭐... titles 라고 바꿨다고 가정하자. 아래 Controller 할때 postman에서 보여줄껀데 서버는 잘만 돌아 갈꺼다 변경된 entity를 그대로 써도 그런데 dto를 안쓰면 다 터지는 거다. 나처럼 dto를 썻다면 실행전에 컴파일 에러가 삑 하고 터지겠지.
또 다른 이유로는 필요한 값만 쏙쏙 요청할수가 있다. Entity에 값이 백개라고 하자 그런데 나는 2~3개만 필요해 그럼 dto를 써주면 2~3개만 쓸수 있어서 불필요하게 전체 호출을 할 필요가 없는거다.
여기까지 설명도 이해가 안간다면 그려러니 하고 넘어가자 어쩔수가 없다.
4-4) return boardRepository.save(board).getBoardId(); 마지막으로 여기서 Repository에 board를 save해주고 boardId를 받아서 Long 타입으로 반환하게 된다. (public Long createBoard) 그럼 1,2,3,4,5,6,7.... 등등이 생성 되겠지?(Repository에서 설명이미 하였다.)
다음으로 Controller를 보자.
@Getter @Setter
@RestController
@RequestMapping("/api/boards")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
@PostMapping
public ResponseEntity postBoard(@RequestBody @Validated BoardPostDto boardPostDto) {
Long boardId = boardService.createBoard(boardPostDto);
return ResponseEntity.status(HttpStatus.CREATED).body(boardId);
}
}
1) @RestController 이건 RestController 타입으로 바꾸는건데 Controller는 크게 두가지가 있다.
@RestController @Controller
사용목적에 따라 나뉘게 된다.
@Controller는 view값 즉 html 같은녀석을 Controller가 바로 끌고 온다.
@RestController는 Json 타입으로 값을 반환해 준다.
예전에는 한 사람이 프론트 백 구분없이 다했는데 지금은 프론트엔드/백엔드 구분 지어서 개발 하다 보니 Json 형태로 반환해서 프론트에서 재가공해서 써먹는게 좋다고 한다. 프론트 안해봐서 나도 잘 모르지만 요즘은 RestController가 기본이다.
2) @RequestMapping("/api/boards") 우리가 URL에 주소값을 입력해 본적이 있을꺼다.
지금 로컬환경에서 톰켓(서버) 의 기본 주소는 http://localhost:8080인데 http://localhost:8080/boards 를 해주면 여기 board 기능을 끌고 오는거다. 즉 리모컨 버튼을 boards라고 만들었다고 생각하자. 참고로 boards는 entity에 보통 /api/ (이름)s 많이 붙여서 작명한다.
3) @PostMapping Post 요청을 받겠다는거다 CRUD 기억하지?
4) ResponseEntity를 사용해야 응답 상태, body값을 설정할수 있다. 참고로 body는 요청의 본체라고 생각하면된다. 실제 내용 즉 응답값이다.
5) @RequestBody는 클라이언트에서 Json 타입으로 값을 보내는데 그걸 우리가 가진 환경에 맞게 변환해주는 녀석이야. 즉 Json 타입을 BoardPostDto에 맞춰서 넣는다는 거다.
6) @Validated는 유효성을 검증하는 녀석인데
@NotEmpty
private String title;
@NotEmpty
private String content;
@NotEmpty는 클라이엔트에서 보낸 Json이 공백인지 확인하는데 @Validated가 최종 컨펌한다고 생각하면된다 충족하지 못하면 Controller에서 에러를 터트려 버리는거다. 뭐 규칙은 @NotEmpty 뿐만 아니라 엄청 많이 만들수 있겠지?
7) Long boardId = boardService.createBoard(boardPostDto); 아까 Service에서 Long 타입으로 반환한걸 기억하는가? 그걸 Controller에서 주워온다. 즉 boardService.createBoard(boardPostDto) 이걸로 Service 일 시키고
boardId에다 저장하는거다.
참고로 헷갈리는 사람 있을까봐 말하는데 지금 보이는 Controller에서 boardId라는 이름은 내 마음대로 지어도 된다.
@PostMapping
public ResponseEntity postBoard(@RequestBody @Validated BoardPostDto boardPostDto) {
Long aa = boardService.createBoard(boardPostDto);
return ResponseEntity.status(HttpStatus.CREATED).body(aa);
}
}
Controller에서 변수값 저장하는 용도인거임
8) 마지막으로 return을 해서 ResponseEntity에 넣어줘야지 어떻게?
status(HttpStatus.CREATED)
body(boardId)
status는 상태를 나타내고 body는 본문값이다. 지금부터 POSTMAN으로 놀아보자. 참고로 POSTMAN은 백엔드 개발자가 프론트엔드 없이 테스트 할때 사용하는 용도로 쓸수 있다. POSTMAN 설치는 그냥 구글들어가서 해라 설명이 필요 없을것 같다.
자 드디어 Json이 뭔지 알려주겠다.
이런 양식이 Json 타입이다 ㅎㅎ. 끝. 뭐... 더 설명할게 없다 그냥 이렇게 생긴 타입을 Json이라고 한다.
그래서 프론트에서 body에다 json 타입으로 실어 보내면 Controller가 받아 주는 역할을 한다.
그리고 결과로 숫자 1을 보여주고 있는데 아까 Controller return값 기억하는가? 지금 까지 설명을 잘 봤다면 숫자가 순서대로 생성된다는걸 알꺼다. 그리고 status 보면 created도 잘 보이고
그래서 1이 결과값에 표시 되게 되는거다. 뭐? 한번더 요청하면 어떻게 되냐고?
당연히 2번이 생성된다.
이젠 DB를 살펴보자.
크롬이던 사파리던 http://localhost:8080/h2 에 접속하면
이런 화면이 보일꺼다 yml에서 설정한 저 test 값을 넣어주고 Connect를 눌러주자
그리고 BOARD를 한번 누르고 RUN을 눌러주면
짠!
여러분들은 방금 게시판을 만든거다. 짝짝짝.
코드는 바뀌겠지만 뭐 이런식으로 흘러간다. 이젠 나머지 PATCH DELETE GET 기능들도 추가하고 페이지네이션도 해보고 예외처리도 하고... 기타 등등 계속 내가 어떻게 살을 붙이는지 보여주겠다.
'스프링boot > 정리' 카테고리의 다른 글
Spring MVC) 사용자를 만들어 보자 1 (0) | 2023.04.18 |
---|---|
Spring MVC) 댓글을 만들어 보자 (1) | 2023.04.09 |
Spring MVC) 게시판을 만들어 보자 4 (0) | 2023.04.07 |
Spring MVC) 게시판을 만들어 보자 3 (0) | 2023.04.07 |
Spring MVC) 게시판을 만들어 보자 1 (1) | 2023.04.05 |