[3] 엔티티 설계(연관관계 매핑) 및 게시판 뷰 만들기
들어가며
이전 포스팅 까지는 로그인및 회원가입을 구현해봤다 이번 포스팅에서는 Board 엔티티를 만들고 Board 엔티티와 Member 엔티티간 연관관계를 매핑하고, 게시판 페이지의 틀을 만들어볼 예정이다.
(이전 포스팅에서 MemberDto를 생성해봤기 때문에 이번 포스팅부터는 만든 Dto클래스는 생략한다.
GetMapping 방식은 Srping MVC로 설계 예정)
프로젝트 환경
[SpringBoot ] 무작정 (REST API)CRUD게시판을 만들어 보자 준비 [1]
[1] 프로젝트 준비 들어가며 프레임워크나, 언어, DB등을 이론적로 공부를 하고난후에 한번 토이 프로젝트를 통해서 실습을 해보고싶었다. 그래서 이번에 간단한 CRUD를 통해 게시판을 만들어봤다
back-stead.tistory.com
엔티티 생성 및 설계
한명의 사용자는 여러개의 게시판을 작성할수있다. 그래서 1(Member)대다(Board)로 양방향 매핑을 할것입니다.
Board.class
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Board {
@Id@GeneratedValue
@Column(name = "board_Id")
private Long Id; //board 아이디
private String title; // 제목
private String writer; // 작성자
private String password; // 게시글 비밀번호
private LocalDateTime dateTime; // 게시글 작성시간
private String content; // 게시글 내용
private Integer views; // 조회수
private Integer count; // 댓글 총합
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_Id")
private Member member;
}
※viesw,count는 아직 사용할 예정은 아니지만 나중에 조회수와 댓글의 총합을 보여주는 기능을 구현할 예정
Board와 똑같은 필드를 갇는 BoardDto를 생성한다
Member.class 추가
@JsonIgnore //추가
@OneToMany(mappedBy = "member")
private List<Board> boards = new ArrayList<>();
@JsonIgnore :애노테이션은 특정 필드 또는 메서드를 JSON 직렬화(객체를 JSON으로 변환)에서 제외하도록 지정하는 데 사용됩니다.(나중에 json 형태로 데이터를 전송하여 값을 저장할것인데 양방향 매핑으로 설계했기때문에 순한 참조가 발생하기여 스택오버플로우가 발생하므로 방지하기 위해서 사용.)
Controller
BoardController
@Controller
@RequiredArgsConstructor
@Slf4j
public class BoardController {
@GetMapping("/board")
public String board(Model model,HttpSession session){
getSession(model,session);
return "board/board";
}
private static void getSession(Model model, HttpSession session) { //로그인한 사용자 가져오기
Member loginMember = (Member) session.getAttribute("loginMember");
model.addAttribute("loginMember",loginMember);
}
}
- getSession(...) : BoardController에서 로그인한 사용자를 반복적으로 넘겨줘여하는 경우가 생길수도 있어서 코드가 반복적으로 작성할수있다. 그래서 유지보수를 하기쉽게 메소드로 만들었다.
HTML
board.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header"></head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script th:inline="javascript">
var loginverfiy = false;
$(document).ready(function () {
var login = document.getElementById('loginMember_username').value;
if (login !==null){
loginverfiy = true;
}
})
function writing(){
if(loginverfiy ===true){
window.location.href="/boardWrit";
}
if (loginverfiy===false){
alert("로그인 먼저 해주세요");
}
}
</script>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#" th:href="@{/}">CRUD 게시판</a>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<div th:if="${loginMember != null}" class="d-flex justify-content-between">
<div class="nav-item">
<a class="nav-link active" aria-current="page" th:text="'사용자: '+${loginMember.username}">Home</a>
</div>
<div class="nav-item">
<form th:action="@{/logout}" method="post">
<button type="submit" class="btn btn-link">로그아웃</button>
</form>
</div>
</div>
<div th:if="${loginMember==null}" class="d-flex justify-content-between">
<li class="nav-item" >
<a class="nav-link active" aria-current="page" th:href="@{/login}">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/createMember}">회원가입</a>
</li>
</div>
</ul>
</div>
</div>
</nav>
<div> 총 상품수 :
<span >19</span>
</div>
<form method="get" th:action="@{/board}">
<div class="col-6 d-flex justify-center">
<label for="title" class="visually-hidden">검색</label>
<input type="text" class="form-control flex-grow-1 me-2" id="title" name="title">
<button type="submit" class="btn btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 9.672a6.5 6.5 0 1 0-1.07 1.07l3.53 3.53a.75.75 0 0 0 1.06-1.06l-3.52-3.54zM10.5 6A4.5 4.5 0 1 1 6 1.5 4.5 4.5 0 0 1 10.5 6z"/>
</svg>
</button>
</div>
</form>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">제목</th>
<th scope="col">작성자</th>
<th scope="col">작성일</th>
<th scope="col">조회수</th>
</tr>
</thead>
<tbody>
<tr >
<th scope="row">
<a>1</a>
</th>
<td>
<span>제목</span>
</td>
<td>작성자</td>
<td>오늘작성</td>
<td>100td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-end">
<button onclick="writing()">글쓰기</button>
</div>
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item ">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" >
<a class="page-link" href="#" >pageNumber</a>
</li>
<li class="page-item">
<a class="page-link" href="#" >
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<div th:if="${loginMember!=null}">
<input type="hidden" id="loginMember_username" th:value="${loginMember.username}">
</div>
</body>
</html>
- jquery를 사용해서 글쓰기 버튼을 클릭시에 로그인이 되어있다면 "/boardWrit" 이동 로그인이 되어있지않다면 alter가 실행되게 했다.
- onclick="writing()" 클릭시 fuction writing() 실행
실행
JPA 에서 대신 생성해준 board 테이블을 보면 member_id가 외래키로 들어가 있는것을 볼수있다.
사진 1과 같은 페이지가 나오게 된다면 성공한 것이다.
(아직 기능을 구현하지 않아서 아무것도 작동하지 않을 것입니다.)
다음으로
이번 포스팅에서 Member와 Board의 연관관계 설정과, 게시판의 기본틀을 만들어봤다. 다음 포스팅에는 RestFul API 방식으로 게시판의 글을 작성하는 기능을 구현해볼 예정입니다.