vue에서 페이지 기능을 사용하기 위해서는 페이징 라이브러리를 가져와야한다. 이것을 사용하면 따로 반복문을 만들지 않아도 알아서 다 만들어주어 편리하다
spring의 변수들을 사용하려면 export default에 date함수에 변수들을 가져와야한다.
<script>
export default {
data() { // 속성을 가지는 함수. 변수들을 가지는 객체를 리턴한다.
return {
dept:[], // 스프링에서 전송할 변수
searchDname: "", // 부서명검색 변수
page: 1, // 현재 페이지 번호
count: 4, // 전체 데이터 개수
pageSize: 3, // 한 페이지당 요소개수
pageSizes: [3,6,9] // 한 페이지당 요소개수 배열
}
}
};
</script>
export default에 data 함수를 만들고 return값을 만들어 여기에 통신할 데이터들을 객체 형태로 넣어주면된다. 그러면 이것들을 백엔드와 통신을 할 수 있게된다. 물론 함수를 통해 spring에서 들어온 데이터를 이 변수들에 넣어주는 과정이 필요하다.
여기서는 페이지기능에 사용할 함수도 만들것이고, spring과 통신에 사용할 함수들도 만들것이다.
<script>
import DeptService from "@/services/basic/DeptService";
export default {
methods: {// 공통 함수 : 페이징 관련 함수들
pageNoChange(value){ // 페이지 번호 클릭시 실행될 함수
// this.속성 => data() 안에 속성들 접근
this.page = value; // 1) 현재페이지 변경되면
this.retrieveDept(); // 2) 재조회를 요청하게 만들었다.
},
// select 박스 변경시 실행될 함수
pageSizeChange(){
this.page = 1; // 현재 페이지 번호 초기화
this.retrieveDept(); // 재조회 요청
},
// spring의 전체조회 요청함수
// getAll 전체조회 함수 : 비동기 함수 -> 한번에 다 나눠주고 오는대로 받는것. 속도는 빠른데 먼저준애 순서대로 받을 수 없다.
// 그래서 해줘야하는게 async 함수
async retrieveDept(){
try{
// 공통 전체 조회 함수 실행 -> DeptService.js에 만든 함수를 가져올 것이다.
let response = await DeptService.getAll(this.searchDname, this.page-1/* spring에서는0부터 시작하므로 */, this.pageSize); // axios 사용 promise가 다 있어서 비동기가 가능하다.
// const dept = response.data.dept;
// const count = response.data.totalItems;
// 객체분할 할당
const {dept, totalItems} = response.data; // 위 두 코드를 한 줄로 합쳐주는 기능, axios파일은 data변수에 담겨서 이동한다.
this.dept = dept;
this.count = totalItems;
// response에 뭐가 들어있는지를 확인하기위해 로깅
console.log(response.data);
}catch(e){
console.log(e)
}
}
},
mounted() {
// 최초 화면이 뜰때 전체 조회
this.retrieveDept();
},
};
</script>
총 3가지의 함수를 만들었다.
페이지 번호를 클릭하면 현재 페이지가 변경되고 재조회가 일어나게 할 pageNoChange함수를 만들었다. 변수로 페이지 번호가 들어오면 page값은 들어온 페이지 값이되고 다시 getAll함수가 실행된다.
select 박스가 변경되면 몇 페이지였던간에 1페이지로 돌아오게 만들 pageSizeChange함수를 만들었다. page값은 1을 주고 getAll로 재조회를 하게했다.
재조회 함수인 pageSizeChange를 만들었다. getAll의 비동기 함수 실행과 getAll의 값에서 dept와 count(전체 요소 수)값을 변수에 넣어주는 함수이다. axios함수는 promise를 가지고 있어 비동기 코딩이 가능해진다.
retrieveDeptd에서 axios함수를 사용하기에 비동기 코딩을 해주어야한다. 비동기 코딩은 함수를 여러개 요청시키고 준비가 대로 실행한다. 그래서 성능은 좋지만 먼저 요청한 함수가 나중에 실행될 수 있다. async와 await으로 먼저와야하는 함수를 지정해줄수 있다.
정의된searchTitle와, page, pageSize가 getAll함수로 들어가서 axios의 get함수가 실행된다. 그럼 백엔드 에서 dept 객체와 page기능을 하는 변수들을 반환해준다.
axios가 가지는 data를 이용해서 객체로 반환된 데이터의 값을 사용할 수 있다. data의 dept와 data의 totalItems를 변수인 dept와 count에 넣어주었다.
마지막으로 에러처리를 해주었다.
이렇게 하면 스프링과 데이터를 주고받을 함수가 완성이 되는 것이다.
부트스트랩뷰를 사용하기 위해서는 라이브러리를 가져와야 사용할 수 있다.
먼저 부트스트랩 뷰를 설치해야하는데 다음 명령어를 입력하면된다.
npm install bootstrap-vue-3
버전 3을 사용해야 호환이 된다.
위경로에서 bootstrap-vue가 있으면 잘 설치가 된 것이다.

main.js파일에 아래 코드를 입력해줘야 import가 된다.
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'bootstrap/dist/js/bootstrap.bundle'
import 'bootstrap/dist/css/bootstrap.min.css'
// TODO: bootstartp vue3 import
import BootstrapVue3 from "bootstrap-vue-3";
// TODO: bootstartp vue3 css import
import "bootstrap-vue-3/dist/bootstrap-vue-3.css";
createApp(App).use(BootstrapVue3).use(store).use(router).mount('#app')
부트스트랩을 이용하여 테이블과 search바를 만들었다. 페이징 기능은 부트스트랩뷰에서 가져와서 사용하기로했다.
<!-- 테이블 -->
<div class="row">
<div class="col-12">
<!-- 테이블 디자인 -->
<table class="table">
<thead>
<tr>
<th scope="col">dno</th>
<th scope="col">dname</th>
<th scope="col">loc</th>
<th scope="col">action</th>
</tr>
</thead>
<tbody>
<tr v-for="(data, index) in dept" :key="index">
<td>{{data.dname}}</td>
<td>{{data.loc}}</td>
<td>
<span class="badge text-bg-success">수정</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
반복문을 사용해서 dept의 요소들을 반복해서 나타내 줄 것이다.
여기서 spring에서 가져온 데이터를 사용하기위해서는 바인딩 기능을 사용해주어야하는데 div처럼 요소를 보여주는 태그에는 콜론(:)의 형태로 해주면된다.
<!-- 페이지 번호 -->
<!-- 현재 페이지는 = page, 한 페이지당 요소 갯수 갯수 = pageSize, 전체 데이터 갯수 = count -->
<b-pagination
class="col-12 mb-3"
v-model="page"
:total-rows="count"
:per-page="pageSize"
></b-pagination>
b-pagination태그는 부트스트랩 뷰에서만든 것으로 아래의 3가지를 채워주어야 페이지 기능이 완성된다. 이것들은 백엔드에서의 page기능을 연결해서 넣어줄 것이다. 연결방법은
jsp 에서는 반복문을 만들어서 사용했는데 여기서는 그냥 만들어진 것을 사용하면 되어 더 편리하다.
미리 만든 함수를 추가해서 페이지를 바꾸면 화면이 바뀌게 만들것이다.
페이지 버튼을 클릭하면 환면이 바뀌는 함수를 만들었다. 이것을 vue에 적용을 해보자.
<button
class="btn btn-outline-secondary"
type="submit"
@click="retrieveDept"
>검색</button>
검색 버튼을 누르면 retrieveDept이 실행되어 input에 입력된 값에 해당하는 데이터를 보여주게 된다.
셀렉트 박스에서 값이 바뀌면 화면에 보여지는 데이터가 변경되게 만들것이다.
<select
class="form-select form-select-sm"
v-model="pageSize"
@change="pageSizeChange">
<!-- todo vue반목문 실행 -->
<option v-for="(data, index) in pageSizes" :key="index" :value="data">{{data}}</option>
</select>
셀렉트 박스는 값이 변화할때 데이터가 변화하므로 @change 속성을 사용하고, pageSizeChange함수를 사용하여 페이지 사이즈가 변화면 보여지는 데이터를 바꿔주었다.
페이지 번호를 누르면 해당 페이지의 값들이 보이게 하자
<b-pagination
class="col-12 mb-3"
v-model="page"
:total-rows="count"
:per-page="pageSize"
@click="retrieveDept"
></b-pagination>
페이지를 클릭해야하는 것이므로 @click속성을 사용했고,그 페이지의 데이터를 보여주어야하므로 retrieveDept함수를 사용했다. 이때 그 페이지가 몇 페인지는 b-pagination가 retrieveDep에게 전달을 해준다.
하나의 페이지에 여러개의 url을 부여하고 싶다면 router에서 alias속성을 이용해 만들어 줄 수 있다.
const routes = [
{
path: '/',
alias: "/dept", // 추가 url을 생성할 수 있다. 이렇게 되면 두 url을 실행해도 하나의 페이지가 나온다.
component: () => import("../views/basic/dept/DeptList.vue")
},
]
/dept를 입력해도 DeptList.vue가 실행되게 만들었다
보통 다른 포트끼리 통신을 하면 해킹이라고 인식하고 데이터를 다운시킨다. spring은 8000번 포트이고 vue는8080이기에 둘을 호환시켜야 통신이 가능하다.
허용해주는 설정파일을 만듬으로 이것을 호환시켜줄 수 있는데 이것을 CORS보안 설정이라고한다.
CORS보안은 1개의 사이트에서 ip주소 또는 포트 번호가 다르면 강제로 차단하는 보안 기능이다. 웹브라우저에 기본적으로 적용이 되어있다.
@Configuration
public class WebConfig implements WebMvcConfigurer {// 얘가 cors를 허용하는 함수를 가진다.
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로를 허용한다.
.allowedOrigins("http://localhost:8080")// 이 port의 접근의 위의 경로는 다 허용한다.
.allowedMethods(
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name()
);// 이 방식들은 다 허용한다. 여기서 특정 방식을 빼면 그것들은 허용하지 않고 해킹으로 인식
}
}
전에 배웠듯이 자바로 설정파일을 만드려면 @Configuration을 붙여준다.
spring의 WebMvcConfigurer인터페이스의 addCorsMappings함수를 재정의 해주면된다.
매개변수로 받은 registry의 addMapping함수로 허용할 경로 범위를 설정해준다.
allowedOrigins함수로 접근 허용할 port를 설정해준다.
allowedMethods함수로 접근 가능한 통신 방식을 설정해주면 끝이다.
controller를 제외한 나머지는 페이지 기능 구현편의 클래스를 가져와서 다 똑같다. 그러므로 나머지는 생략하고 controller클래스만 자세하게 알아보자.
일반 컨트롤러는 return값이 jsp파일이었던것과 다르게 리턴값이 json데이터이다. vue는 내부의 스프링 프로그램이 아닌 외부의 vue와 통신을 하기에 json파일로 통신을 하게 된다.
Rest는 기존의 Get(R)방식과 Post(CUD)만 사용하던 방식에서 Get(R), Post(C), Put(U), Delete(D)로 만들기로 하자는 것을 Rest라고 하고,
이것을 이용해서 만든 프로그램을 Restful Application이라고 한다.
@RestController로 제작된 백엔드를 Rest API라고 한다.
@Slf4j
@RestController // 일반 컨트롤러는 return값이 jsp파일이었던것과 다르게 리턴값이 json데이터이다.
@RequestMapping("/api/basic")
public class DeptController {
private final DeptService deptService;
@Autowired
public DeptController(DeptService deptService) {
this.deptService = deptService;
}
public ResponseEntity<Object> findAll(// 프론트와 신호를 보낼 수 있게 해주는 타입이다. 어떤 자료형이 들어올지 모르니 Object로 해준다. 얘는 객체의 조상이라서 다 받아준다.
@RequestParam(defaultValue = "") String dname,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "3") int size
) {
try {
Pageable pageable = PageRequest.of(page, size);
Page<Dept> pageList = deptService.findAllByDnameContaning(dname, pageable);
Map<String, Object> response = new HashMap<>();// jsp에서는 model을 이용해서 키와 밸류를 프론트로 보내주었는데, 이번에는 그렇게 하지 못하니 Map을 이용하여 키 밸류로 만들어 보내주자.
response.put("dept", pageList.getContent()); // 부서 정보
response.put("currentPage", pageList.getNumber()); // 현재페이지 번호
response.put("totalItems", pageList.getTotalElements()); // 전체데이터 개수
response.put("totalPages", pageList.getTotalPages()); // 전체 페이지수
// 1) pageList 값이 없으면 : dB 테이블 없음 => NO_CONTENT(203) 신호를 보낸다.(203)
// 2) pageList 값이 있으면 : 성공 => OK(200)
if (pageList.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}else {
return new ResponseEntity<>(HttpStatus.OK);
}
}catch (Exception e){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); // 에러가 나면 이신호를 웹브라우저에 보내어 준다.
// 프로그램의 품질이 증가된다.
// 이걸해주면 에러가 나도 신호가 오기에 에러원인을 알 수있다.
// INTERNAL_SERVER_ERROR(500) 백엔드 서버 에러
}
}
}
위에서도 말했듯 json파일로 통신을 할 거기 때문에 @RequestMapping 어노테이션을 사용해주어야한다.
파일 통신시 에러가 나면 에러 신호도 같이 전달하기 위해 ResponseEntity객체타입을 사용한다. 이것을 사용하면 값과 신호를 같이 전달할 수 있다.
나머지는 페이지 기능의 controller와 같은데 한가지 다른점은 여기선 model을 사용하지 않는다. 모델은 spring내의 view에서 사용하는 것이고, 다른 view형태의 프로그램과 통신하기 위해서는 Map에 키, 밸류 형태로 전달을 해주면 된다.
만약 pageList가 비어있을 수도 있으니 if 문을 이용해서 신호를 전달하게 한다. HttpStatus의 NO_CONTENT메세지를 전달하게 한다. 내용이 없다면 203 있다면 200신호를 보낸다.
만약 에러가 날 수 있으니 에러처리를 해주고, 에러가 발생하면 INTERNAL_SERVER_ERROR를 날려준다. 여기서 에러가 나면 500에러인데 이것을통해 에러가 백엔드에서 발생했는지 프론트에서 발생했는지를 알 수 있다.
이렇게 모든 준비가 끝났다. 이제 결과를 확인하자.
값이 잘 나오는 것을 확인했다.
이렇게 vue와 spring간에 get방식 통신을 하게 됐다.