[SpringBoot] 3-4장 게시글 생성(CREATE), 롬복(lombok)과 리팩터링

01_폼데이터를 DTO로 받기


📍 폼 데이터

HTML 요소인 <form> 태그에 실려 전송되는 데이터

웹 브라우저에서 서버에 데이터 전송할 때 사용

 

 

<form>

action : URL 연결 주소를 적어 그 페이지로 폼 데이터를 보낸다.

method : 속성 값으로 get, post 를 설정할 수 있다.

 

src/main/resources/templates/greetings.mustache

{{>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}}

 

 

 

📍 DTO(Data Transfer Object)

<form> 태그에 실어 보낸 데이터를 서버의 컨트롤러가 객체에 담아 받는데, 이 객체를 DTO라고 한다.

DTO로 받은 데이터는 최종적으로 데이터베이스에 저장

 

src/main/java/com.example.firstproject/dto/ArticleForm.java

package com.example.firstproject.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 + '\'' +
                '}';
    }
}

🔍 코드 설명

각 필드는 mustache 파일의 title, content 태그와 연결된다.

 

src/main/java/com.example.firstproject/controller/ArticleController.java

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 "";
    }
}

🔍 코드 설명

  • @PostMapping : URL 요청 접수. <form>의 action URL을 입력한다.
  • ArticleForm form : 폼에서 전송한 데이터를 ArticleForm 타입의 form 객체 매개변수로 받아온다.

 

 

 

 

02_DTO를 DB에 저장하기


📍 JPA(Java Persistence API)

Java 언어로 DB에 명령을 내리는 도구

스프링부트는 Java를 사용하지만 DB는 SQL을 사용하기 때문에 JPA가 필요하다.

데이터를 객체 지향적으로 관리할 수 있게 해준다.

  • entity : Java 객체를 DB가 이해할 수 있게 만든 것
  • repository : entity가 DB 속 테이블에 저장 및 관리될 수 있게 하는 인터페이스

 

 

DTO -> entity 변환하기

Controller에 DTO를 entity로 변환하는 코드를 추가한다.

 

src/main/java/com.example.firstproject/controller/ArticleController.java

@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());
        // 1. DTO를 entitiy로 변환
        Article article = form.toEntity();
        System.out.println(article.toString());
        return "";
    }
}

 

Article이라는 이름의 entity를 생성한다.

 

src/main/java/com.example.firstproject/entity/Article.java

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;

    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 : 클래스가 entity임을 선언. 이 어노테이션이 붙은 클래스를 기반으로 DB에 테이블 생성한다.
  • @Id : entity의 대푯값을 선언.
  • @GeneratedValue : 대푯값을 자동으로 생성(1, 2, 3, ...)
  • @Column : DB 테이블의 각 열과 연결

 

ArticleForm에 toEntity 메서드를 생성한다.

메서드에서는 폼 데이터를 담은 DTO 객체를 entity로 반환한다.

 

src/main/java/com.example.firstproject/dto/ArticleForm.java

public class ArticleForm {
(중략)
	public Article toEntity() {
        return new Article(null, title, content);
    }
}

 

 

repository로 entity를 DB에 저장하기

Controller에 repositiory로 entity를 DB에 저장하는 코드를 추가한다.

 

 

src/main/java/com.example.firstproject/controller/ArticleController.java

@Controller
public class ArticleController {
    @Autowired // 스프링부트가 미리 생성해 놓은 repository 객체 주입(DI)
    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를 entitiy로 변환
        Article article = form.toEntity();
        System.out.println(article.toString());
        // 2. repository로 entity를 DB에 저장
        Article saved = articleRepository.save(article);
        System.out.println(saved.toString());
        return "";
    }
}

🔍 코드 설명

필드 선언부에 repository 객체를 선언한다.

그 다음 article entity를 저장해서 saved 객체로 변환한다.

  • @Autowired : 인터페이스의 구현체를 따로 만들지 않아도 스프링부트가 만들어 놓은 객체를 필드로 가져와 주입해준다.

 

ArticleRepository라는 이름의 interface를 생성한다.

 

src/main/java/com.example.firstproject/repository/ArticleRepository.java

package com.example.firstproject.repository;

import com.example.firstproject.entity.Article;
import org.springframework.data.repository.CrudRepository;

public interface ArticleRepository extends CrudRepository<Article, Long> {
}

🔍 코드 설명

  • CrudRepository<T, ID> : JPA에서 제공하는 인터페이스로 이를 상속해 entity 관리(생성, 조회, 수정, 삭제) 가능하다.
    • T : 관리 대상 entity의 클래스 타입
    • ID : 관리 대상 entity의 대푯값 타입

 

 

 

 

03_H2 DB 사용하기


📍 H2 DB 접속하기

H2 DB에 웹 콘솔로 접근할 수 있도록 설정을 허용해야 한다.

 

src/main/resources/application.properties

spring.application.name=firstproject
server.servlet.encoding.force=true
spring.h2.console.enabled=true // 설정 추가

 

웹 브라우저를 실행하고 localhost:8080/h2-console에 접속한다.

H2 DB는 서버를 실행할 때마다 바뀌므로 JDBC URL 주소를 매번 입력해주어야 한다.

IntelliJ의 [Run] 탭에서 'jdbc:h2:mem:...' 를 복사해서 웹 브라우저의 JDBC URL 주소에 붙여넣고 연결한다.

 

 

 

📍 데이터 조회하기(SELECT)

웹 브라우저에서 localhost:8080/articles/new에 접속해서 제목과 내용을 입력한다.

 

H2 DB로 들어가서 SQL문을 입력한다.

DB에서 데이터를 조회하는 SQL문은 다음과 같다.

💡 SELECT 속성명 FROM 테이블명;

 

 

 

📍 데이터 생성하기(INSERT)

H2 DB에서 데이터를 직접 삽입할 수 있다.

DB에서 데이터를 생성하는 SQL문은 다음과 같다.

💡 INSERT INTO 테이블명(속성명1, 속성명2, 속성명3, ...) VALUES (값1, 값2, 값3, ...);

 

 

 

 

04_롬복(lombok)과 리팩터링(refactoring)


📍롬복(lombok)

코드를 간소화해 주는 라이브러리

필수 코드를 간편하게 작성할 수 있다.

로깅(logging) 기능으로 프로그램의 수행 과정을 기록으로 남길 수 있다.

 

 

롬복 설치하기

build.gradle의 dependencies 내부에 롬복을 추가한다.

 

build.gradle

dependencies {
	compileOnly 'org.projectlombok:lombok' // 추가
	annotationProcessor 'org.projectlombok:lombok' // 추가
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-mustache'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

 

 

 

📍리팩터링(refactoring)

코드의 기능은 그대로 두고 구조나 성능을 개선하는 작업

 

 

DTO, entity 리팩터링

생성자와 toString 메서드를 리팩터링할 수 있다.

 

src/main/java/com.example.firstproject/dto/ArticleForm.java

package com.example.firstproject.dto;

import com.example.firstproject.entity.Article;
import lombok.AllArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@ToString
public class ArticleForm {
    private String title; // 제목을 받을 필드
    private String content; // 내용을 받을 필드

    public Article toEntity() {
        return new Article(null, title, content);
    }
}

 

src/main/java/com.example.firstproject/entity/Article.java

package com.example.firstproject.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@ToString
@Entity
public class Article {
    @Id
    @GeneratedValue
    private Long id;
    @Column
    private String title;
    @Column
    private String content;
}

🔍 코드 설명

  • AllArgsConstructor : 클래스 안쪽의 모든 필드를 매개변수로 하는 생성자가 자동으로 만들어진다.
  • ToString : toString() 메서드를 사용하는 것과 같은 효과가 난다.

 

 

Controller에 log 남기기

println으로 데이터 검증하면 기록에 남지 않는데다가 서버에 성능에도 악영향을 끼친다.

로깅 기능을 이용하면 서버에서 일어나는 모든 일(어떤 데이터가 언제 저장됐는지 등)을 기록할 수 있다.

그동안 찍힌 로그를 나중에도 찾아볼 수 있다.

 

src/main/java/com.example.firstproject/controller/ArticleController.java

import lombok.extern.slf4j.Slf4j;
(중략)

@Slf4j
@Controller
public class ArticleController {
    (중략)

    @PostMapping("/articles/create")
    public String createArticle(ArticleForm form) {
        log.info(form.toString()); // 로깅 코드 추가
        // 1. DTO를 entitiy로 변환
        Article article = form.toEntity();
        log.info(article.toString()); // 로깅 코드 추가
        // 2. repositoryfh entity를 DB에 저장
        Article saved = articleRepository.save(article);
        log.info(saved.toString()); // 로깅 코드 추가
        return "";
    }
}

🔍 코드 설명

  • @Slf4j : Simple Logging Facade for Java의 약자로, 로깅할 때 사용한다.
  • log.info() : 로그를 찍을 때 사용한다.