반응형

기능

  1. 글 작성
  2. 전체 글 보기
  3. 글 보기
  4. 글 수정
  5. 글 삭제
  • 글 수정 및 삭제 시에는 비밀번호가 일치해야 함

프로젝트 구조 및 설명

  • domain : 비지니스 도메인 객체
    • content 객체 생성
  • repository : DB와 연결되는 부분
    • DB를 따로 연결하지 않았기 때문에 Map을 사용해 데이터 저장
    • 프로젝트 재실행 시 데이터 초기화
  • service : 핵심 비지니스 로직
  • controller : 웹 MVC에서의 컨트롤러 역할
    • url mapping, view return, servcie와 연결

구현 코드

사용 라이브러리

  • spring-boot-starter-web
  • spring-boot-starter-thymeleaf
  • spring-boot-devtools
  • lombok
  • spring-boot-starter-test

domain > Content

  • Content 객체
  • Content(게시글)에는 각각 id, title, writer, updateDate, texts, password가 필요
@Data
public class Content {

    private int id;
    private String title;
    private String texts;

    private String writer;
    private String password;

    private String updateDate;

}

repository > ContentRepository

  • Repository는 보통 DB와 연결되는 부분인데 이 프로젝트에서는 DB를 사용하지 않음
  • contents라는 Map을 생성 후 사용
  • contents에 저장, 수정, 삭제, 조회하는 기능 담당 (CURD)
@Repository
public class ContentRepository {

    private static Map<Integer, Content> contents = new HashMap<>();
    private static int sequence = 0;

    public void save(Content content) {
        content.setId(++sequence);
        contents.put(content.getId(), content);
    }

    public void edit(int id, Content content) {
        contents.put(id, content);
    }

    public void delete(int id) {
        contents.remove(id);
    }

    public List<Content> findAll() {
        return new ArrayList<>(contents.values());
    }

    public Content findById(int id) {
        return contents.get(id);
    }
}

service > ContentService

  • 핵심 비지니스 로직
  • Controller, Repository와 연결되어 데이터 송/수신
@Service
@RequiredArgsConstructor
public class ContentService {

    private final ContentRepository contentRepository;

    /**
     * 글 작성
     */
    public void writeContent(Content content) {
        contentRepository.save(content);
    }

    /**
     * 글 수정
     */
    public void editContent(int id, String texts, String password) {
        Content content = contentRepository.findById(id);
        if(!content.getPassword().equals(password)) {
            return;
        }

        content.setTexts(texts);

        LocalDateTime now = LocalDateTime.now();
        String formattedDate = now.format( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") );
        content.setUpdateDate(formattedDate);

        contentRepository.edit(id, content);
    }

    /**
     * 글 삭제
     */
    public void deleteContent(int id, String password) {
        Content content = contentRepository.findById(id);
        if(!content.getPassword().equals(password)) {
            return;
        }
        contentRepository.delete(id);
    }

    /**
     * 전체 글 조회
     */
    public List<Content> getAllContents() {
        return contentRepository.findAll();
    }

    /**
     * Id로 글 조회
     */
    public Content getContent(int id) {
        return contentRepository.findById(id);
    }
}

controller > ContentController

  • home 화면을 포함한 모든 화면들을 mapping 하는 controller
  • View, Service와 연결되어 데이터 송/수신
@Controller
@RequiredArgsConstructor
public class ContentController {

    private final ContentService contentService;

    // 홈 화면
    @GetMapping(value = {"", "/"})
    public String home(Model model) {
        model.addAttribute("contents", contentService.getAllContents());
        return "home";
    }

    // 글 쓰기 화면
    @GetMapping("/content/write")
    public String writePage() {
        return "write-page";
    }

    // 글 쓰기
    @PostMapping("/content/write")
    public String writeContent(Content content) {
        LocalDateTime now = LocalDateTime.now();
        String formattedDate = now.format( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") );
        content.setUpdateDate( formattedDate );

        contentService.writeContent(content);
        return "redirect:/";
    }

    // 글 보기 화면
    @GetMapping("/content/{id}")
    public String showContent(@PathVariable int id, Model model) {
        model.addAttribute("content", contentService.getContent(id));
        return "content-page";
    }

    // 글 수정
    @PostMapping("/content/{id}")
    public String editContent(@PathVariable int id, Content content) {
        contentService.editContent(id, content.getTexts(), content.getPassword());
        return "redirect:/";
    }

    // 글 삭제
    @PostMapping("/content/delete/{id}")
    public String deleteContent(@PathVariable int id, Content content) {
        contentService.deleteContent(id, content.getPassword());
        return "redirect:/";
    }
}

css > style.css

nav {
    text-align: center;
    font-size: x-large;
    font-weight: bold;
    background-color: orange;
    padding-top: 25px;
    height: 50px;
}

button {
    height: 50px;
    width: 100px;
    font-size: large;
}

.form-group {
    padding: 20px;
    margin-bottom: 30px;
    border-width: medium;
    border-color: black;
    border-style: solid;
    width: 800px;
}

textarea {
    width: 800px;
    height: 300px;
}

html > home.html

  • 홈 화면이자 글 전체 보기 화면
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/style.css">
    <title>게시판</title>
    <nav>홈 화면</nav>
</head>
<body>
  <div>
    <br/>
      <div align="right" style="margin-right: 100px">
        <button onclick="location.href='./content/write'">
          글 작성
        </button>
      </div>
  </div>
  <br/>
  <div>
    <table border="1" width="1100" align="center">
      <thead>
        <tr>
          <th width="50">#</th>
          <th width="550">제목</th>
          <th width="200">작성자</th>
          <th width="200">최근 수정일</th>
          <th width="200">글 보기/수정/삭제</th>
        </tr>
      </thead>
      <tbody align="center">
        <tr th:each="content: ${contents}">
          <td th:text="${content.id}"></td>
          <td th:text="${content.title}"></td>
          <td th:text="${content.writer}"></td>
          <td th:text="${content.updateDate}"></td>
          <td> <button th:onclick="|location.href='@{/content/}'+${content.id}|" style="height:40px;">보기</button> </td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
</html>

html > write-page

  • 글 쓰기 화면
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/style.css">
    <title>게시판</title>
    <nav>글 쓰기 화면</nav>
</head>
<body>
<div>
    <br/>
    <div align="center">
        <form action="/content/write" method="post">
            <div class="form-group" align="left">
                <label for="title">글 제목 : </label>
                <input type="text" id="title" name="title" placeholder="제목을 입력하세요" width="400px">
                <br/><br/>
                <label for="writer">작성자 : </label>
                <input type="text" id="writer" name="writer" placeholder="작성자를 입력하세요" width="200px">
                <br/><br/>
                <label for="password">비밀번호 : </label>
                <input type="password" id="password" name="password" width="200px">
                <br/><br/>
                <label for="texts">글 내용 : </label>
                <br/><br/>
                <textarea id="texts" name="texts"></textarea>
            </div>
            <button type="submit">제출</button>
        </form>
    </div>
</div>
</body>
</html>

html > content-page

  • 글 보기 화면
  • 글 내용 수정 기능
  • 글 삭제 기능
  • 수정, 삭제 시 비밀번호 확인 창에 입력한 비밀번호가 일치해야 작동
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="/style.css">
  <title>게시판</title>
  <nav>글 보기 화면</nav>
</head>
<body>
<div>
  <br/>
  <div align="center">
    <form th:action="@{/content/{id} (id=${content.id})}" th:method="post">
      <div class="form-group" align="left">
        <div th:text="'글 제목 : ' + ${content.title}"></div>
        <br/>
        <div th:text="'작성자 : ' + ${content.writer}"></div>
        <br/>
        <label for="password">비밀번호 확인 : </label>
        <input type="password" id="password" name="password" width="200px">
        <br/><br/>
        <label for="texts">글 내용 : </label>
        <br/><br/>
        <textarea id="texts" name="texts">[[${content.texts}]]</textarea>
        <br/>
      </div>
      <button type="submit" style="margin-right: 50px">수정</button>
      <button th:formaction="@{/content/delete/{id} (id=${content.id})}"
            	type="submit">삭제</button>
    </form>
  </div>
</div>
</body>
</html>

결과

  • 홈 화면

  • 글 쓰기 화면

  • 글 작성 시 홈 화면에 글이 추가된 것을 확인할 수 있음

  • 글 보기 화면
  • 비밀번호가 일치하면 수정, 삭제 기능 수행

반응형

↓ 클릭시 이동

복사했습니다!