기록방

[3일차] 3장 : 게시판 만들고 새 글 작성하기: Create 본문

FrameWork/스프링

[3일차] 3장 : 게시판 만들고 새 글 작성하기: Create

Soom_1n 2024. 3. 17. 23:51

길벗 IT도서에서 주관하는 코딩 자율학습단 8기 : Spring Boot 파트에 참여한 기록입니다 [ 목록 ]

3.1 폼 데이터란

  • 폼 데이터(form data)란 HTML 요소인 <form> 태그에 실려 전송되는 데이터
    • 게시판 내용 작성 후 [전송] 버튼 누를 때, 데이터를 서버로 전송할 때 사용 됨
    • 택배처럼 어디로, 어떻게 보낼지 작성함
  • <form> 태그에 실어 보낸 데이터는 서버의 컨트롤러가 객체에 담아 받음.
    이 객체를 DTO(Data Transfer Object)라고 함
    • DTO로 받은 데이터는 최종적으로 데이터베이스(DB, Database)에 저장 됨

 

3.2 폼 데이터를 DTO로 받기

3.2.1 입력 폼 만들기

{{>layouts/header}}

<form action="">
    <input type="text">
    <textarea></textarea>
    <button type="submit">Submit</button>
</form>

{{>layouts/footer}}

3.2.2 컨트롤러 만들기

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

⇒ CSS 수정하기

{{>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.2.3 폼 데이터 전송하기

  • <form> 태그에 action과 method 지정하기
    • <form class="container" action="/articles/create" method="post">

3.2.4 폼 데이터 받기

  • ArticleContorller에 메서드 추가
  • @PostMapping("/articles/create") public String createArticle(){ return ""; }

3.2.5 DTO 만들기

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 + '\'' +
                '}';
    }
}

3.2.6 폼 데이터를 DTO에 담기

@PostMapping("/articles/create")
    public String createArticle(ArticleForm form){
        System.out.println(form.toString());
        return "";
    }

3.2.7 입력 폼과 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}}

🚀 1분 퀴즈

다음 중 옳지 않은 것을 고르세요.

  1. <form> 태그의 action 속성에는 데이터를 전달할 URL 주소가 담긴다.
  2. <form> 태그의 method 속성에는 get만 사용할 수 있다.
  3. @PostMapping 애노테이션은 post 방식으로 전달된 요청을 받아 컨트롤러의 메서드에 전달한다.
  4. 폼 데이터를 자동으로 받으려면 입력 폼에서 <input>, <textarea> 태그의 name 속성과 DTO 클래스의 필드명이 같아야 한다.

⇒ b. method 속성에는 get 뿐만 아니라 post, delete, put 과 같은 HTTP 메서드를 지정 할 수 있다.

 

 

3.3 DTO를 데이터베이스에 저장하기

3.3.1 데이터베이스와 JPA

  • 데이터베이스란 데이터를 관리하는 창고.
    • DB의 모든 데이터를 행과 열로 구성된 테이블(table)에 저장해 관리
    • 실습에서는 H2 DB 사용
  • DB에 명령을 내리기 위해서는 SQL(Structured Query Language)라는 언어 필요
    • Java를 이용해 SQL을 쉽게 사용하기 위해서 JPA(Java Persistence API) 사용
    • JPA를 사용해 데이터를 객체 지향적으로 관리 가능
  • JPA의 핵심 도구는 엔티티(entitiy)레포지토리(repository)
    • 엔티티 : 자바 객체를 DB가 이해할 수 있게 만든 것
      • 이를 기반을 ㅗ테이블이 만들어짐
    • 레포지토리 : 엔티티가 DB 속 테이블에 저장 및 관리될 수 있게 하는 인터페이스

3.3.2 DTO를 엔티티로 변환하기

@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를 엔티티로 변환
        Article article = form.toEntity();
        // 2. 레포지토리로 엔티티를 DB에 저장
        return "";
    }
}
  • createArticle() 수정
    1. Article 클래스 (엔티티) 만들어주기
    2. ArticleForm DTO에 toEntity() 메서드 만들어주기
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 // title 필드 선언. DB 테이블의 title 열과 연결
    private String title;
    @Column // content 필드 선언. DB 테이블의 content 열과 연결
    private String content;

    public Article(Long id, String title, String content) {
        this.id = id;
        this.title = title;
        this.content = content;
    }

    public Article() {

    }

    @Override
    public String toString() {
        return "Article{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}
  • JPA의 엔티티인 Article 클래스 생성
    1. 엔티티 선언은 @Entity
    2. 엔티티 대푯값 지정 @Id
    3. 숫자 자동 증가 생성 @GeneratedValue
    4. JPA 필드 선언 @Column
  • id, title, content 를 받는 생성자 필요
    • 추가로 책에는 안 나온 것 같은데 JPA는 아무 매개변수가 없는 기본 생성자도 필요
  • toString() 메서드 추가

 

public Article toEntity() {
        return new Article(null, title, content);
    }
  • ArticleForm DTO에 toEntity() 메서드 추가

 

3.3.3 리파지터리로 엔티티를 DB에 저장하기

1. 컨트롤러에서 리포지토리 사용

@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를 엔티티로 변환
        Article article = form.toEntity();
        System.out.println(article.toString());
        
        // 2. 레포지토리로 엔티티를 DB에 저장
        Article saved = articleRepository.save(article);
        System.out.println(saved.toString());
        return "";
    }
}

 

2. 리포지토리 생성

  • 리포지토리 인터페이스를 JPA에서 제공하는 CrudRepository<T, ID> 인터페이스를 상속해 생성
    • T는 관리 대상인 엔티티 클래스, Long은 대푯값(id)의 타입
    • DB 데이터의 생성 읽기 수정 삭제의 CRUD 기본 동작을 제공해줌
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.data.repository;

import java.util.Optional;

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S entity);
    <S extends T> Iterable<S> saveAll(Iterable<S> entities);
    Optional<T> findById(ID id);
    boolean existsById(ID id);
    Iterable<T> findAll();
    Iterable<T> findAllById(Iterable<ID> ids);
    long count();
    void deleteById(ID id);
    void delete(T entity);
    void deleteAllById(Iterable<? extends ID> ids);
    void deleteAll(Iterable<? extends T> entities);
    void deleteAll();
}

 

 

 

3. 객체 주입은 별도의 구현체 클래스 없이 @Autowired 사용

  • 스프링 부트에서 제공하는 어노테이션으로, 스프링 부트가 만들어 놓은 객체를 가져와 주입해주어 구현체를 만들 필요 없음
  • 이를 의존성 주입(DI, Dependency Injection) 이라고 부름

 

4. 출력해보면 DTO, 엔티티, DB 저장 데이터 모두 잘 나오는 모습

 

 

🚀 1분 퀴즈

  1. 다음 빈칸에 들어갈 용어를 쓰세요
    • ( @Entity )(이)란 JPA에서 제공하는 어노테이션으로, 이를 부여받은 클래스를 기반으로 DB 속 테이블이 생성됩니다.
    • ( CrudRepository<T, ID> )(이)란 JPA에서 제공하는 인터페이스로, 이를 상속해 엔티티를 관리(생성, 조회, 수정, 삭제)할 수 있습니다. 해당 인터페이스는 2개의 제네릭 요소를 받습니다. 하나는 관리할 대상 엔티티의 클래스 타입이고, 또 다른 하나는 그 엔티티의 대푯값 타입입니다.
    • ( @Autowired )은/는 스프링 부트에서 제공하는 어노테이션으로, 이를 컨트롤러의 필드에 부여할 수 있습니다. 해당 어노테이션은 스프링 부트가 만들어 놓은 객체를 가져와 주입해 줍니다.

 

3.4 DB 데이터 조회하기

  • DB의 테이블(table)은 행(row)와 열(column)으로 구성
  • 현재 DB에 저장된 Article 테이블은 id, title, content라는 3개의 열로 구성
  • DB의 데이터는 CRUD(생성, 조회, 수정, 삭제)를 기본 조작으로 하는데, SQL 에서 INSERT, SELECT, UPDATE, DELETE 문이 사용 됨

3.4.1 H2 DB 접속하기

1. application.properties 수정

server.servlet.encoding.force=true
spring.h2.console.enabled=true
  • H2 DB에 웹 콘솔로 접근 허용

2. 서버 재실행 후 http://localhost:8080/h2-console 접속

  • JDBC URL에 적힌 값이 DB 접근 주소인데 서버를 실행 할 때마다 바뀜

3. 인텔리제이 Run 탭에서 주소 찾기

  • JDBC 검색하면 h2주소가 나옴
  • 메모리 주소는 컴퓨터마다 다르고 서버 재시작 할 때마다 바뀜

4. Connect

 

3.4.2 데이터 조회하기

  • 왼쪽 탭에서 ARTICLE 누르면 SELECT * FROM ARTICLE 문 나오고 Run 누르면 조회 성공

 

🚀 1분 퀴즈

  1. 다음 빈칸에 들어갈 용어를 쓰세요
    • ( 테이블 ) : DB에서 데이터를 저장하는 틀
    • ( 레코드 ) : 테이블의 행(row)을 표현하는 또 다른 말
    • ( CRUD ) : 데이터의 생성/조회/수정/삭제를 뜻하는 말
    • ( INSERT ) : 테이블에 데이터를 생성하는 SQL 문
    • ( SELECT ) : 테이블에 데이터를 조회하는 SQL 문

 

✅ 셀프 체크

  • 회원 가입 페이지 만들기

 

🏓 더 알아 볼 내용

1. @PostMapping

  • HTTP POST 요청을 처리하기 위한 애노테이션으로 @RequestMapping(method = RequestMethod.*POST) 과 정확히 같은 역할을 수행한다.
  • HTTP POST 요청은 GET 요청과 다르게 Body(본문)을 가질 수 있다.
    • Body에 데이터를 넣어 보내겠다는 의미
    • Body의 데이터 형식으로는 JSON 혹은 FORM 등을 사용

2. DTO와 Entity 분리의 장점

  1. 유연하지 못한 응답
    • 만약 엔티티의 특정 필드를 보이면 안된다면, 필드와 연관 된 메서드를 모두 수정해야함
    • 즉 응답과 모델 간의 분리가 힘들어짐
  2. 상호 참조 이슈
    • SpringDataJPA를 사용하고 DB 테이블 간 상호 외래키 참조를 하고 있는 상황에서 모델을 View로 내 보내면 상호 참조 시 에러가 발생해 올바르지 못한 응답을 출력
  3. 보안 이슈
    • Password 같은 정보를 저장하는 User 테이블이 있다면, Password 값이 응답으로 내 보내면 안됨
    • 이런 정보들을 숨기기 위해서 응답용 DTO 클래스를 따로 사용하기도 함
  4. Entity에 없는 값이 필요한 경우
    • DB에 저장하지 않고 비즈니스 로직에서 얻어서 내 보내야 하는 값이 있을 때
    • Entity를 응답으로 사용한다면 Entity 클래스에 지나치게 많은 Getter가 생성될 수도 있고, 불필요한 로직이 존재할 수도 있게 됨
728x90