1.회원가입
1-1.다대다 테이블 처리
:다대다 테이블을 처리하기 위해선 하나의 트랜잭션으로 다뤄져야한다.
insert into user(email, name, password, regdate) values (?,?,?,now());
select LAST_INSERT_ID();
insert into user_role(user_id, role_id) values (?,1)
1-1-1.UserService
import com.example.boardservice.dao.UserDao;
import com.example.boardservice.dto.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor //lombok이 final 필드를 초기화하는 생성자를 자동으로 생성한다. 생성자 생성을 생략시킴
public class UserService {
// dao 생성자 주입
// 생성자 주입: Spring이 UserService를 Bean으로 생성할 때 생성자를 이용해 생성하는데, 이때 UserDao Bean이 있는지 보고
// Bean이 있으면 주입한다.
private final UserDao userDao;
// public UserService(UserDao userDao) {
// this.userDao = userDao;
// }
//회원정보 저장
// 1.값을 집어넣고 2.autoincreament를 구하고 3.권한을 부여하는 작업을 한번에 진행한다.
// 서비스에는 @Transactional을 붙여 하나의 트랜잭션으로 처리한다.
@Transactional
public User addUser(String name, String email, String password) {
User user = new User();
userDao.addUser(email, name, password);
userDao.mappingUserRole(user.getUserId());
return user;
}
}
- UserService클래스에서 회원 가입을 위해 1.회원가입 정보를 저장 2.권한을 부여 두 작업을 한번에 진행한다. 이를 위해 @Transaction을 통해 userDao.addUser()와 userDao.mappingUserRole() 두 메서드를 하나의 @Transaction으로 묶는다.
1-1-2.UserDao
@Repository
public class UserDao {
// insert into user(email, name, password, regdate) values (?,?,?,now());
// select LAST_INSERT_ID();
// insert into user_role(user_id, role_id) values (?,1)
// 하나의 SQL(insert)을 처리하기 위한 과정
@Transactional //propagation.REQUIRED로 인해 Service(addUser) 부모 Transaction에 하나의 트랜잭션으로 묶인다.
public User addUser(String email, String name, String password) {
// insert into user(email, name, password, regdate) values (?,?,?,now());
// SELECT LAST_INSERT_ID(); autoincreament값을 구함
return null;
}
// 권한 정보 저장
@Transactional //propagation.REQUIRED로 인해 Service(addUser) 부모 Transaction에 하나의 트랜잭션으로 묶인다.
public void mappingUserRole(int userId) {
// insert into user_role(user_id, role_id) values (?,1);
}
}
- UserDao에서는 addUser()와 mappingUserRole()가 부모 Transaction과 함께 사용해야하기 떄문에 @Transactional을 통해 하나의 Transaction으로 묶인다.
1-2.Controller
1-2-1.UserController
@Controller
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
// 회원가입 화면 보여주기
// http://localhost:8080/userRegForm
// userRegForm 리턴한다는 것은 classpath:/template/userRegForm.html을 사용한다는 것이다.
@GetMapping("/userRegForm")
public String userRegForm() {
return "userRegForm";
}
@PostMapping("/userReg")
public String userReg(
@RequestParam("name") String name,
@RequestParam("email") String email,
@RequestParam("password") String password
) {
System.out.println("name : " + name);
System.out.println("email : " + email);
System.out.println("password : " + password); //@RequestParam 값이 잘 넘어 왔는지 확인
//회원 정보를 저장한다.
userService.addUser(name,email,password);
return "redirect:/welcome"; // http://localhost:8080/welcom으로 이동
}
}
1-3.Service
1-3-1.UserService
@Service
@RequiredArgsConstructor //lombok이 final 필드를 초기화하는 생성자를 자동으로 생성한다. 생성자 생성을 생략시킴
public class UserService {
// dao 생성자 주입
// 생성자 주입: Spring이 UserService를 Bean으로 생성할 때 생성자를 이용해 생성하는데, 이때 UserDao Bean이 있는지 보고
// Bean이 있으면 주입한다.
private final UserDao userDao;
// public UserService(UserDao userDao) {
// this.userDao = userDao;
// }
//회원정보 저장
// 1.값을 집어넣고 2.autoincreament를 구하고 3.권한을 부여하는 작업을 한번에 진행한다.
// 서비스에는 @Transactional을 붙여 하나의 트랜잭션으로 처리한다.
@Transactional
public User addUser(String name, String email, String password) {
User user = userDao.addUser(email, name, password);
userDao.mappingUserRole(user.getUserId());
return user;
}
}
1-4.Repository
1-4-1.UserDao
@Repository
public class UserDao {
// JDBC
private final NamedParameterJdbcTemplate jdbcTemplate;
private SimpleJdbcInsertOperations insertUser;
public UserDao(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
insertUser = new SimpleJdbcInsert(dataSource)
.withTableName("user")
.usingGeneratedKeyColumns("user_id"); //자동으로 증가되는 id설정
}
@Transactional //propagation.REQUIRED로 인해 Service(addUser) 부모 Transaction에 하나의 트랜잭션으로 묶인다.
public User addUser(String email, String name, String password) {
// insert into user(email, name, password, regdate) values (?,?,?,now());
// SELECT LAST_INSERT_ID(); autoincreament값을 구함
// Spring JDBC 프로그래밍
User user = new User();
user.setName(name);
user.setEmail(email);
user.setPassword(password);
user.setRegdate(LocalDateTime.now());
SqlParameterSource params = new BeanPropertySqlParameterSource(user);
Number number = insertUser.executeAndReturnKey(params);// insert를 실행하고 자동으로 생성된 id(1증가된 id)를 가져온다.
int userId = number.intValue();
user.setUserId(userId);
return user;
}
// 권한 정보 저장
@Transactional //propagation.REQUIRED로 인해 Service(addUser) 부모 Transaction에 하나의 트랜잭션으로 묶인다.
public void mappingUserRole(int userId) {
// insert into user_role(user_id, role_id) values (?,1);
String sql = "insert into user_role(user_id, role_id) values (:userId,1)";
SqlParameterSource params = new MapSqlParameterSource("userId", userId);
jdbcTemplate.update(sql, params);
}
}
1-5.DTO
1-5-1.User
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor // 기본생성자가 자동으로 만들어짐
public class User {
private int userId;
private String email;
private String name;
private String password;
private LocalDateTime regdate;
}
2.로그인
2-1.Contoller
2-1-1.BoardController
@Controller
public class BoardController {
@GetMapping("/")
public String list(HttpSession session, Model model) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo");
model.addAttribute("loginInfo", loginInfo); // 템플릿에게 loinInfo값을 넘긴다.
return "list";
}
}
- session을 통해 로그인 정보가 맞는지 틀린지 확인한다. 로그인 정보가 session에 담긴 값이 맞다면 그대로 값이 나오겠지만, session에 없는 값이라면 null을 반환할거다. 그 결과 값을 model에 담아 list.html로 보낸다.
2-1-2.UserController
@Controller
@RequiredArgsConstructor // 생성자 주입 생성자 생략가능
public class UserController {
private final UserService userService;
@PostMapping("/login")
public String login(
@RequestParam("email") String email,
@RequestParam("password") String password,
HttpSession httpSession // Spring이 자동으로 Session을 처리하는 HttpSession 객체 생성
)
{
System.out.println("email :" + email);
System.out.println("password: " + password);
// email에 해당하는 회원 정보를 읽어온 후
// 암호가 맞다면 세션에 회원정보를 저장한다.
try {
User user = userService.getUser(email);
if(user.getPassword().equals(password)) { //user에 담긴 비밀번호와 파라미터로 넘어온 비밀번호가 같다면
System.out.println("암호가 같습니다.");
LoginInfo loginInfo = new LoginInfo(user.getUserId(), user.getEmail(), user.getName());
httpSession.setAttribute("loginInfo", loginInfo); //로그인 정보를 Session 객체에 넣는다.
System.out.println("세션에 로그인 정보가 저장되었습니다.");
} else {
throw new RuntimeException("암호가 일치하지 않음");
}
} catch (Exception ex) {
return "redirect:/loginForm?error=true";
}
return "redirect:/";
}
@GetMapping("/logout")
public String logout(HttpSession httpSession) {
// 세션에서 회원정보를 삭제한다.
httpSession.removeAttribute("loginInfo");
return "redirect:/";
}
}
2-2.Service
2-2-1.UserService
@Service
@RequiredArgsConstructor //lombok이 final 필드를 초기화하는 생성자를 자동으로 생성한다. 생성자 생성을 생략시킴
public class UserService {
// 회원정보 가져오기
@Transactional
public User getUser(String email) {
return userDao.getUser(email);
}
}
2-3.Repository
@Repository
public class UserDao {
@Transactional
public User getUser(String email) {
String sql="select user_id, email, name, password, regdate from user where email = :email";
SqlParameterSource params = new MapSqlParameterSource("email", email);
RowMapper<User> rowMapper = BeanPropertyRowMapper.newInstance(User.class);
User user = jdbcTemplate.queryForObject(sql, params, rowMapper);
return user;
}
}
2-4.Template
<div class="login-info">
<div class="container">
<span class="login-info_user" th:if="${loginInfo != null}">
<span>남기혁</span>
</span>
<span class="login-info_logout" th:if="${loginInfo != null}">
<a href="/logout">로그아웃</a>
</span>
<span class="login-info_logout" th:if="${loginInfo == null}">
<a href="/loginForm">로그인</a>
</span>
<span class="login-info_join" th:if="${loginInfo == null}">
<a href="/userRegForm" >회원가입</a>
</span>
</div>
</div>
- loginInfo의 값의 null 여부에 따라 Temlplate의 형태가 바뀐다. 이를 위해 Thymeleaf에서 쓰는 if 문법인 th:if="${loginInfo ==조건}"을 사용한다.
3.글쓰기
3-1.Controller
3-1-1.BoardController
@GetMapping("/writeForm")
public String writeForm(HttpSession session, Model model) {
// 로그인한 사람만 글을 쓴다.
// 세션에서 로그인한 정보를 읽어들인다. 로그인 하지 않았다면, 리스트 보기로 자동이동시킨다.
LoginInfo loginInfo = (LoginInfo) session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
if(loginInfo == null) { // 세션에 로그인 정보가 없으면 즉, 로그인 정보가 틀릴경우 /loginform으로 redirect한다.
return "redirect:/loginForm";
}
model.addAttribute("loginInfo", loginInfo); // 모델에 로그인 정보를 담아 'wirteForm'으로 보낸다.
return "writeForm";
}
@PostMapping("/write")
public String write(
@RequestParam("title") String title,
@RequestParam("content") String content,
HttpSession session
) {
// 로그인한 사람만 글을 쓴다.
// 세션에서 로그인한 정보를 읽어들인다. 로그인 하지 않았다면, 리스트 보기로 자동이동시킨다.
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
if(loginInfo == null) { // 세션에 로그인 정보가 없으면 즉, 로그인 정보가 틀릴경우 /loginform으로 redirect한다.
return "redirect:/loginForm";
}
System.out.println("title:"+title);
System.out.println("content:"+content);
//로그인 한 회원 정보 + 제목, 내용을 저장한다.
boardService.addBoard(loginInfo.getUserId(), title, content);
//리스트 보기로 리다이렉트한다.
return "redirect:/";
}
3-2.Service
3-2-1.BoardService
@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardDao boardDao;
@Transactional
public void addBoard(int userId, String title, String content) {
boardDao.addBoard(userId, title, content);
}
}
3-3.Repository
3-3-1.BoardDao
@Repository
public class BoardDao {
private final NamedParameterJdbcTemplate jdbcTemplate;
private final SimpleJdbcInsertOperations insertBoard;
// 생성자 주입, 스프링이 자동으로 DataSource의 구현체 HicariCP Bean을 주입한다.
public BoardDao(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
insertBoard = new SimpleJdbcInsert(dataSource)
.withTableName("board")
.usingGeneratedKeyColumns("board_id"); //자동으로 증가되는 id설정
}
@Transactional
public void addBoard(int userId, String title, String content) {
// Board DTO에 값을 넣는다
Board board = new Board();
board.setUserId(userId);
board.setTitle(title);
board.setContent(content);
board.setRegdate(LocalDateTime.now()); //LocalDateTime.now() 현재시간
// DTO의 값을 파라미터 값으로 넣는다.
SqlParameterSource params = new BeanPropertySqlParameterSource(board);
insertBoard.execute(params);
}
}
3-4.DTO
3-4-1.Board
@Getter
@Setter
@ToString
public class Board {
private int boardId;
private String title;
private String content;
private int userId;
private LocalDateTime regdate;
private int viewCnt;
}
3-5.template
3-5-1.list.html
<div>
<a href="/writeForm" th:if="${loginInfo != null}">글쓰기</a>
<span th:if="${loginInfo == null}">글쓰기</span>
</div>
<span class="login-info_user" th:if="${loginInfo != null}">
<span th:text="${loginInfo.name}"></span> //하드코딩 된 부분을 수정한다.
</span>
- 로그인 정보가 있으면 글쓰기에 링크가 걸리고 로그인 정보가 없으면 글쓰기에 링크가 걸리지 않는다.
- 하드코딩된 이름 부분을 수정한다
3-5-2.writeForm.html
<!DOCTYPE html>
<html lang="en">
<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>
<form action="/write" method="post">
이름 : <span th:text="${loginInfo.name}"></span>
제목 : <input type="text" name="title" /><br />
내용 : <br>
<textarea name="content" cols="50" rows="5"></textarea><br>
<input type="submit" value="등록" />
</form>
</body>
</html>
- th:text="${loginInfo.name}"를 통해 Model로 넘겨받은 로그인 정보의 name값을 넣는다.
4.페이징 처리
페이징을 위해 필요한 자료
1.전체 게시물 수
2.페이지에 해당하는 게시물
4-1.Controller
4-1-1.BoardController
@GetMapping("/")
public String list(HttpSession session, Model model) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo");
model.addAttribute("loginInfo", loginInfo); // 템플릿에게 loinInfo값을 넘긴다.
int page = 1;
int totalCount = boardService.getTotalCount();
List<Board> list = boardService.getBoards(page);
int pageCount = totalCount / 10;
if(totalCount % 10 > 0) {
pageCount++;
}
int currentPage = page;
model.addAttribute("List", list);
model.addAttribute("pageCount", pageCount);
model.addAttribute("currentPage", currentPage);
return "list";
}
- 총 페이지수를 구하기 위해 아래와 같은 코드를 쓴다.
int pageCount = totalCount / 10;
if(totalCount % 10 > 0) {
pageCount++;
}
4-2.Service
4-2-1.BoardService
//전체 게시물 수
@Transactional(readOnly = true)
public int getTotalCount() {
String sql = "select count(*) as total_count from board";
Integer totalCount = jdbcTemplate.queryForObject(sql, Map.of("start", start), Integer.class);
return totalCount.intValue();
}
@Transactional(readOnly = true)
public List<Board> getBoards(int page) {
// start 0,10,20,30는 1page, 2page, 3page, 4page를 나타낸다.
int start = (page - 1) * 10;
String sql = "select b.user_id, b.board_id, b.title, b.regdate, b.view_cnt, u.name " +
"from board b, user u where b.user_id = u.user_id order by board_id desc limit :start, 10";
RowMapper<Board> rowMapper = BeanPropertyRowMapper.newInstance(Board.class);
List<Board> list = jdbcTemplate.query(sql, Map.of("start", start), rowMapper);
return list;
}
- getTotalCount()의 sql은 조회를 한다. 또한 집계함수를 쓰기 때문에 한 건의 데이터를 가져온다. JdbcTemplate에서 한 건의 데이터를 가져오면 queryForObject()를 사용한다.
- getBoards()는 user,board_id,title,regdate,view_cnt, name 컬럼을 조회해야하는데, name을 제외한 컬럼들은 board 테이블에 있는 반면 name컬럼은 user테이블에 있다. 이 둘을 합쳐야 하기에 join한다.
- getBoards()는 start값을 구하기 위해 (page-1)*10 이라는 공식을 만든다.
- getBoards()는 여러건을 구하기 때문에 query()를 사용한다
4-4.Repository
4-4-1.Board
@Getter
@Setter
@ToString
public class Board {
private int boardId;
private String title;
private String content;
private int userId;
private LocalDateTime regdate;
private int viewCnt;
private String name;
}
- 테이블 조인을 위해 name컬럼 추가
4-4.Template
4-4-1.list.html
<tbody>
<tr th:each="board : ${list}">
<td th:text="${board.boardId}"></td>
<th>
<a href="/board?id=3" th:text="${board.title}"></a>
</th>
<td th:text="${board.viewCnt}"></td>
<td th:text="${board.name}"></td>
<td th:text="${board.regdate}"></td>
</tr>
</tbody>
- 반복문을 써서 list를 통해 값을 불러온다.
5.중간결과
게시물과 목록이 잘 나왔다. 하지만 전체 페이지와 페이징 이동 등 페이지에 대한 정보를 보충해야한다.
6.페이징처리
6-1.Controller
@GetMapping("/")
public String list(HttpSession session, Model model) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo");
model.addAttribute("loginInfo", loginInfo); // 템플릿에게 loinInfo값을 넘긴다.
int page = 1; //하드코딩
int totalCount = boardService.getTotalCount(); //전체 게시물 수를 가져온다.
List<Board> list = boardService.getBoards(page); // 만약 page가 1이면 글 번호가 1,2,3,4....10의 자료를 리턴
int pageCount = totalCount / 10;
if(totalCount % 10 > 0) {
pageCount++;
}
int currentPage = page;
for(Board board : list) {
System.out.println(board);
}
model.addAttribute("list", list);
model.addAttribute("pageCount", pageCount);
model.addAttribute("currentPage", currentPage);
return "list";
}
현재 Controller의 페이지 값은 int page = 1;로 하드코딩 되어있다. 하지만 우리는 사용자 입력 값에 따라 페이지가 달라지길 원한다. 하드 코딩된 코드를 수정하기 해야한다.
7.글 상세보기
7-1.Controller
7-1-1.BoardController
- board
@GetMapping("/board")
public String board(@RequestParam("boardId") int boardId, Model model, HttpSession session) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
//id에 해당하는 게시물을 읽어온다.
//id에 해당하는 게시물의 조회수가 1증가한다.
Board board = boardService.getBoard(boardId);
model.addAttribute("board", board);
model.addAttribute("loginInfo", loginInfo);
return "board";
}
7-2.Service
7-2-1.BoardService
- getBoard()
@Transactional
public Board getBoard(int boardId) {
//id에 해당하는 게시물을 읽어온다.
Board board = boardDao.getBoard(boardId);
//id에 해당하는 게시물의 조회수가 1증가한다.
boardDao.updateViewCnt(boardId);
return board;
}
7-3.Repository
7-3-1.BoardDao
- getBoard()
@Transactional(readOnly = true)
public Board getBoard(int boardId) {
String sql ="select b.user_id, b.board_id, b.title, b.regdate, b.view_cnt, u.name, b.content from board b, user u where b.user_id = u.user_id and b.board_id=:boardId";
RowMapper<Board> rowMapper = BeanPropertyRowMapper.newInstance(Board.class);
Board board = jdbcTemplate.queryForObject(sql, Map.of("boardId", boardId), rowMapper);
return board;
}
- 하나의 쿼리문을 조회하기 때문에 'jdbcTemplate.queryForObject()를 쓴다.
- rowMapper는 DTO를 만드는데 사용한다. 아래 코드를 참조하면 이해가 쉽다. 원래 rowMapper를 만들 떄 아래의 코드가 나와야하지만 'BeanPropertyRowMapper를 이용하여 코드를 간결하게 할 수 있다.
- updateViewCnt()
@Transactional
public void updateViewCnt(int boardId) {
String sql ="update board set view_cnt = view_cnt+1 where board_id = :boardId";
jdbcTemplate.update(sql, Map.of("boardId", boardId));
}
7-4.Template
- list.html
<tr th:each="board : ${list}">
<td th:text="${board.boardId}"></td>
<th>
<a th:href="@{/board(boardId=${board.boardId})}" th:text="${board.title}"></a>
</th>
<td th:text="${board.viewCnt}"></td>
<td th:text="${board.name}"></td>
<td th:text="${board.regdate}"></td>
</tr>
- list의 게시물 버튼을 누르면 유기적으로 '/board?boardId=?' 값으로 변화해야하기 때문에 "@{/board(boardId=${board.boardId})}"를 써준다.
- board.html
<!DOCTYPE html>
<html lang="en">
<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 th:text="${board.title}"></H1>
<div>글쓴이 : <span th:text="${board.name}"></span></div>
<div>제목 : <span th:text="${board.title}"></span></div>
<div>조회수 : <span th:text="${board.viewCnt}"></span></div>
<div>작성일 : <span th:text="${board.regdate}"></span></div>
<div>내용 : <span th:text="${board.content}"></span></div>
<div><a href="/">목록보기</a></div>
<!--타임리프의 if문을 사용하여 로그인이 비활성 될 때 수정하기,삭제하기를 없앤다.-->
<div><a th:if="${loginInfo != null}" th:href="@{/updateform(boardId=${board.boardId})}">수정하기</a></div>
<div><a th:if="${loginInfo != null}" th:href="@{/delete(boardId=${board.boardId})}">삭제하기</a></div>
</body>
</html>
- boardController에서 Model을 통해 받아온 board를 ${board.?} 이용하여 불러온다.
- 타임리프의 if문을 사용하여 로그인 여부에 따라 '수정하기'와 '삭제하기' 활성화 여부를 결정한다.
7-5 .결과
8.글 삭제하기
8-1.Controller
8-1-1.BoardController
- delete
@GetMapping("/delete")
public String delete(
@RequestParam("boardId") int boardId,
HttpSession session
) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
if(loginInfo == null) { // 세션에 로그인 정보가 없으면 즉, 로그인 정보가 틀릴경우 /loginform으로 redirect한다.
return "redirect:/loginForm";
}
// loginInfo.getUserId() 사용자가 쓴 글일 경우에만 삭제한다.
boardService.deleteBoard(loginInfo.getUserId(), boardId);
return "redirect:/"; //삭제 후 리스트보기로 redirect한다.
}
8-2.Service
8-2.1.BoardService
- deleteBoard()
@Transactional
public void deleteBoard(int userId, int boardId) {
Board board = boardDao.getBoard(boardId);
if (board.getUserId() == userId) {
boardDao.deleteBoard(boardId);
}
}
8-3.Repository
8-3-1.BoardDao
- deleteBoard()
@Transactional
public void deleteBoard(int boardId) {
String sql = "delete from board where board_id = :boardId";
jdbcTemplate.update(sql, Map.of("boardId", boardId));
}
8-4.결과
9.수정하기
9-1.Controller
9-1-1.BoardController
@GetMapping("/updateForm")
public String update(@RequestParam("boardId") int boardId, Model model, HttpSession session) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
if(loginInfo == null) { // 세션에 로그인 정보가 없으면 즉, 로그인 정보가 틀릴경우 /loginform으로 redirect한다.
return "redirect:/loginForm";
}
// boardId에 해당하는 정보를 읽어와 updateForm 템플릿에게 전달
// viewCnt는 증가하지 않는다. 이를 위해 service를 수정한다.
Board board = boardService.getBoard(boardId, false);
model.addAttribute("board", board);
model.addAttribute("loginInfo", loginInfo);
return "updateForm";
}
@PostMapping("/update")
public String update(
@RequestParam("boardId") int boardId,
@RequestParam("title") String title,
@RequestParam("content") String content,
HttpSession session
) {
LoginInfo loginInfo = (LoginInfo)session.getAttribute("loginInfo"); // 세션에서 로그인 정보를 가져온다
if(loginInfo == null) { // 세션에 로그인 정보가 없으면 즉, 로그인 정보가 틀릴경우 /loginform으로 redirect한다.
return "redirect:/loginForm";
}
// boardId에 해당하는 글의 제목과 내용을 수정. html에서 hidden으로 받음
// 글쓴이만 수정가능.
Board board = boardService.getBoard(boardId, false);
if(board.getUserId() != loginInfo.getUserId()) {
return "redirect:/board?boardId=" + boardId;
}
boardService.updateBoard(boardId, title, content);
return "redirect:/board?boardId=" + boardId; // 수정된 글 보기로 리다이렉트 한다.
}
9-2.Service
9-2-1.BoardService
@Transactional
public Board getBoard(int boardId) {
return getBoard(boardId, true);
}
// updateViewCnt가 true면 글의 조호수 증가, false면 글의 조회수 증가x
@Transactional
public Board getBoard(int boardId, boolean updateViewCnt) {
Board board = boardDao.getBoard(boardId);
if(updateViewCnt) {
boardDao.updateViewCnt(boardId);
}
return board;
}
9-3.Repository
9-3-1.updateBoard()
@Transactional
public void updateBoard(int boardId, String title, String content) {
String sql = "update board set title =:title, content=:content where board_id= :boardId";
Board board = new Board();
board.setBoardId(boardId);
board.setTitle(title);
board.setContent(content);
SqlParameterSource params = new BeanPropertySqlParameterSource(board);
jdbcTemplate.update(sql, params);
// jdbcTemplate.update(sql, Map.of("boardId", boardId, "title", title, "content", content));
}
9-4.Template
9-4-1.board.html
<!DOCTYPE html>
<html lang="en">
<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 th:text="${board.title}"></H1>
<div>글쓴이 : <span th:text="${board.name}"></span></div>
<div>제목 : <span th:text="${board.title}"></span></div>
<div>조회수 : <span th:text="${board.viewCnt}"></span></div>
<div>작성일 : <span th:text="${board.regdate}"></span></div>
<div>내용 : <span th:text="${board.content}"></span></div>
<div><a href="/">목록보기</a></div>
<!--타임리프의 if문을 사용하여 로그인이 비활성 될 때 수정하기,삭제하기를 없앤다.-->
<div><a th:if="${loginInfo != null}" th:href="@{/updateForm(boardId=${board.boardId})}">수정하기</a></div>
<div><a th:if="${loginInfo != null}" th:href="@{/delete(boardId=${board.boardId})}">삭제하기</a></div>
</body>
</html>
9-4-2.updateForm.html
<!DOCTYPE html>
<html lang="en">
<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>
<form action="/update" method="post">
이름 : <span th:text="${loginInfo.name}"></span><br>
제목 : <input type="text" name="title" /><br />
내용 : <br>
<textarea name="content" cols="50" rows="5"></textarea><br>
<input type="submit" value="등록" />
</form>
</body>
</html>
- 어떤 값을 수정할지 알려주기 위해 hidden으로 boardId값을 숨김
9-5.결과
'프로젝트 > 게시판만들기' 카테고리의 다른 글
[사이드프로젝트/게시판만들기] 프로젝트 명세서 (0) | 2023.02.17 |
---|