상세 컨텐츠

본문 제목

프로그래머 도전기 99일차

프로그래머가 될거야!

by Choyee 2024. 1. 10. 21:52

본문

오늘은

 

하루하루 무언가를 느낄 새 없이 정신없이 흘러가는 것 같습니다~

밥을 먹을때에도 잠이 들기 전까지도

뭘 해야할지 생각을 하게 되는 것 같습니다

심지어 꿈에서도 코딩을 하는 꿈을 꾸고있답니다..하하

이력서는 계속 다듬어주어야 할 것 같은데

심플한게 좋겠죠..?

 

 

 

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 (수)

관련글 더보기