spring data jpa, spring boot, oracle, angulerjs적용 게시판 …

18
Spring Data JPA, Spring Boot, Oracle, AngulerJS적용 게시판 실습 http://ojc.asia, http://ojcedu.com 게시판 리스트보기 Spring JDBC 또는 MyBatis로 만들 때 보다 쉽고 빠르게 작성할 수 있다. 스프링 컨트롤러는 RestController를 적용 했으며, 뷰 페이지에 Bootstrap 및 AngulerJS 적용 했다. 프로젝트 전체 구조는 다음과 같다. [BoardRepository.java] 기본적으로 제공하는 JpaRepository를 상속받아 만들면 된다.

Upload: others

Post on 24-Nov-2021

31 views

Category:

Documents


0 download

TRANSCRIPT

Spring Data JPA, Spring Boot, Oracle, AngulerJS적용 게시판 실습

http://ojc.asia, http://ojcedu.com

게시판 리스트보기

Spring JDBC 또는 MyBatis로 만들 때 보다 쉽고 빠르게 작성할 수 있다.

스프링 컨트롤러는 RestController를 적용 했으며, 뷰 페이지에 Bootstrap 및 AngulerJS 적용

했다.

프로젝트 전체 구조는 다음과 같다.

[BoardRepository.java]

기본적으로 제공하는 JpaRepository를 상속받아 만들면 된다.

JpaRepository는 PagingAndSortingRepository를 상속받았고 PagingAndSortingRepository는

CrudRepository(기본적인 CRUD 기능을 제공한다)를 상속 받았으며 다시 CrudRepository는

Repository 인터페이스를 상속받았다.

그러므로 JpaRepository는 모든 기능을 다 사용할 수 있고 추가적으로 영속성 컨텍스트에

flush하거나 엔티티를 배치로 삭제할 수 있다.

기본 기능만으로도 게시판 기능을 구현할 수 있으므로 JpaRepository를 상속 받자.

package jpa.board.repository;

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

import jpa.board.model.Board;

public interface BoardRepository extends JpaRepository<Board, Integer> {

}

서비스쪽 클래스를 작성하자.

[BoardService.java]

package jpa.board.service;

import org.springframework.data.domain.Page;

import jpa.board.model.Board;

public interface BoardService {

//게시판 리스트 보기

public Page<Board> findAll(Integer curPage);

}

JpaRepository의 findAll() 메소드를 호출만 하면 되는데 페이징 기능을 구현하기 위해

PageRequest를 만들고 이를 findAll() 메소드의 인자로 넣어주면 된다.

Board 테이블에서 정렬한 칼럼이 두개 이상이므로 Sort 를 new하면서 Order를 필요한 만큼

생성해주면 되고 ASC는 오름차순, DESC는 내림차순, 그뒤의 문자열은 Board 엔티티의 속성

이다.

[BoardServiceImpl.java]

package jpa.board.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Page;

import org.springframework.data.domain.PageRequest;

import org.springframework.data.domain.Sort;

import org.springframework.data.domain.Sort.Direction;

import org.springframework.data.domain.Sort.Order;

import org.springframework.stereotype.Service;

import jpa.board.model.Board;

import jpa.board.repository.BoardRepository;

@Service

public class BoardServiceImpl implements BoardService {

@Autowired

BoardRepository boardRepository;

@Override

//-----------------------------------------

// 게시판 리스트 보기, 한페이지에 3개씩

// curPage:요청하는 페이지, 첫페이지는 0

//-----------------------------------------

public Page<Board> findAll(Integer curPage) {

PageRequest pr = new PageRequest(curPage, 3,

new Sort(

new Order(Direction.DESC, "reply"),

new Order(Direction.ASC, "replystep")));

return boardRepository.findAll(pr);

}

}

컨트롤러를 작성하자.

[BoardController.java]

package jpa.board.controller;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Page;

import org.springframework.data.domain.Pageable;

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 org.springframework.web.bind.annotation.RequestMethod;

import jpa.board.model.Board;

import jpa.board.service.BoardService;

@RestController

@RequestMapping("/board")

public class BoardController {

@Autowired

BoardService boardService;

//---------------------------------------------

// 루트요청(localhost:8080/board/)시 리스트 보기로

//---------------------------------------------

@RequestMapping(method = RequestMethod.GET)

public ModelAndView index() {

//jsp아래 board.jsp 호출

return new ModelAndView("board");

}

//---------------------------------------------

// 게시판 리스트 보기

//---------------------------------------------

@RequestMapping(value="/{curPage}", method = RequestMethod.GET)

public ResponseEntity<Page<Board>> list(Model model, Pageable pageable,

@PathVariable Integer curPage) {

Page<Board> page = boardService.findAll(curPage);

return new ResponseEntity<Page<Board>>(page, HttpStatus.OK);

}

}

src/main/webapp/js , src/main/webapp/jsp 폴더를 만들고 app.js, board_controller.js,

board_service.js, list.jsp를 만들자.

[src/main/webapp/js /app.js]

'use strict';

var App = angular.module('myBoard',[]);

[src/main/webapp/js /board_service.js]

'use strict';

App.factory('BoardService', ['$http', '$q', function($http, $q){

return {

list: function(curPage) {

return $http.get('http://localhost:8080/board/' + curPage)

.then(

function(response){

console.log("[service:list]server call suceeded.");

return response.data;

},

function(errResponse){

console.error('Error while fetching contents');

return $q.reject(errResponse);

}

);

}

};

}]);

[src/main/webapp/js /board_controller.js]

'use strict';

App.controller('BoardController', ['$scope', 'BoardService',

function($scope, BoardService) {

var self = this;

self.board={id:null,name:'',passwd:'',title:'',content:''};

self.page=[];

//리스트 보기

self.list = function(curPage){

BoardService.list(curPage)

.then(

function(data) {

self.page = data;

console.log("[controller:list]", self.page);

//alert("목록보기 성공!");

},

function(errResponse){

console.error('Error while fetching page...');

}

);

};

self.list(0);

}]);

[board.jsp]

<%@ page contentType="text/html; charset=utf-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<html>

<head>

<title>Spring JPA 게시판</title>

<link rel="stylesheet"

href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

<style>

.name.ng-valid {

background-color: lightgreen;

}

.name.ng-dirty.ng-invalid-required {

background-color: red;

}

.name.ng-dirty.ng-invalid-maxlength {

background-color: yellow;

}

.passwd.ng-valid {

background-color: lightgreen;

}

.passwd.ng-dirty.ng-invalid-required {

background-color: red;

}

.passwd.ng-dirty.ng-invalid-maxlength {

background-color: yellow;

}

.title.ng-valid {

background-color: lightgreen;

}

.title.ng-dirty.ng-invalid-required {

background-color: red;

}

.title.ng-dirty.ng-invalid-maxlength {

background-color: yellow;

}

body, #mainWrapper {

height: 70%;

background-color: rgb(245, 245, 245);

}

body, .form-control {

font-size: 12px !important;

}

.floatRight {

float: right;

margin-right: 18px;

}

.has-error {

color: red;

}

.formcontainer {

background-color: #DAE8E8;

padding: 20px;

}

.tablecontainer {

padding-left: 20px;

}

.generic-container {

width: 80%;

margin-left: 20px;

margin-top: 20px;

margin-bottom: 20px;

padding: 20px;

background-color: #EAE7E7;

border: 1px solid #ddd;

border-radius: 4px;

box-shadow: 0 0 30px black;

}

.custom-width {

width: 80px !important;

}

.pointer {

cursor: pointer;

}

</style>

<!-- For AngularJS -->

<script

src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>

<script src="<c:url value='/js/app.js' />"></script>

<script src="<c:url value='/js/board_service.js' />"></script>

<script src="<c:url value='/js/board_controller.js' />"></script>

</head>

<body ng-app="myBoard" class="ng-cloak" ng-controller="BoardController as ctrl">

<div class="generic-container" ng-controller="BoardController as ctrl"

style="width: 800px;">

<div class="panel panel-default">

<div class="panel-heading">

<span class="lead">Spring Data JPA 게시판 글 쓰기 </span>

</div>

<div class="formcontainer">

<form ng-submit="ctrl.submit()" name="myForm" class="form-

horizontal">

<input type="hidden" ng-model="ctrl.board.id" />

<div class="row">

<div class="form-group col-md-6">

<label class="col-md-2 control-lable"

for="name">Name : </label>

<div class="col-md-7">

<input type="text" ng-

model="ctrl.board.name" id="name"

class="name form-

control input-sm"

placeholder="Enter

your Name" required

ng-maxlength="20"

/>

<div class="has-error" ng-

show="myForm.$dirty">

<span ng-

show="myForm.name.$error.required">This is a

required

field</span> <span ng-show="myForm.name.$error.maxlength">Maximum

length is

20</span> <span ng-show="myForm.name.$invalid">This

field is

invalid </span>

</div>

</div>

</div>

</div>

<div class="row">

<div class="form-group col-md-6">

<label class="col-md-2 control-lable"

for="passwd">Password

:</label>

<div class="col-md-7">

<input type="password" ng-

model="ctrl.board.passwd" id="passwd"

class="passwd

form-control input-sm"

placeholder="Enter

your Password" required ng-maxlength="20" />

<div class="has-error" ng-

show="myForm.$dirty">

<span ng-

show="myForm.passwd.$error.required">This is a

required

field</span> <span ng-show="myForm.passwd.$error.maxlength">Maximum

length is

20</span> <span ng-show="myForm.passwd.$invalid">This

field is

invalid </span>

</div>

</div>

</div>

</div>

<div class="row">

<div class="form-group col-md-6">

<label class="col-md-2 control-lable"

for="title">Title :</label>

<div class="col-md-7">

<input type="text" ng-

model="ctrl.board.title" id="title"

class="title form-

control input-sm"

placeholder="Enter

your Title" required />

<div class="has-error" ng-

show="myForm.$dirty">

<span ng-

show="myForm.title.$error.required">This is a

required

field</span> <span ng-show="myForm.title.$invalid">This

field is

invalid </span>

</div>

</div>

</div>

</div>

<div class="row">

<div class="form-group col-md-6">

<label class="col-md-2 control-lable"

for="content">Contents

:</label>

<div class="col-md-7">

<textarea rows="4"

ng-

model="ctrl.board.content" id="content"

class="content

form-control input-sm"

placeholder="Enter

your Contents" required >

</textarea>

<div class="has-error" ng-

show="myForm.$dirty">

<span ng-

show="myForm.content.$error.required">This is a

required

field</span> <span ng-show="myForm.content.$invalid">This

field is

invalid </span>

</div>

</div>

</div>

</div>

<div class="row">

<div class="form-actions floatRight">

<input type="submit"

value="{{!ctrl.board.id ?

'Add' : 'Update'}}"

class="btn btn-primary btn-

sm" ng-disabled="myForm.$invalid">

<button type="button" ng-

click="ctrl.reset()"

class="btn btn-warning btn-

sm" ng-disabled="myForm.$pristine">Reset

Form</button>

</div>

</div>

</form>

</div>

</div>

<div class="panel panel-default" style="float: center;">

<div class="panel-heading">

<span class="lead">Spring Data JPA 게시판 리스트보기

</span>

</div>

<h5>

총 {{ctrl.page.totalElements}}</span>건

</h5>

<div class="tablecontainer">

<table width="600" border="1" align="left" class="table table-

hover">

<tr align="left">

<th align="center">순번</th>

<th align="center">글번호</th>

<th align="center">제목</th>

<th align="center">글쓴이</th>

<th align="center">등록일</th>

<th align="center">조회수</th>

<th align="center">조회/삭제</th>

</tr>

<tr data-ng-repeat="board in ctrl.page.content">

<td align="center"><span ng-

bind="{{$index+1}}"></span></td>

<td align="center"><span ng-

bind="board.id"></span></td>

<td align="left">

<!-- 레벨의 수만큼 글을 뒤로 민다

--> <span

ng-repeat="n in

[].constructor(board.replylevel) track by $index">

&nbsp;&nbsp; </span>

<span ng-bind="board.title"></span>

</td>

<td align="center"><span ng-

bind="board.name"></span></td>

<td align="center">{{board.regdate |

date:"yy.MM.dd hh:mm"}}</td>

<td align="center"><span ng-

bind="board.readcount"></span></td>

<td>

<button type="button" ng-

click="ctrl.edit(board.id)"

class="btn btn-success

custom-width">Edit</button>

<button type="button" ng-

click="ctrl.remove(board.id)"

class="btn btn-danger

custom-width">Remove</button>

</td>

</tr>

</tbody>

</table>

<div>

<!-- 게시판 페이징 -->

<ul class="pagination">

<li ng-class="{disabled: ctrl.page.number ===

0}"><a

ng-show="ctrl.page.number !== 0"

class="pointer"

ng-click="ctrl.list(ctrl.page.number-

1)">Prev</a>

<span ng-show="ctrl.page.number

=== 0">Prev</span></li>

<li ng-class="{disabled: ctrl.page.number ===

ctrl.page.totalPages - 1}">

<a ng-show="ctrl.page.number !==

ctrl.page.totalPages - 1"

class="pointer"

ng-

click="ctrl.list(ctrl.page.number+1)">Next</a>

<span ng-show="ctrl.page.number

=== ctrl.page.totalPages - 1">Next</span>

</li>

</ul>

</div>

</div>

</div>

</div>

</body>

</html>

[JpaboardApplication.java] – 스프링 부트 메인

package jpa.board;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import jpa.board.model.Board;

import jpa.board.repository.BoardRepository;

@SpringBootApplication

public class JpaboardApplication implements CommandLineRunner{

@Autowired

BoardRepository boardRepository;

public static void main(String[] args) {

SpringApplication.run(JpaboardApplication.class, args);

}

public void run(String...args) {

//테스트를 위해 글9개 입력

Board b1 = new Board();

b1.setContent("JPA강좌 추천해 주세요1~");

b1.setName("홍길동1"); b1.setPasswd("1111");

b1.setReadcount(0); b1.setRegdate(new Date());

b1.setReply(1); b1.setReplylevel(0);

b1.setReplystep(0); b1.setTitle("강좌추천요망1");

boardRepository.save(b1);

Board b2 = new Board();

b2.setContent("JPA강좌 추천해 주세요2~");

b2.setName("홍길동2"); b2.setPasswd("1111");

b2.setReadcount(0); b2.setRegdate(new Date());

b2.setReply(2); b2.setReplylevel(0);

b2.setReplystep(0); b2.setTitle("강좌추천요망2");

boardRepository.save(b2);

Board b3 = new Board();

b3.setContent("JPA강좌 추천해 주세요3~");

b3.setName("홍길동3"); b3.setPasswd("1111");

b3.setReadcount(0); b3.setRegdate(new Date());

b3.setReply(3); b3.setReplylevel(0);

b3.setReplystep(0); b3.setTitle("강좌추천요망3");

boardRepository.save(b3);

Board b4 = new Board();

b4.setContent("OJC로 가세요...");

b4.setName("홍길동4"); b4.setPasswd("1111");

b4.setReadcount(0); b4.setRegdate(new Date());

b4.setReply(6); b4.setReplylevel(1);

b4.setReplystep(1); b4.setTitle("[답변]강좌추천요망6");

boardRepository.save(b4);

Board b5 = new Board();

b5.setContent("OJC로 가세요...");

b5.setName("홍길동5"); b5.setPasswd("1111");

b5.setReadcount(0); b5.setRegdate(new Date());

b5.setReply(2); b5.setReplylevel(1);

b5.setReplystep(1); b5.setTitle("[답변]강좌추천요망2");

boardRepository.save(b5);

Board b6 = new Board();

b6.setContent("JPA강좌 추천해 주세요6~");

b6.setName("홍길동6"); b6.setPasswd("1111");

b6.setReadcount(0); b6.setRegdate(new Date());

b6.setReply(6); b6.setReplylevel(0);

b6.setReplystep(0); b6.setTitle("강좌추천요망6");

boardRepository.save(b6);

Board b7 = new Board();

b7.setContent("JPA강좌 추천해 주세요7~");

b7.setName("홍길동7"); b7.setPasswd("1111");

b7.setReadcount(0); b7.setRegdate(new Date());

b7.setReply(7); b7.setReplylevel(0);

b7.setReplystep(0); b7.setTitle("강좌추천요망7");

boardRepository.save(b7);

Board b8 = new Board();

b8.setContent("OJC로 가세요...");

b8.setName("홍길동8"); b8.setPasswd("1111");

b8.setReadcount(0); b8.setRegdate(new Date());

b8.setReply(7); b8.setReplylevel(1);

b8.setReplystep(1); b8.setTitle("[답변]강좌추천요망7");

boardRepository.save(b8);

Board b9 = new Board();

b9.setContent("JPA강좌 추천해 주세요9~");

b9.setName("홍길동9"); b9.setPasswd("1111");

b9.setReadcount(0); b9.setRegdate(new Date());

b9.setReply(9); b9.setReplylevel(0);

b9.setReplystep(0); b9.setTitle("강좌추천요망9");

boardRepository.save(b9);

}

}

실행(http://localhost/board/) – 리스트 보기에는 페이지 기능이 적용되어 있다.