Spring Boot, JSP 게시판 만들기
Spring Boot와 Gradle 및 JSP의 조합으로 간단한 게시판을 만들어 보겠습니다.
1. gradle 설정
plugins {
id 'java'
id 'war'
id 'org.springframework.boot' version '3.1.10'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.spring'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
/* spring boot web service */
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
/* lombok */
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
/* jasper, JSTL */
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
/* MSSQL 연결 */
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
}
tasks.named('bootBuildImage') {
builder = 'paketobuildpacks/builder-jammy-base:latest'
}
tasks.named('test') {
useJUnitPlatform()
}
spring boot 3.1.10 버전과 Java 17 버전을 사용하였습니다.
의존성 추가로는
spring boot web service와 lombok, jasper, jstl을 추가 하였습니다.
spring boot web service는 spring boot web service의 기본 의존성 구성이며( DispatcherServlet 등등 자동 설정)
lombok은 @Data 어노테이션을 이용하여 getter, setter 자동 생성하고, Controller URL Mapping 위해
jasper, jstl 의존성을 추가하였습니다.
Application Main method
@SpringBootApplication
@ComponentScan("com.spring.config")
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
@ComponentScan("com.spring.config") 컴포넌트 스캔 어노테이션을 이용하여
SpringBoot의 config 설정이 되어있는 java 파일을 스캔합니다.(스캔 할 해당 패키지 경로)
WebConfig.java
@Configuration
@EnableWebMvc
@ComponentScan("com.spring.mvc.*")
public class WebConfig implements WebMvcConfigurer {
/*@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp");
viewResolver.setSuffix(".jsp");
return viewResolver;
}*/
/* viewResolver 설정 WebMvcConfigurer 인터페이스 상속 */
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/jsp", ".jsp");
}
가장 먼저 JSP 파일을 연결 시켜주는 메소드를 작성합니다. ( viewResolver )
implements WebMvcConfigurer 인터페이스를 상속받고 @EnableWebMvc 어노테이션을 사용하게되면
위 예시 코드처럼 직접 @Override 하여 메소드를 구현할 수 있고, 또 한 @Bean를 추가하여 설정 할 수 있습니다.
경로는 JSP가 들어있는 폴더 경로에 맞춰 설정 해 줍니다.
JSP, Controller Class 연결하기
아래 코드와 같이 Controller class를 생성 해 줍니다.
@Controller
@RequestMapping(value="/board")
public class BoardController {
@RequestMapping(value="/boardList")
public String boardListView(){
return "/board/boardList";
}
}
필자의 경로는 아래와 같습니다.

JSP에서 Controller까지 정상적으로 연결 되는지 테스트 해 보면,
<%@ page pageEncoding="UTF-8" contentType="text/html; charset=utf-8" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<script type="text/javascript">
</script>
<title>게시판 목록</title>
</head>
<body>
<h3> 게시판 입니다. </h3>
</body>
</html>

브라우저에 URL 입력 후 확인 해 보면, 잘 연결된 것을 확인 할 수 있습니다.
서비스 로직 및 화면 흐름도 설계하기
이번에는 화면 설계와 화면 흐름 Service와 DB 연결 방법을 설명드리겠습니다. 간단하게 제작한 플로우차트는 아래와 같습니다.

위 사진의 흐름에 따라 필요한 JSP 화면은 게시글 목록, 게시글 상세, 총 2개의 화면이 필요합니다.(로그인과 회원가입의 경우 생략 하겠습니다. 로그인 처리는 간단하게 처리 할 예정 입니다.
로그인 관련 상세 처리하기 링크
https://dalbyutility.tistory.com/11
Spring, Java 웹 사이트 세션(Session) 관리하기(웹 사이트 로그인 기능, 웹 사이트 session, 웹 로그인 처
Spring, Java 웹 사이트 세션(Session) 관리하기(웹 사이트 로그인 기능, 웹 사이트 session, 웹 로그인 처리)Spring, Java를 활용하여 웹 사이트의 Session을 관리하는 class를 작성하여 로그인 기능
dalbyutility.tistory.com
Service 로직의 경우
SELECT - 목록, 상세 (2개)
UPDATE - 게시글 수정 (1개)
INSERT - 게시글 작성 (1개)
INSERT - 댓글 입력 (1개)
총 5개의 쿼리가 필요 해 보입니다.
Service와 화면 흐름 임시 구상 및 설계 작업이 완료되었다면, 추가 설정 및 Service method를 구현 해 보겠습니다.
DB의 경우 MSSQL을 사용할 예정이고, Sql Mapper-MyBatis를 이용하여 처리 할 것 입니다.(비슷한 녀석으로 Hibernate 이용한 JPA / ORM도 있음.)
이전에 정리한 MSSQL, MyBatis 설정방법 SpringBoot, MyBatis, MSSQL 연동하기 참고 부탁드립니다!
https://dalbyutility.tistory.com/16
SpringBoot, Java, MyBatis, MSSQL 연동하기(Java, SQL Mapper, JDBC, JNDI, MyBatis 설정, Java DB연동)
SpringBoot X MyBatis X MSSQL 연동하기사용된 버전 규격은 다음과 같습니다.Java : 17 versionSpringBoot : 2.7 ver versionMyBatis : 3.x ver version 0. 의존성 추가 Gradle implementation 'org.mybatis.spring.boot:mybatis-spring-boot-s
dalbyutility.tistory.com
DB와 MyBatis 설정 구성이 완료되었다면,
Service의 기본 구성은 아래와 같습니다. (추가적인 로직은 미구현)
@Service
public class BoardService {
@Autowired
BoardMapper boardMapper;
/* 게시글 목록 */
public List<BoardDTO> boardList(BoardDTO params){
return boardMapper.boardList(params);
}
/* 게시글 상세 */
public BoardDTO boardDetail(BoardDTO params){
return boardMapper.boardDetail(params);
}
/* 게시글 작성 */
@Transactional
public int boardInsert(BoardDTO params){
int result = boardMapper.boardInsert(params);
return result;
}
/* 게시글 수정 */
@Transactional
public int boardUpdate(BoardDTO params){
int result = boardMapper.boardUpdate(params);
return result;
}
/* 게시글 댓글 작성 */
@Transactional
public int boardCommentInsert(BoardDTO params){
int result = boardMapper.boardCommentInsert(params);
return result;
}
}
INSERT 및 UPDATE의 경우 @Transactional 어노테이션을 추가하여, 처리중 오류 발생 시 트랜잭션처리를 진행 합니다.
(트랜잭션에 관한 내용은 3장에 진행 할 예정입니다.)
Sql-Mapper기본 구성은(SQL이 Mapping될 Method) 아래와 같습니다.(SQL 미구현)
@Mapper
public interface BoardMapper {
/* 게시글 목록 */
public List<BoardDTO> boardList(BoardDTO params);
/* 게시글 상세 */
public BoardDTO boardDetail(BoardDTO params);
/* 게시글 작성 */
public int boardInsert(BoardDTO params);
/* 게시글 수정 */
public int boardUpdate(BoardDTO params);
/* 게시글 댓글 작성 */
public int boardCommentInsert(BoardDTO params);
}
Controller의 구성은 다음과 같습니다.
@Controller
@RequestMapping(value="/board")
public class BoardController {
@Autowired
BoardService boardService;
/* 게시글 목록 화면 */
@RequestMapping(value="/boardList")
public String boardListView(Model model, BoardDTO params){
model.addAttribute("data", boardService.boardList(params));
return "/board/boardList";
}
/* 게시글 상세 화면 */
@RequestMapping(value="/boardDetail")
public String boardDetailView(Model model, BoardDTO params){
model.addAttribute("data", boardService.boardDetail(params));
return "/board/boardDetail";
}
/* 게시글 작성 */
@RequestMapping(value="/boardInsert")
@ResponseBody
public int boardInsert(Model model, @RequestBody BoardDTO params){
return boardService.boardInsert(params);
}
/* 게시글 수정 */
@RequestMapping(value="/boardUpdate")
@ResponseBody
public int boardUpdate(Model model, @RequestBody BoardDTO params){
return boardService.boardUpdate(params);
}
/* 게시글 작성 */
@RequestMapping(value="/boardCommentInsert")
@ResponseBody
public int boardCommentInsert(Model model, @RequestBody BoardDTO params){
return boardService.boardCommentInsert(params);
}
}
웹 화면 생성 및 서비스 로직 구현 1-1
먼저 의존성 2개를 추가하겠습니다.
/* 테스트 환경 개선 */
developmentOnly("org.springframework.boot:spring-boot-devtools")
/* JSTL 태그 사용 */
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
위 주석의 테스트 환경 개선은 JSP파일이 변경되었을 때 바로 적용될 수 있게 추가해 주었고,
JSTL의 <c:if>나 <c:forEach>등 JSTL 태그를 사용하기 위해 추가 해 주었습니다.
그 후 Json view와 Filter, resource 접근 등 @Bean을 몇 가지 생성하겠습니다.
WebConfig.java
// resource 접근
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/resources/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
// 인코딩 필터
@Bean
public FilterRegistrationBean encodingFilter(){
FilterRegistrationBean frb = new FilterRegistrationBean();
CharacterEncodingFilter charact = new CharacterEncodingFilter();
charact.setForceEncoding(true);
charact.setEncoding("UTF-8");
frb.setFilter(charact);
return frb;
}
// json view
@Bean
public MappingJackson2JsonView jsonView(){
return new MappingJackson2JsonView();
}
위 코드를 추가 해 줍니다.
DTO class
@Data
public class BoardDTO {
private String seq;
private String userId;
private String title;
private String content;
private String hit;
private String regDate;
private String updDate;
private String pageNumber;
private String pageGroupSize;
private String tranType;
}
게시판 테이블 추가 (MSSQL)
CREATE TABLE [dbo].[JSP_BOARD](
[SEQ] [int] IDENTITY(1,1) NOT NULL,
[USERID] [nvarchar](30) NOT NULL,
[TITLE] [nvarchar](100) NOT NULL,
[CONTENT] [ntext] NOT NULL,
[HIT] [int] NOT NULL,
[REGDATE] [nvarchar](30) NOT NULL,
[UPDDATE] [nvarchar](30) NULL
PRIMARY KEY CLUSTERED
(
[SEQ] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
SEQ를 PK로 설정, 데이터 생성시 1부터 시작하여 1씩 증가하여 생성 됩니다.
화면에 필요한, Jquery와 bootstrap 게시판 템플릿을 다운로드 하여 프로젝트에 설정 합니다.
무조건 bootstrap을 사용할 필요는 없습니다. ( https://startbootstrap.com/themes )
<script type="text/javascript" src="/static/js/jquery-3.3.1.js"></script>
<script src="/static/css/bootstrap/js/bootstrap.bundle.min.js"></script>
<link type="text/css" rel="stylesheet" href="/static/css/bootstrap/css/styles.css" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet" />
<link rel="icon" type="image/x-icon" href="/static/css/bootstrap/assets/favicon.ico" />
이제 모든 준비가 끝났습니다.
먼저 게시판 목록 조회를 화면을 구현 해 보도록 하겠습니다.
sql mapper에 아래 프로시저를 연결 시켜줍니다.
@Select("EXEC YUNDB.DBO.SEL_JSP_BOARDLIST #{pageNumber}, #{pageGroupSize}")
public List<BoardDTO> boardList(BoardDTO params);
연결시킨 프로시저는 다음과 같습니다. (테스트 데이터를 몇개 추가하여 테스트 해보시길 바랍니다.)
USE [yunDB]
GO
/****** Object: StoredProcedure [dbo].[SEL_JSP_BOARDLIST] Script Date: 2024-03-28 오전 10:52:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[SEL_JSP_BOARDLIST]
@pageNumber INT
,@pageGroupSize INT
/*, @userName NVARCHAR(20)
, @startDate NVARCHAR(20)
, @endDate NVARCHAR(20)*/
AS
BEGIN
SELECT
SEQ AS seq
, USERID AS userId
, TITLE AS title
, CONTENT AS content
, HIT AS hit
, CONVERT(CHAR(10), REGDATE, 23) AS regDate
FROM YUNDB.DBO.JSP_BOARD WITH(NOLOCK)
END
GO
DB에서 가지고온 데이터를 JSP 화면에 response할 수 있게 Controller를 수정 해 줍니다.
/* 게시글 목록 화면 */
@RequestMapping(value="/boardList")
public String boardListView(Model model, BoardDTO params){
model.addAttribute("data", boardService.boardList(params));
return "/board/boardList";
}
JSP는 body는 아래 코드와 같습니다.
<body>
<header class="py-5">
<div class="container px-5">
<div class="row justify-content-center">
<div class="col-lg-8 col-xxl-6">
<div class="text-center my-5">
<h1 class="fw-bolder mb-3">게시판</h1>
<p class="lead fw-normal text-muted mb-4">최신 글을 확인 해 보세요 !</p>
</div>
</div>
</div>
</div>
</header>
<section class="py-5">
<div class="container px-5 my-5">
<%--<button type="button" class="btn btn-success" onclick="">글쓰기</button>--%>
<%-- 나비 --%>
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<%--<img src="/docs/5.0/assets/brand/bootstrap-logo.svg" alt="" width="30" height="24" class="d-inline-block align-text-top">--%>
게시판
</a>
</div>
</nav>
<%-- 나비 --%>
<table class="table table-striped">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>날짜</th>
<th>조회수</th>
</tr>
</thead>
<tbody id="board">
<c:forEach var="data" items="${data}">
<tr>
<td>${data.seq}</td>
<td onclick="">${data.title}</td>
<td>${data.userId}</td>
<td>${data.regDate}</td>
<td>${data.hit}</td>
</tr>
</c:forEach>
</tbody>
</table>
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link">1</a>
</li>
<li class="page-item">
<a class="page-link">2</a>
</li>
<li class="page-item">
<a class="page-link">3</a>
</li>
</ul>
</nav>
</div>
</section>
</body>
테스트
실제 화면을 테스트 해 보면 아래 사진 처럼 확인 할 수 있습니다.

이번에는 게시글 등록을 만들어 보겠습니다.
section 태그를 하나 추가 해 줍니다. 간이 로그인과 게시글 제목, 게시글 내용 입력 할 수 있는 필드를 생성 합니다.
<section class="py-5">
<div class="container px-5 my-5" style="width:700px;">
<div class="input-group mb-3" style="width:300px;">
<input type="text" class="form-control" id="userId" placeholder="간이 로그인 아이디 입력" aria-label="Recipient's username" aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="button" id="loginBtn" onclick="fn_login();">로그인</button>
</div>
<div class="input-group mb-3" id="hiddenTitle">
<input type="text" class="form-control" placeholder="게시글 제목" id="title" aria-label="Recipient's username" aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="button" onclick="fn_content();">게시글 등록</button>
</div>
<div class="input-group" id="hiddenContent">
<span class="input-group-text">게시글 입력</span>
<textarea class="form-control" id="content" aria-label="With textarea"></textarea>
</div>
</div>
</section>
그리고 스크립트를 추가 해 줍니다.
common.js
function ajaxPostCall(url, params, callback){
$.ajax({
url: url,
type: "POST",
dataType: "JSON",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(params),
//async: false,
success: function(data) {
return callback(data);
},
error: function(error) {
error.msg = errorMsg;
return callback(error);
},
});
}
게시판 목록 화면
<script type="text/javascript">
// 로딩 될 때 글 입력 필드 가리기
$(document).ready(function(){
$("#hiddenTitle").hide();
$("#hiddenContent").hide();
});
// 간이 로그인 처리
function fn_login(){
if($("#userId").val() != "" && $("#userId").val() != null){
$("#hiddenTitle").show();
$("#hiddenContent").show();
$("#loginBtn").hide();
$("#userId").prop("readonly", true);
alert("간이 로그인 " + $("#userId").val() + "님 반갑습니다.");
} else {
alert("간이 ID가 유효하지 않음.");
}
}
// 게시글 등록
function fn_content(){
if($("#userId").val() == "" || $("#userId").val() == null){
alert("로그인은 필수 입니다.");
return;
}
let data = {
userId: $("#userId").val()
,title: $("#title").val()
,content: $("#content").val()
};
ajaxPostCall("/board/boardInsert", data, function(res){
if(res > 0){
alert("등록되었습니다.");
$("#ajaxRead").load(location.href + " #ajaxRead");
} else {
alert("오류로 인해 등록되지 않았습니다.");
}
});
}
</script>
화면이 로딩 됬을 때 게시글 입력 필드를 안보이게 처리하고, 간이 로그인 필드만 노출 됩니다.
간이 로그인을 완료 했다면, 게시글 필드가 노출되어 게시글 작성 가능 합니다. 그리고 게시판 <section> 부분은 <div id="ajaxRead"></div> 태그로 묶어 통신 이 후, 게시판 부분만 리로드 되게 load 함수를 추가하였습니다.
동적으로 생성된 태그에 이벤트를 넣기위해, 수정 및 추가 하였습니다.
// 화면이 로딩 될 때 글 입력 필드 가리기, 이벤트 바인딩
$(document).ready(function(){
$("#hiddenTitle").hide();
$("#hiddenContent").hide();
fn_bindingEvent();
});
// 게시글 등록 ajax 호출 후, 바인딩 함수 호출
ajaxPostCall("/board/boardInsert", data, function(res){
if(res > 0){
alert("등록되었습니다.");
$("#ajaxRead").load(location.href + " #ajaxRead");
fn_bindingEvent();
} else {
alert("오류로 인해 등록되지 않았습니다.");
}
});
function fn_bindingEvent(){
$(document).on("click", "#board tr", function(){
let tr = $(this);
let td = tr.children();
let seq = td.eq(0).text();
location.href = "/board/boardDetail?seq="+seq;
});
}
해당 Row를 클릭하면 해당(SEQ) 상세 화면으로 진입 할 수 있게 이벤트를 추가 했습니다.
동적으로 테이블 필드를 만들었기 때문에, 이벤트 또한 동적 바인딩으로 처리해야 합니다.
게시글 작성 java 파일의 구성은 다음과 같습니다.
/* 게시글 작성 Controller */
@RequestMapping(value="/boardInsert")
@ResponseBody
public int boardInsert(Model model, @RequestBody BoardDTO params){
return boardService.boardInsert(params);
}
/* 게시글 작성 Service */
@Transactional
public int boardInsert(BoardDTO params){
params.setTranType("INS");
int result = boardMapper.boardInsert(params);
return result;
}
/* 게시글 작성 Mapper */
@Insert("EXEC YUNDB.DBO.TRAN_JSP_BOARD #{seq}, #{userId}, #{title}, #{content}, #{tranType}")
public int boardInsert(BoardDTO params);
INSERT나 UPDATE를 수행하는 method를 보면 @Transactional 어노테이션이 있는 것을 볼 수 있습니다.
Spring boot에서는 따로 트랜잭션 관리 설정을 하지 않아도 기본적인 셋팅으로 사용 할 수 있습니다.
프로젝트에 필요한 코드가 있어야 한다면
transaction @Bean을 생성하여 추가 셋팅을 할 수 있습니다.
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Transactional
Spring AOP를 통해 프록시 객체를 생성하여 사용 됩니다.
프록시 객체 생성하여 사용하는 이유는 Aspect 클래스에서 제공하는 여러 기능을 사용하기 위함 입니다.
만약 해당 target에 직접 참조하게 된다면, 직접 aspect를 호출해야 하기 때문에 유지보수에 불리 합니다. 프록시 객체를 생성하는 방식은 JDK Dynamic Proxy, CGLib Proxy 등 있습니다. 그리고 실제 코드상으로 try ~ catch()문이 보이지 않지만, 해당 코드가 트랜잭션 처리될때
try{
실행코드
} catch(Exception e){
}
이렇게 실행되는 것을 확인 할 수 있습니다.
INSERT 프로시저의 경우 아래와 같이 처리하였습니다.
CREATE PROCEDURE [dbo].[TRAN_JSP_BOARD]
@seq INT
, @userId NVARCHAR(20)
, @title NVARCHAR(40)
, @content NTEXT
, @tranType NVARCHAR(5)
AS
BEGIN
IF(@tranType = 'INS')
BEGIN
INSERT INTO DBO.JSP_BOARD
(
USERID
, TITLE
, CONTENT
, HIT
, REGDATE
, UPDDATE
)
VALUES
(
@userId
, @title
, @content
, 0
, CONVERT(CHAR(19), GETDATE(), 20)
, ''
)
END
ELSE IF(@tranType = 'UPD')
BEGIN
UPDATE DBO.JSP_BOARD
SET TITLE = @title
, CONTENT = @content
, UPDDATE = CONVERT(CHAR(19), GETDATE(), 20)
WHERE SEQ = @seq
AND USERID = @userId
END
END
GO
결과:


웹 화면 생성 및 서비스 로직 구현 1-2 (게시판 상세 화면, 댓글 구현)
먼저 상세 페이지 화면은 다음과 같습니다.(HTML CSS의 경우 Bootstrap을 사용하였습니다.)

Controller
/* 게시글 상세 화면 */
@RequestMapping(value="/boardDetail", method = RequestMethod.GET)
public String boardDetailView(Model model, BoardDTO params){
model.addAttribute("data", boardService.boardDetail(params));
model.addAttribute("isLogin", params.getUserId());
return "/board/boardDetail";
}
간이 로그인 값을 추가로 가지고와 게시글 수정 가능 여부를 DB에서 확인합니다.
쿼리는 다음과 같습니다.
CREATE PROCEDURE [dbo].[SEL_JSP_BOARDDETAIL]
@seq INT
,@userId NVARCHAR(30)
AS
BEGIN
SELECT
SEQ AS seq
, USERID AS userId
, TITLE AS title
, CONTENT AS content
, HIT AS hit
, CONVERT(CHAR(10), REGDATE, 23) AS regDate
, CASE WHEN USERID = @userId THEN 1
ELSE 0
END AS chk
FROM YUNDB.DBO.JSP_BOARD WITH(NOLOCK)
WHERE SEQ = @seq
END
GO
CASE문에서 간이 로그인의 userId 값을 가지고와 해당 게시글의 작성자가 동일하다면 1 아니면 0 값을 반환합니다.
서버의 응답된 데이터로 JavaScript와 HTML태그를 셋팅합니다.
HTML
<c:if test="${data.chk eq 1}">
<a class="badge bg-secondary text-decoration-none link-light" onclick="fn_modify();">수정</a>
</c:if>
JavaScript
/* 수정 모드 활성화 */
<c:if test="${data.chk eq 1}">
let title = "${data.title}";
let content = "${data.content}";
let seq = "${data.seq}";
let userId = "${data.userId}";
window.onload = function(){
$("#modify-content").val(content);
$("#modify-title").val(title);
}
function fn_modify(){
$("#modify-field").show();
}
function fn_save(){
let data = {
userId: userId
,seq: seq
,title: $("#modify-title").val()
,content: $("#modify-content").val()
};
ajaxPostCall("/board/boardUpdate", data, function(res){
if(res > 0){
alert("등록되었습니다.");
// 새로고침
location.reload();
} else {
alert("오류로 인해 등록되지 않았습니다.");
}
});
}
</c:if>
위 스크립트 코드와 같이 수정버튼 클릭시 수정모드를 활성화 합니다.
수정모드의 HTML은 다음과 같습니다.
<div id="modify-field">
<section class="py-5">
<div class="container px-5 my-5" style="width:700px;">
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="게시글 제목" id="modify-title" aria-label="Recipient's username" aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="button" onclick="fn_save();">게시글 등록</button>
</div>
<div class="input-group">
<span class="input-group-text">게시글 입력</span>
<textarea class="form-control" id="modify-content" aria-label="With textarea"></textarea>
</div>
</div>
</section>
</div>
게시글 수정을 저장하면 Controller -> Service -> Mapper 아래 코드를 호출하여 처리합니다.
/* 게시글 수정 */
@RequestMapping(value="/boardUpdate")
@ResponseBody
public int boardUpdate(Model model, @RequestBody BoardDTO params){
return boardService.boardUpdate(params);
}
/* 게시글 수정 */
@Transactional
public int boardUpdate(BoardDTO params){
params.setTranType("UPD");
int result = boardMapper.boardUpdate(params);
return result;
}
@Update("EXEC YUNDB.DBO.TRAN_JSP_BOARD #{seq}, #{userId}, #{title}, #{content}, #{tranType}")
public int boardUpdate(BoardDTO params);
해당 userId 로그인 수정 버튼 활성화

수정 결과

이제 댓글 처리 작업을 진행 하겠습니다.
DB 처리 목록은 다음과 같습니다.
테이블 생성
CREATE TABLE [dbo].[JSP_BOARDCOMMENT](
[SEQ] [int] IDENTITY(1,1) NOT NULL,
[BOARDSEQ] [int] NOT NULL,
[ORDERNO] [int] NOT NULL,
[USERID] [nvarchar](30) NOT NULL,
[COMMENT] [nvarchar](500) NOT NULL,
[REGDATE] [nvarchar](30) NOT NULL,
[UPDDATE] [nvarchar](30) NULL,
PRIMARY KEY CLUSTERED
(
[SEQ] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
BOARDSEQ = 게시판 seq 번호
ORDERNO = 게시판 seq 기준, 댓글 카운트 (이벤트 형 ex) 1등!! 댓글, 100번째 댓글 입니다. 축!! )
댓글 입력 프로시저 INSERT
CREATE PROCEDURE [dbo].[INS_JSP_BOARDCOMMENT]
@seq INT
, @userId NVARCHAR(20)
, @comment NTEXT
AS
BEGIN
INSERT INTO YUNDB.DBO.JSP_BOARDCOMMENT
(
BOARDSEQ
, ORDERNO
, USERID
, COMMENT
, REGDATE
, UPDDATE
)
VALUES
(
@seq
, (SELECT (COUNT(*) + 1)
FROM YUNDB.DBO.JSP_BOARDCOMMENT
WHERE BOARDSEQ = @seq)
, @userId
, @comment
, CONVERT(CHAR(19), GETDATE(), 20)
, ''
)
END
GO
댓글 목록 조회 프로시저
CREATE PROCEDURE [dbo].[SEL_JSP_BOARDCOMMENT]
@seq INT
AS
BEGIN
SELECT
BOARDSEQ AS boardSeq
, ORDERNO AS orderNo
, USERID AS userId
, COMMENT AS comment
, REGDATE AS regDate
FROM YUNDB.DBO.JSP_BOARDCOMMENT WITH(NOLOCK)
WHERE BOARDSEQ = @seq
ORDER BY REGDATE ASC
END
GO
게시판 상세 조회 Controller 수정 commentList 추가 , DTO 수정, Service, Mapper 추가
addAttribute commentList를 추가합니다.
/* 게시글 상세 화면 */
@RequestMapping(value="/boardDetail", method = RequestMethod.GET)
public String boardDetailView(Model model, BoardDTO params){
model.addAttribute("data", boardService.boardDetail(params));
model.addAttribute("isLogin", params.getUserId());
model.addAttribute("commentList", boardService.boardCommentList(params));
return "/board/boardDetail";
}
/* 댓글용 추가 */
private int boardSeq;
private int orderNo;
private String comment;
/* 게시글 댓글 목록 조회 */
public List<BoardDTO> boardCommentList(BoardDTO params){
return boardMapper.boardCommentList(params);
}
/* 게시글 댓글 목록 조회 */
@Select("EXEC YUNDB.DBO.SEL_JSP_BOARDCOMMENT #{seq}")
public List<BoardDTO> boardCommentList(BoardDTO params);
댓글 작성 Controller, Service, Mapper 추가
/* Controller 게시글 작성 */
@RequestMapping(value="/boardCommentInsert")
@ResponseBody
public int boardCommentInsert(Model model, @RequestBody BoardDTO params){
return boardService.boardCommentInsert(params);
}
/* Service 게시글 댓글 작성 */
@Transactional
public int boardCommentInsert(BoardDTO params){
int result = boardMapper.boardCommentInsert(params);
return result;
}
/* Mapper 게시글 댓글 작성 */
@Insert("EXEC YUNDB.dbo.INS_JSP_BOARDCOMMENT #{seq}, #{userId}, #{comment}")
public int boardCommentInsert(BoardDTO params);
화면 처리
간이 로그인이기 때문에 최대한 현재 환경에 맞춰 처리하였습니다.
<div class="card-body">
<c:if test="${not empty isLogin and isLogin ne ''}">
<form class="mb-4">
<textarea class="form-control" rows="3" placeholder="댓글 입력" id="comment"></textarea>
</form>
<div class="d-flex">
<div class="flex-shrink-0">
<button class="btn btn-outline-secondary" type="button" onclick="fn_conmmentSave();">댓글 등록</button>
</div>
<div class="ms-3">
<div class="fw-bold">※댓글 작성 유의사항!</div>
바르고 고운말을 사용합시다.
</div>
</div>
</c:if>
<c:forEach var="data" items="${commentList}">
<div class="d-flex mb-4">
<div class="ms-3">
<div class="fw-bold">${data.userId}</div>
${data.comment}
</div>
</div>
</c:forEach>
</div>
화면 스크립트 처리
<c:if test="${not empty isLogin and isLogin ne ''}">
function fn_conmmentSave(){
let data = {
userId: "${isLogin}"
,seq: "${data.seq}"
,comment: $("#comment").val()
};
ajaxPostCall("/board/boardCommentInsert", data, function(res){
if(res > 0){
alert("등록되었습니다.");
// 새로고침
location.reload();
} else {
alert("오류로 인해 등록되지 않았습니다.");
}
});
}
</c:if>
결과

그 외 게시판의 추가적인 기능이면서 중요한 게시판 페이징은 아래 링크에 저번 작성했던 가이드 글이 있습니다.
많은 관심 부탁드리겠습니다. JSP 게시판 만들기는 마무리 하도록 하겠습니다.
페이징처리 링크
https://dalbyutility.tistory.com/9
Java 웹사이트 페이징 처리 방법(게시판 만들기, 게시판 페이징 처리)
Java 웹사이트 페이징 처리 방법(게시판 만들기, 게시판 페이징 처리)Java를 활용하여 웹 사이트 페이징 처리 방법을 포스팅 하겠습니다. 웹 사이트에서 페이징 처리하는 이유? 성능 이슈 : 페이
dalbyutility.tistory.com
정말 긴 글을 봐주셔서 감사합니다 !!
