쌓고 쌓다

[스프링 부트] 게시글 작성 및 전체 출력 - 1 본문

프로그래밍/spring

[스프링 부트] 게시글 작성 및 전체 출력 - 1

승민아 2023. 6. 23. 17:31

디렉토리 구조

 

 

일반적인 웹 애플리케이션 구조 (출처 인프런: 김영한)

코드를 작성해보면 컨트롤러, 서비스, 리포지토리의 차이가 긴가민가하다.

  • 컨트롤러: 웹 MVC의 컨트롤러 역할. 서비스의 제어를 담당하는 느낌 같다. (서비스를 이용해)
  • 서비스: 핵심 비즈니스 로직 구현. 게시글을 작성하고 찾는 기능을 제공. (리포지토리를 이용해)
  • 리포지토리: DB에 접근하여 도메인 객체를 DB에 저장하고 관리한다.

아직 차이가 긴가민가하다... 많이 경험해봐야할듯!

 

 

HomeController

@Controller
public class HomeController {

    @GetMapping("/")
    public String home() {
        return "home";
    }
}

 

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>home 페이지입니당~!</h1>
<a href="/poster/write">게시글 작성</a>
<a href="/posters">게시글 보기</a>
</body>
</html>

/poster/write : 게시글 작성 페이지

/posters : 게시글 조회 페이지

 

Poster

public class Poster {
    private Long id;
    private String title;
    private String writer;
    private String content;
    // 추후에 작성일도 해보자.

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    ... Getter, Setter들
}

게시글 ID, 제목, 작성자, 내용으로 구성.

추후에 작성일 추가 계획.

 

PosterRepository (Interface)

public interface PosterRepository {

    Poster save(Poster poster);
    Optional<Poster> findById(Long id);
    Optional<Poster> findByTitle(String title);
    List<Poster> findAll();
}

인터페이스로 만들어 놓으면

DB를 연결할때 간단히 가능하다는데

나중에 DB 연결할때 한번 그 장점을 느껴보자...

 

MemoryPosterRepository (구현체)

package com.example.board.repository;

import com.example.board.domain.Poster;
import org.springframework.stereotype.Repository;

import java.util.*;


@Repository
public class MemoryPosterRepository implements PosterRepository{

    private Map<Long, Poster> store = new HashMap<>();
    private Long sequence = 0L;

    @Override
    public Poster save(Poster poster) {
        poster.setId(++sequence);
        store.put(poster.getId(), poster);
        return poster;
    }

    @Override
    public Optional<Poster> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Poster> findByTitle(String title) {
        return store.values().stream()
                .filter(member -> member.getTitle().equals(title))
                .findAny(); // 추후에 중복 제목 불가도 해보고 중복 제목 검색 작업 해보자.
    }

    @Override
    public List<Poster> findAll() {
        return new ArrayList<>(store.values());
    }
}

Null이 반환될 수 있는 부분에 Optional로 감싸는걸로 배웠었고 앞전의 학습 코드들을 참고하여 작성했다.

findByTitle에서 title 가지고 게시글을 찾는 과정이 있는데

이 부분도 추후에 여러 게시글을 조회 가능하게 수정할 필요가 있다.

 

PosterController

@Controller
public class PosterController {

    private final PosterService posterService;

    @Autowired
    public PosterController(PosterService posterService) {
        this.posterService = posterService;
    }


    @GetMapping("/poster/write")
    public String writeForm() {
        return "posters/createPosterForm";
    }

    @PostMapping("/poster/write")
    public String write(Poster poster) {
        posterService.write(poster);
        return "redirect:/";
    }

    @GetMapping("/posters")
    public String list(Model model) {
        List<Poster> posters = posterService.findPosters();
        model.addAttribute("posters", posters);
        return "posters/posterList";
    }

}

(1) 게시글 작성 (/poster/write)

GET 요청으로 /poster/write 들어오면

posters/createPosterForm.html을 반환한다.

이 html에서 게시글 내용에 대한 입력을 받고 

<form action="/poster/write" method="post">
    <label>제목</label>
    <input type="text" name="title"><br>
    ...
    <button type="submit">제출</button>
</form>

제출시 /poster/write로 Poster 클래스에 담아 POST 요청을 보낸다. 

input 태그에 작성되어있는 name="title"처럼 name 값에 매핑되어

POST요청을 처리하는 메소드에 있는 매개변수에 담겨진다.

 

(2) 게시글 조회 (/posters)

게시글들에 대한 출력을 posters에 있는 posterList.html을 통해 출력을 한다.

Controller의 메서드는 Model이라는 타입의 객체를 파라미터로 받을 수 있다.

model.addAttribute로 (키, 값)으로 키에 값을 매핑하여 postList.html. 뷰에 데이터를 넘길 수 있다.

템플릿 엔진 타임리프로 받은 데이터를 출력할 예정.

 

PosterService

@Service
public class PosterService {
    private PosterRepository posterRepository;

    @Autowired
    public PosterService(PosterRepository posterRepository) {
        this.posterRepository = posterRepository;
    }

    public Long write(Poster poster) {
        posterRepository.save(poster);
        return poster.getId();
    }

    public List<Poster> findPosters() {
        return posterRepository.findAll();
    }

    public Optional<Poster> findByOne(Long posterId) {
        return posterRepository.findById(posterId);
    }

}

 

createPosterForm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/poster/write" method="post">
    <label>제목</label>
    <input type="text" name="title"><br>
    <label>작성자</label>
    <input type="text" name="writer"><br>
    <label>내용</label>
    <textarea type="text" name="content"></textarea><br>
    <button type="submit">제출</button>
</form>
</body>
</html>

 

posterList.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table>
    <th>ID</th>
    <th>title</th>
    <th>writer</th>
    <th>content</th>
    <tr th:each="poster : ${posters}">
        <td th:text="${poster.id}"></td>
        <td th:text="${poster.title}"></td>
        <td th:text="${poster.writer}"></td>
        <td th:text="${poster.content}"></td>
    </tr>
</table>
</body>
</html>

"http://www.thymeleaf.org"에 www를 빼면 타임리프 문법에 빨간줄이 뜨는걸 없앨수 있다.

 

 

결과

 

다음 목표: 게시글 상세 조회, 게시글 작성일, DB 연결

Comments