본문 바로가기
개발 공부 Today I Learned

[국비 59일차 TIL] Spring - 게시판 detail, error, 모달 창 구현

by 개발자신입 2024. 2. 16.
반응형

< BoardController.java >

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.-----.dto.BoardDTO;
import org.-----.service.BoardService;
import org.-----.util.Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;


@Controller
public class BoardController {
	
		// 서비스와 자동 연결하기
		@Autowired
		private BoardService boardService;
        
		@Autowired
		private Util util;
		
		
		@GetMapping("/index")
		public String index() {
			return "index";
		}
		
		@GetMapping("/board")
		public String board(Model model) {
			System.out.println("컨트롤러입니다.");
			List<BoardDTO> list = boardService.boardList();
			model.addAttribute("list", list); // ("이름", 값)
			
			return "board"; // jsp file name
		}
		 
		@GetMapping("/detail")
		public String detail(HttpServletRequest request) {
			// no 잡기
			String no = request.getParameter("no");
//			System.out.println(util.str2Int(no)); // 숫자 = 숫자, 문자 = 0
//			System.out.println(no);
			
			if(util.str2Int(no) != 0) { 
				// 0이 아니라면 (정상: DB에 물어보기, 값 가져오기, 붙이기, 이동하기)
				return "detail"; 	// 주소창이 detail?no= 이렇게 detail이 들어감.
				
			} else { // 0이라면 (비정상: 에러 페이지로 이동하기)				
				return "redirect:/error"; 	// controller에 있는 error매핑을 실행해 (http://localhost/error)
			}

		}
		
	}

 

boardList() 생성 (서비스, DAO)

BoardService.java

@Service
public class BoardService {
	
	@Autowired
	private BoardDAO boardDAO;

	public java.util.List<BoardDTO> boardList() {
		return boardDAO.boardList();
	}
}

 

BoardDAO.java

@Repository
public class BoardDAO {
	
	@Autowired
	private SqlSession sqlSession;

	public List<BoardDTO> boardList() {
		return sqlSession.selectList("board.boardList");
	}
}

BoardController.java

	@GetMapping("/detail")
	public String detailHttpServletRequest request) {

	if(util.str2Int(no) != 0) { 
		BoardDTO detail = boardService.detail(no);
		return "detail";  // 주소창이 detail?no= 이렇게 detail이 들어감.

	} else { // 0이라면 (비정상: 에러 페이지로 이동하기)
		return "redirect:/error";  // controller에 있는 error매핑을 실행해 (http://localhost/error)
	}
}

위의 BoardController.java에서 BoardDTO detail = boardService.detail(no); 부분의 detail을 create하기 

-> BoardService, BoardDAO 만들어주기

BoardService.java

Util을 쓰기 위해 @Autowired 필수

	@Autowired
	private Util util;

	public BoardDTO detail(String no) {
		// 문자? util에 숫자로 변경해주는 메소드 생성
		int boardNo = util.str2int(no);
		return boardDAO.detail(); 
	}
}

 

BoardDAO.java

	public BoardDTO detail() {
		return sqlSession.selectOne("board.detail");
	}

 

▶ 위의 방법말고 다른 방법으로 쓰기

BoardController.java

코드 변경 : util.str2Int(no)를 reNo로

 

-> int값이 아닌 것이 들어오면 400에러가 남. 
-> @Param("no") 뒤에 String으로 변경 : detail?no=뒤에 숫자가 아닌 문자가 들어갔을 경우 error페이지로 넘어감.

 

	@GetMapping("/detail")
	public String detail(@Param("no") String no) { // @Param : spring이 알아서 처리함 

		int reNo = util.str2Int(no);
		if(reNo != 0) { 
			BoardDTO detail = boardService.detail(reNo);
			return "detail"; 	// 주소창이 detail?no= 이렇게 detail이 들어감.
				
		} else { // 0이라면 (비정상: 에러 페이지로 이동하기)
				return "redirect:/error"; 	// controller에 있는 error매핑을 실행해 (http://localhost/error)
		}
	}

BoardService

detail 수정


*** 중요함 ***

변수명이 아니라 데이터 타입을 확인해야 함.
보드컨트롤러에서 int, 보드서비스에서 int임.

	// 문자? util에 숫자로 변경해주는 메소드 생성
	public BoardDTO detail(int no) { 
	// 컨트롤러에는 int reNo, 서비스에는 int no (데이터 타입을 확인해야함)
		return boardDAO.detail(no); 
}

더보기

 

20240302 집에서 직접 만들어 봤을 때

 

@Param("no") String no : String으로 통일?! 왜 예전에 적어놓은 노트에는 뒤죽박죽일까 

 

reNo는 int임. 보드서비스, dao에서도 int로 통일해야함.

 

보드컨트롤러

	@GetMapping("/detail")
	public String detail(@Param("no") String no) { // url에 int 외의 문자열이 들어오면 error로 넘어가도록.
//		String no = request.getParameter("no");
//		System.out.println(no);
		
		int reNo = util.str2Int(no);
		if(reNo != 0) {
			BoardDTO detail = boardService.detail(reNo);
			return "detail";
		} else {
			
			return "redirect:/error";
		}
	}

보드서비스

보드DAO



 


BoardDAO.java

	public BoardDTO detail(int no) {
		return sqlSession.selectOne("board.detail", no); // 반환타입 DTO 하나 나옴.
	}

 


ErrorController.java

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ErrorController {
	
	// (/error) 잡아주기
	@GetMapping("/error")
	public String error() {
		return "error/error";	// 위치 : /WEB-INF/views/error/error.jsp
	}
	
}

error 폴더 생성


board-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
  <mapper namespace="board"> <!-- namespace와 id는 단 하나 -->
	<select id="boardList" resultType="boardDTO">
		SELECT board_no, board_title, board_write, board_date, board_count, comment 
		FROM boardview		<!-- board.boardList -->
		LIMIT 0, 10
	</select>
	
	<select id="detail" resultType="boardDTO" parameterType="Integer">
		SELECT board_no, board_title, board_write, board_content, board_date, board_count, board_ip, board_del
		FROM board
		WHERE board_no=#{no} <!-- mybatis 변수명 # -->
	
	</select>
	
</mapper>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
  
<configuration>
	<typeAliases>
		<typeAlias type="java.lang.Integer" alias="integer"/>
		<typeAlias type="org.-----.dto.BoardDTO" alias="boardDTO"/>
	</typeAliases>
</configuration>



<!-- board-mapper의 resultType에 org.-----.dto.BoardDTO을 그대로 적어줘도 됨. -->

BoardController.java

 

* 추가된 내용: 
public에 Model model 
model.addAttribute("detail", detail);

 

		@GetMapping("/detail")
        
        // @Param 어노테이션을 사용하여 "no"라는 요청 파라미터를 받아와서 사용
		public String detail(@Param("no") String no, Model model) { 
			
            // 문자열 형태의 게시물 번호를 정수형으로 변환 (str2Int 사용)
			int reNo = util.str2Int(no);
			if(reNo != 0) { 
				BoardDTO detail = boardService.detail(reNo); // detail 정보 가져오기
				model.addAttribute("detail", detail);
				return "detail"; 
				
			} else { // 0이라면 (비정상: 에러 페이지로 이동하기)
				return "redirect:/error"; 	// controller에 있는 error매핑을 실행해 (http://localhost/error)
			}
		}

< board-mapper.xml >

 

  1. SELECT절 : board 테이블과 member 테이블을 조인하여 게시물의 번호, 제목, 내용, 작성자 이름, 작성일자, IP 주소, 삭제 여부를 선택
  2. FROM board b JOIN member m ON b.mno = m.mno  : board 테이블과 member 테이블을 조인, board 테이블의 회원 번호(mno)와 member 테이블의 회원 번호(mno)를 조인 조건으로 사용
  3. WHERE board_no=#{no} AND board_del=1 : WHERE 절에서 게시물 번호(board_no)가 파라미터로 전달된 값(#{no})과 일치하고, 삭제되지 않은 게시물(board_del=1)만을 선택하는 조건을 설정
<select id="detail" resultType="boardDTO" parameterType="Integer">
	SELECT b.board_no, b.board_title, b.board_content, m.mname AS 
	board_write, b.board_date, b.board_ip, b.board_del 
	FROM board b JOIN member m ON b.mno = m.mno 
	WHERE board_no=#{no} AND board_del=1  <!-- mybatis 변수명 # -->
</select>

detail.jsp

detail값 넣어주기.

	<!-- 게시판 -->
	<br>
	<br>
	<section class="page-section" id="detail">
		<div class="container">
			<div class="text-center">
				<h2 class="section-heading text-uppercase">게시글 디테일</h2>
			</div>
			<div class="row text-center">
				${detail.board_no } / ${detail.board_title } 
				${detail.board_title } / ${detail.board_date } 
				${detail.board_content }
			</div>
		</div>
	</section>

board.jsp

글쓰기 버튼과 모달 창 구현하기 (modal)

<!-- 페이징 -->
<button class="btn btn-success" type="button" data-bs-toggle="modal">글쓰기</button>

 

<body id="page-top">
	<c:import url="menu.jsp" />
	<section class="page-section" id="services">
		<div class="container">
			<div class="text-center">
				<h3>집에서 만들어보는 게시판입니다.</h3>
				<table>
					<thead>
						<tr>
							<th>번호</th>
							<th>제목</th>
							<th>작성자</th>
							<th>날짜</th>
							<th>조회수</th>
						</tr>
					</thead>
					<tbody>
						<c:forEach items="${list }" var="row">
							<tr>
								<td>${row.board_no }</td>
								<td class="title"><a href="./detail?no=${row.board_no }">${row.board_title } [${row.comment }]</a></td>
								<td>${row.board_write }</td>
								<td>${row.board_date }</td>
								<td>${row.board_count }</td>
							</tr>
						</c:forEach>
					</tbody>
				</table>
                
				<!-- 페이징 + Modal -->
				<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#write">글쓰기</button>
			</div>
		</div>
	</section>

모달(modal) 

자동으로 숨겨짐.
글쓰기 버튼을 누르면 작동하도록 해야함. 

글쓰기 버튼에 코드 추가 : data-bs-toggle="modal" data-bs-target="#write"

<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#write">글쓰기</button>


-> data-bs-target은 id임. 모달을 불러올 때 id로 write를 사용해야 함.

이렇게 코드를 실행하면 불투명한 검은 화면이 나와야 함.

required="required" // 입력을 해야 다음으로 넘어갈 수 있도록.

	<!-- 글쓰기 모달 만들기 -->
	<div class="modal" id="write">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<h3 class="modal-title">글쓰기 창입니다.</h3>
					<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
				</div>
				<div class="modal-body">
					<div class="mt-2">
						<form action="./write" method="post" onsubmit="return writeCheck()" name="frm">
							<input type="text" name="title" class="form-control mb-2" required="required" placeholder="제목을 입력하세요.">
							<textarea name="content" class="form-control mb-2" style="height: 300px" required="required" ></textarea>
							<button type="submit" class="btn btn-dark">글쓰기</button>
						</form>
					</div>
				</div>
			</div>
		</div>
	</div>

WriteDTO 생성

import lombok.Data;

@Data
public class WriteDTO {
	private String title, content, mid;
}

BoardController.java

게시물의 작성 정보를 전달받는 WriteDTO 객체를 파라미터로 받음

 

  1. 사용자로부터 입력받은 정보를 WriteDTO에 담습니다.
  2. boardService.write(dto)를 호출하여 글 작성을 수행합니다.
		@PostMapping("/write") // 내용,제목 -> DB에 저장 -> 게시판으로 
		public String write(WriteDTO dto) { 
			System.out.println(dto.getTitle());
			System.out.println(dto.getContent());

			int result = boardService.write(dto);
			System.out.println("결과는 : " + result);
			
			return "redirect:/board";
		}
		
	}

BoardService.java

int result = boardService.write(dto); 의 write 생성해주기

	public int write(WriteDTO dto) {
		return boardDAO.write(dto);
	}

 

BoardDAO.java

return boardDAO.write(dto);의 write 생성해주기

	public int write(WriteDTO dto) {
	return sqlSession.insert("board.write", dto);
}

board-mapper.xml

  • resultType : 나올 키 타입, 어떤 자바 객체에 매핑할 것인지.
  • parameterType : sql쿼리에서 필요한 매개변수의 타입 

 

  • keyProperty : 불러올 컬럼명
  • order="AFTER" 실행 후에
	<insert id="write" parameterType="writeDTO">
		INSERT INTO board (board_title, board_content, mno) 
		values (#{title}, #{content}, (SELECT mno FROM member WHERE mid=#{mid}))
	<selectKey resultType="Integer" keyProperty="board_no" order="AFTER">
		SELECT LAST_INSERT_ID()  <!-- 작성 후 내가 쓴 글로 돌아가기 -->
	</selectKey>
	</insert>
</mapper>

mybatis-config.xml

WriteDTO 추가하기

 

alias를 지정하는 이유 : 1) 코드의 간결성, 2) 이식성 향상, 3) 가독성 향상

<typeAlias type="org.----.dto.WriteDTO" alias="writeDTO"/>

BoardService.java

	public int write(WriteDTO dto) {
		dto.setMid("test1");  // mid에 있는 아이디 불러오기
		return boardDAO.write(dto);
	}

WriteDTO

@Data
public class WriteDTO {
	private int board_no;
	private String title, content, mid;
}

BoardController.java

리턴을  /detail?no=" + dto.getBoard_no(); 로 설정.

		@PostMapping("/write") // 내용,제목 -> DB에 저장 -> 게시판으로 
		public String write(WriteDTO dto) { 
			System.out.println(dto.getTitle());
			System.out.println(dto.getContent());

			int result = boardService.write(dto);
			System.out.println("결과는 : " + result);
			
			return "redirect:/detail?no="+dto.getBoard_no(); // 작성된 글 확인하도록
		}

BoardController

		@PostMapping("/write") // 내용,제목 -> DB에 저장 -> 게시판으로 
		public String write(WriteDTO dto) { 
			System.out.println(dto.getTitle());
			System.out.println(dto.getContent());

			int result = boardService.write(dto);
			
			if(result == 1) {
				return "redirect:/detail?no="+dto.getBoard_no();
			} else {
			return "redirect:/error";
		}
		
	}
}

board.jsp

<form>에 writeCheck() 기능 추가하기 (글쓰기 버튼 function)

 

focus()는 title, content 입력 필드에 포커스를 설정.

만약 제목과 내용 길이가 짧다면 return false로 해서, 저장되지 않도록.

<script type="text/javascript">
	function writeCheck(){
		let title = document.querySelector("#title");
		let content = document.querySelector("#content");
		// alert("title : " + title.value + "/ content : " + content.value);
        
		if(title.value.length < 4) {
			alert("제목은 다섯글자 이상 써주세요.");
			title.focus();
			return false;	
		}
		if(content.value.length < 10){
			alert("내용은 10글자 이상 써주세요.");
			content.focus();
			return false;
		}
	}
</script>

 

 <form> 태그에 있는 input과 textarea 부분에 id를 추가해주기.

 

<input type="text" id="title" name="title" class="form-control mb-2" required="required">
<textarea id="content" name="content" class="form-control mb-2" required="required"></textarea>

 

	<!-- 글쓰기 모달 만들기 -->
	<div class="modal" id="write">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<h3 class="modal-title">글쓰기 창입니다.</h3>
					<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
				</div>
				<div class="modal-body">
					<div class="mt-2">
						<form action="./write" method="post" onsubmit="return writeCheck()" name="frm">
							<input type="text" id="title" name="title" class="form-control mb-2" required="required" placeholder="제목을 입력하세요.">
							<textarea id="content" name="content" class="form-control mb-2" style="height: 300px" required="required"></textarea>
							<button type="submit" class="btn btn-dark">글쓰기</button>
					</div>
				</div>
			</div>
		</div>
	</div>

 

detail.jsp 전체코드

<button onclick="location.href='./board'">
<button onclick="history.back()">  : 전 페이지로 돌아가기
<button onclick="history.go(-1)">  : 전 페이지로 돌아가기
<button onclick="history.go(-2)">  : 마우스 2번 클릭한 거처럼 

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport"
	content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>detail</title>
<!-- Favicon-->
<link rel="shortcut icon" href="/path/to/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="assets/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="assets/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="assets/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="assets/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="assets/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="assets/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="assets/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="assets/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="assets/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<!-- Font Awesome icons (free version)-->
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js"	crossorigin="anonymous"></script>
<!-- Google fonts-->
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700"	rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="css/styles.css" rel="stylesheet" />
<link href="css/board.css" rel="stylesheet" />
</head>

<body id="page-top">
	<!-- Navigation 코드 이동 : menu.jsp -->
	<c:import url="menu.jsp" />

	<!-- 게시판 -->
	<br>
	<br>
	<section class="page-section" id="detail">
		<div class="container">
			<div class="text-center">
				<h2 class="section-heading text-uppercase">게시글 디테일</h2>
			</div>
			<div class="card mb-4" style="min-height: 500px;">
				<div class="card-body">
					<div class="h2">${detail.board_title }</div>
					<div class="row p-2 bg-secondary">
						<div class="col align-middle text-start">${detail.board_write }</div>
						<div class="col align-middle text-end">${detail.board_date }</div>
					</div>
					<div class="mt-4 vh -75">${detail.board_content }</div>
				</div>
			</div>
		<button class="btn btn-warning" onclick="history.back()">게시판으로</button>
		<button class="btn btn-warning" onclick="history.go(-1)">게시판으로</button>
		</div>
		</div>
	</section>
반응형

댓글