블로그 웹 페이지를 만들 것이다.
블로그 프로젝트의 파일 구조는 이러하다.
이번 시간에는 회원가입 페이지,
로그인 페이지, 글 목록 페이지 UI 작업을 할 것이다.
우선 컨트롤러를 만들어보자.
package site.metacoding.dbproject.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@Controller
public class PostController {
// 글쓰기 페이지 /post/writeForm
@GetMapping("/post/writeForm")
public String writeForm() {
return "post/writeForm";
}
// 메인 페이지
// 글 목록 페이지 /post/list/
@GetMapping({ "/", "post/list" }) // 두 가지 방법으로 들어올 수 있음
public String list() {
return "post/list";
}
// 글 상세보기 페이지 /post/{id} (삭제버튼 만들어두면 되니까 삭제페이지 필요 X)
@GetMapping("/post/{id}")
public String detail(@PathVariable Integer id) { // int는 null이 없음, 초기값이 0
// Integer는 초기값이 null
return "post/" + id;
}
// 글 수정 페이지 /post/{id}/updateForm
@GetMapping("/post/{id}/updateForm")
public String updateForm(@PathVariable Integer id) {
return "post/updateForm"; // ViewResolver 도움 받음
}
// DELETE 글 삭제 /post/{id} -> 글 목록으로 가기
@DeleteMapping("/post/{id}")
public String delete(@PathVariable Integer id) {
return "redirect:/";
}
// UPDATE 글 수정 /post/{id} -> 글 상세보기 페이지 가기
@PutMapping("/post/{id}")
public String update(@PathVariable Integer id) {
return "redirect:/post/" + id;
}
// POST 글 쓰기 /post -> 글 목록으로 가기
@PostMapping("/post")
public String post() {
return "redirect:/"; // 다시 컨트롤러의 메서드를 찾아가는 것
}
}
package site.metacoding.dbproject.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@Controller
public class UserController {
// 회원가입 페이지 (정적) - 인증(로그인) X
@GetMapping("/joinForm")
public String joinForm() {
return "user/joinForm";
}
// 회원가입 INSERT - 인증(로그인) X
@PostMapping("/join")
public String join() { // 행위, 페이지 아님
return "redirect:/user/loginForm"; // 로그인페이지 이동해주는 컨트롤러 메서드를 재활용
}
// 로그인 페이지 (정적) - 인증(로그인) X
@GetMapping("/loginForm")
public String loginForm() {
return "user/loginForm";
}
// 로그인 SELECT * FROM user WHERE username=? AND password=?
// 원래 SELECT는 무조건 GET요청
// 근데 로그인만 예외! POST요청
// 이유 : 주소에 패스워드를 남길 수 없으니까!! 보안을 위해!!
// 로그인 - 인증(로그인) X
@PostMapping("/login")
public String login() {
return "메인 페이지를 돌려주면 됨"; // PostController 만들고 수정하자
}
// 유저 상세 페이지 (동적 -> DB연동 필요) - 인증(로그인) O
@GetMapping("/user/{id}")
public String detail(@PathVariable int id) {
return "user/detail";
}
// 유저 수정 페이지 - 인증(로그인) O
@GetMapping("/user/{id}/updateForm")
public String updateForm(@PathVariable int id) {
return "user/updateForm";
}
// 유저 수정 - 인증(로그인) O
@PutMapping("/user/{id}")
public String update(@PathVariable int id) {
return "redirect:/user/" + id;
}
// 로그아웃 - 인증(로그인) O
@GetMapping("/logout")
public String logout() {
return "메인 페이지를 돌려주면 됨"; // PostController 만들고 수정하자
}
}
글 목록 페이지가 메인 페이지이다.
웹 페이지 디자인을 할 때
누가 만들어놓은 디자인을 그대로 갖다 쓰면 편하다.
https://www.w3schools.com/bootstrap5/index.php
bootstrap은 트위터에서 만든 것이다.
미리 만들어놓은 클래스를 사용하는 것이다.
추가 참고 사이트
material css https://materializecss.com/
ant design https://ant.design/
아래 두 줄의 링크를 걸어주면 사용이 가능하다.
css 파일을 다운로드하여서 사용하는 게 아니라
링크를 걸어서 사용해주는 것을 CDN이라고 한다.
CDN(Content Delivery Network) : 컨텐츠를 효율적으로 전달하기 위해
네트워크에 데이터를 저장하여 제공하는 시스템
CDN방식을 사용하면 웹서버의 과부하를 줄일 수 있다.
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
아래 사진과 같은 내비게이션 바 클래스를 가져올 것이다.
사용하기 위해 Example 아래
Try it Yourself 버튼을 눌러준다.
그리고 왼쪽의 코드를 모두 복사한다.
list.mustache 파일에
그대로 옮겨주자.
<!DOCTYPE html>
<html lang="en">
<head>
<title>블로그</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<!-- 네비게이션 시작 -->
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/loginForm">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/joinForm">회원가입</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 네비게이션 끝 -->
</body>
</html>
이때 모든 mustache 파일의
header부분과 footer부분은 반복되기 때문에
따로 layout 파일을 만들어 재사용해주자.
mustache의 부분 템플릿 기능을 이용해
파일을 이어 붙이기가 가능하다.
네비게이션 바 역시 웹 페이지에서 반복되므로
header에 넣어준다.
header.mustache▼
<!DOCTYPE html>
<html lang="en">
<head>
<title>블로그</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<!-- 네비게이션 시작 -->
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/loginForm">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/joinForm">회원가입</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 네비게이션 끝 -->
footer.mustache▼
<!--웹 페이지의 footer에는 회사의 정보, 홈페이지 정보가 담겨져있다.-->
<div class="mt-4 p-5 bg-secondary text-white rounded">
<h1>JJJAE_Oo_ni</h1>
<p>https://jaewon2336.tistory.com/</p>
</div>
</body>
</html>
https://bibi6666667.tistory.com/269
부분 템플릿을 사용하여 파일을 이어 붙여주자.
{{> /layout/header}}
사용하려는 list 파일 기준
layout폴더는 상위에 있기 때문에
/ 를 붙여준다.
{{> /layout/header}}
<!-- list.mustache -->
{{> /layout/footer}}
메인 페이지에 글 목록 css도 가져와보자.
Try it Yourself에 다시 들어갈 필요 없이
Example의 코드만 복사해온다.
링크를 한 번만 걸어놓으면
다른 클래스를 또 사용할 때 전체 코드를
복사해올 필요는 없다.
메인 페이지에 한 번에 보이는 글 개수를 관리하기 위해
페이징 기법을 사용하는데
처음 위치가 왼쪽에 붙어있다.
이때 얘들을 가운데로 옮기기 위해서는
flex를 사용하는 게 최고!!
얘들을 가운데로 오게 flex를 걸어주려면
부모 태그가 필요하다.
근데 이미 ul 태그로 감싸져있네?
검사해서 확인해보니까 ul에 이미 flex가 걸려있다!
그럼 방향을 센터로 설정만 해주면 되겠다!
justify-content-center
<!-- ../는 상위폴더로 올라가는 것 -->
{{> /layout/header}}
<!-- 컨테이너 시작 -->
<div class="container mt-3"> <!-- container, mt-3은 css 클래스임!! -->
<!-- 게시글 아이템 시작 -->
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title">제목입니다.</h4>
<a href="/post/1" class="btn btn-dark">상세보기</a> <!--나중에 동적으로 id 바꿔줘야함!!!-->
</div>
</div>
<!-- 게시글 아이템 끝 -->
<!-- 페이지 시작 -->
<ul class="pagination justify-content-center">
<li class="page-item disabled"><a class="page-link" href="#">이전</a></li>
<li class="page-item"><a class="page-link" href="#">다음</a></li>
</ul>
<!-- 페이지 시작 -->
</div>
<!-- 컨테이너 끝 -->
{{> /layout/footer}}
똑같은 방법으로 필요한 css를 찾아
로그인 페이지와 회원가입 페이지를 만들어주자.
joinForm.mustache ▼
{{> /layout/header}}
<!-- 컨테이너 시작 -->
<div class="container mt-3"> <!-- container, mt-3은 css 클래스임!! -->
<!-- 회원가입 폼 시작 -->
<form action="/join" method="post">
<div class="mb-3 mt-3">
<input type="text" class="form-control" placeholder="Enter username" name="username">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="Enter password" name="password">
</div>
<div class="mb-3">
<input type="email" class="form-control" placeholder="Enter emails" name="email">
</div>
<button type="submit" class="btn btn-dark">회원가입</button>
</form>
<!-- 회원가입 폼 끝 -->
</div>
<!-- 컨테이너 끝 -->
{{> /layout/footer}}
loginForm.mustache ▼
{{> /layout/header}}
<!-- 컨테이너 시작 -->
<div class="container mt-3"> <!-- container, mt-3은 css 클래스임!! -->
<!-- 로그인 폼 시작 -->
<form action="/login" method="post">
<div class="mb-3 mt-3">
<input type="text" class="form-control" placeholder="Enter username" name="username">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="Enter password" name="password">
</div>
<!-- 쿠키 - 세션 -->
<div class="form-check mb-3">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="remember"> Remember me
</label>
</div>
<button type="submit" class="btn btn-dark">로그인</button>
</form>
<!-- 로그인 폼 끝 -->
</div>
<!-- 컨테이너 끝 -->
{{> /layout/footer}}
remember키는 체크하면 쿼리 스트링에 on으로 날아가고
체크하지 않으면 아무 값도 날아가지 않는다.
[출처]
https://cafe.naver.com/metacoding
메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9