[Spring Boot] 게시판 만들고 새 글 작성하기: Create
폼 데이터란?
HTML 요소인
<form>
태그에 실려 전송되는 데이터, 웹 브라우저에서 서버로 데이터를 전송할 때 사용
- form 태그에 실어 보낸 데이터는 서버의 컨트롤러가 객체에 담아 받는다.
- 이 객체를 DTO(Data Transfer Object)라고 한다.
- DTO로 받은 데이터는 최종적으로 데이터베이스에 저장된다.
폼 데이터를 DTO로 받기
HTML에서 요청 보내기
- 어떻게 보낼지는
action
속성으로, 어떻게 보낼지는method
속성으로 설정한다.
1
<form class="container" action="/articles/create" method="post">
action
: URL 연결 주소를 적어 localhost:8080/URL 연결 주소 페이지로 폼 데이터를 보낸다는 의미다.
method
: 속성 값으로 get
과 post
를 설정할 수 있다.
Controller에서 요청 받기
1
2
3
4
5
6
7
@Controller
public class ArticleController {
@PostMapping("/articles/create")
public String createArticle() {
return "";
}
}
form에서 보낸 데이터를 Controller에서 받아준다.
뷰 페이지에서 post
방식으로 폼 데이터를 전송했으므로 받을 때도 @PostMapping
으로 받아주어야 한다.
❔ @GetMapping과 @PostMapping의 차이
@GetMapping
: HTTP GET
요청을 특정 핸들러 메소드에 매핑하기 위한 주석이다. 특히 특히 @GetMapping
은 @RequestMapping(method = RequestMethod.GET)
에 대한 바로가기 역할을 하도록 구성된 주석.
@PostMapping
: HTTP POST
요청을 특정 핸들러 메소드에 매핑하기 위한 주석이다. 특히 특히 @PostMapping
은 @RequestMapping(method = RequestMethod.POST)
에 대한 바로가기 역할을 하도록 구성된 주석.
HTTP POST
요청은 GET
요청과 다르게 JSON과 FORM 등의 데이터 형식을 사용해 데이터를 보낼 수 있는 Body(본문)를 가진다.
DTO 만들기
폼 데이터를 받아올 그릇을 만든다.
- DTO는 com.example.firstproject.dto와 같이 dto 패키지를 만들어 보관한다.
- 전송할 내용에 따라 필드를 만들어 준다.
- 전송받은 내용을 필드에 저장하는 생성자와 폼 데이터를 잘 받았는지 확인하기 위한
toString()
메서드를 추가해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ArticleForm {
private String title;
private String content;
public ArticleForm(String title, String content) {
this.title = title;
this.content = content;
}
@Override
public String toString() {
return "ArticleForm{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
폼 데이터를 DTO에 담기
이제 폼 데이터를 담을 그릇이 준비되었으니, 데이터를 DTO에 담아 Controller로 가져온다. 만든 Controller의 매개변수로 DTO를 받도록 한다. toString()
을 통해 받은 내용이 출력될 수 있도록 한다.
1
2
3
4
5
@PostMapping("/articles/create")
public String createArticle(ArticleForm form) {
System.out.println(form.toString());
return "";
}
입력 폼과 DTO 필드 연결하기
입력 폼에서 전송한 데이터를 DTO로 받기 위해서는 DTO의 필드명과 폼 데이터에 담긴 name
명이 일치해야 한다. name
이 곧 키 값으로 사용되기 때문이다.
1
2
<input type="text" name="title" class="form-control">
<textarea class="form-control" name="content" rows="3"></textarea>
사용자로부터 입력받을 title
, content
요소에 각각 name
속성을 추가한다.
서버를 재시작하고 제목과 내용을 입력한 후 Console을 확인하면 정상적으로 데이터가 보내지고 있음을 확인할 수 있다.
과정 요약
- 뷰 페이지를 만들고
<form>
태그의action
속성으로 데이터를 어디로 보낼지,method
속성으로 데이터를 어떻게 보낼지 정의한다. - 컨트롤러를 만들고
PostMapping
방식으로 URL 주소를 연결한다. - 전송받은 데이터를 담아 둘 객체인 DTO를 만든다.
- 컨트롤러에서 폼 데이터를 전송받아 DTO에 담는다.
DTO를 데이터베이스에 저장하기
JPA와 H2 Database를 사용해 DTO를 데이터베이스에 저장할 수 있다. DTO를 엔티티로 변환하고, 리파지터리를 이용해 엔티티를 DB에 저장한다.
JPA(Java Persistence API)란?
- 자바 언어로 DB에 명령을 내리는 도구로, 데이터를 객체 지향적으로 관리할 수 있게 해준다.
- 자바 애플리케이션과 JDBC 사이에서 동작하며 자바 인터페이스로 정의되어 있다.
- 자바에서 사용하는 ORM(Object-Relational Mapping) 기술 표준이다.
- 핵심 도구로는 Entity와 repository가 있다.
entity
: 자바 객체를 DB가 이해할 수 있게 만든 것으로, 이를 기반으로 테이블이 만들어진다.repository
: 엔티티가 DB 속 테이블에 저장 및 관리될 수 있게 하는 인터페이스
❔ ORM(Object-Relational Mapping, 객체 관계 매핑)
- 객체와 관계형 데이터베이스의 데이터를 매핑하는 기술
- ORM 프레임워크가 객체와 데이터베이스 중간에서 매핑
- 객체와 테이블을 매핑하여 패러다임 불일치 문제 해결
DTO를 엔티티로 변환하기
- Entity는 DTO와 같이 따로 관리해주어야 한다. 이는 다음과 같은 이유들 때문이다. 자세한 내용은 추후 포스팅으로 다루어 보겠다.
- 유연한 응답
- 상호 참조 이슈 해결
- 보안 이슈 해결
- Entity에 없는 값이 필요한 경우 불필요한 로직 생성 방지
- entity 패키지를 만들고 Entity 클래스를 생성한다.
@Entity
: 이 클래스가 Entity임을 선언한다. 이 어노테이션이 붙은 클래스를 기반으로 DB에 테이블이 생성된다. 테이블 이름은 클래스 이름과 동일하다.@Column
: DB 테이블의 각 열과 연결된다.@Id
: Entity의 대표값을 지정한다.@GeneratedValue
: 대푯값을 자동으로 생성하게 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
public class Article {
@Id
@GeneratedValue
private Long id;
@Column
private String title;
@Column
private String content;
public Article(Long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
@Override
public String toString() {
return "Article{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
- DTO 클래스에
toEntity()
메서드를 추가한다.toEntity()
메서드는 DTO인form
객체를 엔티티 객체로 변환하는 역할을 한다. Entity 클래스의 생성자 형식에 맞게 작성한다.
1
2
3
public Article toEntity() {
return new Article(null, title, content);
}
- Controller에
form
객체의toEntity()
라는 메서드를 호출해서 그 반환값을Article
타입의article
엔티티에 저장한다.
1
2
3
4
5
6
7
8
@PostMapping("/articles/create")
public String createArticle(ArticleForm form) {
System.out.println(form.toString());
// 1. DTO를 엔티티로 변환
Article article = form.toEntity();
// 2. 리파지터리로 엔티티를 DB에 저장
return "";
}
리파지터리로 엔티티를 DB에 저장하기
repository 패키지를 만들고
ArticleRepository
인터페이스를 만든다. 사용자가 직접 구현할 수도 있지만, JPA에서 제공하는 리파지터리 인터페이스를 활용해 만들 수도 있다.CrudRepository
인터페이스를 상속받는다. <관리 대상 엔티티의 클래스 타입, 관리 대상 엔티티의 대푯값 타입>을 제네릭 요소로 받는다.
1
2
3
public interface ArticleRepository extends CrudRepository<Article, Long> {
}
- 스프링 부트에서는 객체를 만들지 않아도 스프링 부트가 알아서 객체를 만들어 준다.
@Autowired
어노테이션을 붙이면 스프링 부트가 미리 생성해 놓은 객체를 가져다가 연결해 준다. 이를 의존성 주입(DI, Dependency Injection) 이라고 한다.
1
2
@Autowired
private ArticleRepository articleRepository;
articleRepository.save()
메서드를 호출해 엔티티를 저장한다.save()
메서드는 저장된 엔티티를 반환하므로 이를Article
타입의saved
라는 객체에 받아온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class ArticleController {
private ArticleRepository articleRepository;
@GetMapping("/articles/new")
public String newArticleForm() {
return "articles/new";
}
@PostMapping("/articles/create")
public String createArticle(ArticleForm form) {
System.out.println(form.toString());
// 1. DTO를 엔티티로 변환
Article article = form.toEntity();
// 2. 리파지터리로 엔티티를 DB에 저장
Article saved = articleRepository.save(article);
return "";
}
}
toString()
메소드를 통해 article이 DB에 잘 저장되는지 확인할 수 있다.
🔗 참고자료
📚 더 공부해야 할 것
- 의존성 주입
- DTO와 Entity 분리하는 이유와 장단점
- JPA