상세 컨텐츠

본문 제목

Springboot와 JPA로 간단한 게시판 만들기

IT/Spring 및 Boot

by SINAFLA 2021. 7. 27. 17:25

본문

반응형

 

build.gradle

더보기
plugins {
	id 'org.springframework.boot' version '2.5.2'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
	id 'war'
}

group = 'kr.com.board'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	providedRuntime 'org.apache.tomcat.embed:tomcat-embed-jasper'
	implementation 'javax.servlet:jstl'
	runtimeOnly 'org.postgresql:postgresql'
	annotationProcessor 'org.projectlombok:lombok'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
	useJUnitPlatform()
}

 

application.properties

더보기
# DataSource 
spring.datasource.url=jdbc:postgresql://localhost:6022/board
spring.datasource.username=douze
spring.datasource.password=douze1234
spring.datasource.hikari.auto-commit=false

# JPA Configuration
spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.show_sql=true
#spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=false

# View
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

server.port=8000

 

프로젝트 구조

 

 

BoardDto.java

더보기
package kr.com.board.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@SequenceGenerator(name = "seq_board", sequenceName = "seq_board", allocationSize = 1)
@EntityListeners(AuditingEntityListener.class)
@Table(name = "board")
@Entity
public class BoardDto {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_board")
	private Long id;
	
	@Column(length = 200, nullable = true)
	private String title;
	
	@Column(length = 5000, nullable = true)
	private String contents;
	
	@CreatedBy
    @Column(nullable = false, updatable = false)
    private String regId;
	
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Date regDte;
	
    @LastModifiedBy
    @Column(insertable = false)
    private String updId;
    
    @UpdateTimestamp
    @Column(insertable = false)
    private Date updDte;
    
	@Builder
	public BoardDto (String title, String contents, Long id) {
		this.id = id;
		this.title = title;
		this.contents = contents;
	}
}

 

 

BoardRepository.java

더보기
package kr.com.board.model;

import java.util.List;

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

public interface BoardRepository extends JpaRepository<BoardDto, Long>{
	public List<BoardDto> findAllByOrderByIdDesc();
}

 

 

BoardVo.java

더보기
package kr.com.board.model;

import lombok.Data;

@Data
public class BoardVo {
	// 게시르 ID
	private Long id;
	
	// 제목
	private String title;
	
	// 내용
	private String contents;
}

 

 

BoardPageController.java

더보기
package kr.com.board.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import kr.com.board.service.BoardService;

@Controller
public class BoardPageController {
	
	@Autowired
	private BoardService boardService;
	
	/**
	 * 목록 페이지
	 * @return
	 */
	@RequestMapping("/list_page")
	public String list(Model model) {
		model.addAttribute("board", boardService.findByAllOrderByDesc());
		return "list";
	}
	
	/*
	 * 추가 페이지
	 */
	@RequestMapping("/add_page")
	public String add(Model model) {
		return "add";
	}
	
	/**
	 * 상세보기 페이지
	 */
	@RequestMapping("/detail_page")
	public String detail(Long num, Model model) {
		model.addAttribute("board", boardService.findById(num));
		return "detail";
	}
	
	/**
	 * 수정 페이지
	 * @return
	 */
	@RequestMapping("/modify_page/{num}")
	public String modify(@PathVariable Long num, Model model) {
		model.addAttribute("content", boardService.findById(num));
		return "modify";
	}
}

 

 

BoardController.java

더보기
package kr.com.board.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import kr.com.board.model.BoardVo;
import kr.com.board.service.BoardService;

@RestController
public class BoardController {

	@Autowired
	private BoardService boardService;
	
	@PostMapping("/add")
	public ResponseEntity<String> add(BoardVo boardVo, Model model) {
		boardService.contentSave(boardVo);
		HttpHeaders responseHeaders = new HttpHeaders();
		responseHeaders.add("Content-Type", "text/html; charset=UTF-8");
		return new ResponseEntity<String>(responseHeaders, HttpStatus.OK);
    }
	
	@PostMapping("/update")
	public ResponseEntity<String> update(BoardVo boardVo) {
		boardService.contentUpdate(boardVo);
		HttpHeaders responseHeaders = new HttpHeaders();
		responseHeaders.add("Content-Type", "text/html; charset=UTF-8");
		return new ResponseEntity<String>(responseHeaders, HttpStatus.OK);
	}
	
	@DeleteMapping("/delete")
	public ResponseEntity<String> delete(Long num) {
		boardService.contentDelete(num);
		HttpHeaders responseHeaders = new HttpHeaders();
		responseHeaders.add("Content-Type", "text/html; charset=UTF-8");
		return new ResponseEntity<String>(responseHeaders, HttpStatus.OK);
	}
}

 

 

DataConfig.java

더보기
package kr.com.board.config;

import java.util.Optional;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class DataConfig {

	@Bean
	public AuditorAware<String> auditorAware() {
		return new AuditorAware<String>() {

			@Override
			public Optional<String> getCurrentAuditor() {
				return Optional.of("SYSTEM");
			}
		};
	}
}

 

 

BoardService.java

더보기
package kr.com.board.service;

import java.util.List;

import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import kr.com.board.model.BoardDto;
import kr.com.board.model.BoardRepository;
import kr.com.board.model.BoardVo;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class BoardService {

	private final BoardRepository boardRepository;
	
	/**
	 * 추가
	 */
	@Transactional
	public void contentSave(BoardVo boardVo) {
		boardRepository.save(BoardDto.builder()
				.title(boardVo.getTitle())
				.contents(boardVo.getContents())
				.build()
				);
	}
	
	/**
	 * 수정
	 */
	@Transactional
	public void contentUpdate(BoardVo boardVo) {
		boardRepository.save(BoardDto.builder()
				.id(boardVo.getId())
				.title(boardVo.getTitle())
				.contents(boardVo.getContents())
				.build()
				);
	}
	
	/**
	 * 삭제
	 */
	@Transactional
	public void contentDelete(Long id) {
		boardRepository.deleteById(id);
	}
	
	/**
	 * 전체 조회
	 */
	@Transactional(readOnly = true)
	public List<BoardDto> findByAll() {
		return boardRepository.findAll();
	}
	
	
	/**
	 * 단건 조회
	 */
	@Transactional(readOnly = true)
	public BoardDto findById(long id) {
		return boardRepository.findById(id)
				.orElseThrow(() -> new IllegalArgumentException("no such data"));
	}
	
	@Transactional
	public int boardCount() {
		return findByAll().size();
	}
	
	@Transactional
	public List<BoardDto> findByAllOrderByDesc() {
		return boardRepository.findAllByOrderByIdDesc();
	}
}

 

 

list.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./js/common/jquery-1.10.2.min.js"></script>
	<script type="text/javascript" src="./js/common/jquery-ui-1.10.3.js"></script>
	<script type="text/javascript" src="./js/board.js"></script>
	<link rel="stylesheet" type="text/css" href="./css/common.css" />
<title>List</title>
</head>
<body>
	<div class="body-area">
		<div class="button-area">
			<!-- 버튼 영역 -->
			<button id="addPageBtn">추가</button>
		</div>
		<div>
			<!-- 목록 영역 -->
			<table class="list-table">
				<colgroup>
					<col width="6%" />	<!-- No 	-->
					<col />				<!-- 제목 	-->
					<col width="25%" />	<!-- 작성일 	-->
					<col width="7%">
				</colgroup>
				<thead>
					<tr>
						<th>No.</th>
						<th>제목</th>
						<th>작성일</th>
						<th>삭제</th>
					</tr>
				<thead>
				<tbody>
					<c:forEach var="item" items="${board}">
						<tr>
							<td class="text-c">${item.id}</td>
							<td>
								<a href="./detail_page?num=${item.id}">${item.title}</a>
							</td>
							<td class="text-c">
								<fmt:parseDate var="parseRegDate" value="${item.regDte}" pattern="yyyy-MM-dd HH:mm:ss"/>
								<fmt:formatDate var="resultRegDt" value="${parseRegDate}" pattern="yyyy-MM-dd HH:mm:ss"/>
								${resultRegDt}
							</td>
							<td class="text-c"><button class="delete" value="${item.id}">삭제</button></td>
						</tr>
					</c:forEach>
				</tbody>
			</table>
		</div>
	</div>
	
</body>
</html>

 

 

detail.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<script type="text/javascript" src="./js/common/jquery-1.10.2.min.js"></script>
	<script type="text/javascript" src="./js/common/jquery-ui-1.10.3.js"></script>
	<script type="text/javascript" src="./js/board.js"></script>
	<link rel="stylesheet" type="text/css" href="./css/common.css" />
<title>Detail</title>
</head>
<body>
	<div class="body-area">
		<div class="button-area">
			<!-- 버튼 영역 -->
			<button class="prevBtn">이 전</button>
			<button id="updatePageBtn">수 정</button>
		</div>
		<div class="content-area">
			<!-- 내용 영역 -->
			<table class="add-table">
				<colgroup>
					<col width="15%" />	<!-- 제목 	-->
					<col />				<!-- 내용 	-->
				</colgroup>
				<tbody>
					<tr>
						<th>No.</th>
						<td>
							${board.id}
							<input id="num" type="hidden" value="${board.id}" />
						</td>
					</tr>
					<tr>
						<th>제목</th>
						<td>${board.title}</td>
					</tr>
					<tr>
						<th>내용</th>
						<td>${board.contents}</td>
					</tr>		
				</tbody>
			</table>
		</div>
	</div>
</body>
</html>

 

 

add.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<script type="text/javascript" src="./js/common/jquery-1.10.2.min.js"></script>
	<script type="text/javascript" src="./js/common/jquery-ui-1.10.3.js"></script>
	<script type="text/javascript" src="./js/board.js"></script>
	<link rel="stylesheet" type="text/css" href="./css/common.css" />
<title>Add</title>
</head>
<body>
	<div class="body-area">
		<div class="button-area">
			<!-- 버튼 영역 -->
			<button class="prevBtn">이 전</button>
			<button id="addBtn">저 장</button>
		</div>
		<div class="content-area">
			<!-- 내용 영역 -->
			<table class="add-table">
				<colgroup>
					<col width="15%" />	<!-- 제목 	-->
					<col />				<!-- 내용 	-->
				</colgroup>
				<tbody>
					<tr>
						<th>제목</th>
						<td><input type="text" id="title" name="title"/></td>
					</tr>
					<tr>
						<th>내용</th>
						<td><textarea id="contents" name="contents"></textarea></td>
					</tr>		
				</tbody>
			</table>
		</div>
	</div>
</body>
</html>

 

 

modify.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<script type="text/javascript" src="./js/common/jquery-1.10.2.min.js"></script>
	<script type="text/javascript" src="./js/common/jquery-ui-1.10.3.js"></script>
	<script type="text/javascript" src="./js/board.js"></script>
	<link rel="stylesheet" type="text/css" href="./css/common.css" />
<title>Modify</title>
</head>
<body>
	<div class="body-area">
		<div class="button-area">
			<!-- 버튼 영역 -->
			<button class="listPageBtn">목 록</button>
			<button class="prevBtn">이 전</button>
			<button id="updateBtn">저 장</button>
		</div>
		<div class="content-area">
			<!-- 내용 영역 -->
			<table class="modify-table">
				<colgroup>
					<col width="15%" />	<!-- 제목 	-->
					<col />				<!-- 내용 	-->
				</colgroup>
				<tbody>
					<tr>
						<th>No.</th>
						<td>
							${board.id}
							<input id="num" type="hidden" value="${board.id}" />
						</td>
					</tr>
					<tr>
						<th>제목</th>
						<td>
							<input type="text" id="modify-title" name="title" value="${board.title}" />
						</td>
					</tr>
					<tr>
						<th>내용</th>
						<td>
							<textarea id="modify-contents" name="contents">${board.contents}</textarea>
						</td>
					</tr>		
				</tbody>
			</table>
		</div>
	</div>
</body>
</html>

 

 

board.js

더보기
/**
 * Board
 */
$(function() {
	Board.init();
	$(".prevBtn").click(Board.prev);
	$(".listPageBtn").click(Board.listPage);

	$("#addPageBtn").click(Board.addPage);
	$("#updatePageBtn").click(Board.modifyPage);
	
	$("#addBtn").click(Board.newContent);
	$("#updateBtn").click(Board.updateContent);
	$(".delete").click(Board.deleteContent);
	
});

var Board = {
	/**
	 * 이벤트 등록
	 */
	init : function() {
		try {
		} catch(e) {
			console.log(e);
		}
	},
	
	
	/**
	 * 목록 페이지 이동
	 */
	listPage : function() {
		try {
			location.href = "./list_page";
		} catch (e) {
			console.log(e);
		}
	},
	
	/**
	 * 신규 페이지 이동
	 */
	addPage : function() {
		try {
			location.href = "./add_page";
		} catch(e) {
			console.log(e);
		}
	},
	
	modifyPage : function() {
		try {
			var num = $("#num").val();
			location.href = "./modify_page?num=" + num;
		} catch(e) {
			console.log(e);
		}
	},
	
	/**
	 * 신규 등록
	 */
	newContent : function() {
		try {
			var title = $("#title").val();
			var contents = $("#contents").val();
			
			$.ajax({
				type: 'POST',
				url: "/add",
				data: {
					title: title,
					contents: contents
				},
				success: function(tr) {
					location.href = "/list_page";
				},
				error: function(err) {
					console.log('>>> error: ' + JSON.stringify(err));
				}
				
			});
		} catch(e) {
			console.log(e);
		}
	},
	
	/**
	 * 이전 화면
	 */
	prev : function() {
		try {
			history.back();
		} catch(e) {
			console.log(e);
		}
	},
	
	/**
	 * 수정
	 */
	updateContent: function() {
		try {
			var num = $("#num").val();
			var title = $("#modify-title").val();
			var contents = $("#modify-contents").val();
			
			$.ajax({
				type: 'POST',
				url: "/update",
				data: {
					id: num,
					title: title,
					contents: contents
				},
				success: function(tr) {
					location.href = "/list_page";
				},
				error: function(err) {
					console.log('>>> error: ' + JSON.stringify(err));
				}
				
			});
		} catch(e) {
			console.log(e);
		}
	},
	
	/**
	 * 삭제
	 */
	deleteContent : function() {
		try {
			var deleteBtn = event.currentTarget;
			
			$.ajax({
				type: 'DELETE',
				url: "/delete?num=" + deleteBtn.value,
				success: function(tr) {
					location.href = "/list_page";
				},
				error: function(err) {
					console.log('>>> error: ' + JSON.stringify(err));
				}
				
			});
		} catch (e) {
			console.log(e);
		}
	}
};

 

 

common.css

더보기
@charset "UTF-8";

html, body {
	width: 100%;
	height: 100%;
}

.list-table {
	width: 800px;
	height: 400px;
}
table, th, td {
	border-collapse: collapse;
	border: 1px solid #bcbcbc;
}
th {
	background: grey;
	text-align: center;
}

.text-c {
	text-align: center;
}

.body-area{
	display: block;
	width: 800px;
}

.button-area {
	float: right;
	margin-bottom: 10px;
}

.content-area input {
	width: 98%;
}

.content-area textarea {
	width: 98%;
}

.add-table {
	width: 800px;
}

 

 

javascript 추가파일

common.zip
0.13MB

 

 

 

 

반응형

관련글 더보기

댓글 영역