728x90

이전 글들 중에서 작성한 코드 중 일부 수정, 추가하여 진행합니다. (Controller, .jsp, .js)

코드 : https://github.com/wmdwjddyd6/Board-Spring-MVC

 

GitHub - wmdwjddyd6/Board-Spring-MVC

Contribute to wmdwjddyd6/Board-Spring-MVC development by creating an account on GitHub.

github.com


 

1. View (write.jsp)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
	language="java"%>
<%@ page session="false"%>
<html>
<head>
<title>글쓰기</title>
</head>
<body>
	<h1>글쓰기 화면</h1>

	<form>
		제목 <br />
		<input size="120" type="text" id="title" name="title" value="${board.title}" /> <br /> <br />
		내용 <br />
		<textarea cols="100" rows="13" id="content" name="content"><c:out value="${board.content}"></c:out></textarea>
		<button type="button" id="write_btn">작성</button>
	</form>

	<a href="/">메인으로 돌아가기</a>

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
	<script src="/resources/js/write.js"></script>
	
</body>
</html>
  • [글쓰기 버튼을 클릭하여 접근하는 새 글 작성 & 수정 버튼을 클릭하여 접근하는 글 수정] 화면을 한 jsp파일을 사용하여 구현합니다.
  • 수정버튼을 클릭하여 접근할 경우 해당 글의 제목과 내용을 보여주기위해 기존 write.jsp 코드에서 input의 value를 추가했습니다. 

 

2. JavaScript (write.js)

var writeBtn;
var boardId = location.search.substring(9);

if(boardId < 1) {
	writeBtn = document.getElementById("write_btn").addEventListener("click", write);
} else {
	writeBtn = document.getElementById("write_btn").addEventListener("click", update);
}

function write() {
    console.log("작성 메서드 실행");
    var title = document.getElementById("title").value;
    var content = document.getElementById("content").value;
    var data = { "title": title, "content": content };

    $.ajax({
    	contentType: 'application/json',
        type: "POST",
        url: "/board",
        data: JSON.stringify(data),
        success: function (response) {
            console.log("성공 : " + response);
            location.href="list";
        },
        error: function (response) {
            console.log("실패 : " + response);
            console.log(response);
        }
    })
}

function update() {
    console.log("수정 메서드 실행");
    var title = document.getElementById("title").value;
    var content = document.getElementById("content").value;
    var data = { "title": title, "content": content };

    $.ajax({
    	contentType: 'application/json',
        type: "PATCH",
        url: "/board/" + boardId,
        data: JSON.stringify(data),
        
        success: function (response) {
            console.log("성공 : " + response);
            location.href="list";
        },
        error: function (response) {
            console.log("실패 : " + response);
            console.log(response);
        }
    })
}
  • 기존 write.js 코드를 수정, 추가했습니다.
  • location.search를 통해 url 파라미터를 받아 올 수 있습니다. 받아온 파라미터를 사용하기 위해 substring으로 사용할 부분만 잘라냈습니다. (예로 /board/write?boardId=27 의 경우 ?boardId=27을 추출합니다.)
  • 새 글 작성 & 기존 게시글 수정 유/무(url에 boardId 존재 유/무)에 따라 작성 버튼 이벤트를 분리했습니다.
  • contentType을 JSON형식에 맞추기 위해 application/json으로 수정합니다.
  • data 또한 JSON형태로 데이터를 보내기 위해 JSON.stringify()를 사용하여 가공합니다.
    • Controller에서 @RequestBody 어노테이션을 사용할 것인데 위와 같이 contentType과 data를 JSON에 맞게 작성하지 않으면 WARN 레벨의 에러를 반환합니다. (물론 기능 작동 또한 제대로 진행되지 않습니다.)

 

 

 

3. Controller

@Controller
public class BoardController {

	private static final Logger logger = LoggerFactory.getLogger(BoardController.class);

	@Autowired
	private BoardService boardService;

	/*
	 * 게시글 작성 & 수정 화면 이동
	 */
	@RequestMapping(method = RequestMethod.GET, value = "/board/write")
	public String writeForm(Model model, @RequestParam(required = false) Long boardId) {
		if (boardId != null) {
			BoardDTO boardDTO = boardService.getPost(boardId);
			model.addAttribute("board", boardDTO);
		}

		return "board/write";
	}

	/*
	 * 게시글 수정
	 */
	@ResponseBody
	@RequestMapping(method = RequestMethod.PATCH, value = "/board/{boardId}")
	public int update(Model model, @RequestBody BoardDTO boardDTO, @PathVariable(value = "boardId") Long boardId) {
		logger.debug("{}번 게시글 수정 호출", boardId);

		int result = 0;
		result = boardService.update(boardDTO, boardId);
		logger.debug("result is {}", result);

		return result;
	}

}
  • 기존 게시글 작성 화면으로 이동하는 코드를 수정하고, 게시글을 수정하는 메서드를 추가합니다.
  • writeForm
    • 게시글 작성 시에는 새로운 글을 작성하는 것으로써, boardId가 존재하지 않습니다. 반면, 게시글 수정 시엔 해당 게시글을 조회하고 수정 버튼을 클릭하여 접근하기 때문에 기존 boardId가 존재하게 됩니다.
      • 즉, boardId가 존재한다면 수정을 목적으로 접근했기 때문에 수정하고자하는 데이터를 보여주기 위해 해당 게시글의 정보를 addAttribute해줍니다.
    • @RequestParam을 이용하여 url에 존재하는 boardId를 가져와서 어떤 게시글을 수정하는지 알 수 있도록 합니다.
  • update
    • 게시글의 정보를 일부 수정하는 것이기 때문에 PUT이 아닌 PATCH method를 사용합니다.
    • value & PathVariable : /board/27의 경우 27번 글의 수정을 의미합니다. 또한, @PathVariable 어노테이션을 통해 이 27 값을 변수로 사용할 수 있게 합니다.
    • @RequestBody를 사용하여 JSON 형태로 들어오는 값으로 BoardDTO를 초기화 해줍니다.
    • boardService의 update 함수에서 수정 로직을 수행할 수 있도록 호출합니다.

 

4. Service

@Service
public class BoardServiceImpl implements BoardService {

	private final BoardMapper boardMapper;
	
	@Autowired
	public BoardServiceImpl(BoardMapper boardMapper) {
		this.boardMapper = boardMapper;
	}

	@Override
	public int update(BoardDTO boardDTO, Long boardId) {
		boardDTO.setId(boardId);
		
		return boardMapper.updateBoard(boardDTO);
	}

}
  • Service구현체의 update메서드에서 수정하고자 하는 게시판ID를 setter 합니다.
  • DB가 정상적으로 이뤄지면 1을 반환합니다.

 

5. Mapper

public interface BoardMapper {

	// 게시글 수정
	int updateBoard(BoardDTO boardDTO);
	
}
<?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.spring.shoppingmall.mapper.BoardMapper">

	<sql id="boardColumns">
		id
		,title
		,content
		,delete_yn
		,create_date
		,views
		,type
	</sql>

	<!-- 게시글 수정 -->
	<update id="updateBoard" parameterType="com.spring.shoppingmall.model.BoardDTO">
		UPDATE 
			tb_board
		SET
			title = #{title},
			content = #{content}
		WHERE
			id = #{id}
	</update>
</mapper>
  • Update 구문을 통해 해당 id의 게시글과 제목을 수정합니다.

 

6. 결과

결과

 

728x90

이번 포스팅은 게시글 상세 글 확인을 위한 게시글 조회 기능을 구현합니다.

* 코드는 https://github.com/wmdwjddyd6/Board-Spring-MVC 에서 확인 가능합니다.

 

GitHub - wmdwjddyd6/Board-Spring-MVC

Contribute to wmdwjddyd6/Board-Spring-MVC development by creating an account on GitHub.

github.com


 

1. View (post.jsp)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
	language="java"%>
<%@ page session="false"%>
<html>
<head>
<title>게시글 상세 화면</title>
</head>
<body>
	<h1>게시글 상세 화면</h1>

	<h2>
		제목 :
		<c:out value="${board.title}" />
	</h2>
	내용
	<br />
	<textarea rows="13" style="background-color: white;" readonly><c:out value="${board.content}" /></textarea> <br /><br />
	<a href="/">메인으로 돌아가기</a>

</body>
</html>
  • 이전 글에서 만든 list.jsp에서 제목을 클릭하면 보여줄 화면입니다.
  • c:out으로 제목과 내용을 각각 보여줍니다.
  • readonly 속성을 통해 수정을 불가능하게 해요.

 

2. Controller

@Controller
public class BoardController {

	private static final Logger logger = LoggerFactory.getLogger(BoardController.class);

	@Autowired
	private BoardService boardService;

	/*
	 * 게시글 상세 페이지 화면 이동
	 */
	@RequestMapping(method = RequestMethod.GET, value = "/board/post")
	public String detailPost(Model model, @RequestParam(required = false) Long boardId) {
		BoardDTO boardDTO = boardService.getPost(boardId);
		model.addAttribute("board", boardDTO);
		
		return "board/post";
	}
}
  • @RequestParam을 이용해서 url의 게시글 id를 받아옵니다.
    • 예) http://localhost:8080/board/post?boardId=24
  • 게시글 데이터를 담을 boardDTO를 선언하고 boardService를 통해 받아옵니다.
  • 받아온 데이터를 View에 보여주기 위해 model.addAttribute를 사용

 

3. Service

public interface BoardService {

	// 게시글 조회
	BoardDTO getPost(Long boardId);

}
@Service
public class BoardServiceImpl implements BoardService {

	private final BoardMapper boardMapper;
	
	@Autowired
	public BoardServiceImpl(BoardMapper boardMapper) {
		this.boardMapper = boardMapper;
	}

	@Override
	public BoardDTO getPost(Long boardId) {
		BoardDTO boardDTO = boardMapper.selectPost(boardId);
		
		return boardDTO;
	}

}
  • Service에선 Mapper로 id값을 넘겨주고, 게시글 데이터를 조회하여 가져옵니다.

 

4. Mapper

public interface BoardMapper {

	// 게시글 조회
	BoardDTO selectPost(Long boardId);
	
}
	<!-- 게시글 조회 -->
	<select id="selectPost" resultType="com.spring.shoppingmall.model.BoardDTO">
		SELECT
			<include refid="boardColumns" />
		FROM
			tb_board
		WHERE
			id = #{id}
	</select>
  • 각각 인터페이스와 xml 파일입니다.
  • Service에서 넘겨받은 boardId를 #{id}로 사용하고 있습니다.

 

5. 결과

글 작성하고

테스트 데이터 입력

 

작성된 글 확인하고 제목을 클릭하면

list.jsp

 

조회가 잘 되네요.

post.jsp

 

728x90

이번 글에선 저번 글에서 작성한 글들의 리스트를 불러오는 기능을 구현합니다.

작성한 글들은 DB에 저장돼있습니다. (DB : MySQL)

* 코드는 https://github.com/wmdwjddyd6/Board-Spring-MVC 에서 확인 가능합니다.

 

GitHub - wmdwjddyd6/Board-Spring-MVC

Contribute to wmdwjddyd6/Board-Spring-MVC development by creating an account on GitHub.

github.com

 


 

1. View (list.jsp)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
	language="java"%>
<%@ page session="false"%>
<html>
<head>
<title>목록</title>
</head>
<body>
	<h1>게시글 목록</h1>

	<table border="1">
		<caption>List</caption>
		<thead>
			<tr align="center" bgcolor="white">
				<th>No</th>
				<th>제목</th>
				<th>작성일</th>
				<th>조회수</th>
			</tr>
		</thead>

		<tbody>
			<c:forEach var="board" items="${boards}">
				<tr align="center" bgcolor="white">
					<td><c:out value="${board.id}" /></td>
					<td><a href="/board/post?boardId=${board.id}"><c:out value="${board.title}" /></a></td>
					<td><fmt:formatDate pattern="yyyy-MM-dd hh:mm"
							value="${board.createDate}" /></td>
					<td><c:out value="${board.views}" /></td>
				</tr>
			</c:forEach>
		</tbody>
	</table>

	<br />
	<a href="/">메인으로 돌아가기</a>

</body>
</html>
  • 첫 줄과 둘째 줄의 taglib을 선언함으로써 기본 jstl(forEach 등)과 fmt 태그를 사용할 수 있게 됩니다.
  • 데이터를 보여줄 테이블을 정의하고, Controller에서 전달받은 데이터(List)의 양만큼 반복해서 보여주기 위해 c:forEach를 사용합니다.
    • var : 임시로 사용할 변수명
    • items : Controller에서 전달받은 Collection 객체 (해당 글에선 List입니다.)
  • fmt:formatDate를 통해 화면에 보여줄 날짜 형식을 지정합니다.
  • * 추가
    • c:out value="${board.title}" : a태그로 감싸서 제목 클릭 시 해당 글로 이동하는 기능 구현을 위해 추가했습니다.
    • [3. 게시글 조회] 에서 상세 기능 구현

 

2. Controller

@Controller
public class BoardController {

	private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
	
	@Autowired
	private BoardService boardService;
	
	/*
	 * 게시글 목록 화면 이동
	 * */
	@RequestMapping(method = RequestMethod.GET, value = "/board/list")
	public String list(Model model) {
		List<BoardDTO> boards = boardService.getList();
		model.addAttribute("boards", boards);
		
		return "board/list";
	}
}
  • 조회 관련 기능으로 GET method를 사용합니다. /board/list url 요청 시 해당 메서드로 매핑됩니다.
  • boardService의 getList 함수를 통해 현재 DB에 저장된 모든 게시글 목록을 반환 받습니다.
  • model.addAttribute("View로 넘겨줄 변수명", View로 넘겨줄 변수) : 위 1번 View의 forEach의 items부분 입니다. 

 

3. Service

public interface BoardService {
	
	// 게시글 목록 확인
	List<BoardDTO> getList();

}
@Service
public class BoardServiceImpl implements BoardService {

	private final BoardMapper boardMapper;
	
	@Autowired
	public BoardServiceImpl(BoardMapper boardMapper) {
		this.boardMapper = boardMapper;
	}
	
	@Override
	public List<BoardDTO> getList() {
		List<BoardDTO> list = boardMapper.selectAll();
		
		return list;
	}
}
  • getList에서는 boardMapper를 통해 모든 게시글을 List에 담아 Controller로 반환합니다.

 

4. Mapper (DAO)

public interface BoardMapper {

	// 게시글 목록 조회
	List<BoardDTO> selectAll();
	
}
<?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.spring.shoppingmall.mapper.BoardMapper">

	<sql id="boardColumns">
		id
		,title
		,content
		,delete_yn
		,create_date
		,views
		,type
	</sql>

	<!-- 게시글 목록 확인 -->
	<select id="selectAll" resultType="com.spring.shoppingmall.model.BoardDTO">
		SELECT 
			<include refid="boardColumns" />
		FROM
			tb_board
		ORDER BY
			id desc
	</select>
</mapper>
  • 게시글 작성 시 마다 Auto-Increment되는 값인 id를 desc(내림차순)으로 조회함으로써, 제일 최근에 생성된 게시글을 제일 앞에 위치하도록 합니다.

 


 

결과

 

728x90

지난 포스팅 [프로젝트 셋팅]에 이어 게시글 생성에 대한 구현 방법을 정리합니다.

게시글 생성을 위해 생성할 파일들은 다음과 같습니다.

BoardDTO - BoardController - BoardService (interface) - BoardServiceImpl (구현체) - BoardMapper - View (+ JS)

* 코드는 https://github.com/wmdwjddyd6/Board-Spring-MVC 에서 확인 가능합니다.

 

GitHub - wmdwjddyd6/Board-Spring-MVC

Contribute to wmdwjddyd6/Board-Spring-MVC development by creating an account on GitHub.

github.com

 


 

0.A 게시판 테이블 생성

CREATE TABLE `shoppingmall`.`tb_board` (
  `id` int AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `delete_yn` varchar(1) DEFAULT 'N',
  `create_date` datetime DEFAULT NULL,
  `views` int DEFAULT '0',
  `type` varchar(20) DEFAULT 'board',
  PRIMARY KEY (`id`)
)
  • id (게시글 번호) : 게시글 고유 번호 (PK)
  • delete_yn : 게시글 삭제 여부 (N: 삭제 안 된 상태, Y: 삭제 된 상태)
  • create_date : 게시글 생성 날짜
  • views : 조회수
  • type : 게시글 타입 (후에 기능의 확장을 위해 넣음. 자유게시판, 비밀게시판, 공지사항 등 분리 수단)

 

0.B servlet-context.xml 설정

	<context:component-scan base-package="com.spring.shoppingmall.controller" />
	<context:component-scan base-package="com.spring.shoppingmall.service" />
  • 다음 코드를 추가하여 @Component 어노테이션이 붙은 코드를 읽을 수 있도록 합니다. (@Service, @Controller 등 @Component에 포함)

 

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
		<beans:property name="contentType" value="text/html; charset=UTF-8"/>
	</beans:bean>
	
	<context:component-scan base-package="com.spring.shoppingmall.controller" />
	<context:component-scan base-package="com.spring.shoppingmall.service" />
	
</beans:beans>
  • servlet-context.xml 전체 코드

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- Root Context: defines shared resources visible to all other web components -->

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="com.mysql.cj.jdbc.Driver"></property>
		<property name="url"
			value="jdbc:mysql://localhost:3306/shoppingmall?serverTimezone=Asia/Seoul">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="asdf1234"></property>
	</bean>

	<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
		<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
	</bean>

	<mybatis-spring:scan base-package="com.spring.shoppingmall.mapper"/>
	
</beans>
  • root-context.xml 전체 코드
  • 현재는 DataBase를 위한 설정 코드가 대부분 (전 포스팅에서 설정했습니다.)

 

0.C View (write.jsp)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
	language="java"%>
<%@ page session="false"%>
<html>
<head>
<title>글쓰기</title>
</head>
<body>
	<h1>글쓰기 화면</h1>

	<form>
		제목 <br />
		<input size="120" type="text" id="title" name="title" /> <br /> <br />
		내용 <br />
		<textarea cols="100" rows="13" id="content" name="content"></textarea>
		<button type="button" id="write_btn">작성</button>
	</form>

	<a href="/">메인으로 돌아가기</a>

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
	<script src="/resources/js/write.js"></script>
	
</body>
</html>
  • 제목과 내용을 담을 input 태그를 작성해줍니다.
  • button에 이벤트를 추가하기 위해 id를 지정해줍니다.
  • ajax사용을 위해 아래의 스크립트 코드를 삽입합니다.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

 

1. BoardDTO ( +DataBase)

@Data
@Getter
@Setter
public class BoardDTO {
	private Long id;
	private String title;
	private String content;
	private String deleteYn;
	private Timestamp createDate;
	private Long views;
	private String type;
}
  • Board테이블에 생성한 컬럼들을 변수로 선언해줍니다.

 

2.. BoardController

@Controller
public class BoardController {

	private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
	
	@Autowired
	private BoardService boardService;
	
	/*
	 * 게시글 작성 화면 이동
	 * */
	@RequestMapping(method = RequestMethod.GET, value = "/board/write")
	public String writeForm(Model model) {
		return "board/write";
	}
	
	/*
	 * 게시글 작성
	 */
	@ResponseBody
	@RequestMapping(method = RequestMethod.POST, value = "/board")
	public int write(Model model, @RequestBody BoardDTO boardDTO) {
		logger.debug("게시글 작성 호출");

		int result = 0;
		result = boardService.write(boardDTO);
		logger.debug("result is {}", result);

		return result;
	}
}
  • GET 요청은 게시글 작성 화면으로 이동하고, POST 요청은 게시글 등록 로직을 수행합니다.
  • boardService.write()를 통해 로직을 수행합니다.
  • @RequestBody로 JSON 데이터를 받아 BoardDTO객체를 생성 및 초기화합니다. (@RequestParam은 url을 통해 값을 받아오기 때문에 해당 작업에 사용하지 않음)

 

3. BoardService

public interface BoardService {
	
	// 게시글 작성
	int write(BoardDTO boardDTO);
}
@Service
public class BoardServiceImpl implements BoardService {

	@Autowired
	private BoardMapper boardMapper;
	
	@Override
	public int write(BoardDTO boardDTO) {
		boardDTO.setCreateDate(Timestamp.valueOf(LocalDateTime.now()));
		boardDTO.setType("board");
        
		return boardMapper.insertBoard(boardDTO);
	}
}
  • Controller에서 받은 boardDTO에 나머지 설정할 값을 setter를 통해 설정합니다.
  • MyBatis를 이용하여 Mysql에 데이터를 추가하기 위해 boardMapper.insertBoard()를 호출합니다.

 

4. Mapper

public interface BoardMapper {
	public int insertBoard(BoardDTO boardDTO);
}
  • 게시글 관련 Mapper를 생성하고, 게시글 생성 쿼리를 작성할 메서드를 정의합니다.

 

<?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.spring.shoppingmall.mapper.BoardMapper">

	<sql id="boardColumns">
		id
		,title
		,content
		,delete_yn
		,create_date
		,views
		,type
	</sql>


	<!--게시글 작성 -->
	<insert id="insertBoard" parameterType="com.spring.shoppingmall.model.BoardDTO">
		
		INSERT INTO
			tb_board (title, content, create_date, type)
		VALUES
			(#{title}, #{content}, #{createDate}, #{type})
			
	</insert>
</mapper>
  • 이전 포스팅에서 생성했던 BoardMapper.xml을 수정해서 사용합니다.
  • namespace랑 parameterType 등 패키지 명에 주의!
  • 해당 구문이 정상 실행되면 결과값으로 1이 나오고, 실패 시 0이 나옵니다.

 

5. js 코드 (ajax 요청)

var writeBtn = document.getElementById("write_btn").addEventListener("click", write);

function write() {
    var title = document.getElementById("title").value;
    var content = document.getElementById("content").value;
    var data = { "title": title, "content": content };

    $.ajax({
        contentType: 'application/json',
        type: "POST",
        url: "/board",
        data: JSON.stringify(data),
        success: function (response) {
            console.log("성공 : " + response);
            // location.href="list"; (성공 시 /board/list.jsp로 이동)
        },
        error: function (response) {
            console.log("실패 : " + response);
            console.log(response);
        }
    })
}
  • 작성 버튼 클릭 시 이벤트를 추가하기 위해 addEventListener를 사용합니다.
  • title과 content 값을 가져오고 ajax를 통해 /board/write url로 POST 요청을 보냅니다.
  • 서버와 통신에 성공(요청에 대한 응답이 제대로 왔을 때)하면 success, 실패하면 error의 response로 응답 값이 들어옵니다.
  • 서버의 Controller와 통신 성공 시 응답 받은 값은 1 또는 0일테니 이를 이용해서 수행할 동작을 정의 가능
  • location.href를 통해 게시글 등록 성공 시 이동 할 페이지를 지정 가능합니다.
  • JSON타입으로 데이터를 주고받기 위해 contentType은 json으로 맞춰주고, JSON.stringify를 사용하여 JSON 문자열로 변환하여 서버에 데이터를 전달합니다.

 

 


 

구현 중 겪은 에러 참고 ^-'

https://black-mint.tistory.com/74

 

[Spring] org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class

ajax를 이용하여 게시글 등록 구현 중 에러가 발생했습니다. 게시글 등록 요청 시 database에 입력은 잘 되지만 ajax요청의 응답으로 success구문이 실행돼야 하는데 아래의 error 부분 구문이 실행되던

black-mint.tistory.com

 

728x90

휴지통 삭제, 복원을 구현하는 방법으로 ajax 통신을 사용하기로 한다.

이 때 체크박스 체크된 게시글만 삭제, 복원 가능하도록 하려고한다.

 -> 리스트로 boardId를 서버로 보내서 처리하도록 한다!


1. Controller

    // 휴지통 게시글 복원
    @PatchMapping("/trash")
    @ResponseBody
    public void restoreBoards(@RequestParam(name = "boardIdList[]", required = false) List<String> boardIdList) {
        for(int i = 0; i < boardIdList.size(); i ++) {
            boardService.restore(Long.parseLong(boardIdList.get(i)));
        }
    }

    // 휴지통 게시글 영구 삭제
    @DeleteMapping("/trash")
    @ResponseBody
    public void clearBoards(@RequestParam(name = "boardIdList[]", required = false) List<String> boardIdList) {
        for(int i = 0; i < boardIdList.size(); i ++) {
            boardService.clear(Long.parseLong(boardIdList.get(i)));
        }
    }
  • @RequestParam(name = "boardIdList[]") : 배열을 List로 받을 것이기 때문에 변수 끝에 대괄호([])를 붙여준다.

2. View

            <!-- contents -->
            <form class="row g-3 justify-content-end">
                <table class="table caption-top table-bordered table-hover">
                    <caption>List</caption>
                    <thead>
                        <tr>
                            <th class="text-center" width="50" scope="col"></th>
                            <th class="text-center" width="50" scope="col">No</th>
                            <th class="text-center" width="950" scope="col">제목</th>
                            <th class="text-center" width="200" scope="col">작성일</th>
                            <th class="text-center" width="180" scope="col">작성자</th>
                        </tr>
                    </thead>

                    <tbody>
                        <tr th:each="board : ${boardList}">
                            <td class="mt-5 text-center" scope="row">
                                <div class="checkbox">
                                    <input type="checkbox" name="boardIdList" th:value="${board.id}">
                                </div>
                            </td>
                            <td class="mt-5 text-center" scope="row" th:text="${board.id}">1</td>
                            <td><a th:text="${board.title}" th:href="@{/board/post(boardId=${board.id})}"
                                    style="text-decoration:none; color:black;">제목</a></td>
                            <td class="text-center" th:text="${#dates.format(board.createDate, 'yyyy/MM/dd HH:mm')}">작성일
                            </td>
                            <td class="text-center" th:text="${board.writer}">작성자</td>
                        </tr>
                    </tbody>
                </table>
                <div class="nav justify-content-end">
                    <button type="submit" class="btn btn-primary me-2" onclick="restoreBoards()">복원하기</button>
                    <button type="submit" class="btn btn-danger" onclick="deleteBoards()">삭제하기</button>
                </div>
            </form>
  • form 태그안에 버튼을 생성하고 각 버튼의 onclick을 등록한다.

3. ajax(script코드)

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script th:inline="javascript">
        // 게시글 복원
        function restoreBoards() {
            var boardIdList = []

            $('input[name="boardIdList"]:checked').each(function (i) {
                boardIdList.push($(this).val());
            });

            if (!isEmptyArr(boardIdList)) {
                var param = { "boardIdList": boardIdList }

                $.ajax({
                    url: "/trash",
                    type: "PATCH",
                    data: param,
                    async: false,
                    success: function (response) {
                        alert('복원 됐습니다.')
                        location.reload()
                    },
                    error: function (response) {
                        alert('복원에 실패했습니다.')
                    }

                })
            } else {
                alert('복원할 게시글을 선택해주세요.')
            }
        }

        // 게시글 삭제
        function deleteBoards() {
            var boardIdList = []

            $('input[name="boardIdList"]:checked').each(function (i) {
                boardIdList.push($(this).val());
            });

            if (!isEmptyArr(boardIdList)) {
                var param = { "boardIdList": boardIdList }

                $.ajax({
                    url: "/trash",
                    type: "DELETE",
                    data: param,
                    async: false,
                    success: function (response) {
                        alert('영구 삭제 됐습니다.')
                        location.reload()
                    },
                    error: function (response) {
                        alert('영구 삭제 실패했습니다.')
                    }

                })
            } else {
                alert('삭제할 게시글을 선택해주세요.')
            }

        }

        // 빈배열 체크
        function isEmptyArr(arr) {
            if (Array.isArray(arr) && arr.length === 0) {
                return true;
            }

            return false;
        }
    </script>
  • var boardIdList = [] : 자바스크립트 배열 선언
  • $('input[name="boardIdList"]:checked').each(function (i) {  boardIdList.push($(this).val());  }); 
    • 체크박스에 체크된 개수만큼 each로 반복.
    • push를 통해 값을 배열에 초기화
  • isEmptyArr : 배열이 비었는지 체크

4. 결과

시연

 

+ 3번 스크립트 코드 수정!

만약 다중 게시글을 삭제하는데 팝업이 뜨지 않는다면? 당연하다.. 비동기적 통신으로 ajax를 실행해서 그렇다.

이를 해결하려면 동기적으로 ajax 통신을 사용해야하는데, async: false를 ajax 코드에 추가해주면된다.


* ajax 사용하기 : https://black-mint.tistory.com/35

 

[Spring Boot] Ajax(비동기) 통신으로 댓글 구현 (+ Jquery 사용법)

게시판 댓글을 구현하는 도중 비동기 호출에 대해 알게 됐고, 이를 이용하려면 Ajax를 활용해야 한다는 정보를 얻었다. 비동기란? 비동기의 반대인 동기적 통신의 경우 절차적으로 일을 차례로

black-mint.tistory.com

 

728x90

저번에 작성한 댓글 기능 구현 코드를 REST API 규칙에 맞게 수정하도록한다.

 

* 이전 댓글 구현 글

https://black-mint.tistory.com/35

 

[Spring Boot] Ajax(비동기) 통신으로 댓글 구현 (+ Jquery 사용법)

게시판 댓글을 구현하는 도중 비동기 호출에 대해 알게 됐고, 이를 이용하려면 Ajax를 활용해야 한다는 정보를 얻었다. 비동기란? 비동기의 반대인 동기적 통신의 경우 절차적으로 일을 차례로

black-mint.tistory.com


1. REST(Representational State Transfer) API 란?

  • 네트워크 아키텍처 원리의 모음
  • 네트워크 아키텍처 : 자원을 정의하고 자원에 대한 주소를 지정하는 방법 전반을 의미
  • 예를 들어 학생

2. REST API 설계

  • URI는 정보의 자원을 표현
  • 자원에 대한 행위로 HTTP Method(GET, POST, PUT, PATCH, DELETE)로 표현
GET 데이터를 조회 (게시글 조회) GET /boards/1
게시글 중 1번 게시글을 조회
POST 데이터를 등록 (게시글 작성) POST /boards
게시글 등록
PATCH 데이터를 일부 수정 (게시글 수정) PATCH / boards/1
게시글 중 1번 게시글을 수정
PUT 데이터를 전체적으로 수정 PUT /boards/1
게시글 중 1번 게시글을 수정
DELETE 데이터를 삭제 DELETE /boards/1
게시글 중 1번 게시글을 삭제

3. REST API 규칙

  • 소문자로 작성하고 밑줄(_) 대신 하이픈(-)을 사용
  • 동사가 아닌 명사로 작성
  • 슬래시 구분자(/)는 계층 관계를 나타낼 때 사용하고 URI 마지막에 쓰지 않는다.
  • URI에 파일 확장자는 작성하지 않는다.

이 외 규칙이 많지만 이러한 2~3번 규칙을 잘 지켜서 만든 API를 RESTful API라고 한다.


4. 컨트롤러

@RequestMapping("/comments")
@RestController
public class CommentController {

    @Autowired
    private CommentService commentService;

    // 댓글 조회
    @GetMapping("/{boardId}")
    public List<Comment> getCommentList(@PathVariable(value = "boardId") Long boardId) throws Exception {
        List<Comment> comments = commentService.getCommentList(boardId);
        return comments;
    }

    // 댓글 작성
    @PostMapping("/{boardId}")
    public void commentWrite(@PathVariable(value = "boardId") Long boardId,
                             @RequestBody Comment comment,
                               Principal principal) throws Exception {
        String username = principal.getName();
        commentService.write(boardId, comment.getContent(), username);
    }

    // 댓글 수정
    @PatchMapping("/{commentId}")
    public void updateComment(@PathVariable(value = "commentId") Long commentId,
                              @RequestBody Comment comment) throws Exception {
        commentService.update(commentId, comment.getContent());
    }

    // 댓글 삭제
    @DeleteMapping("/{commentId}")
    public void deleteComment(@PathVariable(value = "commentId") Long commentId) throws Exception {
        commentService.delete(commentId);
    }
  • @RestController : 클래스 레벨에 선언하면 이전에 각 매핑마다 @ResponseBody 선언 했던 것을 하지 않아도 된다.
  • @ResponseBody : 해당 어노테이션이 선언된 매핑은 HTTP의 BODY에 자바 객체를 직접 반환한다.
  • @RequestBody : View에서 받아오는 JSON데이터를 파싱해준다. (변환할 데이터를 매핑 가능하도록 필드명 맞춰줘야함) 단, Content-Type이 multipart/form-data로 전달받을 때는 오류를 일으킨다. (주로 파일, 이미지를 전달받을 때)
  • @PathVariable : REST방식에서 주로 사용하며, @RequestParam과 유사. URI에 전달받을 변수를 지정

5. Ajax 통신 스크립트 코드

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
    <script th:inline="javascript">
        $(document).ready(function () {
            getComments()
        })

        function getComments() {
            var loginUsername = [[${ loginUser }]]
            var boardId = $('input[name=boardId]').val()
            var url = '/comments/' + boardId

            $.ajax({
                type: 'GET',
                url: url,
                success: function (response) {
                    var a = ''
                    var size = 0
                    $.each(response, function (key, value) {
                        size = size + 1;
                        a += '<hr /><div>'
                        a += '<input type="hidden" id="commentId" name="commentId" value="' + value.id + '">'
                        a += '<span id="writer" style="font-weight: bold;">' + value.writer + '</span>'
                        if (value.writer == loginUsername) {
                            a += '<ul name="commentChange" class="justify-content-end" style="display: inline;">'
                            a += '<li name="commentUpdate" type="button" style="display: inline; opacity: 0.7; font-size: small; margin-right: 5px" onclick="updateCommentForm(' + value.id + ')">수정</li>'
                            a += '<li name="commentDelete" type="button" style="display: inline; opacity: 0.7; font-size: small;" onclick="deleteComment(' + value.id + ')">삭제</li></ul>'
                        }
                        a += '<pre id="' + value.id + '" name="comment' + value.id + '" style="margin-bottom: 5px; font-size: large;">' + value.content + '</pre>'
                        a += '<p name="createDate' + value.id + '" style="margin-bottom: 5px; opacity: 0.5; font-size: small;">' + moment(value.createDate).format("YYYY-MM-DD HH:mm") + '</p></div>'
                    });
                    $("#count").html(size)
                    $("#comment").html(a)
                },
                error: function (response) {
                    console.log("getComments error : " + response)
                },
                complete: function () { }
            })
        }

        function insertComment() {
            var boardId = $('input[name=boardId]').val()
            var content = document.getElementById("content").value
            var param = {"content": content}
            var url = '/comments/' + boardId

            if (isEmpty(content) == true) {
                alert('댓글을 입력해주세요.')
                return false;
            } else {
                $.ajax({
                    contentType: 'application/json',
                    type: 'POST',
                    url: url,
                    data: JSON.stringify(param),
                    success: function (response) {
                        getComments()
                    },
                    error: function (response) {
                        console.log("insertComment error : " + response)
                    },
                })
            }
        }

        function updateCommentForm(id) {
            var commentId = id
            var content = document.getElementById(id).innerText

            $('ul[name=commentChange]').hide()
            $('pre[name=comment' + commentId + ']').contents().unwrap().wrap('<textarea id="newComment" class="form-control mt-2" name="updateContent" rows="4"></textarea>');
            $('p[name=createDate' + commentId + ']').contents().unwrap().wrap('<input name="update" type="button" class="me-2 mt-2 btn btn-primary" value="수정하기" onclick="updateComment(' + commentId + ')">');
            $('input[name=update]').after("<button class=\"me-2 mt-2 btn btn-primary\" onclick=\"getComments()\">취소</button>")
        }

        function updateComment(id) {
            var commentId = id
            var content = document.getElementById("newComment").value
            var param = {"content": content}
            var url = '/comments/' + commentId

            if (isEmpty(content) == true) {
                alert('댓글을 입력해주세요.')
                return false;
            } else {
                $.ajax({
                    contentType: 'application/json',
                    type: 'PATCH',
                    url: url,
                    data: JSON.stringify(param),
                    success: function (response) {
                        getComments()
                    },
                    error: function (response) {
                        console.log("updateComment error : " + response)
                    },
                    complete: function () { }
                })
            }
        }

        function deleteComment(id) {
            var commentId = id
            var url = '/comments/' + commentId

            if (confirm("정말 삭제하시겠습니까?")) {
                $.ajax({
                    type: 'DELETE',
                    url: url,
                    success: function (response) {
                        getComments()
                    },
                    error: function (response) {
                        console.log("deleteComment error : " + response)
                    },
                    complete: function () { }
                })
            } else {
                return;
            }
        }
        function isEmpty(strIn) {
            if (strIn === undefined) {
                return true;
            }
            else if (strIn == null) {
                return true;
            }
            else if (strIn == "") {
                return true;
            }
            else {
                return false;
            }
        }
    </script>
  • 이전 글과 바뀐 부분
    • Json으로 서버에 데이터를 보내주기 위해 contentType을 json으로 설정
    • url을 REST API 규칙에 맞춰 작성
    • data: json.stringify( ) : ajax 통신의 데이터 전달부분을 미리 만들어둔 json형식의 자바스크립트 변수를 서버로 전달
    • type을 REST API 규칙에 맞춰 작성
    • 날짜 포맷을 변경하기 위해 moment라이브러리 사용
    • https://black-mint.tistory.com/51
 

[JavaScript] 날짜 포맷 변경 라이브러리

서버에서 Json 형태로 프론트로 넘어오면 날짜 형식이 다음과 같이 변경돼서 날아온다. "createDate": "Jan 5, 2022, 4:25:48 PM" 원하는 날짜 형식으로 바꿔 보여주고 싶을 때 사용할 라이브러리 https://m

black-mint.tistory.com

 

* REST 관련 참고 

https://congsong.tistory.com/30?category=749196 

728x90

게시판 댓글을 구현하는 도중 비동기 호출에 대해 알게 됐고, 이를 이용하려면 Ajax를 활용해야 한다는 정보를 얻었다.

비동기란?

  • 비동기의 반대인 동기적 통신의 경우 절차적으로 일을 차례로 하나씩 해 내려가는 것이다.
  • 비동기는 어떤 하나의 일을 하는 도중 기다리는 시간이 필요하다면 그 시간 동안 다른 일을 먼저 처리할 수 있도록 하는 것이다.
  • 즉, 절차적인 동기적 통신.   비절차적인 비동기 통신이라고 생각하면 된다!

Jquery 사용법과 Ajax를 통한 비동기 통신의 내용을 간단하게 정리. (1, 2번은 사용법)

만약 ajax를 사용할 때 동기적으로 실행하고 싶은 경우 코드에 async: false 를 붙여주면된다!

1. Jquery 시작

Jquery를 시작하기 위해 다음과 같은 코드를 html 내부에 작성한다.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  • 어디에 적어도 상관없다고 한다.
  • 대부분 </body> 바로 윗 부분 즉, 마지막 부분에 작성하는 것을 많이 볼 수 있었다.

2. (Jquery) javascript 코드 작성

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script>
        $(document).ready(function() {
            getAlert();
        })

        function getAlert() {
            alert('gd')
        }
    </script>
  • $(document).ready(function() {} : 문서가 준비되면 매개변수를 넣은 콜백함수를 실행하라는 의미
    • 여기서는 html파일이 로드되면 alert로 팝업을 띄우게 했다.

3. Ajax(비동기 통신) 및 자바스크립트(Jquery)

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script th:inline="javascript">
        $(document).ready(function () {
            getComments();
        })

        function getComments() {
            var loginUsername = [[${ loginUser }]]
            var boardId = $('input[name=boardId]').val()

            $.ajax({
                type: 'GET',
                url: '/board/comment/getCommentList',
                data: { boardId },
                success: function (response) {
                    var a = '';
                    var size = 0;
                    $.each(response, function (key, value) {
                        size = size + 1;
                        a += '<hr /><div>'
                        a += '<input type="hidden" id="commentId" name="commentId" value="' + value.id + '">'
                        a += '<span id="writer" style="font-weight: bold;">' + value.writer + '</span>'
                        if (value.writer == loginUsername) {
                            a += '<ul name="commentChange" class="justify-content-end" style="display: inline;">'
                            a += '<li name="commentUpdate" type="button" style="display: inline; opacity: 0.7; font-size: small; margin-right: 5px" onclick="updateCommentForm(' + value.id + ')">수정</li>'
                            a += '<li name="commentDelete" type="button" style="display: inline; opacity: 0.7; font-size: small;" onclick="deleteComment(' + value.id + ')">삭제</li></ul>'
                        }
                        a += '<pre id="' + value.id + '" name="comment' + value.id + '" style="margin-bottom: 5px; font-size: large;">' + value.content + '</pre>'
                        a += '<p name="createDate' + value.id + '" style="margin-bottom: 5px; opacity: 0.5; font-size: small;">' + value.createDate.substring(0, 10) + ' ' + value.createDate.substring(11, 19) + '</p></div>'
                    });
                    $("#count").html(size)
                    $("#comment").html(a)
                },
                error: function (response) {
                    console.log("error : " + response)
                },
                complete: function () { }
            })
        }

        function updateCommentForm(id) {
            var commentId = id;
            var content = document.getElementById(id).innerText;

            $('ul[name=commentChange]').hide()
            $('pre[name=comment' + commentId + ']').contents().unwrap().wrap('<textarea id="newComment" class="form-control mt-2" name="updateContent" rows="4"></textarea>');
            $('p[name=createDate' + commentId + ']').contents().unwrap().wrap('<input name="update" type="button" class="me-2 mt-2 btn btn-primary" value="수정하기" onclick="updateComment(' + commentId + ')">');
            $('input[name=update]').after("<button class=\"me-2 mt-2 btn btn-primary\" onclick=\"getComments()\">취소</button>")
        }

        function updateComment(id) {
            var commentId = id;
            var content = document.getElementById("newComment").value;

            $.ajax({
                type: 'POST',
                url: '/board/comment/update',
                data: { 
                    commentId:commentId,
                    content:content 
                },
                success: function (response) {
                    getComments()
                },
                error: function (response) {
                    console.log("update error : " + response)
                },
                complete: function () { }
            })
        }

        function deleteComment(id) {
            var commentId = id;

            if (confirm("정말 삭제하시겠습니까?")) {
                $.ajax({
                    type: 'POST',
                    url: '/board/comment/delete',
                    data: { commentId },
                    success: function (response) {
                        getComments()
                    },
                    error: function (response) {
                        console.log("delete error : " + response)
                    },
                    complete: function () { }
                })
            } else {
                return;
            }
        }
    </script>

댓글 [작성, 수정, 삭제]하는 동시에 페이지를 리로드하지 않고 비동기 통신으로 작성한 댓글을 바로 화면에 보여주는 코드이다. 즉, ajax 통신으로 수정이나 삭제 후 success: 에 getComments()를 호출하면 해당 페이지를 리로드 하지 않고 댓글이 최신화 된다. 

  • <script th:inline="javascript"> : 자바스크립에서 타임리프 변수를 쓰기위해 추가
  • [[${loginUser}]] : 컨트롤러에서 가져온 로그인해있는 사용자 아이디이다.
  • $('input[name=id]').val() : input 태그의 name이 id인 값을 가져와서 설정
    • 만약 $('input#idx').val() 이면 input태그의 id값이 idx인것을 가져와서 설정
    •  .val() : form 양식의 값을 설정하거나 가져옴
      • 만약 $('input#idx').val('abc') id가 idx인 input요소의 값을 abc로 설정
  • url : 서버의 컨트롤러에 호출할 url
  • data : 넘겨줄 데이터 (여기선 var id)
    • 여기서 들어가는 data 변수 이름은 컨트롤러의 인자명과 같아야 한다.
  • success : ajax 통신을 성공하고 서버로부터의 결과값을 response에 담아서 함수 실행
  • $.each(response, function(key, value) {}); : 서버에서 응답받은 데이터의 개수만큼 반복 (for or while)
    • 컨트롤러에서 List<Comment> comments 형태의 객체를 리턴했다.
    • key는 0, 1, 2, 3 ..
    • value.content 식으로 데이터 사용 가능. (comments.get(i).getContent와 동일)
    • size : 댓글 개수
    • $().html : 해당 태그의 요소를 수정
    • https://www.codingfactory.net/10324
  • if문을 이용해서 로그인한 사용자와 댓글 작성자가 동일하면 수정과 삭제를 가능하도록 버튼 표시한다.
  • error : ajax 통신을 실패하면 실행 (에러 정보 확인)
  • complete : success나 error가 끝나고 실행 (try catch의 finally와 동일)
  • 이외
    • datatype : 서버에서 받아올 데이터를 해석하는 형태 (xml, json 등)
    • cache : 요청 페이지의 캐시 여부 (true or false)
$('ul[name=commentChange]').hide()
  • name이 commentChange인 ul 태그를 숨김(한 댓글을 수정 중 다른 댓글을 수정, 삭제 못하게 함)
$('pre[name=comment' + commentId + ']').contents().unwrap().wrap('<p></p>')
  • 위 코드는 해당하는 name의 값을 가진 pre 태그를 p태그로 대체
$('input[name=a]').after("<button class=\"me-2 mt-2 btn btn-primary\" onclick=\"getComments()\">취소</button>")
  • 3번(ajax) 자바스크립트(jquery) 코드의 updateCommentForm 함수에 다음 코드를 추가해서 수정 버튼 옆에 취소버튼을 추가
  • 취소버튼 클릭 시 수정사항을 반영하지 않고 다시 댓글 목록을 보여줌
  • 이 외 자바스크립트 코드 https://black-mint.tistory.com/41
 

[JavaScript] 다른 태그 값, 양식 작성값(input, textarea ..)가져오기

1, 다른 태그 값 가져오기 var content = document.getElementById(id).innerText; 2. 사용자가 input 또는 textarea에 작성한 값 가져오기 (버튼 클릭시 실행) var content = document.getElementById("newCommen..

black-mint.tistory.com

4. 화면

                <!-- Comment -->
                <h4>댓글</h4>
                <div class="mb-5">
                    <div id="comment">
                    </div>

                    <form action="#" th:action="@{/board/comment/write}" method="post">
                        <input type="hidden" name="boardId" th:value="${board.id}">
                        <div class="mb-3">
                            <label for="content" class="form-label"></label>
                            <textarea class="form-control" id="content" name="content" rows="4"
                                placeholder="댓글을 작성해주세요."></textarea>
                        </div>
                        <button type="submit" class="me-2 btn btn-primary">write</button>
                    </form>
                </div>
  • div > id=comment : 작성된 댓글을 표시한 div 태그
  • 동적으로 댓글을 추가해주는 비동기 통신을 쓰기 때문에 3번 코드(자바스크립트)에 html이 닮겨 있다.

5. 컨트롤러

@Controller
@RequestMapping("/board/comment")
public class CommentController {

    @Autowired
    private CommentService commentService;

    // 댓글 작성
    @PostMapping("/write")
    @ResponseBody
    public void commentWrite(@RequestParam(name = "boardId", required = false) Long boardId,
                               @RequestParam(name = "content") String content,
                               Principal principal) throws Exception {
        String username = principal.getName();
        commentService.write(boardId, content, username);
    }

    // 댓글 조회
    @GetMapping("/getCommentList")
    @ResponseBody
    public List<Comment> getCommentList(@RequestParam(name = "boardId") Long boardId) throws Exception {
        List<Comment> comments = commentService.getCommentList(boardId);
        return comments;
    }

    // 댓글 수정
    @PostMapping("/update")
    @ResponseBody
    public void updateComment(@RequestParam(name = "commentId") Long commentId,
                              @RequestParam(name = "content") String content) throws Exception {
        commentService.update(commentId, content);
    }

    // 댓글 삭제
    @PostMapping("/delete")
    @ResponseBody
    public void deleteComment(@RequestParam(name = "commentId") Long commentId) throws Exception {
        commentService.delete(commentId);
    }
}
  • 댓글 작성
    • boardId : @RequestParam으로 url을 이용해 받는다. (현재 조회하고 있는 게시글)
    • content : 작성한 댓글 내용
  • 댓글 조회, 수정, 삭제
    • @ResponsBody : http요청의 body에 데이터를 넣어서 반환
    • @RequestParam(name = " ") : name은 ajax에서 보내주는 파라미터의 변수명과 같게 해야한다.

6. 서비스

    // 댓글 작성
    public void write(Long boardId, String content, String username) throws Exception {
        Comment comment = new Comment();
        Member member = memberService.getMember(username);

        comment.setBoardId(boardId);
        comment.setContent(content);
        comment.setWriter(member.getUsername());
        comment.setWriterId(member.getId());
        comment.setCreateDate(Timestamp.valueOf(LocalDateTime.now()));

        commentRepository.insertComment(comment);
    }

    // 댓글 조회
    public List<Comment> getCommentList(Long boardId) throws Exception {
        return commentRepository.selectCommentList(boardId);
    }

    // 댓글 수정
    public void update(Long commentId, String content) throws Exception {
        Comment comment = new Comment();

        comment.setId(commentId);
        comment.setContent(content);

        commentRepository.updateComment(comment);
    }

    // 댓글 삭제
    public void delete(Long commentId) throws Exception {
        commentRepository.deleteComment(commentId);
    }

7. 결과

시연 영상

 

+ Recent posts