상세 컨텐츠

본문 제목

프로그래머 도전기 96일차

프로그래머가 될거야!

by Choyee 2024. 1. 5. 22:19

본문

오늘은

 

오늘은 금요일입니다

내일부터 주말인데 내일부터는 이력서도 쓰고 취업준비를 위한

실질적인 준비를 해볼까 합니다

공부는 얼마나 할 수 있을지 모르겠네요!!

 

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

관련글 더보기