게시판 만들기

김영민·2022년 2월 4일
0

글을 올리고 삭제하는 간단한 게시판 만들기

개발 스펙

  • Language : Java 8

  • Type : Gradle

  • Packaging : Jar

  • Dependencies : Spring Web, Spring Data JPA, Lombok
    - SQL : H2 Database(Test용), MySQL Driver(배포용)

  • Spring Framework
    : Spring 서버를 구현하였기 때문에 Controller, Service, Repository로 구분하여 사용함. 하지만 기능이 단순하여 Service에서의 역할을 Controller에서 대부분 이행시킴


Entity

import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@NoArgsConstructor // 기본 생성자를 만들어 줌
@Getter // Get 함수를 자동으로 생성
@Entity // 아래 나열되어 있는 목록들이 DB에서 테이블 역할을 할 것을 알려줌
public class Bulletin extends Timestamped { // 게시글의 생성시간과 수정시간을 알려줄 Class를 상속받음
    @GeneratedValue(strategy = GenerationType.AUTO) // PK(Primary Key) 값을 자동으로 1씩 증가시켜줌
    @Id // 아래 값이 PK라는 것을 알려줌
    private Long id;

    @Column(nullable = false) // null(데이터 없음) 상태가 불가함
    private String title;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String contents;

    public Bulletin(BulletinRequestDto requestDto) { // Dto에 대한 설명은 아래에서 하겠음
        this.title = requestDto.getTitle();
        this.name = requestDto.getName();
        this.contents = requestDto.getContents();
    }
}

게시글을 작성하게 되면 글의 제목, 작성자명, 작성 내용이 포함되기 때문에 각각을 Entity에 포함시켰으며, 각 Column에 들어갈 Data를 Dto에 담아서 운반하는 방식을 사용함.


Timestamped

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@MappedSuperclass // 멤버변수를 Column이 되게 만들어줌
@EntityListeners(AuditingEntityListener.class) // 수정이 있을 시 자동으로 기록
@Getter
public class Timestamped {

    @CreatedDate // 최초 생성된 시점
    private LocalDateTime createdAt;

    @LastModifiedDate // 마지막에 수정된 시점
    private LocalDateTime modifiedAt;
}

Dto

import lombok.Getter;

@Getter
public class BulletinRequestDto {
    private String title;
    private String name;
    private String contents;
}

Client로부터 요청 받을 때의 경우와 Client에게 응답을 줄 때의 경우를 나눠서 Dto를 생성해야 하지만 현재 프로젝트에서는 요청을 받는 경우의 Dto만 생성함.


Controller


import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequiredArgsConstructor // final(상수)로 선언된 멤버 변수를 자동으로 생성
@RestController // @Controller라고만 하면 해당 클래스가 Controller라는 것을 알려주지만
				// @Restcontroller를 선언하면 Json 형태로 데이터를 주고받음을 선언
public class BulletinController {
    private final BulletinRepository bulletinRepository;    

    // 게시글 작성 api
    @PostMapping("/api/bulletins")
    public Bulletin createBulletin(@RequestBody BulletinRequestDto requestDto) {
        Bulletin bulletin = new Bulletin(requestDto);
        return bulletinRepository.save(bulletin); 
        // controller에서는 Dto에 담긴 Data를 client로부터 받고 실질적인 기능은
        // service로 넘겨줘야 하지만 현재는 모두 controller에서 진행함
    }

	// 게시글 조회 api
    @GetMapping("/api/bulletins")
    public List<Bulletin> getBulletins() {
        return bulletinRepository.findAllByOrderByModifiedAtDesc();
    }

	// 게시글 삭제 api
    @DeleteMapping("/api/bulletins/{id}")
    public Long deleteBulletin(@PathVariable Long id) {
        bulletinRepository.deleteById(id);
        return id;
    }
}

Service

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service // 해당 글래스가 Service임을 선언
public class BulletinService {
    private final BulletinRepository bulletinRepository;
}

controller에서 service 역할까지 같이 하고 있기 때문에 이번 프로젝트에서는 비어있음.
다음 프로젝트부터는 service에 비중을 대폭 늘릴 필요가 있음.


Repository

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface BulletinRepository extends JpaRepository<Bulletin, Long> {
    List<Bulletin> findAllByOrderByModifiedAtDesc();
}

Jpa Repository를 상속해 왔기 때문에 db에 연결하는 것부터 종료하는 작업까지 모두 대신 해줌.
그리고 게시물을 조회할 때 수정한 날짜 순서대로 게시를 해야하기 때문에 query문을 하나 만듦.


Java Script

$(document).ready(function () {    
    showBulletin();
})


// 게시글 작성
function addBulletin() {
    let title = $('#title').val();
    let name = $('#name').val();
    let contents = $('#contents').val();
    let data = {'title':title, 'name':name, 'contents':contents};

    $.ajax({
        type: "POST",
        url: '/api/bulletins',
        contentType: "application/json",
        data: JSON.stringify(data),
        success: function (response) {
            $('#title').val("");
            $('#name').val("");
            $('#contents').val("");

            $('div.nav-write').removeClass('active');
            $('div.nav-find').addClass('active');
            $('#write-area').hide();
            $('#find-area').show();

            showBulletin();
        }
    })
}

// 게시글 조회
function showBulletin() {
    $('#bulletin-boxes').empty();
    $.ajax({
        type: 'GET',
        url: '/api/bulletins',
        success: function (response) {
            for (let i = 0; i < response.length; i++) {
                let bulletin = response[i];
                let id = bulletin['id'];
                let title = bulletin['title'];
                let name = bulletin['name'];
                let modifiedAt = bulletin['modifiedAt'].split("20")[1].split("T")[0];
                let contents = bulletin['contents'];
                addHTML(id, title, name, modifiedAt, contents);
            }
        }
    })
}

function addHTML(id, title, name, modifiedAt, contents) {
    let tempHtml1 = `<button type="button" class="btn btn-light" data-toggle="modal" data-target="#${id}-exampleModalCenter" style="margin-right: 20px">
                        <p style="font-weight: bold; font-size: 20px" id="${id}-title">${title}</p>
                        <p style="font-size: 15px" id="${id}-name">${name}</p>
                        <p style="font-size: 10px;" id="${id}-modifiedAt">${modifiedAt}</p>                        
                    </button>`

    let tempHtml2 = `<div class="modal fade" id="${id}-exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
                                    <div class="modal-dialog modal-dialog-centered" role="document">
                                                <div class="modal-content">
                                                    <div class="modal-header">
                                                        <h5 class="modal-title" id="${id}-exampleModalLongTitle">${title}</h5>
                                                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                                            <span aria-hidden="true">&times;</span>
                                                        </button>
                                                    </div>
                                                    <div class="modal-body">
                                                        <p id="${id}-name">${name}</p>
                                                        <p>${modifiedAt}</p>
                                                        <p id="${id}-contents">${contents}</p>
                                                        <p class="deleteButton" style="color: blue" onclick="deleteBulletin('${id}')">삭제</p>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>`
    $('#bulletin-boxes').append(tempHtml1);
    $('#modal-box').append(tempHtml2);
}

// 게시글 삭제
function deleteBulletin(id) {
    $.ajax({
        type: "DELETE",
        url: `/api/bulletins/${id}`,
        success: function (response) {
            alert('메시지 삭제에 성공하였습니다.');
            window.location.reload()
        }
    })
}

총평

Spring Framework를 이용하여 간단한 게시글을 만들어 보면서 Spring이 어떻게 작동되는지 원리를 이해하게 되었고, 어떠한 기능에 대한 코드를 작성하려고 할 때 해당 코드를 어디에 입력하면 되는지 이해할 수 있는 프로젝트였다.

profile
Macro Developer

0개의 댓글