프로젝트/게시판만들기

[사이드프로젝트/게시판 만들기] CRUD 기능별 구현

2023. 2. 22. 12:28
목차
  1. 1.회원가입
  2. 1-1.다대다 테이블 처리
  3. 1-2.Controller
  4. 1-3.Service
  5.  
  6. 1-4.Repository
  7. 1-5.DTO
  8. 2.로그인
  9. 2-1.Contoller
  10. 2-2.Service
  11. 2-3.Repository
  12.  2-4.Template
  13.  4.페이징 처리
  14. 4-1.Controller
  15. 4-2.Service
  16. 4-4.Repository
  17. 4-4.Template
  18. 6.페이징처리
  19. 6-1.Controller
  20. 7.글 상세보기
  21. 7-1.Controller
  22. 7-2.Service
  23. 7-3.Repository
  24. 7-4.Template
  25. 7-5 .결과
  26.  
  27. 8.글 삭제하기
  28. 8-1.Controller
  29. 8-2.Service
  30. 8-3.Repository
  31. 8-4.결과
  32. 9.수정하기
  33. 9-1.Controller
  34. 9-2.Service
  35. 9-3.Repository
  36. 9-4.Template
  37. 9-5.결과
  38.  

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
  1. 1.회원가입
  2. 1-1.다대다 테이블 처리
  3. 1-2.Controller
  4. 1-3.Service
  5.  
  6. 1-4.Repository
  7. 1-5.DTO
  8. 2.로그인
  9. 2-1.Contoller
  10. 2-2.Service
  11. 2-3.Repository
  12.  2-4.Template
  13.  4.페이징 처리
  14. 4-1.Controller
  15. 4-2.Service
  16. 4-4.Repository
  17. 4-4.Template
  18. 6.페이징처리
  19. 6-1.Controller
  20. 7.글 상세보기
  21. 7-1.Controller
  22. 7-2.Service
  23. 7-3.Repository
  24. 7-4.Template
  25. 7-5 .결과
  26.  
  27. 8.글 삭제하기
  28. 8-1.Controller
  29. 8-2.Service
  30. 8-3.Repository
  31. 8-4.결과
  32. 9.수정하기
  33. 9-1.Controller
  34. 9-2.Service
  35. 9-3.Repository
  36. 9-4.Template
  37. 9-5.결과
  38.  
'프로젝트/게시판만들기' 카테고리의 다른 글
  • [사이드프로젝트/게시판만들기] 프로젝트 명세서
각시탈코더
각시탈코더
각시탈코더각시탈코더 님의 블로그입니다.
각시탈코더
각시탈코더
각시탈코더
전체
오늘
어제
  • 분류 전체보기 (203)
    • java (46)
      • 객체지향 (20)
      • 기본문법 (8)
      • 자바의 정석 연습문제 오답노트 (9)
      • 백준 알고리즘 (9)
    • Spring (17)
      • SpringMVC (8)
      • Spring DI와 AOP (5)
      • REST API와 Ajax (1)
      • Spring 프로젝트 (1)
      • Spring Security (0)
    • DB (47)
      • Oracle (15)
      • SQL오답노트 (25)
      • 튜닝 (0)
      • ERD (1)
      • DB 모델링 (5)
    • Servlet & JSP (3)
    • JDBC (2)
      • 기본 (1)
      • 스프링JDBC (1)
    • MyBatis (2)
    • JavaScript (2)
      • 코딩애플 (0)
      • 문법 (1)
    • React (0)
      • 코딩애플 (0)
    • HTML (0)
      • 모던 웹을 위한 HTML5+CSS3 바이블 (0)
    • CSS (0)
      • 모던 웹을 위한 HTML5+CSS3 바이블 (0)
    • Linux (0)
    • Git & GitHub (2)
      • Git (1)
    • CS (19)
      • 네트워크 (6)
      • HTTP (7)
      • 컴퓨터구조 (0)
      • 자료구조와 알고리즘 (2)
      • 기타 (4)
    • 개발설정 (2)
    • 기술면접 (0)
      • JAVA (0)
      • Spring (0)
      • DB (0)
      • 네트워크 (0)
      • 공통 (0)
    • 프로젝트 (2)
      • 게시판만들기 (2)
    • 기혁씨의 삽질표류기 (28)
    • 참고자료 (2)
      • 국비수업 (0)
      • 당당 프로젝트 (1)
    • 뉴렉처 (17)
      • 자바 (11)
      • 자바스크립트 (3)
      • 키워드 (0)
      • 숙제 (0)
      • CSS (0)
      • DB (3)
      • 서블릿 (0)
      • 스프링 (0)
      • DOM (0)
    • 내가 만든 학습그림 (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 백준
  • 공부
  • 알고리즘
  • Oracle
  • 오류
  • 자바
  • 스프링
  • 연습문제
  • 객체지향
  • 모두의네트워크
  • db
  • 코딩
  • spring
  • 자바의정석기초편
  • 백엔드
  • 웹프로그래밍
  • 뉴렉처
  • 프로그래머스
  • 개발자
  • 국비수업
  • 네트워크
  • 자바의정석
  • 쿼리
  • 에러
  • It
  • SQL
  • 배열
  • 서브쿼리
  • 오라클
  • Java

최근 댓글

최근 글

hELLO · Designed By 정상우.
각시탈코더
[사이드프로젝트/게시판 만들기] CRUD 기능별 구현
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.