백엔드/코딩 자율학습 스프링 부트 3 자바 백엔드 개발 입문 1~2장

3장. 게시판 만들고 새 그 작성하기: Create

dlng23 2024. 12. 21. 13:24

3.1 폼 데이터란

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

 

<form> 태그는 웹 브라우저에서 서버로 데이터를 전송할 때 사용

어디로(where), 어떻게(how) 보낼지를 적어서 보냄

 

<form> 태그에 실어서 보낸 데이터는 서버의 컨트롤러가 DTO(Data Transfer Object) 객체에 담아 받음

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

 

 

3.2 폼 데이터를 DTO로 받기

입력 폼 만들기

1. src > main > resources > templates 디렉터리에서 articles 디렉터리 생성 후 new.mustache 뷰 페이지 생성

 

2. 코드 작성

{{>layouts/header}}

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

{{>layouts/footer}}

 

 

컨트롤러 만들기

1. src > main > java > com.example.firstproject > controller 패키지에서 ArticleController 클래스 생성

 

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"; //뷰 페이지를 반환값으로
    }
}

 

3. localhost:8080/articles/new 페이지에 접속

 

4. CSS 코드로 디자인 적용 (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}}

 

5. 빌드 후 localhost:8080/articles/new 접속

 

폼 데이터 전송하기

localhost:8080/articles/new 페이지에서 제목, 내용을 입력하고 [Submit] 버튼을 클릭하면 입력한 텍스트만 사라질 뿐 아무 일도 일어나지 않음

→ <form> 태그를 입력할 때 필요한 2가지 정보(where, how)를 주지 않았기 때문

 

<form> 태그에 2가지 정보 추가

어디에 보낼 지는 action 속성으로, 어떻게 보낼지는 method 속성으로 설정

  • action: URL 연결 주소를 적어 action="/articles/create"로 설정
    localhost:8080/articles/create 페이지로 폼 데이터를 보낸다는 의미
  • method: 속성 값으로 get과 post, 2가지를 설정
    여기서는 method="post" 방식으로 설정
<form action="container" action="/articles/create" method="post">
(중략)
</form>

 

폼 데이터 받기

서버의 컨트롤러가 action, method 속성 정보를 조합하여 사용자가 전송한 폼 데이터를 받도록 함

 

ArticleController에 createArticle() 메서드 추가

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") //뷰 페이지에서 post 방식으로 전송했으므로 받을 때도 @PostMapping()으로 받음
    public String createArticle(){ //createArticle 메서드 생성
        return ""; //형식을 맞추기 위해 return 값에 빈 문자
    }
}

@PostMapping() 에서 괄호 안에는 받는 URL 주소를 넣음

new.mustache에서 <form> 태그에 action="/articles/create"로 설정했으므로 괄호 안에도 동일하게 설정

 

DTO 만들기

1. 새 패키지(com.example.firstproject.dto) 생성

2. dto 패키지에서 ArticleForm.java 클래스(DTO) 생성

 

3. 입력 폼에서 제목과 내용을 전송하므로 필드 2개 title, content 선언

4. 생성자 추가

5. 폼 데이터 잘 받았는지 확인위해 toString() 메서드 추가

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() { //데이터를 잘 받았는지 확인할 toString() 메서드 추가
        return "ArticleForm{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

 

폼 데이터를 DTO에 담기

ArticleController 코드 수정

package com.example.firstproject.controller;

import com.example.firstproject.dto.ArticleForm; //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){ //폼 데이터를 DTO로 받기
        System.out.println(form.toString()); //DTO에 폼 데이터가 잘 담겼는지 확인
        return "";
    }
}

 

입력 폼과 DTO 필드 연결하기

new.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"> <!-- DTO의 title 필드와 연결 -->
    </div>
    <div class="mb-3">
        <label class="form-label">내용</label>
        <textarea class="form-control" rows="3" name="content"></textarea> <!-- DTO의 content 필드와 연결 -->
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

{{>layouts/footer}}

 

 

서버 재시작

제목은 abcd, 내용은 1234 입력한 후 [Submit] 버튼 클릭

[Run] 탭에서 title='abcd', content='1234' 출력된 것을 확인할 수 있음 

 

  1. 뷰 페이지 만들고 <form> 태그의 action 속성으로 데이터를 어디에 보낼지, method 속성으로 데이터를 어떻게 보낼 지 정의
  2. 컨트롤러를 만들고 PostMapping 방식으로 URL 주소를 연결
  3. 전송받은 데이터를 담아 둘 객체 DTO 만들기
  4. 컨트롤러에서 폼 데이터를 전송받아 DTO에 담기

 

 

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

데이터베이스와 JPA

데이터베이스는 데이터를 관리하는 창고로, 줄여서 DB라고 함

DB의 모든 데이터는 행과 열로 구성된 테이블에 저장해 관리

 

JPA(Java Persistence API): 자바 언어로 DB에 명령을 내리는 도구

 

JPA의 핵심 도구

  • 엔티티: 자바 객체를 DB가 이해할 수 있게 만든 것으로, 이를 기반으로 테이블이 만들어짐
  • 리파지터리: 엔티티가 DB 속 테이블에 저장 및 관리될 수 있게 하는 인터페이스

 

DTO를 엔티티로 변환하기

Article article = form.toEntity(); 입력을 통해 DTO를 엔티티로 변환

빨간색 오류 표시 → 엔티티 클래스인 Article 클래스를 만들어야 함

 

Article 클래스 만들기

1. Create class 'Article'을 클릭한 후 controller가 아닌 entity로 수정

 

2. 기본 패키지(com.example.firstproject)아래에 entity 패키지가 만들어짐, 내부에 Article 클래스가 만들어진 것을 확인

 

3. 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 //title 필드 선언, DB 테이블의 title 열과 연결됨
    private String title;
    @Column //content 필드 선언, DB 테이블의 content 열과 연결됨
    private String content;
}
  1. 클래스가 엔티티임을 선언하기 위해 @Entity 어노테이션 붙임
    (어노테이션이 붙은 클래스를 기반으로 DB에 테이블 생성됨, 테이블 이름은 클래스 이름과 동일하게 생성됨)
  2. title, content 필드를 선언, 두 필드도 DB에서 인식할 수 있게 @Column 어노테이션 붙임
  3. 엔티티의 대푯값 넣음, 대푯값을 id로 선언, @Id 어노테이션 붙임,
    @GeneratedValue 어노테이션 붙여 대푯값을 자동으로 생성하게 함

 

4.  Article 생성자 추가, toString() 메서드 추가

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

 

5. ArticleController에서 Article 오류 표시가 사라졌음을 확인 가능

 

toEntity() 메서드 추가하기

 toEntity() 메서드는 DTO인 form 객체를 엔티티 객체로 변환하는 역할을 함

 

1. Create method 'toEntity' in 'ArticleForm' 클릭

 

2. toEntity 메서드에서 DTO 객체를 엔티티로 반환, 전달값은 Article 클래스의 생성자 형식에 맞춰 작성

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

ArticleController에서 toEntity() 메서드 오류 표시가 사라진 것을 확인할 수 있음

 

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

일단 ArticleController에 리파지터리(articleRepository)가 있다는 가정하에 코드 작성

 

1. articleRepository.save() 메서드 호출하여 article 엔티티 저장

2. 필드 선언부에 ArticleRepository 타입의 articleRepository 객체 선언

package com.example.firstproject.controller;

import com.example.firstproject.dto.ArticleForm;
import com.example.firstproject.entity.Article;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class ArticleController {
    private ArticleRepository 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); // ① article 엔티티를 저장해 saved 객체에 반환
        return "";
    }
}

 

리파지터리 만들기

1. com.example.firstproject.repository 패키지 생성

 

2. repository 패키지에 New → Java Class 선택한 후  Interface 클릭하여 ArticleRepository 생성

 

3. CrudRepository 인터페이스 상속받음

CrudRepository 를 상속하여 엔티티를 관리(생성, 조회, 수정, 삭제) 할 수 있음

 

4. CrudRepository에 홑화살괄호(<>)를 붙이고 2개의 제네릭 요소를 받음

  • Article: 관리 대상 엔티티의 클래스 타입, 여기서는 Article.
  • Long: 관리 대상 엔티티의 대푯값 타입, id의 타입이 Long이므로 Long 입력
package com.example.firstproject.repository;

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

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

DB에 데이터를 생성하고, 읽고, 수정하고, 삭제하는 기본 동작을 추가 코드로 구현할 필요 없이 CrudRepository에서 상속받아 사용할 수 있음

 

ArticleController에서 ArticleRepository를 자동으로 임포트하고 필드 선언문과 save() 메서드의 빨간색 오류 표시가 사라졌음을 확인할 수 있음

 

객체 주입하기

ArticleRepository 인터페이스의 구현 객체를 new 키워드로 만든 적이 없는데도 articleRepository 객체를 사용하였음

@Autowired 어노테이션을 붙이면 스프링 부트가 알아서 미리 생성해 놓은 객체를 가져다가 연결해줌

의존성 주입(DI, Dependency Injection)

 

데이터 저장 확인하기

article 엔티티가 DB에 테이블로 저장됐는지 확인

1. DTO가 엔티티로 잘 변환되는지 article.toString() 메서드 호출

2. article이 DB에 잘 저장되는지 saved.toString() 메서드 호출

@PostMapping("/articles/create")
public String createArticle(ArticleForm form){
    System.out.println(form.toString());
    //1. DTO를 엔티티로 변환
    Article article =form.toEntity();
    System.out.println(article.toString()); //DTO가 엔티티로 잘 변환되는지 확인 출력
    //2. 리파지터리로 엔티티를 DB에 저장
    Article saved = articleRepository.save(article);
    System.out.println(saved.toString()); //article이 DB에 잘 저장되는지 확인 출력
    return "";
}

 

 서버 재시작 후 localhost:8080/articles/new 페이지에 접속

위와 같이 입력한 후, [Submit] 버튼 클릭

 

[Run] 탭에서 위와 같은 결과 확인 가능

ArticleForm{title='가가가가', content='1111'} // 폼 데이터를 받는 객체인 DTO에 title='가가가가', content='1111'이 저장됨
Article{id=null, title='가가가가', content='1111'} // DTO가 엔티티로 변한돼 id=null, title='가가가가', content='1111'이 저장됨
Article{id=1, title='가가가가', content='1111'} //리파지터리가 엔티티를 DB에 저장해 saved 라는 엔티티 변수에 반환함

 

 

3.4 DB 데이터 조회하기

H2 DB 접속하기

1. src > main > resources 에서 application.properties 파일 열기

 

2. 파일에 아래와 같이 작성

spring.h2.console.enable=true

 

3. 바뀐 설정값 반영하기 위해 서버 재시작 후 localhost:8080/h2-console 페이지에 접속

[Connect] 버튼 클릭해도 접속이 안됨

JDBC URL 에 적힌 값이 DB 접근 주소 → 서버 실행할 때마다 바뀌므로 해당 값을 찾아 다시 입력해주어야 함

 

4. 인텔리제이의 [Run] 탭에서 Ctrl + F 로 jdbc 검색

jdbc:h2:men: …  : JDBC H2 DB가 메모리에서 동작하는데 그 주소가 … 라는 의미

메모리 주소는 컴퓨터마다 다르고 서버를 재시작할 때마다 바뀜

해당 문구를 복사한 후 JDBC URL에 붙여 넣고 [Connect] 버튼 클릭하여 DB에 접속

 

데이터 조회하기

1. ARTICLE 테이블 클릭하면 SELECT * FROM ARTICLE 이 적힌 SQL문 나옴

 

2. [Run] 클릭 → H2 DB를 설정한 후 서버를 재시작해서 데이터가 날라가 아무런 데이터가 없다고 나옴

데이터는 테이블에 행 단위로 저장되는데 이러한 행 하나하나를 레코드(record) 라고 함

 

3. 웹 브라우저에서 새 탭을 추가하고 localhost:8080/articles/new 페이지로 이동

제목과 내용 입력한 후 [Submit] 버튼 클릭한 후 DB로 가서 [Run] 클릭하면 입력한대로 레코드가 존재함을 확인할 수 있음

 

4. SQL문으로 직접 레코드 삽입하기

INSERT 문 사용

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