kyuhyuk.kr/category/spring-boot.html
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping
public String HelloWorld(){
return "Hello, world!";
}
}
@RestController : @Controller + @ResponseBody
@GetMapping : HTTP GET 요청을 특정 Method에 맵핑한다.
+ 포트 삭제하기
1. cmd에서 netstat -ano 입력하여 해당 포트 검색, PID확인.
2. 작업 관리자에서 해당 PID를 찾아 작업 끝내기 버튼 누르기
실습
localhost:8080/greeting에 접속했을 때 JSON 포맷으로 {"id":1, "content":"Hello World!"}를 출력하기
1. 모델링
Greeting.java를 생성해서 필드와 Getter가 있는 클래스를 만들자.
> 필드 생성은 어떻게 하나요?
private final로 선언하면 직접 값을 참조할 수 없으나 생성자를 통해 값을 참조할 수 있다.
메소드 별로 호출 시 새로운 값이 할당되고, 초기화는 옵션이다.
반면, private static final은 생성자를 통해 값을 참조할 수 없고 무조건 초기화되어있는 상태이다.
메모리에 한번 올라가면 같은 값을 클래스 내부의 전체 필드, 메서드에 공유하는 것이다.
public class StaticFinalExample(){
private final int var;
public StaticFinalExample(int var) {
this.var = var;
}
}
이 코드는 생성자를 사용할 수 있는 private final로 변수를 선언했으므로 오류가 발생하지 않으나,
public class StaticFinalExample(){
private static final int var;
public StaticFinalExample(int var) {
this.var = var;
}
}
다음 코드는 오류가 발생한다.
package com.example.demo;
public class Greeting {
private final Long id;
private final String content;
public Greeting(long id, String content){
this.id = id;
this.content = content;
}
public String getContent() {
return content;
}
public Long getId() {
return id;
}
}
2. Controller 만들기
RESTful 웹 서비스는 SpringBoot에서 HTTP요청은 컨트롤러에 의해 처리된다. (@RestController 어노테이션으로 식별)
-> Greeting Controller는 Greeting에 대한 새 인스턴스를 반환하여 /greeting에 대한 GET요청을 처리하도록 한다.
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
@RestController
public class GreetingController {
private static final String template = "Hello, %s";
private static AtomicLong counter = new AtomicLong();
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name){
return new Greeting (counter.incrementAndGet(), String.format(template, name));
}
}
greeting()메소드는 id와 content를 가진 Greeting객체를 만들고 반환한다.
- @RequestParam : 파라미터인 name을 greeting()메소드의 name 파라미터 변수에 바인딩한다. 파라미터가 정해져있지 않으면 default 값인 World가 name 변수에 들어가게 된다.
- @RestController 어노테이션 : ViewPage 이름을 반환해주어 사용자가 View Page를 추가해주는 Controller의 기능에 @Responsebody의 기능을 추가하여 반환되는 값을 HTTP ResponseBody에 직접 쓰여 추가하게 된다.
- AtomicLong은 Long 자료형을 가지고 있는 Wrapping클래스이다.
* Wrapper클래스란? : 기본 타입의 데이터를 객체로 포장해주는 클래스이다. 기본 타입의 데이터가 래퍼 클래스의 인스턴스(즉, 객체)로 변환되는 것이다.
- incrementAndGet() : 현재 값을 리턴하고 +1을 증가시킨다.
- format(string, object) : 문자열에 하나 이상의 형식 항목을 지정된 개체의 문자열 표현으로 바꿉니다.
>String format 문법
private static final String template = "Hello, %s"
System.out.println(String.format(template, "World")); // Hello, World
다음과 같이 conversion(%)이 무조건 존재하여야 한다. 그리고 format 메소드를 이용해서 문자열을 다르게 출력할 수 있다.
즉 이 코드는 /greeting URL로 들어갔을 때 새로운 Greeting의 인스턴스를 생성하는데, 이때 파라미터는 +1 증가한 counter와 Hello의 %s부분이 name변수로 바뀐 형태로 들어가게 된다.
화면을 출력할 때마다 id값이 증가하는 것을 볼 수 있다. 이는 GET요청에 대해서 동일한 GreetingController 인스턴스에서 작업중이며 id필드가 증가하고 있음을 알 수 있다.
게시판 구현하기
- 글 작성 & 글 목록
MySQL을 이용해 데이터베이스를 설정한다.
application.properties에 들어가서 내용을 다음과 같이 수정한다.
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/example?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
spring.datasource.username=user
spring.datasource.password=UserPassword
spring.jpa.hibernate.ddl-auto : Entity 참고하여 데이터베이스의 테이블 설정을 자동으로 한다.
- none
- create
- create-drop
- update
- validate
spring.datasource.url : 데이터베이스의 URL을 지정한다.
spring.datasource.username : 데이터베이스에 접근하는 사용자 이름이다.
spring.datasource.password : 데이터베이스에 접근한느 사용자 패스워드이다.
일단 게시판의 목록을 만드는 페이지를 html로 생성해보자.
- 게시물 목록 및 작성 페이지 만들기
scr/main/resources/templates/board에는 게시판 html이 들어갈 예정이고, common에는 board에서 사용되는 파일들을 관리할 것이다.
html 파일들
common 폴더의 header.html
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="navbar navbar-dark bg-dark shadow-sm mb-3">
<div class="container d-flex justify-content-between">
<a href="/" class="navbar-brand d-flex align-items-center">
<strong>게시판</strong>
</a>
</div>
</div>
</body>
</html>
board 폴더의 list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>
</head>
<body>
<header th:insert="common/header.html"></header>
<div class="container">
<table calss="table">
<thead class = "thead-light">
<tr class = "text-center">
<th scope="col">#</th>
<th scope="col">제목</th>
<th scope="col">작성자</th>
<th scope="col">작성일</th>
</tr>
</thead>
<tbody>
<tr class="text-center" th:each="post : ${postList}">
<th scope="row">
<span th:text = "${post.id}"></span>
</th>
<td>
<a th:href="@{'/post/' + ${post.id}}">
<span th:text="${post.title}"></span>
</a>
</td>
<td>
<span th:text="${post.author}"></span>
</td>
<td>
<span th:text="${#temprals.format(post.createdDate, 'yyyy-mm-dd:mm')}"></span>
</td>
</tr>
</tbody>
</table>
<div class = "row">
<div class = "col-auto mr-auto"></div>
<div class="col-auto">
<a class="btn btn-primary" th:href="@{/post}" role="button">글쓰기</a>
</div>
</div>
</div>
<script src = "/webjars/jquery/3.5.1/jquery.min.js"></script>
<script src = "/webjars/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</body>
</html>
board폴더의 post.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시판 - 글쓰기</title>
<link rel='stylesheet' href='/webjars/bootstrap/4.5.0/css/bootstrap.min.css'>
</head>
<body>
<header th:insert="common/header.html"></header>
<div class="container">
<form action="/post" method="post">
<div class="form-group row">
<label for="inputTitle" class="col-sm-2 col-form-label"><strong>제목</strong></label>
<div class="col-sm-10">
<input type="text" name="title" class="form-control" id="inputTitle">
</div>
</div>
<div class="form-group row">
<label for="inputAuthor" class="col-sm-2 col-form-label"><strong>작성자</strong></label>
<div class="col-sm-10">
<input type="text" name="author" class="form-control" id="inputAuthor">
</div>
</div>
<div class="form-group row">
<label for="inputContent" class="col-sm-2 col-form-label"><strong>내용</strong></label>
<div class="col-sm-10">
<textarea type="text" name="content" class="form-control" id="inputContent"></textarea>
</div>
</div>
<div class="row">
<div class="col-auto mr-auto"></div>
<div class="col-auto">
<input class="btn btn-primary" type="submit" role="button" value="글쓰기">
</div>
</div>
</form>
</div>
<script src="/webjars/jquery/3.5.1/jquery.min.js"></script>
<script src="/webjars/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</body>
</html>
MySQL과 연동하여 게시물 작성 구현하기
- Controller 구현
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class BoardController {
@GetMapping("/")
public String list() {
return "board/list.html";
}
@GetMapping("/post")
public String post(){
return "board/post.html";
}
}
먼저 View Page를 구현하기 위해 Controller 어노테이션을 추가해주어 각각의 html 파일을 맵핑해준다.
run해서 데이터베이스와 연결이 되는지 확인하려 했으나 에러가 떴다.
에러 내용 및 원인 유추
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
에러 내용은 이렇다.
데이터 베이스 설정이 안된 것으로 예상된다. 어케 해결 도대체?
일단 Entity를 만들자. Entity는 데이터베이스 테이블과 매핑되는 객체이다.
package com.example.demo.domain.entity;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@NoArgsConstructor(access= AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class) //JPA에게 해당 Entity는 Auditiong기능을 사용함을 알린다.
public class Board {
@Id
@GeneratedValue
private Long id;
@Column(length = 10, nullable = false)
private String author;
@Column(length = 100, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
@Builder
public Board(Long id, String author, String title, String content) {
this.id = id;
this.author = author;
this.title = title;
this.content = content;
}
}
@Column 어노테이션 : 객체 필드와 DB테이블 컬럼을 매핑한다.
@Builder : 해당 클래스에 자동으로 빌더를 추가. 빌더는 객체를 생성하고 값을 넣을 수 있다.(생성자 혹은 Getter setter보다 코드 절약가능)
JPA Auditing기능이란? : 시간 데이터를 자동으로 입력해주는 기술
-> 이를 구현하기 위해 BoardApplication에 @EnableJpaAuditing 어노테이션을 붙인다.
- Repository 구현하기
: 데이터 조작을 담당하고, JpaRepository를 상속받는다.
domain/repository/BoardRepository.java
package com.example.demo.domain.repository;
import com.example.demo.domain.entity.Board;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BoardRepository extends JpaRepository<Board,Long> {
}
JpaRepository의 값은 매핑할 Entity와 Id의 타입이다. (Entity, 즉 생성할 테이블의 이름인 Board와 id의 타입인 Long을 입력한다)
- DTO 구현하기
: DTO란, Data Transfer Object의 줄임말으로서 Controller와 Service사이에서 데이터를 주고 받는 객체이다.
> 잘 이해가 안가니 자세하게 알아보자.
DTO란 Data Transfer Object, 계층 간 데이터 교환을 위한 객체(Java Beans)이다.
- DB에서 데이터를 얻어 Service나 Controller등으로 데이터를 보낼 때 사용한다.
- 데이터가 로직에 쓰이기 위해 넘어올 때 DTO의 모습으로 바뀐다.
- 순수한 데이터 객체이고 Getter/Setter 메서드만 갖는다.
- 생성자에서 값을 할당하고, DB에서 꺼낸 값을 변경하지 않으므로 DTO 클래스에는 Setter가 없다.
DAO란 Data Access Object, 실제로 DB에 접근하는 객체이다.
- DB에 data를 CRUD하고 Service와 DB를 연결한다. SQL을 사용하여 DB에 접근한 후 적절한 CRUD API를 제공한다. extends JpaRepository<User, Long> 을 통해 기본적으로 CRUD method를 구현할 수 있다.
Entity Class란 domain 패키지 안에 위치하며 실제 DB의 테이블과 매칭될 클래스를 뜻한다.
- 이 클래스 안에서 필요한 로직 method를 구현한다.
- Domain Logic만 가지고 있어야 하고 Presentation Logic을 가지고 있어서는 안된다.
* Domain Logic(Business Logic) : 현실세계에서 어떻게 데이터를 만들고 저장할고 바꿀 것인지에 대한 비지니스 규칙(클래스 또는 함수의 이름, 변수 이름, 알고리즘의 전반적인 틀을 문서화한 설계)를 인코드한 소프트 웨어 안 의 프로그램의 일부를 의미한다.
Presentation Logic : 비지니스 로직을 실제 인터페이스 하기 위한 로직이다. 즉 Business Logic 층에 있는 수 많 은 비지니스 로직을 어떻게 사용자에게 보여줄지에 대한 로직이라고 할 수 있다. 다시 말해서 UI(User Interface)를 어떻게 보여줄지에 대한 로직이라고 할 수 있다.
→ 즉 두 로직은 백엔드와 프론트엔드의 추상화된 개념이라고 볼 수 있다.
- Entity에서 구현된 클래스는 주로 Service 계층에서 사용된다.
dto/BoardDTo.java에 다음과 같이 코드를 생성한다.
package com.example.demo.dto;
import com.example.demo.domain.entity.Board;
import lombok.Builder;
import java.time.LocalDateTime;
public class BoardDto {
private Long id;
private String author;
private String title;
private String content;
private LocalDateTime createdDate;
private LocalDateTime modifiedDate;
public Board toEntity(){
Board build = Board.builder()
.id(id)
.author(author)
.title(title)
.content(content)
.build();
return build;
} // DTO에서 필요한 부분을 빌더 패턴을 이용해 Entity로 만드는 역할을 한다.
@Builder
public BoardDto(Long id, String author, String title, String content, LocalDateTime createdDate, LocalDateTime modifiedDate){
this.id = id;
this.author = author;
this.title = title;
this.content = content;
this.createdDate = createdDate;
this.modifiedDate = modifiedDate;
}
}
중요한 건 왜 이렇게 코드를 짰는가를 이해하는 것이다.
먼저 이 코드는 Controller와 Service 간의 유기성을 만들어주는 것을 목적으로 만들었다.
Service에서는 글을 저장할 코드를 만들어낼 것이므로, 데이터베이스에 값을 넣기 위해 데이터베이스에서 꺼내온 DTO에서 필요한 부분들을 빌더 패턴을 이용해 Entity로 만들 것이다.
그 과정이 toEntity()메소드에 구현되어 있으며, Board.java를 보면 클래스 안에 테이블의 각각의 컬럼의 이름과 제약사항들이 객체와 어노테이션으로 적혀있다. build라는 Board형 필드를 생성한 다음 builder()메소드를 통해 변수에 값을 넣고 BoardDto 메소드에서 객체를 생성한다.
- Service 구현하기
service/BoardService.java
package com.example.demo.service;
import com.example.demo.domain.repository.BoardRepository;
import com.example.demo.dto.BoardDto;
import org.springframework.transaction.annotation.Transactional;
public class BoardService {
private BoardRepository boardRepository;
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Transactional
public Long savePost(BoardDto boardDto) {
return boardRepository.save(boardDto.toEntity()).getId();
}
}
목적은 save함수를 만드는 것이다.
@Transactional : savePost 메소드에서 처리한 쿼리문이 완료가 되고 save 처리 도중 에러가 났을 때 boardRepository에서 처리한 쿼리를 자동으로 Rollback 해주기 위해서이다.
* rollback : 데이터베이스에서 업데이트에 오류가 발생했을 때 이전 상태로 되돌리는 것을 의미
Q. 어떻게 글쓰기 버튼을 눌렀을 때 Post형식으로 요청이 올 수 있는 것인가?
해결해보자
<form action="/post" method="post">
다음 코드에 의해서 버튼을 누르면 /post로 post요청이 이루어진다. ㅋㅋ 이게 끝!
BoardDto안의 각각의 필드를 boardDto 변수에 담고, 이 DTO를 빌더 패턴을 이용해 Entity로 만들어 준 뒤 BoardRepository 인터페이스로 선언된 boardRepository 객체에 해당 Entity를 저장하고 Id 즉 PK를 클래스에 반환한다.
Q. 인터페이스로 private 객체를 선언할 수 있나요?
- Controller 수정하기
글쓰기 버튼을 누르면 /post로 Post요청이 오고, 이 요청에 맞게 BoardController에서 받은 데이터를 데이터 베이스에 추가해주면 된다.
package com.example.demo.controller;
import com.example.demo.dto.BoardDto;
import com.example.demo.service.BoardService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class BoardController {
private BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
@GetMapping("/")
public String list() {
return "board/list.html";
}
@GetMapping("/post")
public String post() {
return "board/post.html";
}
@PostMapping("/post")
public String write(BoardDto boardDto) {
boardService.savePost(boardDto);
return "redirect:/";
}
}
수정한 BoardController.java이다.
실습을 진행해보자.
org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
다음 두 개의 에러가 뜬다. 구글링하여 해결해보자. 일단 둘다 hibernate와 관련된 에러이다. hibernate가 뭔가?
- hibernate : Java 프로그래밍 언어를 위한 객체 관계형 매핑 도구. Java 클래스에서 DB 테이블로 매핑하고 Java 데이터 유형에서 SQL 데이터 유형으로 매핑하는 것.
아래 접은 글에서 더 자세히 알아보겠다.
suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/
Hibernate는 JPA라는 명세의 구현체이다. 즉 javax.persistence.EntityManager와 같은 인터페이스를 직접 구현한 라이브러리 이름이다. JPA와 Hibernate의 관계는 자바의 interface와 해당 class와 같은 관계이다.
Hibernate는 JPA라는 인터페이스를 사용하기 위한 수단 중 하나이다. 다른 구현체로 DataNucleus, EclipseLink 등이 있으며 Hibernate가 가장 자주 사용하는 구현체일 뿐이다.
- javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.exception.GenericJDBCException: Unable to open JDBC Connection for DDL execution
- org.hibernate.exception.GenericJDBCException: Unable to open JDBC Connection for DDL execution
- java.sql.SQLException: Access denied for user 'user'@'localhost' (using password: YES)
새로 뜬 에러들(‾◡◝) 눈물이 흐른다
그냥 실습 진행 및 게시판 구현 확인은 다음에 하기로 하고 ㅋ.. 코드부터 공부하기로 했다.
글 조회 게시판 구현하기
templates/board/detail.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="@{'게시판 - ' + ${post.title}}"></title>
<link rel='stylesheet' href='/webjars/bootstrap/4.5.0/css/bootstrap.min.css'>
</head>
<body>
<header th:insert="common/header.html"></header>
<div class="container">
<div class="card">
<div class="card-body">
<h5 class="card-title" th:text="@{${post.title} + ' - ' + ${post.author}}"></h5>
<p class="card-text"><small class="text-muted" th:text="${#temporals.format(post.createdDate, 'yyyy-MM-dd HH:mm')}"></small></p>
<p class="card-text" th:text="${post.content}"></p>
</div>
</div>
<div class="row mt-3">
<div class="col-auto mr-auto"></div>
<div class="col-auto">
<a class="btn btn-info" th:href="@{'/post/edit/' + ${post.id}}" role="button">수정</a>
</div>
<div class="col-auto">
<form id="delete-form" th:action="@{'/post/' + ${post.id}}" method="post">
<input type="hidden" name="_method" value="delete"/>
<button id="delete-btn" type="submit" class="btn btn-danger">삭제</button>
</form>
</div>
</div>
</div>
<script src="/webjars/jquery/3.5.1/jquery.min.js"></script>
<script src="/webjars/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</body>
</html>
BoardService.java를 수정해보자. 목적은 게시글의 id를 받아 화면에 내용(content) 데이터를 출력하는 것이다.
함수 이름 : getPost()
package com.example.demo.service;
import com.example.demo.domain.entity.Board;
import com.example.demo.domain.repository.BoardRepository;
import com.example.demo.dto.BoardDto;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
public class BoardService {
private BoardRepository boardRepository;
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Transactional
public Long savePost(BoardDto boardDto) {
return boardRepository.save(boardDto.toEntity()).getId();
}
@Transactional
public List<BoardDto> getBoardList(){
List<Board> boardList = boardRepository.findAll();
List<BoardDto> boardDtoList = new ArrayList<>();
for(Board board : boardList){
BoardDto boardDto = BoardDto.builder()
.id(board.getId())
.author(board.getAuthor())
.title(board.getTitle())
.content(board.getContent())
.createdDate(board.getCreatedDate())
.build();
boardDtoList.add(boardDto);
}
return boardDtoList;
}
@Transactional
public BoardDto getPost(Long id) {
Board board = boardRepository.findById(id).get();
BoardDto boardDto = BoardDto.builder()
.id(board.getId())
.author(board.getAuthor())
.title(board.getTitle())
.content(board.getContent())
.createdDate(board.getCreatedDate())
.build();
return boardDto;
}
}
BoardDto는 id와 author, title 등등의 필드를 생성한 메소드이다. 자료형은 Board, 테이블을 생성한 Entity Class이다. 그리고 boardDto 인터페이스에 해당 id와 author, title,content, createdDate 정보를 넣는다.
그리고 BoardDto 데이터 타입의 getPost함수를 생성한다. 가져온 id값을 파라미터로 넣고, board변수를 만들어 boardRepository라는 객체(BoardRepository인터페이스로 선언되었다.)에서 id를 찾아 반환된 값을 value로 넣는다. 그리고 이 boardDto 변수를 리턴하면 이 함수는 파라미터 id에 해당하는 id, author, title, content, date 값을 포함하게 된다.
중간 점검
할 수록 모르겠다는 생각이 진해진다. 이렇게 공부하는게 맞나 싶고 모르는 것을 아는 것처럼 넘겨짚고 마는 것 같은 느낌이다. 그리고 눈에 보이는 성과가 없으니 지치고 공부의 의미를 잃는 느낌이다. How do I..? 어떻게 할지에 대한 의문도 해결하지 못한 채 다짜고짜 블로그를 따라해보고 예상 결과와 다른 결과에 오류를 찾으려 삽질 또 삽질.. 이게 맞는 건가 싶다. 음 어쩌지 모르겠다!
Controller 수정하기
목적 : post/{id} get방식으로 url을 입력하였을 때 detail.html 해당 id의 글의 데이터가 View에 전달되도록 한다.
package com.example.demo.controller;
import com.example.demo.dto.BoardDto;
import com.example.demo.service.BoardService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Controller
public class BoardController {
private BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
@GetMapping("/")
public String list(Model model) {
List<BoardDto> boardDtoList = boardService.getBoardList();
model.addAttribute("postList", boardDtoList);
return "board/list.html";
}
@GetMapping("/post")
public String post() {
return "board/post.html";
}
@PostMapping("/post")
public String write(BoardDto boardDto) {
boardService.savePost(boardDto);
return "redirect:/";
}
@GetMapping("/post/{id}")
public String detail(@PathVariable("id") Long id, Model model) {
BoardDto boardDto = boardService.getPost(id);
model.addAttribute("post", boardDto);
return "board/detail.html";
}
}
@PathVariable : URL에 변수를 넣어주는 기능이다.
model.addAttribute("post", boardDto)는 boardDto변수를 post라는 이름으로 사용하겠다는 뜻이다. detail.html에 해당 id의 데이터 값의 총체의 이름이 post이기 때문에 이 코드를 쓰는 것이다.
여기까지가 글 CRUD에 관한 스프링 포스팅이다.
'Framework > Spring' 카테고리의 다른 글
스프링 부트와 JPA 활용1 - 프로젝트 환경 설정 (0) | 2021.05.19 |
---|---|
스프링 관련 의문점 다루기 (0) | 2021.02.06 |
인프런 스프링 입문 강의 #JPA (0) | 2021.02.02 |
인프런 스프링 입문 강의 #백엔드 개발 (0) | 2021.01.30 |
인프런 스프링 입문 강의 #데이터베이스 (0) | 2021.01.29 |