🌝이전 시간에 배운 것들 - MVC 패턴
Model에서 데이터를 변수나 객체로 관리하고,
Controller에서 클라이언트의 요청에 따라 서버에서 데이터를 처리하여,
View에서 화면으로 보여주는 패턴입니다.
Model은 보통 DTO(Data Transfer Object, 데이터 전송 객체)란 프로세스 간에 데이터를 전달하는 객체를 사용합니다.
👉3단원 CREATE
1. 폼 데이터 form data
html에서 form태그는 안에 작성된 input등의 요소를 하나로 묶어 전송합니다.
<form> 태그의 action과 method 속성에는 각각 주소(URL)과 POST등 데이터 보내는 방식을 지정합니다.
<form action="/submit" method="POST">
<input type="text" name="username" placeholder="Enter your name">
<input type="password" name="password" placeholder="Enter your password">
<button type="submit">Submit</button>
</form>
이렇게 하나로 묶어진 데이터를 서버의 controller가 객체(DTO Data Transfer Object)에 담아 받습니다. DTO로 받은 데이터는 최종적으로 데이터베이스에 저장됩니다.
실습💦
1. resources/templates에 articles/new.mustache를 작성합니다.
{{>layouts/header}}
<form class="container" action="/articles/new" method="post">
<div class="mb-3">
<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}}
//layouts/header
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
layouts/footer
<div class="mb-5 container-fluid">
<hr>
<p>@innocentHead | <a href="#">privacy</a> | <a href="#">Terms</a></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>
2. 컨트롤러 만들기
controller/ArticleController의 Get메소드는 BASE_URL/articles/new을 치면 해당 화면(view)로 연결해줍니다.
package com.example.demo.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";
}
}
@Controller
해당 클래스는 컨트롤러임
@GetMapping
매개변수로 들어오는 url을 받으면 아래 메소드의 return값을 반환함
return articles/new
articles/new라는 mustache를 반환함
3. ArticleController.java에 Post메소드를 추가하여 사용자가 form데이터로 전송한 데이터를 받겠습니다.
@Controller
public class ArticleController {
@GetMapping("/articles/new")
public String newArticleForm() {
return "articles/new";
}
@PostMapping("/articles/new")
public String createArticle(){
return "";
}
}
POST 메소드는 GET메소드와 달리 값을 받아오기 때문에 백엔드에서 값에 따라 여러가지 처리를 해주어야 합니다.
게다가 form 데이터는 객체 형태로 전달되기 때문에 DTO(Data Transfer Object)를 사용할 것인데요. DTO 없이 바로 @RequestParam을 이용해서 데이터를 받을 수도 있지만 이것은 관리가 불편하고 한 form데이터에서 유효성 검사를 별도로 처리해야 하기 때문에 비효율적입니다.
controller패키지와 같은 위치에 dto패키지를 만들고 ArticleForm을 만듭시다.
그리고 입력 폼으로 전송할 필드를 dto 작성하겠습니다.
package com.example.demo.dto;
public class ArticleForm {
private String title;
private String content;
}
위와 같이 작성한 후, 우클릭으로 content를 눌러 generate->constructor-> 2개다 선택하면 자동으로 아래처럼 생성자가 추가됩니다.



package com.example.demo.dto;
public class ArticleForm {
private String title;
public ArticleForm(String title, String content) {
this.title = title;
this.content = content;
}
private String content;
}
데이터를 잘 받았는지 그 값을 반환하는 toString메서드를 작성해보겠습니다. 생성자 아래에서 우클릭으로 toString을 쉽게 만들 수 있습니다.

package com.example.demo.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 + '\'' +
'}';
}
}
마지막으로 new.mustache의 input에 필드를 지정해주는 작업을 하여 DTO와 연결해줍니다.
{{>layouts/header}}
<form class="container" action="/articles/new" method="post">
<div class="mb-3">
<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}}
이제 controller에서 post메서드에 매개변수로 form객체를 받고 toString으로 확인해봅시다. 서버를 시작해주세요.
package com.example.demo.controller;
import com.example.demo.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/new")
public String createArticle(ArticleForm form){
System.out.println(form.toString());
return "";
}
}
Console 로그에 입력한 내용이 출력 되는 것을 확인할 수 있습니다.
ArticleForm{title='abcd', content='12345'}

2. DTO를 데이터베이스에 저장하기
DB프로그램은 MySQL, Oracle, MariaDB 등 다양한 종류가 있습니다. 여기서는 로컬에서 동작하는 H2 DB를 사용하겠습니다.
# H2 DB는 Java에서 사용되는 경량화된 관계형 데이터베이스입니다. 주로 개발 환경에서 빠르고 가볍게 데이터베이스를 사용할 수 있도록 설계되었습니다. H2는 메모리 기반 또는 디스크 기반에서 작동할 수 있기 때문에 테스트 및 프로토타입 개발에 매우 유용합니다.
현대의 웹 애플리케이션에서는 Oracle, MySQL, MSSQL등 관계형 데이터베이스(RDB, Relational DataBase)를 대부분 사용합니다. 관계형 데이터베이스는 SQL만을 인식하는데 이 때문에 각 테이블마다 CRUD(Create, Read, Update, Delete)를 매번 생성해야 합니다.
객체를 관계형 데이터베이스에서 관리하려면 SQL을 사용하는 것이 필수적이지만 반복적으로 SQL을 사용해야 하는 문제와, 객체지향 프로그래밍과의 패러다임 불일치 문제가 있습니다.기능과 속성을 관리하는 데 초점이 맞춰진 객체지향 프로그래밍과 데이터를 어떻게 저장하는 지에 초점이 맞춰진 관계형 데이터 베이스는 미묘하게 어긋나는 부분들이 있습니다. 이를 패러다임 불일치라고 할 수 있는데, 이를 JPA가 자동으로 맞춰줍니다. 개발자는 객체지향적으로 프로그래밍만 하면 됩니다.
JPA의 핵심도구로는 entity와 repository가 있습니다. 엔티티는 자바객체를 DB가 이해할 수 있도록 만든 것으로 이를 기반으로 테이블이 만들어집니다.
레포지터리는 엔티티가 DB속 테이블에 저장 및 관리될 수 있게 하는 인터페이스입니다.
이제 DTO를 엔티리로 변환하고 리포지터리를 이용해 엔티티를 DB에 저장할 것입니다.
실습 이어하기💦
우리는 ArticleController에 아래의 내용을 만들 것입니다!
@PostMapping("/articles/new")
public String createArticle(ArticleForm form){
//1. DTO를 엔티티로 변환
Article article = form.toEntity();
//2. 리포지터리로 엔티티를 DB에 저장
Article saved = articleRepository.save(article);
return "";
}
1. entity 패키지 안에 Article.java를 만듭니다.
package com.example.demo.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;
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 + '\'' +
'}';
}
}
@Entity
이 클래스가 엔티티임을 선언합니다. 이 어노테이션이 붙이 클래스를 기반으로 DB에 테이블이 생성됩니다.
@Id
id 필드는 이 엔티티의 기본 키임을 나타냅니다. 데이터베이스에서 이 값을 기준으로 레코드를 식별합니다.
@Id 어노테이션이 없다면, 해당 클래스는 JPA에서 엔티티로 인식되지 않거나 데이터베이스의 테이블과 매핑되지 않습니다.
@GeneratedValue
id 필드의 값을 자동으로 생성 시킵니다. 기본적으로 자동 증가 방식(Auto Increment)으로 설정되어, 새 레코드가 추가될 때마다 id가 자동으로 증가합니다.
private Long id;
위의 어노테이션이 달린 id필드를 설정하였습니다. id필드는 일반적으로 Long 타입으로 설정합니다.
@Column
@Column 어노테이션은 이 필드가 데이터베이스 테이블의 컬럼과 매핑됨을 나타냅니다.
이 어노테이션은 선택적으로 컬럼의 이름, 제약 조건 등을 설정할 수 있으며, 여기서는 단순히 필드가 테이블의 컬럼에 대응됨을 나타냅니다.
DTO와 연결
DTO에서 작성했던 title, content 필드도 설정해주었습니다.

마지막으로 객체의 생성및 초기화를 위해 생성자를 만들고 확인을 위해 toString메서드를 추가해주었습니다.
여기까지 진행하면 Article 기본 생성자가 없다는 오류가 발생합니다. toEntity가 아직 작성이 되지 않아서 그런 것입니다.
2.toEntity를 작성하러 가봅시다.
dto/ArticleForm.java
package com.example.demo.dto;
import com.example.demo.entity.Article;
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);
}
}
이제 문제가 사라졌습니다.
3. 리포지터리로 엔티티를 DB에 저장하자!
다음과 같이 엔티티로 변환한 DTO를 리보지터리로 저장해보겠습니다.
//1. DTO를 엔티티로 변환
Article aricle = form.toEntity();
return "";
}
먼저 repository 패키지를 만들고 그 안에 ArticleRepository라는 INTERFACE 인터페이스를 만듭시다.

리포지터리는 직접 구현할 수도 있지만 JPA에서 직접 제공하는 리포지터리 인터페이스를 이용해 만들 수도 있습니다.
package com.example.demo.repository;
import com.example.demo.entity.Article;
import org.springframework.data.repository.CrudRepository;
public interface ArticleRepository extends CrudRepository<Article, Long> {
//save 저장하기
//findId id값으로 찾기 기능을 제공한다.
}
CrudRepository<T, ID>를 extend상속하여 사용하면
단순 인터페이스를 생성한 후, JpaRepositiory<entity클래스, pk타입=""> 을 상속하면 기본적인 CRUD가 자동으로 생성됩니다.
4. ArticleController작성
package com.example.demo.controller;
import com.example.demo.dto.ArticleForm;
import com.example.demo.entity.Article;
import com.example.demo.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/new")
public String createArticle(ArticleForm form){
// System.out.println(form.toString());
//1. DTO를 엔티티로 변환
Article article = form.toEntity();
//2. 리포지터리로 엔티티를 DB에 저장
Article saved = articleRepository.save(article);
//System.out.println(article.toString());
//System.out.println(saved.toString());
return "";
}
}
DTO를 엔티티로 변환하여 저장하고 있는 article 객체를 articleRepository객체에서 받아서 save 메소드를 저장하였습니다.
보시다시피 articleRepository는 인터페이스인데 어째서 구현객체(new로 만듬) 없이 동작할까요. @Autowired 어노테이션을 사용하면 스프링부트가 미리 생성해놓은 리포지터리 객체를 주입합니다. 이것을 의존성 주입(Dependency Injection)이라고 합니다.
마지막으로 println으로 잘 전달되고 있는지 확인합시다.

지금은 entity클래스와 repository클래스를 각각 entity와 repository라는 다른 패키지에서 관리하고 있지만 프로젝트 규모가 커지면 같은 도메인에 속하는 entity클래스와 repository인터페이스를 같은 디렉토리 안에서 관리합니다.
3. DB 데이터 조회하기
앞에서 폼 데이터를 컨트롤러에서 DTO로 맏아 엔티티로 변환한 후 리포지터리를 이용해 DB에 저장하는 과정을 거쳤습니다.
DB에 저장된 데이터는 테이블이라는 틀에 맞춰 관리됩니다. 행과 열로 이루어진 테이블을 CRUD를 할수있고, 각각 sql문으로는 INSERT, SELECT, UPDATE, DELETE로 동작시킵니다.
실습💦
1. src/main/resources/application.properties에 spring.h2.console.enabled=true 를 작성하여 h2 DB에 웹 콘솔(브라우저)로 접근가능하도록 설정합니다.
spring.application.name=demo
server.servlet.encoding.force-response=true
spring.h2.console.enabled=true
2. JDBC URL에 적힌 메모리 주소를 바꾸어줍니다.
이 값은 서버를 실행할 때마다 바뀌므로 이 값을 매번 찾아 입력해주어야 합니다.
인텔리제이에 ctrl+F 단축키로 jdbc를 검색해 항상 넣어줄 수도 있지만 우리는 고정된 주소를 사용합시다.
spring.application.name=demo
server.servlet.encoding.force-response=true
spring.h2.console.enabled=true
# H2 데이터베이스 고정된 메모리 주소 설정
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
3. 콘솔에 정상적으로 접속하고 값을 조회해봅시다.
SELECT * FROM ARTICLE

sql문으로 값 직접 넣기
INSERT INTO article(id, title, content) VALUES(3, 'cccc','3333');

'대외활동 > DRACONIST-백엔드' 카테고리의 다른 글
스스로 공부하는 스프링부트. 6,7,8,9단원 CRUD완성 (0) | 2025.02.13 |
---|---|
스스로 공부하는 스프링부트. 4단원 롬복리팩터링, 5단원 게시글 조회 (1) | 2025.02.12 |
스스로 공부하는 스프링부트 2.4 mustache 사용하기 (0) | 2025.02.10 |
스프링부트 스터디 4주차. 8단원 EC2서버에 프로젝트 배포하기 (0) | 2025.02.03 |
스프링부트 스터디 3주차. 7단원 AWS에 데이터베이스-AWS RDS (0) | 2025.01.26 |