오늘은
하루하루 무언가를 느낄 새 없이 정신없이 흘러가는 것 같습니다~
밥을 먹을때에도 잠이 들기 전까지도
뭘 해야할지 생각을 하게 되는 것 같습니다
심지어 꿈에서도 코딩을 하는 꿈을 꾸고있답니다..하하
이력서는 계속 다듬어주어야 할 것 같은데
심플한게 좋겠죠..?
Spring 공부
<검색유형 체크박스 체크>
* PageRequestDTO
public class PageRequestDTO {
// 검색유형 체크박스 체크
public boolean checkType(String type) {
// 타입에 체크가 없으면 반환값이 없음
if(types == null || types.length == 0) {
return false;
}
// type에 일치되는 유형을 반환한다는 의미
return Arrays.stream(types).anyMatch(type::equals);
}
}
* paginglist
<form action="/todo/paging" method="get">
<div class="mb-3">
<input type="checkbox" name="types" value="t"
${pageRequestDTO.checkType("t") ? "checked" : ""} >제목
<input type="checkbox" name="types" value="w"
${pageRequestDTO.checkType("w") ? "checked" : ""} >작성자
<input type="text" name="keyword" class="form-control">
</div>
<div class="mb-3">
<div class="float-end">
<button type="submit" class="btn btn-primary">Search</button>
<button type="reset" class="btn btn-info">Clear</button>
</div>
</div>
</form>
<script>
// 검색 조건 초기화
let btnClear = document.getElementById("btnClear");
btnClear.addEventListner("click", function(){
location.href="/todo/paging";
})
</script>
<<Ajax - 비동기 통신>>
(Async JAvascript Xml)
=> Jquery 소속 > $.ajax
* AjaxDTO
package com.khit.todoweb.dto;
import lombok.Data;
@Data
public class AjaxDTO {
private String greet;
private int num;
}
* AjaxViewController
package com.khit.todoweb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AjaxViewController { // 페이지 컨트롤러
@GetMapping("/ajax/main")
public String main() {
return "/ajax-ex/main";
}
@GetMapping("/ajax/ex01")
public String ex01() {
return "/ajax-ex/ex01";
}
@GetMapping("/ajax/ex02")
public String ex02() {
return "/ajax-ex/ex02";
}
@GetMapping("/ajax/ex03")
public String ex03() {
return "/ajax-ex/ex03";
}
@GetMapping("/ajax/ex04")
public String ex04() {
return "/ajax-ex/ex04";
}
}
* AjaxController
package com.khit.todoweb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.khit.todoweb.dto.AjaxDTO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class AjaxController { // Ajax요청 처리 컨트롤러
@GetMapping("/ex01")
public String ex01() {
log.info("GET 요청 처리");
return "/ajax-ex/main"; // main.jsp
// http://localhost:8080/ajax/ex01 경로는 유지 = 비동기 방식
}
// @ResponseBody
@PostMapping("/ex02")
public @ResponseBody String ex02() {
log.info("POST 요청 처리");
return "/ajax-ex/main"; // 문자열로 /ajax-ex/main 반환
// http://localhost:8080/ajax/ex02 경로는 유지 = 비동기 방식
}
@GetMapping("/ex03")
public @ResponseBody String ex03(
@RequestParam("greet") String greet,
@RequestParam("num") int num) {
log.info("greet: " + greet);
log.info("num: " + num);
return "success";
}
// ajaxDTO - object(객체)이므로 json(자바스크립트 데이터)으로 변환을 위해
// jackson-databind를 주입해줘야 함
@PostMapping("/ex04")
public @ResponseBody AjaxDTO ex04(
@ModelAttribute AjaxDTO ajaxDTO) {
log.info("ajaxDTO= " + ajaxDTO);
return ajaxDTO;
}
}
* ex01.jsp
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script>
<body>
<div class="content">
<h2>1. GET 요청하기</h2>
<p>
<button type="button" onClick="myFunction()">전송</button>
</p>
</div>
<script>
// Ajax는 제이쿼리 라이브러리를 포함(import)시켜야 한다
const myFunction = function(){
//alert("text...");
// ajax() 안에 {} 객체로 구성되어 있다는 의미 => {key: value, key: value, ,,,}형식
$.ajax({
// 요청 방식: GET, 요청 주소: /ex01, (함수) -> 성공, 실패
type: "GET",
url: "/ex01",
success : function(res){ // res= 서버에서 보내주는 자료 => 페이지를 문자로 읽어줌
console.log("성공", res);
},
error: function(){
console.log("실패");
}
})
}
</script>
</body>
* ex02
<body>
<div class="content">
<h2>2. POST 요청하기</h2>
<p>
<button type="button" onClick="myFunction()">전송</button>
</p>
</div>
<script>
// Ajax는 제이쿼리 라이브러리를 포함(import)시켜야 한다
const myFunction = function(){
//alert("text...");
// ajax() 안에 {} 객체로 구성되어 있다는 의미 => {key: value, key: value, ,,,}형식
$.ajax({
// 요청 방식: GET, 요청 주소: /ex01, (함수) -> 성공, 실패
type: "POST",
url: "/ex02",
success : function(res){ // res= 서버에서 보내주는 자료 => 페이지를 문자로 읽어줌
console.log("성공", res);
},
error: function(){
console.log("실패");
}
})
}
</script>
</body>
* ex03
<body>
<div class="content">
<h2>3. DATA(GET) 요청하기</h2>
<p>
<button type="button" onClick="myFunction()">전송</button>
</p>
</div>
<script>
// Ajax는 제이쿼리 라이브러리를 포함(import)시켜야 한다
const myFunction = function(){
//alert("text...");
// ajax() 안에 {} 객체로 구성되어 있다는 의미 => {key: value, key: value, ,,,}형식
$.ajax({
// 요청 방식: GET, 요청 주소: /ex01, (함수) -> 성공, 실패
// 객체 = 키값은 문자열로 함 (자바스크립트에서는 "" 생략 가능)
type: "GET",
url: "/ex03",
data: {
greet: "안녕하세요",
num : 10
},
success : function(res){ // res= 서버에서 보내주는 자료 => 페이지를 문자로 읽어줌
console.log("성공", res);
if(res == "success") {
alert("처리완료");
}
},
error: function(){
console.log("실패");
}
})
}
</script>
</body>
* ex04
<body>
<div class="content">
<h2>4. DTO(POST) 요청하기</h2>
<p>
<button type="button" onClick="myFunction()">전송</button>
</p>
</div>
<script>
// Ajax는 제이쿼리 라이브러리를 포함(import)시켜야 한다
const myFunction = function(){
let greeting = "새해복많이받으세요";
let number = 2024;
//alert("text...");
// ajax() 안에 {} 객체로 구성되어 있다는 의미 => {key: value, key: value, ,,,}형식
$.ajax({
// 요청 방식: GET, 요청 주소: /ex01, (함수) -> 성공, 실패
// 객체 = 키값은 문자열로 함 (자바스크립트에서는 "" 생략 가능)
type: "POST",
url: "/ex04",
data: { // 변수를 받아서 넘겨줄 수 있음
greet: greeting,
num : number
},
success : function(res){ // res= 서버에서 보내주는 자료 => 페이지를 문자로 읽어줌
console.log("성공", res);
},
error: function(){
console.log("실패");
}
})
}
</script>
</body>
<Ajax로 댓글기능 구현하기>
* ReplyController
@Slf4j
@RequestMapping("/reply")
@Controller
public class ReplyController {
// 서비스 클래스 주입(생성자, Autowired)
@Autowired
private ReplyService replyService;
@PostMapping("/insert")
public @ResponseBody List<ReplyDTO> replyInsert(@ModelAttribute ReplyDTO replyDTO) {
// 댓글 폼에 입력된 데이터 출력
log.info("replyDTO:" + replyDTO);
// 댓글 저장 처리
replyService.insert(replyDTO);
// 저장 후 댓글 목록 가져와서 ajax쪽(detail페이지)으로 보내주기
List<ReplyDTO> replyList = replyService.getReplyList(replyDTO.getBoardId());
return replyList;
}
}
* detail.jsp
<div id="reply-list">
<c:forEach items="${replyList}" var="reply">
<div class="reply">
<p>${reply.replyContent}</p>
<p>작성자: ${reply.replyer}
<c:choose>
<c:when test="${empty reply.updateTime}">
(작성일 : <fmt:formatDate value="${reply.createTime}"
pattern="yyy-MM-dd HH:mm:ss" />)
</c:when>
<c:otherwise>
(수정일 : <fmt:formatDate value="${reply.updateTime}"
pattern="yyy-MM-dd HH:mm:ss" />)
</c:otherwise>
</c:choose>
</p>
<P>
<a href="/reply/update?boardId=${board.id}&id=${reply.id}">수정</a> |
<a href="/reply/delete?boardId=${board.id}&id=${reply.id}"
onclick="return confirm('정말로 삭제하시겠습니까?')">삭제</a>
</P>
</div>
</c:forEach>
</div>
<script>
const reply = function() {
let content = document.getElementById("replyContent").value;
let replyer = document.getElementById("replyer").value;
let boardId = document.getElementById("boardId").value;
if(content == "") {
alert("댓글을 입력해주세요");
document.getElementById("replyContent").focus();
return false;
}
//ajax 구현
$.ajax({
// 요청 방식: POST, 요청주소: /reply/insert
type: "POST",
url: "/reply/insert",
data: {
boardId: boardId,
replyer: replyer,
replyContent: content
},
success: function(replyList){
console.log("댓글 등록 성공");
console.log(replyList);
// 댓글 목록
let output = "";
for(let i in replyList) {
output += "<div class='reply'>";
output += "<p>" + replyList[i].replyContent + "</p>";
output += "<p>작성자 : " + replyList[i].replyer + "";
output += "(작성일 : " + replyList[i].createTime + ")</p>";
output += "</div>";
}
document.getElementById("reply-list").innerHTML = output;
// 댓글창 초기화
document.getElementById("replyContent").value = "";
},
error: function(){
console.log("댓글 등록 실패");
}
});
}
</script>
React 공부
<<최적화>>
* component 최적화 => 어떤 component가 최적화의 대상인지 찾아낼 수 있어야 한다
=> React Developer Tools의 기능 -> component 탭 이용
Highlight updates when components render 기능 사용
어떤 component가 낭비되고 있는지 확인할 수 있다
* 일기를 삭제하는 상황에서 diary component가 깜박임 = 낭비되고 있음을 알 수 있다
일기를 삭제하는 상황에서는 일기 리스트가 아닌 일기 작성 폼이 render가 될 필요가 없다
DiaryEditor component에서 React.memo()를 이용하여 onCreate를 감싸 component최적화를 해준다
// useEffect를 import 해준다
import React, { useEffect } from "react";
// React.memo()를 이용하여 onCreate를 감싸 component최적화를 해준다
const DiaryEditor = = React.memo(({ onCreate }) => {
~
});
=> 안에 코드가 많기 때문에
export default React.memo(DiaryEditor);
끝 부분에서 export 할 때 이렇게 해주어도 같은 효과를 갖는다
App component의
이 부분에서 rendering이 한번 일어나고,
const App = () => {
// 처음 시작할 때의 data state배열은 빈 값
const [data, setData] = useState([]);
}
이 부분에서 다시 rendering이 일어난다
const App = () => {
setData(initData);
};
= App component는 mount 되자마자 두번의 rendering을 한다
그래서 DiaryEditor component가 전달받는 onCreate 함수도
App component가 rendering 되면서 계속 다시 생성이 되는 것이다
결론적으로 onCreate 함수때문에 DiaryEditor가 다시 rendering되는 것이다
=> onCreate 함수가 재생성되지 않아야만 DiaryEditor component를
React.memo()와 함께 최적화 할수 있다
** 하지만 여기서 useMemo()를 사용할 수는 없다
=> useMemo()는 함수를 반환하는 것이 아니기 때문에
onCreate 함수를 전달해주어야 하는 상황에서는 값만 전달하는 useMemo()를 사용할 수 없다
<useCallback()>
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
= 메모이제이션된 콜백을 반환
= 메모이제이션된, 콜백된, 함수를 다시 반환해준다
=> [a, b] dependency array의 값이 변하지 않으면
첫번째 인자로 전달한 callback 함수를 재사용할 수 있도록 도와주는 React Hook이다
(React Hook = 구성 요소에서 다양한 React 기능을 사용)
* App.js
// useCallback() 으로 onCreate함수를 감싸준다
const onCreate = useCallback(
(author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData((data) => [newItem, ...data]); // => state 변화 함수에 함수를 전달하는 것 = 함수형 업데이트라고 한다
},
// useCallback을 활용하면서 [] depency array에 아무값도 넣어주지 않아서
// 추가된 데이터만 나오고 기존의 데이터가 날아가 버린다
// onCreate 함수는 component가 mount되는 시점에 한번만 생성이 되기 때문에
// 그 당시의 data state의 값이 [] 빈배열이기 때문에
// onCreate 함수가 마지막으로 생성됐을 때의 state가 빈배열이기 때문에 이런 현상이 발생
// []
// [data]
// onCreate가 재생성 되지 안도록 해주기 위해서 useCallback을 쓰는데
// data 값이 변경되면 onCreate 함수가 재생성 되고,
// 최신의 데이터를 받아오기 위해서는 onCreate가 최신의data 값을 가져와야 한다
// 위에서 함수형 업데이트를 해줌으로써
[]
// 빈 배열을 인자로 사용하더라도 setData()의 인자에서
// 최신의 데이터를 참고 할수 있게 되어서 []를 비워놓아도 괜찮도록 해준다
);
2023. 01. 10 (수)
프로그래머 도전기 101일차 (0) | 2024.01.13 |
---|---|
프로그래머 도전기 100일차 (2) | 2024.01.11 |
프로그래머 도전기 98일차 (2) | 2024.01.10 |
프로그래머 도전기 97일차 (2) | 2024.01.08 |
프로그래머 도전기 96일차 (2) | 2024.01.05 |