feat: 샘플 게시판 CRUD 기능 전체 추가
- 백엔드 엔티티, 서비스, DAO 및 REST 컨트롤러 계층 추가 - 데이터베이스 CRUD 처리를 위한 MyBatis PostgreSQL 매퍼 추가 - 생성, 조회, 수정, 삭제 전체 흐름을 지원하는 관리자용 Vue 컴포넌트 추가 - 게시글 등록 및 수정 요청에 대한 입력값 검증 추가
This commit is contained in:
@@ -0,0 +1,381 @@
|
|||||||
|
<!-- 샘플 게시판 CRUD 페이지 템플릿이다. -->
|
||||||
|
<template>
|
||||||
|
<!-- 페이지 전체 래퍼이다. -->
|
||||||
|
<div class="ui--content-wrapper">
|
||||||
|
<!-- 공통 브레드크럼을 출력한다. -->
|
||||||
|
<sdl-breadcrumb></sdl-breadcrumb>
|
||||||
|
<!-- 목록 헤더 영역이다. -->
|
||||||
|
<div class="ui--list-heading clearfix">
|
||||||
|
<!-- 전체 건수를 출력한다. -->
|
||||||
|
<span class="ui--text-total"><strong>Total</strong> {{ $filters.numberFormat(boardList.length) }}</span>
|
||||||
|
<!-- 우측 버튼 영역이다. -->
|
||||||
|
<div class="float-end">
|
||||||
|
<!-- 신규 등록 폼으로 초기화한다. -->
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" @click="resetForm()">새 글</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 목록과 입력 폼을 2단으로 배치한다. -->
|
||||||
|
<div class="row">
|
||||||
|
<!-- 좌측 목록 영역이다. -->
|
||||||
|
<div class="col-6">
|
||||||
|
<!-- 게시글 목록 테이블이다. -->
|
||||||
|
<table class="table table-bordered ui--table">
|
||||||
|
<!-- 컬럼 폭 정의이다. -->
|
||||||
|
<colgroup>
|
||||||
|
<!-- 번호 컬럼 폭이다. -->
|
||||||
|
<col style="width: 12%" />
|
||||||
|
<!-- 제목 컬럼 폭이다. -->
|
||||||
|
<col style="width: 40%" />
|
||||||
|
<!-- 작성자 컬럼 폭이다. -->
|
||||||
|
<col style="width: 20%" />
|
||||||
|
<!-- 등록일 컬럼 폭이다. -->
|
||||||
|
<col style="width: 28%" />
|
||||||
|
</colgroup>
|
||||||
|
<!-- 테이블 헤더이다. -->
|
||||||
|
<thead>
|
||||||
|
<!-- 헤더 행이다. -->
|
||||||
|
<tr>
|
||||||
|
<!-- 번호 헤더이다. -->
|
||||||
|
<th scope="col">번호</th>
|
||||||
|
<!-- 제목 헤더이다. -->
|
||||||
|
<th scope="col">제목</th>
|
||||||
|
<!-- 작성자 헤더이다. -->
|
||||||
|
<th scope="col">작성자</th>
|
||||||
|
<!-- 등록일 헤더이다. -->
|
||||||
|
<th scope="col">등록일</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<!-- 테이블 본문이다. -->
|
||||||
|
<tbody>
|
||||||
|
<!-- 데이터가 없을 때 안내 행을 출력한다. -->
|
||||||
|
<tr v-if="boardList.length === 0">
|
||||||
|
<!-- 빈 목록 안내 셀이다. -->
|
||||||
|
<td colspan="4" class="text-center">등록된 게시글이 없습니다.</td>
|
||||||
|
</tr>
|
||||||
|
<!-- 게시글 목록을 반복 출력한다. -->
|
||||||
|
<tr v-for="board in boardList" :key="board.id" :class="{ 'table-primary': board.id === form.id }">
|
||||||
|
<!-- 게시글 번호를 출력한다. -->
|
||||||
|
<td class="text-center">{{ board.id }}</td>
|
||||||
|
<!-- 제목을 클릭하면 상세를 조회한다. -->
|
||||||
|
<td>
|
||||||
|
<!-- 상세 조회 버튼처럼 동작하는 링크이다. -->
|
||||||
|
<a href="javascript:void(0);" @click="selectBoard(board.id)">{{ board.title }}</a>
|
||||||
|
</td>
|
||||||
|
<!-- 작성자를 출력한다. -->
|
||||||
|
<td class="text-center">{{ board.author }}</td>
|
||||||
|
<!-- 등록일을 출력한다. -->
|
||||||
|
<td class="text-center">{{ formatDate(board.createdAt) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- 우측 입력 폼 영역이다. -->
|
||||||
|
<div class="col-6">
|
||||||
|
<!-- 입력 폼 컨테이너이다. -->
|
||||||
|
<div class="ui--form-container">
|
||||||
|
<!-- 폼 제목을 출력한다. -->
|
||||||
|
<h4 class="mb-3">Sample Board {{ mode === 'CREATE' ? '등록' : '수정' }}</h4>
|
||||||
|
<!-- 번호와 등록일을 표시한다. -->
|
||||||
|
<div class="row">
|
||||||
|
<!-- 번호 영역이다. -->
|
||||||
|
<div class="col-6 mb-3">
|
||||||
|
<!-- 번호 라벨이다. -->
|
||||||
|
<label>번호</label>
|
||||||
|
<!-- 번호 입력창이다. -->
|
||||||
|
<input :value="form.id || ''" type="text" class="form-control" readonly />
|
||||||
|
</div>
|
||||||
|
<!-- 등록일 영역이다. -->
|
||||||
|
<div class="col-6 mb-3">
|
||||||
|
<!-- 등록일 라벨이다. -->
|
||||||
|
<label>등록일</label>
|
||||||
|
<!-- 등록일 입력창이다. -->
|
||||||
|
<input :value="formatDate(form.createdAt)" type="text" class="form-control" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 제목 입력 영역이다. -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<!-- 제목 라벨이다. -->
|
||||||
|
<label>제목 <span class="ui--text-required">*</span></label>
|
||||||
|
<!-- 제목 입력창이다. -->
|
||||||
|
<input v-model.trim="form.title" type="text" class="form-control" maxlength="200" />
|
||||||
|
</div>
|
||||||
|
<!-- 작성자 입력 영역이다. -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<!-- 작성자 라벨이다. -->
|
||||||
|
<label>작성자 <span class="ui--text-required">*</span></label>
|
||||||
|
<!-- 작성자 입력창이다. -->
|
||||||
|
<input v-model.trim="form.author" type="text" class="form-control" maxlength="100" />
|
||||||
|
</div>
|
||||||
|
<!-- 내용 입력 영역이다. -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<!-- 내용 라벨이다. -->
|
||||||
|
<label>내용 <span class="ui--text-required">*</span></label>
|
||||||
|
<!-- 내용 입력창이다. -->
|
||||||
|
<textarea v-model.trim="form.content" class="form-control" rows="10"></textarea>
|
||||||
|
</div>
|
||||||
|
<!-- 버튼 영역이다. -->
|
||||||
|
<div class="ui--button-container">
|
||||||
|
<!-- 입력 폼을 초기화한다. -->
|
||||||
|
<button type="button" class="btn btn-secondary btn-lg" @click="resetForm()">초기화</button>
|
||||||
|
<!-- 현재 입력값을 저장한다. -->
|
||||||
|
<button type="button" class="btn btn-primary btn-lg" @click="saveBoard()">저장</button>
|
||||||
|
<!-- 수정 모드일 때만 삭제 버튼을 노출한다. -->
|
||||||
|
<button v-if="mode === 'EDIT'" type="button" class="btn btn-danger btn-lg" @click="confirmDelete(form.id)">삭제</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 샘플 게시판 CRUD 페이지 스크립트이다. -->
|
||||||
|
<script>
|
||||||
|
// HTTP 통신을 위해 axios를 사용한다.
|
||||||
|
import axios from 'axios';
|
||||||
|
// 공통 유틸을 사용한다.
|
||||||
|
import SDLUtil from '@/utils/SDLUtil';
|
||||||
|
|
||||||
|
// 샘플 게시판 CRUD 컴포넌트를 정의한다.
|
||||||
|
export default {
|
||||||
|
// 컴포넌트 이름을 정의한다.
|
||||||
|
name: 'PgBoardPage',
|
||||||
|
// 화면 상태를 정의한다.
|
||||||
|
data() {
|
||||||
|
// 초기 상태를 반환한다.
|
||||||
|
return {
|
||||||
|
// 게시글 목록 상태이다.
|
||||||
|
boardList: [],
|
||||||
|
// 현재 폼 모드 상태이다.
|
||||||
|
mode: 'CREATE',
|
||||||
|
// 입력 폼 상태이다.
|
||||||
|
form: {
|
||||||
|
id: null,
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
author: '',
|
||||||
|
createdAt: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 화면 동작 메서드를 정의한다.
|
||||||
|
methods: {
|
||||||
|
// 빈 입력 폼 객체를 생성한다.
|
||||||
|
createEmptyForm() {
|
||||||
|
// 신규 등록용 기본값을 반환한다.
|
||||||
|
return {
|
||||||
|
// 게시글 번호 기본값이다.
|
||||||
|
id: null,
|
||||||
|
// 제목 기본값이다.
|
||||||
|
title: '',
|
||||||
|
// 내용 기본값이다.
|
||||||
|
content: '',
|
||||||
|
// 작성자 기본값이다.
|
||||||
|
author: '',
|
||||||
|
// 등록일 기본값이다.
|
||||||
|
createdAt: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 페이지 초기화 메서드이다.
|
||||||
|
init() {
|
||||||
|
// 목록을 먼저 조회한다.
|
||||||
|
this.fetchBoardList();
|
||||||
|
},
|
||||||
|
// 게시글 목록을 조회한다.
|
||||||
|
async fetchBoardList() {
|
||||||
|
// 로딩 바를 표시한다.
|
||||||
|
SDLUtil.showLoadingBar(true);
|
||||||
|
try {
|
||||||
|
// 목록 조회 API를 호출한다.
|
||||||
|
const { data } = await axios.get(`${SDLUtil.API_URL}/pg-board/samples`);
|
||||||
|
// 조회 결과를 목록 상태에 저장한다.
|
||||||
|
this.boardList = data || [];
|
||||||
|
} catch (err) {
|
||||||
|
// 오류가 발생하면 공통 에러 알림을 출력한다.
|
||||||
|
SDLUtil.errorAlert(err);
|
||||||
|
} finally {
|
||||||
|
// 로딩 바를 종료한다.
|
||||||
|
SDLUtil.showLoadingBar(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 게시글 상세를 조회한다.
|
||||||
|
async selectBoard(id) {
|
||||||
|
// 로딩 바를 표시한다.
|
||||||
|
SDLUtil.showLoadingBar(true);
|
||||||
|
try {
|
||||||
|
// 상세 조회 API를 호출한다.
|
||||||
|
const { data } = await axios.get(`${SDLUtil.API_URL}/pg-board/samples/${id}`);
|
||||||
|
// 조회한 데이터를 폼에 반영한다.
|
||||||
|
this.form = {
|
||||||
|
// 게시글 번호를 반영한다.
|
||||||
|
id: data.id,
|
||||||
|
// 제목을 반영한다.
|
||||||
|
title: data.title,
|
||||||
|
// 내용을 반영한다.
|
||||||
|
content: data.content,
|
||||||
|
// 작성자를 반영한다.
|
||||||
|
author: data.author,
|
||||||
|
// 등록일을 반영한다.
|
||||||
|
createdAt: data.createdAt,
|
||||||
|
};
|
||||||
|
// 수정 모드로 전환한다.
|
||||||
|
this.mode = 'EDIT';
|
||||||
|
} catch (err) {
|
||||||
|
// 오류가 발생하면 공통 에러 알림을 출력한다.
|
||||||
|
SDLUtil.errorAlert(err);
|
||||||
|
} finally {
|
||||||
|
// 로딩 바를 종료한다.
|
||||||
|
SDLUtil.showLoadingBar(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 입력 폼을 신규 등록 상태로 초기화한다.
|
||||||
|
resetForm() {
|
||||||
|
// 모드를 등록으로 변경한다.
|
||||||
|
this.mode = 'CREATE';
|
||||||
|
// 폼 값을 초기화한다.
|
||||||
|
this.form = this.createEmptyForm();
|
||||||
|
},
|
||||||
|
// 입력값 유효성을 검증한다.
|
||||||
|
validateForm() {
|
||||||
|
// 제목 입력 여부를 검증한다.
|
||||||
|
if (!this.form.title) {
|
||||||
|
// 제목 누락 알림을 출력한다.
|
||||||
|
SDLUtil.alert('제목을 입력하세요.');
|
||||||
|
// 검증 실패를 반환한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 작성자 입력 여부를 검증한다.
|
||||||
|
if (!this.form.author) {
|
||||||
|
// 작성자 누락 알림을 출력한다.
|
||||||
|
SDLUtil.alert('작성자를 입력하세요.');
|
||||||
|
// 검증 실패를 반환한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 내용 입력 여부를 검증한다.
|
||||||
|
if (!this.form.content) {
|
||||||
|
// 내용 누락 알림을 출력한다.
|
||||||
|
SDLUtil.alert('내용을 입력하세요.');
|
||||||
|
// 검증 실패를 반환한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 모든 검증을 통과했음을 반환한다.
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 현재 폼 데이터를 저장한다.
|
||||||
|
async saveBoard() {
|
||||||
|
// 저장 전 유효성을 검증한다.
|
||||||
|
if (!this.validateForm()) {
|
||||||
|
// 검증 실패 시 저장을 중단한다.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 서버로 전송할 payload를 구성한다.
|
||||||
|
const payload = {
|
||||||
|
// 제목을 전송한다.
|
||||||
|
title: this.form.title,
|
||||||
|
// 내용을 전송한다.
|
||||||
|
content: this.form.content,
|
||||||
|
// 작성자를 전송한다.
|
||||||
|
author: this.form.author,
|
||||||
|
};
|
||||||
|
// 로딩 바를 표시한다.
|
||||||
|
SDLUtil.showLoadingBar(true);
|
||||||
|
try {
|
||||||
|
// 저장 결과를 담을 변수를 선언한다.
|
||||||
|
let savedData = null;
|
||||||
|
// 수정 모드 여부를 확인한다.
|
||||||
|
if (this.mode === 'EDIT' && this.form.id) {
|
||||||
|
// 수정 API를 호출한다.
|
||||||
|
const { data } = await axios.put(`${SDLUtil.API_URL}/pg-board/samples/${this.form.id}`, payload);
|
||||||
|
// 수정 결과를 저장한다.
|
||||||
|
savedData = data;
|
||||||
|
} else {
|
||||||
|
// 등록 API를 호출한다.
|
||||||
|
const { data } = await axios.post(`${SDLUtil.API_URL}/pg-board/samples`, payload);
|
||||||
|
// 등록 결과를 저장한다.
|
||||||
|
savedData = data;
|
||||||
|
}
|
||||||
|
// 저장 후 목록을 새로 조회한다.
|
||||||
|
await this.fetchBoardList();
|
||||||
|
// 저장된 게시글이 있으면 상세를 다시 로드한다.
|
||||||
|
if (savedData && savedData.id) {
|
||||||
|
// 저장된 게시글 상세를 선택한다.
|
||||||
|
await this.selectBoard(savedData.id);
|
||||||
|
}
|
||||||
|
// 저장 완료 알림을 출력한다.
|
||||||
|
SDLUtil.alert('저장되었습니다.');
|
||||||
|
} catch (err) {
|
||||||
|
// 오류가 발생하면 공통 에러 알림을 출력한다.
|
||||||
|
SDLUtil.errorAlert(err);
|
||||||
|
} finally {
|
||||||
|
// 로딩 바를 종료한다.
|
||||||
|
SDLUtil.showLoadingBar(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 삭제 전 사용자 확인을 수행한다.
|
||||||
|
confirmDelete(id) {
|
||||||
|
// 삭제 대상이 없으면 즉시 종료한다.
|
||||||
|
if (!id) {
|
||||||
|
// 함수 실행을 종료한다.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 공통 확인 팝업을 출력한다.
|
||||||
|
SDLUtil.confirm({
|
||||||
|
// 확인 메시지를 설정한다.
|
||||||
|
msg: '삭제하시겠습니까?',
|
||||||
|
// 확인 버튼 이벤트를 설정한다.
|
||||||
|
onOkEvt: () => {
|
||||||
|
// 실제 삭제를 수행한다.
|
||||||
|
this.deleteBoard(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 게시글을 삭제한다.
|
||||||
|
async deleteBoard(id) {
|
||||||
|
// 로딩 바를 표시한다.
|
||||||
|
SDLUtil.showLoadingBar(true);
|
||||||
|
try {
|
||||||
|
// 삭제 API를 호출한다.
|
||||||
|
await axios.delete(`${SDLUtil.API_URL}/pg-board/samples/${id}`);
|
||||||
|
// 삭제 후 목록을 새로 조회한다.
|
||||||
|
await this.fetchBoardList();
|
||||||
|
// 삭제 후 폼을 초기화한다.
|
||||||
|
this.resetForm();
|
||||||
|
// 삭제 완료 알림을 출력한다.
|
||||||
|
SDLUtil.alert('삭제되었습니다.');
|
||||||
|
} catch (err) {
|
||||||
|
// 오류가 발생하면 공통 에러 알림을 출력한다.
|
||||||
|
SDLUtil.errorAlert(err);
|
||||||
|
} finally {
|
||||||
|
// 로딩 바를 종료한다.
|
||||||
|
SDLUtil.showLoadingBar(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 등록일 표시 형식을 변환한다.
|
||||||
|
formatDate(value) {
|
||||||
|
// 값이 없으면 빈 문자열을 반환한다.
|
||||||
|
if (!value) {
|
||||||
|
// 빈 문자열을 반환한다.
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// 공통 날짜 포맷 필터를 사용해 문자열을 반환한다.
|
||||||
|
return this.$filters.dateFormat(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 컴포넌트 마운트 후 초기화를 수행한다.
|
||||||
|
mounted() {
|
||||||
|
// 다음 렌더 사이클에서 초기화를 호출한다.
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 화면 초기화를 실행한다.
|
||||||
|
this.init();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- 샘플 게시판 CRUD 페이지 스타일이다. -->
|
||||||
|
<style scoped>
|
||||||
|
/* 제목 링크 커서를 포인터로 보이게 한다. */
|
||||||
|
a {
|
||||||
|
/* 링크 클릭 가능 상태를 표현한다. */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.samsung.sample.board; // 샘플 게시판 서비스 패키지이다.
|
||||||
|
|
||||||
|
import java.util.List; // 목록 반환을 위해 List 타입을 사용한다.
|
||||||
|
|
||||||
|
import com.samsung.sample.board.entity.SampleBoard; // 샘플 게시판 엔티티를 사용한다.
|
||||||
|
|
||||||
|
public interface SampleBoardService { // 샘플 게시판 비즈니스 로직 인터페이스이다.
|
||||||
|
|
||||||
|
List<SampleBoard> getSampleBoardList(); // 게시글 목록 조회 기능이다.
|
||||||
|
|
||||||
|
SampleBoard getSampleBoard(Integer id); // 게시글 상세 조회 기능이다.
|
||||||
|
|
||||||
|
SampleBoard createSampleBoard(SampleBoard sampleBoard); // 게시글 등록 기능이다.
|
||||||
|
|
||||||
|
SampleBoard updateSampleBoard(SampleBoard sampleBoard); // 게시글 수정 기능이다.
|
||||||
|
|
||||||
|
void deleteSampleBoard(Integer id); // 게시글 삭제 기능이다.
|
||||||
|
} // 서비스 인터페이스 정의를 종료한다.
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.samsung.sample.board.controller; // 샘플 게시판 컨트롤러 패키지이다.
|
||||||
|
|
||||||
|
import java.util.List; // 목록 반환을 위해 List 타입을 사용한다.
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils; // 문자열 검증을 위해 StringUtils를 사용한다.
|
||||||
|
import org.springframework.stereotype.Controller; // 일반 컨트롤러 등록을 위해 Controller를 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping; // 삭제 매핑을 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping; // 조회 매핑을 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable; // 경로 변수를 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping; // 등록 매핑을 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping; // 수정 매핑을 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody; // 요청 본문 매핑을 사용한다.
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody; // 메서드 단위 응답 바디 처리를 사용한다.
|
||||||
|
|
||||||
|
import com.samsung.sample.board.SampleBoardService; // 샘플 게시판 서비스 인터페이스를 사용한다.
|
||||||
|
import com.samsung.sample.board.entity.SampleBoard; // 샘플 게시판 엔티티를 사용한다.
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation; // Swagger 요약 설명을 사용한다.
|
||||||
|
import jakarta.servlet.http.HttpServletRequest; // 페이지 포워딩 요청 정보를 사용한다.
|
||||||
|
|
||||||
|
@Controller // 페이지 포워딩과 REST 응답을 함께 처리하는 컨트롤러를 등록한다.
|
||||||
|
public class SampleBoardController { // samples 테이블 CRUD API와 pgBoard 페이지 포워딩을 함께 처리하는 컨트롤러이다.
|
||||||
|
|
||||||
|
private final SampleBoardService sampleBoardService; // 샘플 게시판 서비스이다.
|
||||||
|
|
||||||
|
public SampleBoardController(SampleBoardService sampleBoardService) { // 생성자에서 서비스를 주입받는다.
|
||||||
|
this.sampleBoardService = sampleBoardService; // 주입받은 서비스를 필드에 저장한다.
|
||||||
|
} // 생성자를 종료한다.
|
||||||
|
|
||||||
|
@GetMapping({"/admin/pgBoard/sampleBoardPage"}) // pgBoard 페이지 URL을 처리한다.
|
||||||
|
public String forwardSampleBoardPage(HttpServletRequest request) { // 요청 URL을 유지한 채 프론트 진입점으로 전달한다.
|
||||||
|
return "forward:/index.html?redirect=" + request.getRequestURI(); // 현재 요청 URI를 redirect 파라미터로 전달한다.
|
||||||
|
} // 포워딩 메서드를 종료한다.
|
||||||
|
|
||||||
|
@ResponseBody // 목록 조회 결과를 JSON 본문으로 반환한다.
|
||||||
|
@Operation(summary = "샘플 게시판 목록 조회") // 게시글 목록 조회 API 설명이다.
|
||||||
|
@GetMapping("/pg-board/samples") // GET 목록 조회 경로를 매핑한다.
|
||||||
|
public List<SampleBoard> getSampleBoardList() { // 게시글 목록을 반환한다.
|
||||||
|
return sampleBoardService.getSampleBoardList(); // 서비스 목록 조회를 호출한다.
|
||||||
|
} // 목록 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
@ResponseBody // 상세 조회 결과를 JSON 본문으로 반환한다.
|
||||||
|
@Operation(summary = "샘플 게시판 상세 조회") // 게시글 상세 조회 API 설명이다.
|
||||||
|
@GetMapping("/pg-board/samples/{id}") // GET 상세 조회 경로를 매핑한다.
|
||||||
|
public SampleBoard getSampleBoard(@PathVariable Integer id) { // 게시글 상세를 반환한다.
|
||||||
|
return sampleBoardService.getSampleBoard(id); // 서비스 상세 조회를 호출한다.
|
||||||
|
} // 상세 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
@ResponseBody // 등록 결과를 JSON 본문으로 반환한다.
|
||||||
|
@Operation(summary = "샘플 게시판 등록") // 게시글 등록 API 설명이다.
|
||||||
|
@PostMapping("/pg-board/samples") // POST 등록 경로를 매핑한다.
|
||||||
|
public SampleBoard createSampleBoard(@RequestBody SampleBoard sampleBoard) { // 게시글을 등록한다.
|
||||||
|
validateSampleBoard(sampleBoard); // 요청 데이터를 검증한다.
|
||||||
|
return sampleBoardService.createSampleBoard(sampleBoard); // 서비스 등록을 호출한다.
|
||||||
|
} // 등록 메서드를 종료한다.
|
||||||
|
|
||||||
|
@ResponseBody // 수정 결과를 JSON 본문으로 반환한다.
|
||||||
|
@Operation(summary = "샘플 게시판 수정") // 게시글 수정 API 설명이다.
|
||||||
|
@PutMapping("/pg-board/samples/{id}") // PUT 수정 경로를 매핑한다.
|
||||||
|
public SampleBoard updateSampleBoard(@PathVariable Integer id, @RequestBody SampleBoard sampleBoard) { // 게시글을 수정한다.
|
||||||
|
validateSampleBoard(sampleBoard); // 요청 데이터를 검증한다.
|
||||||
|
sampleBoard.setId(id); // 경로의 식별자를 엔티티에 설정한다.
|
||||||
|
return sampleBoardService.updateSampleBoard(sampleBoard); // 서비스 수정을 호출한다.
|
||||||
|
} // 수정 메서드를 종료한다.
|
||||||
|
|
||||||
|
@ResponseBody // 삭제 결과를 HTTP 본문 없이 처리한다.
|
||||||
|
@Operation(summary = "샘플 게시판 삭제") // 게시글 삭제 API 설명이다.
|
||||||
|
@DeleteMapping("/pg-board/samples/{id}") // DELETE 삭제 경로를 매핑한다.
|
||||||
|
public void deleteSampleBoard(@PathVariable Integer id) { // 게시글을 삭제한다.
|
||||||
|
sampleBoardService.deleteSampleBoard(id); // 서비스 삭제를 호출한다.
|
||||||
|
} // 삭제 메서드를 종료한다.
|
||||||
|
|
||||||
|
private void validateSampleBoard(SampleBoard sampleBoard) { // 등록 및 수정 요청 데이터를 검증한다.
|
||||||
|
if (sampleBoard == null) { // 요청 본문이 없는 경우를 확인한다.
|
||||||
|
throw new IllegalArgumentException("게시글 정보는 필수입니다."); // 필수 데이터 누락 예외를 발생시킨다.
|
||||||
|
} // null 검사 블록을 종료한다.
|
||||||
|
if (StringUtils.isBlank(sampleBoard.getTitle())) { // 제목 공백 여부를 확인한다.
|
||||||
|
throw new IllegalArgumentException("제목은 필수입니다."); // 제목 누락 예외를 발생시킨다.
|
||||||
|
} // 제목 검사 블록을 종료한다.
|
||||||
|
if (StringUtils.isBlank(sampleBoard.getContent())) { // 본문 공백 여부를 확인한다.
|
||||||
|
throw new IllegalArgumentException("내용은 필수입니다."); // 본문 누락 예외를 발생시킨다.
|
||||||
|
} // 본문 검사 블록을 종료한다.
|
||||||
|
if (StringUtils.isBlank(sampleBoard.getAuthor())) { // 작성자 공백 여부를 확인한다.
|
||||||
|
throw new IllegalArgumentException("작성자는 필수입니다."); // 작성자 누락 예외를 발생시킨다.
|
||||||
|
} // 작성자 검사 블록을 종료한다.
|
||||||
|
} // 검증 메서드를 종료한다.
|
||||||
|
} // 컨트롤러 정의를 종료한다.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.samsung.sample.board.dao; // 샘플 게시판 DAO 패키지이다.
|
||||||
|
|
||||||
|
import java.util.List; // 목록 조회를 위해 List 타입을 사용한다.
|
||||||
|
|
||||||
|
import org.apache.ibatis.session.SqlSession; // MyBatis SqlSession을 사용한다.
|
||||||
|
import org.springframework.stereotype.Repository; // DAO 빈 등록을 위해 Repository를 사용한다.
|
||||||
|
|
||||||
|
import com.samsung.sample.board.entity.SampleBoard; // 샘플 게시판 엔티티를 사용한다.
|
||||||
|
|
||||||
|
@Repository // 샘플 게시판 DAO를 스프링 빈으로 등록한다.
|
||||||
|
public class SampleBoardDao { // samples 테이블 CRUD를 처리하는 DAO이다.
|
||||||
|
|
||||||
|
private static final String MAPPER = "sampleBoardMapper."; // MyBatis 매퍼 네임스페이스 접두어이다.
|
||||||
|
|
||||||
|
private final SqlSession sqlSession; // MyBatis 세션 객체이다.
|
||||||
|
|
||||||
|
public SampleBoardDao(SqlSession sqlSession) { // 생성자에서 의존성을 주입받는다.
|
||||||
|
this.sqlSession = sqlSession; // 주입받은 세션을 필드에 저장한다.
|
||||||
|
} // 생성자를 종료한다.
|
||||||
|
|
||||||
|
public List<SampleBoard> selectSampleBoardList() { // 게시글 목록을 조회한다.
|
||||||
|
return sqlSession.selectList(MAPPER + "selectSampleBoardList"); // 매퍼의 목록 조회 쿼리를 실행한다.
|
||||||
|
} // 목록 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
public SampleBoard selectSampleBoard(Integer id) { // 게시글 한 건을 조회한다.
|
||||||
|
return sqlSession.selectOne(MAPPER + "selectSampleBoard", id); // 매퍼의 단건 조회 쿼리를 실행한다.
|
||||||
|
} // 단건 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
public void insertSampleBoard(SampleBoard sampleBoard) { // 게시글을 등록한다.
|
||||||
|
sqlSession.insert(MAPPER + "insertSampleBoard", sampleBoard); // 매퍼의 등록 쿼리를 실행한다.
|
||||||
|
} // 등록 메서드를 종료한다.
|
||||||
|
|
||||||
|
public void updateSampleBoard(SampleBoard sampleBoard) { // 게시글을 수정한다.
|
||||||
|
sqlSession.update(MAPPER + "updateSampleBoard", sampleBoard); // 매퍼의 수정 쿼리를 실행한다.
|
||||||
|
} // 수정 메서드를 종료한다.
|
||||||
|
|
||||||
|
public void deleteSampleBoard(Integer id) { // 게시글을 삭제한다.
|
||||||
|
sqlSession.delete(MAPPER + "deleteSampleBoard", id); // 매퍼의 삭제 쿼리를 실행한다.
|
||||||
|
} // 삭제 메서드를 종료한다.
|
||||||
|
} // DAO 정의를 종료한다.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.samsung.sample.board.entity; // 샘플 게시판 엔티티 패키지이다.
|
||||||
|
|
||||||
|
import java.util.Date; // 생성일시 표현을 위해 Date 타입을 사용한다.
|
||||||
|
|
||||||
|
import lombok.Data; // Lombok Data 어노테이션을 사용한다.
|
||||||
|
|
||||||
|
@Data // 샘플 게시판 엔티티의 접근자 메서드를 자동 생성한다.
|
||||||
|
public class SampleBoard { // samples 테이블 한 건을 표현하는 엔티티이다.
|
||||||
|
|
||||||
|
private Integer id; // 게시글 식별자이다.
|
||||||
|
|
||||||
|
private String title; // 게시글 제목이다.
|
||||||
|
|
||||||
|
private String content; // 게시글 본문이다.
|
||||||
|
|
||||||
|
private String author; // 게시글 작성자이다.
|
||||||
|
|
||||||
|
private Date createdAt; // 게시글 생성일시이다.
|
||||||
|
} // 엔티티 정의를 종료한다.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.samsung.sample.board.impl; // 샘플 게시판 서비스 구현 패키지이다.
|
||||||
|
|
||||||
|
import java.util.List; // 목록 반환을 위해 List 타입을 사용한다.
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service; // 서비스 빈 등록을 위해 Service를 사용한다.
|
||||||
|
|
||||||
|
import com.samsung.sample.board.SampleBoardService; // 샘플 게시판 서비스 인터페이스를 사용한다.
|
||||||
|
import com.samsung.sample.board.dao.SampleBoardDao; // 샘플 게시판 DAO를 사용한다.
|
||||||
|
import com.samsung.sample.board.entity.SampleBoard; // 샘플 게시판 엔티티를 사용한다.
|
||||||
|
|
||||||
|
@Service // 샘플 게시판 서비스 구현체를 스프링 빈으로 등록한다.
|
||||||
|
public class SampleBoardServiceImpl implements SampleBoardService { // 샘플 게시판 서비스 구현체이다.
|
||||||
|
|
||||||
|
private final SampleBoardDao sampleBoardDao; // 샘플 게시판 DAO이다.
|
||||||
|
|
||||||
|
public SampleBoardServiceImpl(SampleBoardDao sampleBoardDao) { // 생성자에서 DAO를 주입받는다.
|
||||||
|
this.sampleBoardDao = sampleBoardDao; // 주입받은 DAO를 필드에 저장한다.
|
||||||
|
} // 생성자를 종료한다.
|
||||||
|
|
||||||
|
@Override // 목록 조회 인터페이스 구현이다.
|
||||||
|
public List<SampleBoard> getSampleBoardList() { // 게시글 목록을 조회한다.
|
||||||
|
return sampleBoardDao.selectSampleBoardList(); // DAO 목록 조회를 호출한다.
|
||||||
|
} // 목록 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
@Override // 상세 조회 인터페이스 구현이다.
|
||||||
|
public SampleBoard getSampleBoard(Integer id) { // 게시글 상세를 조회한다.
|
||||||
|
return sampleBoardDao.selectSampleBoard(id); // DAO 단건 조회를 호출한다.
|
||||||
|
} // 상세 조회 메서드를 종료한다.
|
||||||
|
|
||||||
|
@Override // 등록 인터페이스 구현이다.
|
||||||
|
public SampleBoard createSampleBoard(SampleBoard sampleBoard) { // 게시글을 등록한다.
|
||||||
|
sampleBoardDao.insertSampleBoard(sampleBoard); // DAO 등록을 호출한다.
|
||||||
|
return sampleBoardDao.selectSampleBoard(sampleBoard.getId()); // 등록된 게시글을 다시 조회해 반환한다.
|
||||||
|
} // 등록 메서드를 종료한다.
|
||||||
|
|
||||||
|
@Override // 수정 인터페이스 구현이다.
|
||||||
|
public SampleBoard updateSampleBoard(SampleBoard sampleBoard) { // 게시글을 수정한다.
|
||||||
|
sampleBoardDao.updateSampleBoard(sampleBoard); // DAO 수정을 호출한다.
|
||||||
|
return sampleBoardDao.selectSampleBoard(sampleBoard.getId()); // 수정된 게시글을 다시 조회해 반환한다.
|
||||||
|
} // 수정 메서드를 종료한다.
|
||||||
|
|
||||||
|
@Override // 삭제 인터페이스 구현이다.
|
||||||
|
public void deleteSampleBoard(Integer id) { // 게시글을 삭제한다.
|
||||||
|
sampleBoardDao.deleteSampleBoard(id); // DAO 삭제를 호출한다.
|
||||||
|
} // 삭제 메서드를 종료한다.
|
||||||
|
} // 서비스 구현체 정의를 종료한다.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!-- 샘플 게시판 MyBatis 매퍼 선언부이다. -->
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<!-- MyBatis 매퍼 DTD 선언이다. -->
|
||||||
|
|
||||||
|
<mapper namespace="sampleBoardMapper"> <!-- 샘플 게시판 매퍼 네임스페이스이다. -->
|
||||||
|
|
||||||
|
<resultMap id="sampleBoardResultMap" type="com.samsung.sample.board.entity.SampleBoard"> <!-- samples 테이블과 엔티티 매핑 정의이다. -->
|
||||||
|
<id property="id" column="id"/> <!-- 기본키 컬럼을 엔티티 id 필드에 매핑한다. -->
|
||||||
|
<result property="title" column="title"/> <!-- 제목 컬럼을 엔티티 title 필드에 매핑한다. -->
|
||||||
|
<result property="content" column="content"/> <!-- 내용 컬럼을 엔티티 content 필드에 매핑한다. -->
|
||||||
|
<result property="author" column="author"/> <!-- 작성자 컬럼을 엔티티 author 필드에 매핑한다. -->
|
||||||
|
<result property="createdAt" column="created_at"/> <!-- 생성일시 컬럼을 엔티티 createdAt 필드에 매핑한다. -->
|
||||||
|
</resultMap> <!-- 결과 매핑 정의를 종료한다. -->
|
||||||
|
|
||||||
|
<select id="selectSampleBoardList" resultMap="sampleBoardResultMap"> <!-- 게시글 목록 조회 쿼리이다. -->
|
||||||
|
SELECT id, title, content, author, created_at <!-- 조회할 컬럼 목록이다. -->
|
||||||
|
FROM public.samples <!-- samples 테이블을 조회한다. -->
|
||||||
|
ORDER BY id DESC <!-- 최신 게시글이 먼저 오도록 정렬한다. -->
|
||||||
|
</select> <!-- 목록 조회 쿼리를 종료한다. -->
|
||||||
|
|
||||||
|
<select id="selectSampleBoard" parameterType="int" resultMap="sampleBoardResultMap"> <!-- 게시글 상세 조회 쿼리이다. -->
|
||||||
|
SELECT id, title, content, author, created_at <!-- 조회할 컬럼 목록이다. -->
|
||||||
|
FROM public.samples <!-- samples 테이블을 조회한다. -->
|
||||||
|
WHERE id = #{id} <!-- 식별자로 단건을 조회한다. -->
|
||||||
|
</select> <!-- 상세 조회 쿼리를 종료한다. -->
|
||||||
|
|
||||||
|
<insert id="insertSampleBoard" parameterType="com.samsung.sample.board.entity.SampleBoard" useGeneratedKeys="true" keyProperty="id"> <!-- 게시글 등록 쿼리이다. -->
|
||||||
|
INSERT INTO public.samples (title, content, author) <!-- 등록할 컬럼 목록이다. -->
|
||||||
|
VALUES (#{title}, #{content}, #{author}) <!-- 입력값을 바인딩해 저장한다. -->
|
||||||
|
</insert> <!-- 등록 쿼리를 종료한다. -->
|
||||||
|
|
||||||
|
<update id="updateSampleBoard" parameterType="com.samsung.sample.board.entity.SampleBoard"> <!-- 게시글 수정 쿼리이다. -->
|
||||||
|
UPDATE public.samples <!-- samples 테이블을 수정한다. -->
|
||||||
|
SET title = #{title}, <!-- 제목을 수정한다. -->
|
||||||
|
content = #{content}, <!-- 내용을 수정한다. -->
|
||||||
|
author = #{author} <!-- 작성자를 수정한다. -->
|
||||||
|
WHERE id = #{id} <!-- 식별자로 대상 행을 지정한다. -->
|
||||||
|
</update> <!-- 수정 쿼리를 종료한다. -->
|
||||||
|
|
||||||
|
<delete id="deleteSampleBoard" parameterType="int"> <!-- 게시글 삭제 쿼리이다. -->
|
||||||
|
DELETE FROM public.samples <!-- samples 테이블에서 삭제한다. -->
|
||||||
|
WHERE id = #{id} <!-- 식별자로 대상 행을 지정한다. -->
|
||||||
|
</delete> <!-- 삭제 쿼리를 종료한다. -->
|
||||||
|
</mapper> <!-- 매퍼 정의를 종료한다. -->
|
||||||
Reference in New Issue
Block a user