3.0 3일차 학습 목표
'3일차 게시판 만들고 새 글 작성하기 : Create' 에서는
게시판을 생성하고 게시글을 쓰는 방법을 알아보면서
서버 프로그래밍과 데이터베이스의 기본기를 익혀보자.
1) 폼 데이터란?
2) 폼 데이터를 DTO로 받기
3) DTO를 데이터베이스에 저장하기
4) DB 데이터 조회하기
3.1 폼 데이터란?
폼 데이터 (form data)
- HTML 의 <form> 태그에 실려 전송되는 데이터
- <form> 태그는 웹 브라우저에서 서버로 데이터를 전송할 때 사용
- 데이터를 어디로, 어떻게 보낼지를 작성함
- <form> 태그에 실어 보낸 데이터는, 서버의 컨트롤러가 DTO 객체에 담아서 받음

3.2 폼 데이터를 DTO로 받기
1) 입력 폼 만들기
먼저 뷰 템플릿 페이지를 만들기 위해
src > main > resources > templates 디렉토리에 articles 디렉토리를 생성하고,
new.mustache 파일을 아래와 같이 작성한다.
{{>layouts/header}}
<form action="">
<input type = "text">
<textarea></textarea>
<button type="submit">Submit</button>
</form>
{{>layouts/footer}}
1) input type : 게시글 제목 입력
2) textarea : 게시글 내용 입력
3) button : 전송 버튼
2) 컨트롤러 만들기
다음으로 뷰 페이지를 웹 브라우저에서 보기 위해
controller 패키지 아래에 ArticleController 를 생성하고,
아래와 같이 코드를 작성한다.
package com.example.firstproject.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ArticleController {
@GetMapping("/articles/new")
public String newArticleForm(){
return "articles/new";
}
}
[ 주의할 점 ] articles 디렉토리를 새로 만들었으므로, return 값에 파일 경로를 함께 적어준다.
localhost:8080/articles/new 페이지에 접속하면,
앞서 작성한 new.mustache 뷰 페이지가 제대로 출력되는 것을 확인할 수 있다.

이후 디자인 수정을 위해 new.mustache 파일 코드를 아래와 같이 수정하였다.
{{>layouts/header}}
<form class="container">
<div class="mb-3">
<label class="form-label">제목</label>
<input type = "text" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">내용</label>
<textarea class="form-control" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{{>layouts/footer}}

3) 폼 데이터 전송하기
웹 페이지에 내용을 입력하면, 서버에 전송할 수 있도록 해보자.
제목과 내용을 입력한 후 submit 버튼을 눌렀을 때, 데이터가 전송되기 위해서는
"어디로", "어떻게" 보낼지에 대한 정보를 추가해야 한다.
<form> 태그에 아래의 정보를 추가한다.
<form class="container" action="/articles/create" method="post">
1) action
- URL 연결 주소를 적어 localhost:8080/articles/create 페이지로 폼 데이터를 보냄
2) method
- get / post 2가지 방식을 설정할 수 있음
4) 폼 데이터 받기
다음으로 앞서 전송한 폼 데이터를 컨트롤러가 받을 수 있도록 해주자.
ArticleController 클래스 코드를 아래와 같이 수정한다.
package com.example.firstproject.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class ArticleController {
@GetMapping("/articles/new")
public String newArticleForm(){
return "articles/new";
}
@PostMapping("/articles/create")
public String createArticle(){
return "";
}
}
1) @PostMapping
- 뷰 페이지에서 폼 데이터 방식을 post 방식으로 전송했으므로
- 괄호 안에 받는 URL 주소를 작성
2) createArticle() 메서드
- 형식을 맞추기 위해 return 값에 빈 문자열 적음
5) DTO(Data Transfer Object) 만들기
컨트롤러에서 form 데이터를 받을 때는 DTO 객체에 담아 받게 된다.
먼저 com.example.firstproject 에서 dto 패키지를 생성하고,
ArticleForm 클래스를 생성한 후 아래와 같이 코드를 작성한다.
package com.example.firstproject.dto;
// 폼 데이터를 받아 올 그릇 = DTO
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 + '\'' +
'}';
}
}
1) title / content 필드
- 각각 제목과 내용을 받을 필드
2) ArticleForm 생성자
- 전송받은 제목과 내용을 필드에 저장하는 생성자
- Generate > Constructor 을 클릭하여 자동 생성할 수 있음
3) toString() 메서드
- 폼 데이터를 잘 받았는지 확인
- Generate > toString 을 클릭하여 자동 생성할 수 있음
6) 폼 데이터를 DTO에 담기
전송받은 폼 데이터를 DTO에 담아보자.
ArticleController 코드를 다음과 같이 수정한다.
package com.example.firstproject.controller;
import com.example.firstproject.dto.ArticleForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class ArticleController {
@GetMapping("/articles/new")
public String newArticleForm(){
return "articles/new";
}
@PostMapping("/articles/create")
public String createArticle(ArticleForm form){
System.out.println(form.toString());
return "";
}
}
1) ArticleForm form
- DTO로 만든 클래스 ArticleForm 타입의 form 객체를 매개변수로 선언
2) form.toString()
- 폼에서 전송한 데이터가 잘 담겼는지 확인하기 위한 출력문
7) 입력 폼과 DTO 필드 연결하기
앞서 작성한 title과 content 필드에 값이 들어가기 위해서는,
new.mustache 입력 폼에 필드명을 지정해야 한다.
아래와 같이 text 와 textarea에 name 을 각각 title과 content로 지정한다.
이렇게 DTO 필드명과 동일한 이름을 name 값으로 주면,
입력 폼에서 작성한 데이터가 DTO의 해당 필드와 연결된다.
{{>layouts/header}}
<form class="container" action="/articles/create" method="post">
<div class="mb-3">
<label class="form-label">제목</label>
<input type = "text" class="form-control" name = "title">
</div>
<div class="mb-3">
<label class="form-label">내용</label>
<textarea class="form-control" rows="3" name = "content"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{{>layouts/footer}}
서버를 재시작 한 후,
브라우저에서 제목과 내용을 입력한 후 submit 버튼을 눌러보자.
아래와 같이 IntelliJ 실행 창에서
ArticleForm 데이터가 출력되는 것을 확인할 수 있다.

[ 과정 정리 ]
1) 뷰 페이지를 만든 후, <form> 태그의 action과 method 속성을 지정한다.
2) 컨트롤러를 만들고, PostMapping 방식으로 URL 주소를 연결한다.
3) 전송받은 데이터를 담을 DTO 객체를 만든다.
4) 컨트롤러에서 폼 데이터를 전송받아 DTO 객체에 담는다.
3.3 DTO를 데이터베이스에 저장하기
앞선 과정에서는 웹 브라우저에서 전송한 폼 데이터를
컨트롤러를 통해 DTO 객체에 담았다.
이번에는 받은 데이터가 데이터베이스에 저장되도록 해보자.
데이터베이스와 JPA
- 해당 실습에서는 H2 DB를 사용
- JPA(Java Persistence API)를 이용하여 DB에 자바로 데이터 저장 명령을 내릴 수 있음
- JPA를 통해 데이터를 객체 지향적으로 관리할 수 있음
- JPA 의 핵심 도구로는 Entity와 Repository가 있음
DTO를 DB에 저장하는 과정은 다음과 같다.

[ 참고 ] Entity 와 Repository
- Entity : 자바 객체가 DB를 이해할 수 있도록 만든 것
- Repository : Entity가 DB속 테이블에 저장, 관리 될 수 있도록 하는 인터페이스
1) DTO를 엔티티로 변환하기
먼저 DTO를 Entity로 변환하려면, 엔티티 클래스를 생성해야 한다.
com.example.firstproject 아래에 entity 패키지를 생성한 후,
Article 클래스를 아래와 같이 작성한다.
package com.example.firstproject.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Article {
@Id
@GeneratedValue
private Long id;
@Column
private String title;
@Column
private String content;
// Article 생성자 추가
public Article(Long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
// toString 메서드 추가
@Override
public String toString() {
return "Article{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
1) @Entity
- 해당 클래스가 엔티티임을 선언 (JPA에서 제공하는 어노테이션)
- 해당 어노테이션이 붙은 클래스를 기반으로 DB에 테이블 생성
2) @Id
- 엔티티의 대푯값을 지정
3) @GeneratedValue
- 대푯값을 자동으로 생성하게 함 (숫자가 자동으로 매겨짐)
4) @Column
- title과 content 필드를 선언하여, DB테이블의 title, content 열과 연결됨
다음으로 ArticleForm DTO 클래스에
아래와 같이 toEntity() 메서드를 추가한다.
package com.example.firstproject.dto;
import com.example.firstproject.entity.Article;
// 폼 데이터를 받아 올 그릇 = DTO
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 + '\'' +
'}';
}
public Article toEntity() {
return new Article(null, title, content);
}
}
toEntity() 메서드는 DTO 객체를 엔티티로 반환하는 역할을 수행한다.
이때 전달값은 Article 클래스의 생성자 형식에 맞게 작성한다.
(ArticleForm에 id 정보는 없으므로, id 값은 null로 처리)
2) Repository를 이용해 Entity를 DB에 저장하기
마지막으로 Entity를 DB에 저장해보자.
먼저 com.example.firstproject에 repository 패키지를 생성한 후,
ArticleRepository 인터페이스를 아래와 같이 작성한다.
package com.example.firstproject.repository;
import com.example.firstproject.entity.Article;
import org.springframework.data.repository.CrudRepository;
public interface ArticleRepository extends CrudRepository<Article, Long> {
}
1) CrudRepository<T, ID>
- JPA에서 제공하는 Repository 인터페이스를 상속받음
- 엔티티 생성, 조회, 수정, 삭제가 가능
- 2개의 제네릭 요소(관리 대상 엔티티의 클래스 타입, 관리 대상 엔티티의 대푯값 id) 을 받음
다음으로 ArticleController 에서 Repository 객체를 선언하고,
save() 메서드를 호출하여 article 엔티티를 저장한다.
package com.example.firstproject.controller;
import com.example.firstproject.dto.ArticleForm;
import com.example.firstproject.entity.Article;
import com.example.firstproject.repository.ArticleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class ArticleController {
@Autowired
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를 Entity로 변환
Article article = form.toEntity();
// 2. Repository로 Entity를 DB에 저장
Article saved = articleRepository.save(article);
return "";
}
}
이때 ArticleRepository 인터페이스의 별도의 구현 객체를 만들지 않아도,
@Autowired 어노테이션을 통해 스프링 부트가 미리 생성해 놓은 객체를 연결해준다. (DI)
[ 참고 ] @Autowired
- 스프링부트에서 제공하는 어노테이션
- 컨트롤러의 필드에 붙이면, 스프링부트가 만들어 놓은 객체를 가져와 주입
- 의존성 주입(DI - Dependenct Injection)이라고 함
실제로 서버를 재시작하고, toString() 메서드를 통해 확인해보면,
DTO에 저장된 후, DTO가 Entity로 잘 변환되었고,
이후 DB에도 저장되는 것을 확인할 수 있다.

3.4 DB 데이터 조회하기
H2 데이터베이스에 접속하여 앞서 생성한 게시글을 조회해보자.
1) H2 DB 접속하기
먼저 H2 DB에 웹 콘솔로 접근할 수 있도록
application.properties 파일에 아래의 코드를 추가한다.
spring.h2.console.enabled=true
이후 서버를 재시작하고, localhost:8080/h2-console 페이지에 접속한다.
JDBC URL의 경우 서버를 실행할 때마다 바뀌므로,
IntelliJ [Run] 탭에서 'jdbc:h2:mem: ...' 문구를 복사해서 입력한 후,
connect 버튼을 클릭한다.

2) 데이터 조회하기
H2 DB는 모든 입출력을 메모리 모드에서 돌리므로
서버를 재시작하면 DB에 저장된 내용이 사라진다.
따라서 localhost:8080/articles/new 에서 새로운 데이터를 생성한 후,
ARTICLE 의 테이블을 조회해보면
아래와 같이 작성한 레코드가 나타나는 것을 확인할 수 있다.

💡3장 셀프 체크
[ signup.mustache ]
{{>layouts/header}}
<form class="container" action="/join" method="post">
<div class="mb-3">
<label class="form-label">이메일</label>
<input type = "email" class="form-control" name = "email">
</div>
<div class="mb-3">
<label class="form-label">비밀번호</label>
<input type="password" class="form-control" name = "password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/members">Back</a>
</form>
{{>layouts/footer}}
[ MemberController ]
package com.example.firstproject.controller;
import com.example.firstproject.dto.MemberForm;
import com.example.firstproject.entity.Member;
import com.example.firstproject.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class MemberController {
@Autowired
private MemberRepository memberRepository;
@GetMapping("/signup")
public String newSignUpForm(){
return "articles/signup";
}
@PostMapping("/join")
public String createMember(MemberForm memberForm){
// 1. DTO를 Entity로 변환
Member member1 = memberForm.toEntity();
// 2. Repository로 Entity를 DB에 저장
Member saved = memberRepository.save(member1);
return "";
}
}
[ MemberForm ] - DTO
package com.example.firstproject.dto;
import com.example.firstproject.entity.Member;
public class MemberForm {
private String email;
private String password;
public MemberForm(String email, String password) {
this.email = email;
this.password = password;
}
public Member toEntity() {
return new Member(null, email, password);
}
}
[ Member ] - Entity
package com.example.firstproject.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
@Column
private String email;
@Column
private String password;
public Member(){
}
public Member(Long id, String email, String password) {
this.id = id;
this.email = email;
this.password = password;
}
}
[ MemberRepository ] - Repository
package com.example.firstproject.repository;
import com.example.firstproject.entity.Member;
import org.springframework.data.repository.CrudRepository;
public interface MemberRepository extends CrudRepository<Member, Long> {
}

홍팍 '스프링 부트3 자바 백엔드 개발 입문' 책 내용을 기반으로 작성하였습니다.
'[코딩자율학습단 5기] SpringBoot' 카테고리의 다른 글
[코딩자율학습단] 2일차. MVC 패턴 이해와 실습 (5) | 2023.11.26 |
---|---|
[코딩자율학습단] 1일차. 스프링 부트 시작하기 (1) | 2023.11.25 |
[코딩자율학습단] 0일차. 자율학습단을 신청하게 된 이유 / 스터디 소개 (0) | 2023.11.25 |