오늘은
오늘은 금요일입니다
내일부터 주말인데 내일부터는 이력서도 쓰고 취업준비를 위한
실질적인 준비를 해볼까 합니다
공부는 얼마나 할 수 있을지 모르겠네요!!
Spring 공부
댓글 쓰기
- 댓글 등록 : insert() = void
=> 1. form방식, 2. ajax방식
ReplyDTO 필드
- id(댓글번호)
- replyer(작성자)
- replyContent(댓글내용),
- boardId(게시글 번호)
- createTime
- updateTime
- 댓글 목록
List<Reply> replyList() = 자료형 필요
- 글 상세보기에서 출력됨(실행)
* boardController
public class BoardController {
private ReplyService replyService;
@GetMapping
public String getBoard(@RequestParam("id") Long id, Model model) {
// 댓글 목록 보기
List<ReplyDTO> replyListDTO = replyService.getReplyList(id);
model.addAttribute("replyList", replyListDTO);
}
* ReplyDTO
package cohttp://m.khit.web.dto;
import java.sql.Timestamp;
import lombok.Data;
@Data
public class ReplyDTO {
private Long id; // 댓글 번호
private Long boardId; // 게시글 번호
private String replyer; // 작성자
private String replyContent; // 댓글 내용
private Timestamp createTime; // 작성일
private Timestamp updateTime; // 수정일
}
계층 구조 만들기
컨트롤러 - 서비스 - 매퍼 구조
* ReplyController
package cohttp://m.khit.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import cohttp://m.khit.web.dto.ReplyDTO;
import cohttp://m.khit.web.service.ReplyService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/reply")
@Controller
public class ReplyController {
// 서비스 클래스 주입(생성자, Autowired)
@Autowired
private ReplyService replyService;
@PostMapping("/insert")
public String replyInsert(@ModelAttribute ReplyDTO replyDTO) {
// 댓글 폼에 입력된 데이터 출력
log.info("replyDTO:" + replyDTO);
// 댓글 저장 처리
replyService.insert(replyDTO);
return "redirect:/board?id=" + replyDTO.getBoardId();
}
}
* ReplyService (interface)
package cohttp://m.khit.web.service;
import cohttp://m.khit.web.dto.ReplyDTO;
public interface ReplyService {
void insert(ReplyDTO replyDTO);
List<ReplyDTO> getReplyList(Long boardId);
}
* ReplyServiceImpl (interface 구현)
package cohttp://m.khit.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cohttp://m.khit.web.dto.ReplyDTO;
import cohttp://m.khit.web.mapper.ReplyMapper;
@Service // 서비스를 구현하고 @빈(Bean)을 등록해준다
public class ReplyServiceImpl implements ReplyService{
@Autowired
private ReplyMapper replyMapper;
@Override
public void insert(ReplyDTO replyDTO) {
replyMapper.insert(replyDTO);
}
@Override
public List<ReplyDTO> getReplyList(Long boardId) {
return replyMapper.getReplyList(boardId);
}
}
* ReplyMapper (interface)
package cohttp://m.khit.web.mapper;
import cohttp://m.khit.web.dto.ReplyDTO;
public interface ReplyMapper {
void insert(ReplyDTO replyDTO);
List<ReplyDTO> getReplyList(Long boardId);
}
* ReplyMapper (xml파일)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cohttp://m.khit.web.mapper.ReplyMapper">
<insert id="insert">
insert into reply(boardid, replyer, replycontent)
values(#{boardId), #{replyer}, #{replyContent})
</insert>
</mapper>
* detail.jsp
<!-- 댓글 목록 -->
<c:forEach items="${replyList}" var="reply">
<div class="reply">
<p>${reply.replyContent}</p>
<p>작성자: ${reply.replyer} (작성일:${reply.createTime})</p>
</div>
</c:forEach>
<!-- 댓글 등록 -->
<form action="/reply/insert" method="post" id="replyform">
<input type="hidden" name="boardId" value="${board.id}">
<p><input type="text" name="replyer" value="${sessionId}" readonly></p>
<p>
<textarea rows="3" cols="50" name="replyContent" placeholder="댓글을 남겨주세요"></textarea>
</p>
<input type="submit" value="등록">
</form>
<!-- 댓글 등록 로그인 이동 -->
<div class="replylogin">
<a href="/user/login">로그인한 사용자만 댓글 등록이 가능합니다</a>
</div>
<<페이지 처리>>
1. 40~50개
2. mysql 페이지 처리
- limit 명령어
=> select * from boards order by id desc limit 0, 5;
3. 페이지별로 게시글 출력 구현 (List, Map 자료구조)
4. 하단 페이지 번호별 링크 구현 (PageDTO 자료형)
- 현재페이지 = page
- 최대페이지(총개수에따른) = maxPage
- 시작페이지 = startPage
- 마지막페이지 = endPage
${paging.page}, ${paging.starPage} ......
* boardController
// 글 목록 페이지 처리
// /board/paging?page=2
// @RequestParam(required=true/false) false는 필수가 아니다 라는 의미
@GetMapping("/paging")
public String getPageList(Model model,
// page가 필수가 아니라는 의미
@RequestParam(value="page", required=false, defaultValue="1") int page) {
log.info("page=", + page);
// 페이지와 글 개수를 구현된 목록 보기
List<BoardDTO> pagingList = boardService.pagingList(page);
model.addAttribute("boardList", pagingList);
// 화면 하단 구현
PageDTO pageDTO = boardService.pagingParam(page);
model.addAttribute("paging", pageDTO);
return "/board/pagelist";
}
* BoardService
public interface BoardService {
List<BoardDTO> pagingList(int page);
PageDTO pagingParam(int page);
}
* BoardServiceImpl
// class변수로 선언
int pageLimit = 5; // 페이지에 표시할 게시글 수
int blockLimit = 5; // 하단에 표시할 페이지 수
@Override
public List<BoardDTO> pagingList(int page) {
/*
1page (0, 5), 2page(5,5), 3page(10,5), 4page(15,5)
select * from boards order by id desc limit 0, 5;
*/
int pageStart = (page - 1) * pageLimit; // 페이지 시작 인덱스 번호
Map<String, Integer> pagingParam = new HashMap<>();
pagingParam.put("start", pageStart); // 페이지 시작 번호
pagingParam.put("limit", pageLimit); // 페이지 게시글 수
List<BoardDTO> pagingList = boardMapper.pagingList(pagingParam);
return pagingList;
}
@Override
public PageDTO pagingParam(int page) {
// 하단에 보여줄 페이지 블럭 계산
int totalRow = boardMapper.boardCount(); // 전체 게시글 수
// 전체 페이지 개수 - 올림(Math.ceil) 52/10 = 5.2 -> 6.2 => (int)6
int maxPage = (int)(Math.ceil((double)totalRow/pageLimit));
// 하단에 보여줄 시작 페이지값 계산(1~5, 6~10 = 1, 6, 11, 16)
int startPage = (((int)(Math.ceil((double)page/blockLimit))-1) * blockLimit) + 1; ;
// 하단에 보여줄 마지막 페이지값 계산(5, 10, 15)
int endPage = startPage + blockLimit - 1;
if(endPage > maxPage) {
endPage = maxPage;
}
PageDTO pageDTO = new PageDTO();
pageDTO.setPage(page);
pageDTO.setMaxPage(maxPage);
pageDTO.setStartPage(startPage);
pageDTO.setEndPage(endPage);
return pageDTO;
}
* BoardMapper
public interface BoardMapper {
List<BoardDTO> pagingList(Map<String, Integer> pagingParam);
}
* BoardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
<select id="pagingList" resultType="cohttp://m.khit.web.dto.BoardDTO">
select * from boards order by id desc limit #{start}, #{limit};
</select>
<select id="boardCount" resultType="Integer">
select count(id) from boards
</select>
</mapper>
* pagelist.jsp
<!-- page 처리 영역 -->
<div class="pagenation">
<!-- 이전 페이지 -->
<c:if test="${paging.page > 1}">
<a href="/board/paging?page= 1 "><i class="fa-solid fa-angles-left"></i></a>
<a href="/board/paging?page=${paging.page-1}"><i class="fa-solid fa-caret-left"></i></a>
</c:if>
<!-- 현재 페이지 -->
<c:forEach var = "i" begin="${paging.startPage}" end="${paging.endPage}">
<c:choose>
<c:when test="${paging.page eq i}">
<span class="current_page">${i}</span>
</c:when>
<c:otherwise>
<a href="/board/paging?page=${i}">${i}</a>
</c:otherwise>
</c:choose>
</c:forEach>
<!-- 다음 페이지 -->
<c:if test="${paging.page < paging.maxPage}">
<a href="/board/paging?page=${paging.page+1}"><i class="fa-solid fa-caret-right"></i></a>
<a href="/board/paging?page=${paging.maxPage}"><i class="fa-solid fa-angles-right"></i></a>
</c:if>
</div>
React 공부
<<Lifecycle>>
React 컴포넌트 = 생명주기 Lifecycle을 갖는다
= 탄생 -> 변화 -> 죽음
화면에 나타나는 것 업데이트 (리렌더) 화면에서 사라짐
Mount Update UnMount
=> 컴포넌트의 생애주기를 제어한다
Task A -> Task B -> Task C
ex) 초기화 작업 ex) 예외 처리 작업 ex) 메모리 정리 작업
= React Component Lifecycle Methods = component가 변화하는 순간에 사용할 수 있는 method
Mount -> Update -> UnMount
ComponentDidMount ComponentDidUpdate ComponentWillUnMount
=> Class React Component Only = 이러한 method들은 class형 component에서 밖에 사용할 수 없다
* 이론상 함수형 component에서는 이러한 method나 state기능도 사용할 수 없다
<React Hooks>
=> 함수형 component에서도 React Hooks를 이용하여 사용 가능
->useState ->useEffect ->useRef
=> React Hooks를 사용 하는 이유
= Class형 Component의 길어지는 코드 길이 문제
중복 코드, 가독성 문제 등등을 해결하기 위해 등장함
* useEffect를 import하여 사용해야 한다
import React, { useEffect } from "react";
useEffect (() => {
// todo... = Callback 함수 부분
}, [ ] );
=> [ ] = Dependency Array (의존성 배열) = 뎁스배열
이 배열 내에 들어있는 값이 변화하면 콜백 함수가 수행된다
* App.js
const App = () => {
return (
<div className="App">
{/* LifeCycle component rendering => import */}
<LifeCycle />
</div>
);
};
* LifeCycle.js
import React, { useEffect, useState } from "react";
// LifeCycle Component의 자식 component인 UnMountTest component 생성
// LifeCycle의 isVisible의 값이 true일때만 UnMount component가 화면에 rendering됨
const UnMountTest = () => {
useEffect(() => {
console.log("Sub Component Mount");
return () => {
// UnMount되는 시점에 실행되는 함수 를 return 시킴
console.log("Sub Component Unmount");
};
}, []);
return <div>UN MOUNT TEST</div>;
};
// LifeCycle Component
const LifeCycle = () => {
// count에 사용될 state
const [count, setCount] = useState(0);
// input에 사용될 state
const [text, setText] = useState("");
const [isVisible, setIsVisible] = useState(false);
const toggle = () => setIsVisible(!isVisible);
// Mount되는 시점을 제어
useEffect(() => {
// component가 Mount되는 시점에 console이 수행된다
console.log("Mount!"); // = Callback 함수
}, []);
useEffect(() => {
console.log("Update!");
});
useEffect(() => {
console.log(`count is update : ${count}`);
// if (count > 5) {
// alert("count가 5를 넘었습니다, 따라서 1로 초기화합니다");
// setCount(1);
// }
}, [count]); // Dependency Array의 값이 바뀌게 되면 callback함수가 다시 호출이된다
useEffect(() => {
console.log(`text is update : ${text}`);
}, [text]);
// Dependency에 따라 감지하고 싶은것의 변화만 감지
return (
<div>
{/* 위에서 생성한 state 사용 */}
<div>
{count}
<button onClick={() => setCount(count + 1)}>count up</button>
</div>
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
<button onClick={toggle}>ON/OFF BUTTON</button>
{/* && = 단락회로평가를 이용하여 isVisible의 boolean 값에 따라
UnMountTest의 render 여부가 달라진다 */}
{isVisible && <UnMountTest />}
</div>
);
};
export default LifeCycle;
<<React에서 API 호출하기>>
= useEffect를 이용하여 component Mount 시점에
API를 호출하고 해당 API의 결과값을
일기 데이터의 초기값으로 이용하기
https://jsonplaceholder.typicode.com/comments 에서
API 오픈소스 가져오기
* App.js
const App = () => {
// asunc()를 사용하여 geData가 promise를 반환하도록 비동기 함수로 만들어줌
// getData() 생성
const getData = async () => {
// 원하는 json 값들만 가져오기
const res = await fetch(
"https://jsonplaceholder.typicode.com/comments"
).then((res) => res.json());
// 가져온 json값의 요소들을 각 일기의 데이터 기초값으로 활용
// slice()를 활용하여 20개만 추려냄
// map()함수를 활용하여 데이터를 순회하면서 새로운 배열로 return하도록 해줌
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
// Math객채의 random()함수를 이용하여 무작위로 emotion 점수 부여
// floor()함수를 이용하여 소수점을 버리고 정수로 만들어 줌
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime() + 1,
// 현재의 id값을 넣고나서 id값 1 증가
id: dataId.current++,
};
});
setData(initData);
};
};
2023. 01. 05 (금)
프로그래머 도전기 98일차 (2) | 2024.01.10 |
---|---|
프로그래머 도전기 97일차 (2) | 2024.01.08 |
프로그래머 도전기 95일차 (1) | 2024.01.04 |
프로그래머 도전기 94일차 (2) | 2024.01.03 |
프로그래머 도전기 93일차 (0) | 2024.01.01 |