< 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
}
}
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 >
- SELECT절 : board 테이블과 member 테이블을 조인하여 게시물의 번호, 제목, 내용, 작성자 이름, 작성일자, IP 주소, 삭제 여부를 선택
- FROM board b JOIN member m ON b.mno = m.mno : board 테이블과 member 테이블을 조인, board 테이블의 회원 번호(mno)와 member 테이블의 회원 번호(mno)를 조인 조건으로 사용
- 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 객체를 파라미터로 받음
- 사용자로부터 입력받은 정보를 WriteDTO에 담습니다.
- 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>
'개발 공부 Today I Learned' 카테고리의 다른 글
[국비 61일차 TIL] spring 글 삭제, 페이징, 로그인 (1) | 2024.02.20 |
---|---|
[국비 60일차 TIL] spring 글쓰기, 디테일, 댓글 modal (0) | 2024.02.19 |
[국비 58일차 TIL] 스프링 다시 만들기 3 (0) | 2024.02.15 |
[국비 57일차 TIL] 스프링 설정하기 반복 (0) | 2024.02.14 |
[국비 56일차 TIL] 스프링 기본 설치 (1) | 2024.02.13 |
댓글