스프링수업 16일차

한 일

  • DB 연결 (+쿼리문 테스트)
  • service 패키지
  • controller 클래스
  • 게시글 등록하기

게시판 만들기

MyBatis를 이용한 게시판 만들기

DB 연결


테이블에 있는 데이터를 가져오는데 사용할 클래스 BoardVO 생성

- BoardVO -

@Data
public class BoardVO {
	
	private int bno;		// 게시글 번호
	private String title;	// 제목
	private String content;	// 내용
	private String writer;	// 글쓴이
	private LocalDateTime regdate;	// 등록날짜 timestemp에서 날짜시간을 가져오는 자바 날짜시간데이터
	private LocalDateTime updateDate;	// 수정날짜
}

@Data: @Getter @Setter @ToString 세 어노테이션을 모두 포함하는 기능


BoardMapper인터페이스 생성(dao와 같은 의미이므로 혼용해도 상관은 없음)

- BoardMapper -

@Mapper
public interface BoardMapper {
	public void enroll(BoardVO board);	// 게시판 등록
}

새 게시글 등록 기능 선언. (구현은 다른 클래스에서 인터페이스를 구현하여야 함.)
기능을 구현하기 전 인터페이스를 만듦.

- application.properties -

# MyBatis
# mapper.xml
mybatis.mapper-locations: mapper/*.xml

# model 열이름 언더바 '_' -> camel case setting
mybatis.configuration.map-underscore-to-camel-case=true

# 패키지 result type의 패키지를 생략할 수 있도록 model packge 위치 지정
mybatis.type-aliases-package=com.myapp.bbs.model

# mapper log level setting
logging.level.com.myapp.mybatis.mapper=TRACE

- BoardMapper.xml -

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

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.myapp.bbs.dao.BoardMapper">

	<select id="enroll" resultType="BoardVO">
		INSERT INTO board (title, content, writer)
        VALUES (#{title}, #{content}, #{writer})
	</select>
</mapper>

자바소스의 BoardMapper의 경로를 <mapper>태그에 적용시켜야 함.
BoardMapper인터페이스의 구현을 BoardMapper.xml에서 하게 됨.
id는 인터페이스에서 정한 메서드의 이름(enroll), resultType은 리턴받을 객체(BoardVO)를 입력.

쿼리문 테스트

jpa와는 달리 직접 작성하므로 확인을 위해 테스트 툴을 사용.

- pom.xml -

<!-- 마이바티스 테스트 -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter-test</artifactId>
	<version>2.2.2</version>
	<scope>test</scope>
</dependency>

dependencies 맨 아래에 추가. 테스트 툴의 사용을 위해 라이브러리를 추가한다.


테스트용 BoardMapperTest클래스 생성. (*인터페이스가 아니라 클래스로 생성)

- BoardMapperTest -

@MybatisTest
@AutoConfigureTestDatabase(replace = Replace.NONE)	// 현재 연결된 실제DB로 테스트함을 의미
@Rollback(value = false)							// 테스트시 롤백 안함
public class BoardMapperTests {
	// Junit 5버전으로 테스트
	@Autowired
	private BoardMapper boardMapper;
	
	@Test
	public void testEnroll() {
		BoardVO vo = new BoardVO();
		
		vo.setTitle("test Title");
		vo.setContent("test Content");
		vo.setWriter("test Writer");
		
		boardMapper.enroll(vo);
	}
}

@AutoConfigureTestDatabase(replace = Replace.NONE) 현재 연결된 실제DB로 테스트함을 의미
@Rollback(value = false) 테스트 실행 후 데이터를 롤백하지 않음. 데이터를 롤백하면 입력테스트가 제대로 완료되었는지 확인하기 어렵기 때문.



testEnroll메서드를 실행.

테스트가 정상적으로 완료되면 DB의 board테이블에 데이터가 새로 입력된 것을 확인가능.

테스트는 한번 설정해두면 다른 메서드를 테스트할때도 활용가능함.


service 패키지

바로 controller에 적용하지 않고 service층을 만들어 사용.

- BoardService.interface -

public interface BoardService {
	
	public void enroll(BoardVO board);	// 게시글 등록
}

서비스 인터페이스를 구현할 BoardServiceImpl클래스 생성

- BoardServiceImpl -

@Service
public class BoardServiceImpl implements BoardService {
	
	private BoardMapper boardMapper;
	
	// 생성자 주입
	public BoardServiceImpl(BoardMapper boardMapper) {
		this.boardMapper = boardMapper;
	}
	
	@Override
	public void enroll(BoardVO board) {
		boardMapper.enroll(board);
	}
}

클래스에 @Service 어노테이션을 작성해야 스프링이 인식함.
생성자를 주입하였으므로 BoardServiceImpl 객체가 생성될때 boardMapper객체도 자동으로 주입

enroll메서드가 실제로 해야할 기능을 여기서 override하여 재정의.
boardMapper.enroll(board)는 매개변수로 받은 board객체를 DB에 입력하는 기능임.


controller 클래스

- BoardController -

@Controller
@RequestMapping("/board")
@Log		// 콘솔에 로그 출력 (print out 대신 로그출력)
public class BoardController {
	@GetMapping("/list")
	public String boardListGet() {
		log.info("게시판 리스트 페이지 진입");
		return "list";
	}
	@GetMapping("/enroll")
	public String boardEnrollGet() {
		log.info("게시판 등록 페이지 진입");
		return "enroll";
	}
}

@Log 로그로 간단히 결과를 확인가능

http://localhost:8080/board/list

view를 만들지않아 오류가 나지만 로그에 뜬 문구로 정상적으로 접근이 가능함을 확인가능.


view 만들기

- list.html -

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>게시판 리스트</title>
  </head>
  <body>
    <h1>목록 페이지</h1>
    <a th:href="@{/board/enroll}">게시글 등록</a>
  </body>
</html>

게시글 리스트를 보여줄 list.html을 생성

- enroll.html -

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>게시글 등록</title>
  </head>
  <body>
    <h1>등록 페이지</h1>
  </body>
</html>

http://localhost:8080/board/list

다시 리스트페이지에 진입하면 정상적으로 view가 출력됨.

각 페이지에 진입할 때마다 로그에 뜨는것을 확인가능.
sysout보다 확인에 용이함.

view에 템플릿 적용


템플릿 복사 붙여넣기후 HomeConroller를 생성하여 확인

- HomeController -

@Controller
public class HomeController {

	@GetMapping(value = {"/", "dashboard"})
	public String dashBoard() {
		return "pages/dashboard";
	}
	
	@GetMapping("/tables")
	public String showTable() {
		return "pages/tables";
	}
	
	@GetMapping("/profile")
	public String showProfile() {
		return "pages/profile";
	}
	
	@GetMapping("/sign-in")
	public String login() {
		return "pages/sign-in";
	}
	
	@GetMapping("/sign-up")
	public String register() {
		return "pages/sign-up";
	}	
}

http://localhost:8080/
기본주소로 진입하여 확인

enroll.html 수정
- enroll.html -

<div class="card-header">
  <h4 class="font-weight-bolder">새 게시글 작성</h4>
  <p class="mb-0">새로운 게시글을 작성하세요</p>
</div>
<div class="card-body bg-white">
  <form role="form" th:action="@{/board/enroll}" method="post" th:object="${board}">
    <div class="input-group input-group-outline mb-3">
      <label class="form-label">제목</label>
      <input type="text" class="form-control" th:field="*{title}" required />
    </div>
    <div class="input-group input-group-outline mb-3">
      <div class="input-group input-group-dynamic">
        <textarea th:field="*{content}" class="form-control" rows="5" placeholder="내용을 적어주세요." spellcheck="false" required></textarea>
      </div>
    </div>
    <div class="input-group input-group-outline mb-3">
      <label class="form-label">글쓴이</label>
      <input type="text" class="form-control" th:field="*{writer}" required />
    </div>
    <div class="text-center">
      <button type="submit" class="btn btn-lg bg-gradient-primary btn-lg w-100 mt-4 mb-0">게시하기</button>
    </div>
  </form>
</div>

게시글 등록하기

- BoardController -

@GetMapping("/enroll")
public String boardEnrollGet(Model model) {
	
	model.addAttribute("board", new BoardVO());
	return "enroll";
}

boardEnrollGet매서드 수정.
form의 데이터를 사용할 수 있도록 Model을 매개변수로 사용, BoardVO의 객체로 게시글데이터를 입력.

http://localhost:8080/board/enroll

- BoardController -

@PostMapping("/enroll")
public String boardEnrollPost(BoardVO board) {
	log.info("Board : "+ board);
	return "redirect:/board/enroll";
}

글 작성하기 버튼을 눌렀을때 post로 전송받을 메서드를 컨트롤러에 추가.
redirect로 다시 /board/enroll 페이지로 넘겨줌

http://localhost:8080/board/enroll 에서 테스트용 내용을 기입한 후 '게시하기'버튼을 눌러 submit하면
콘솔에 2022-04-15 11:28:07.692 INFO 11764 --- [nio-8080-exec-3] c.myapp.bbs.controller.BoardController : BoardVO : BoardVO(bno=0, title=제목1, content=내용, writer=홍길동, regdate=null, updateDate=null) 문구가 뜨는걸 확인가능.

테스트를 마치면 이제 DB에 입력하는 기능을 작성할 차례.

- BoardController -

private BoardService boardService;

public BoardController(BoardService boardService) {
	this.boardService = boardService;
}
	// ...생략...
@PostMapping("/enroll")
public String boardEnrollPost(BoardVO board) {
//		log.info("BoardVO : "+ board); 테스트 완료
	boardService.enroll(board);
	return "redirect:/board/enroll";	// post - redirect
}

nav.html 수정

<nav aria-label="breadcrumb">
  <h6 id="pageName" class="font-weight-bolder mb-0 py-3">페이지 제목</h6>
  <script th:if="${message}">
    let m = '[[${message}]]'; // 리다이렉트로 넘어온 메시지 저장
    alert(m); // 화면에 메시지 띄우기
  </script>
</nav>

[[${message}]]: redirect로 넘어온 ${message}를 javascript에서 사용할때

- BoardController -

@PostMapping("/enroll")
public String boardEnrollPOST(BoardVO board, RedirectAttributes attr) {
	//log.info("BoardVO : " + board);
	boardService.enroll(board);
	attr.addFlashAttribute("message", "게시글 등록 성공!");
	return "redirect:/board/list";
}

list.html

<body class="g-sidenav-show bg-gray-200">
  <aside th:replace="fragments/aside :: 어사이드"></aside>
  <!-- 메인 컨텐트 시작 -->
  <main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg">
    <!-- 상단 네브바 -->
    <nav th:replace="fragments/nav :: 네브"></nav>
    <!-- 컨텐츠 시작 -->
    <div class="container-fluid py-4">
      <!-- 빈 컨텐츠 (여기서 시작)-->
      <h1>게시글 리스트 페이지</h1>
    </div>
  </main>

  <!-- 오른쪽 숨김창 , JS 링크들 -->
  <hidden th:replace="fragments/hidden :: 숨김창"></hidden>
  <jslink th:replace="fragments/jslink :: 링크"></jslink>
  <script>
    document.getElementById('pageName').textContent = '게시판 목록';
  </script>
</body>


작성 후 게시하기 클릭

게시글 리스트페이지로 이동하며 alert창이 뜸

확인을 누르면 게시글 리스트페이지로 이동함을 확인가능

DB의 board테이블에도 저장됨.

좋은 웹페이지 즐겨찾기