캡쳐링-부모에서 자식으로
버블링-자식에서 부모로
컴포넌트 설계 → 부모에서 자식으로 전해주는 건 가능하지만 반대는 안된다
렌더링 되는 경우 3가지 → state, props, 부모컴포넌트가 바뀔 때!
<해커뉴스 페이지처리 - HackerNewsPage.jsx>
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import HackerNewsList from "../hacker/HackerNewsList";
import Bottom from "../include/Bottom";
import Header from "../include/Header";
const NewsListUL = styled.ul`
background-color: gray;
padding-top: 10px;
padding-left: 6px;
padding-right: 6px;
padding-bottom: 18px;
`
const HackerNewsPage = ({ authLogic }) => {
const [newsList, setNewsList] = useState([]);
// 페이징 처리에 필요한 코드 추가
// 현재 바라보는 페이지
const [currentPage, setCurrentPage] = useState(1)
// 한 페이지에 출력될 로우의 수
const [newsPerPage, setNewsPerPage] = useState(5)
// 배열 객체에서 어디부터 어느 구간까지를 잘라낼 것인가를 위한 변수 선언 및 초기화
const indexOfLast = currentPage * newsPerPage // 10 20 30 40... 같이 처리됨(1n, 10일경우)
const indexOfFirst = indexOfLast - newsPerPage // 0 10 20 50... 같이 처리됨
const currentNews = news => {
let currentNews = 0
// 파라미터로 받은 배열에서 잘라내기 - slice배열내장함수(begin부터 end 인덱스 전까지)
currentNews = news.slice(indexOfFirst, indexOfLast) // 0~9, 10~19, 20~29... 같이 처리됨
return currentNews
}
const NEWSURL = "https://api.hnpwa.com/v0/news/1.json";
const navigate = useNavigate();
const onLogout = () => {
console.log("HackerNewsPage onLogout 호출");
authLogic.logout();
};
useEffect(() => {
axios.get(NEWSURL).then((response) => {
console.log(response.date)
console.log(response.data.length);
setNewsList(response.data);
});
}, []);
useEffect(() => {
authLogic.onAuthChange((user) => {
if (!user) {
navigate("/");
}
});
});
return (
<>
<Header onLogout={onLogout} />
<NewsListUL>
<HackerNewsList newsList={currentNews(newsList)} newsPerPage={newsPerPage} totalNews={newsList.length} paginate={setCurrentPage} />
</NewsListUL>
<Bottom />
</>
);
};
export default HackerNewsPage;
<해커뉴스 페이지처리 - HackerNewsList.jsx>
import React from 'react'
import Pagination from './Pagination'
/*
HackerNewsPage에서 조회된 결과를 가지고 페이징 처리에 필요한 연산을 수행하여
필요한 정보를 HackerNewList에 props로 넘겨야함
데이터셋이 배열 객체이므로 배열 내장 함수들을 활용할 수 있을 것
*/
const HackerNewsList = (props) => {
const { newsList, newsPerPage, totalNews, paginate } = props
console.log(newsList.length) // 10
console.log(newsPerPage) // 10
console.log(totalNews) // 30
console.log(paginate) // 함수 출력
return (
<>
<div>
{newsList && Object.keys(newsList).map((key) => (
<li key={key}>[id:{newsList[key].id}]
{newsList[key].title}
</li>
))}
</div>
<Pagination newsPerPage={newsPerPage} totalNews={totalNews} paginate={paginate} />
</>
)
}
export default HackerNewsList
<해커뉴스 페이지처리 - Pagination.jsx>
import React from 'react'
import styled from 'styled-components';
const PageUl = styled.ul`
float: center;
list-style: none;
text-align: center;
border-radius: 6px;
color: white;
padding: 1px;
border-top: 3px solid #FFC444;
border-left: 3px solid #FFC444;
border-right: 3px solid #FFC444;
border-bottom: 3px solid #FFC444;
background-color: rgba(78, 77, 77, 0.4);
`;
const PageLi = styled.li`
display: inline-block;
font-size: 17px;
font-weight: 600;
padding: 5px;
border-radius: 16px;
width: 25px;
&:hover {
cursor: pointer;
color: white;
background-color: #3DB33D;
}
&:focus::after {
color: white;
background-color: #266C3B;
}
`;
// css를 js문법을 사용하여 컴포넌트로 만들어주는 라이브러리 - styled-components
// 페이지 숫자에 마우스를 올리면 동그라미처리, 글자색은 흰색
const PageSpan = styled.span`
&:hover::after,
&:focus::after {
border-radius: 100%;
color: white;
background-color: #263A6C;
}
`;
const Pagination = (props) => {
const { newsPerPage, totalNews, paginate} = props
console.log(`newsPerPage: ${newsPerPage}, totalNews: ${totalNews}, paginate: ${paginate}`)
// 페이지네이션에 들어갈 숫자를 담을 배열 선언
const pageNumbers = []
for(let i=1; i<=Math.ceil(totalNews/newsPerPage); i++){
// 배열에 값 추가 - 배열 함수 push
pageNumbers.push(i)
}
return (
<div>
<PageUl>
{pageNumbers.map(number => (
<PageLi key={number}>
<PageSpan onClick={()=>paginate(number)}>
{number}
</PageSpan>
</PageLi>
))}
</PageUl>
</div>
)
}
export default Pagination
<우편번호 검색기 - CommonController.java>
package com.pojo.step3;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.util.HashMapBinder;
public class CommonController implements Controller3 {
Logger logger = Logger.getLogger(CommonController.class);
private CommonLogic commonLogic = new CommonLogic();
@Override
public ModelAndView zipcodeList(HttpServletRequest req, HttpServletResponse res) {
logger.info("zipcodeList 호출");
List<Map<String, Object>> zList = null;
// 사용자가 조건 검색을 원하는 경우 - 조건 값을 전달할 객체 생성함
// MyBatis에서는 동적쿼리를 지원하므로 하나로 2가지 경우 사용 가능함
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
zList = commonLogic.zipcodeList(pMap);
ModelAndView mav = new ModelAndView(req);
// WEB-INF/views/common/jsonZipcodeList.jsp
mav.setViewName("common/jsonZipcodeList");
mav.addObject("zList", zList);
return mav; // 리턴이 mav면 webapp/WEB-INF/views/common 아래의 jsp
}
@Override
public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardList(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageUpload(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageDownload(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object imageGet(HttpServletRequest req, HttpServletResponse res) {
return null;
}
@Override
public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
@Override
public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
@Override
public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
return null;
}
}
<우편번호 검색기 - memberMgr.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원관리 시스템</title>
<%@ include file="../common/easyUI_common.jsp"%>
</head>
<body>
<script>
$(document).ready(function() {
// 우편번호 찾기버튼
$("#btn_zipcode").linkbutton({
onClick : function() {
// $('#dlg_zipcode').dialog('open');
console.log('우편번호 찾기')
$('#dlg_zipcode').dialog('open');
}
})
// 찾기버튼
$("#btn_search").linkbutton({
onClick:function(){
const u_dong = $("#_easyui_textbox_input1").val();
if(u_dong == null || u_dong.length < 1) {
alert("검색할 동을 입력하세요");
// 동 정보가 없으면 처음부터 새로 시작해야하니 return함
return;
} else {
console.log('사용자가 입력한 동이름 => ' + u_dong);
// 오라클 서버를 경유해서 조회된 결과를 datagrid에 출력해보기
// location.href = "/common/zipcodeList.st3?dong="+u_dong;
$('#dg_zipcode').datagrid({
url:'/common/zipcodeList.st3?dong='+u_dong,
columns:[[
{field:'ZIPCODE',title:'우편번호',width:100},
{field:'ADDRESS',title:'주소',width:300,align:'left'}
]]
});
}
}
}) // end of 찾기버튼
// 엔터쳤을때
$('#dong').textbox('textbox').bind('keydown', function(e){
if(e.keyCode == 13){
// alert('사용자가 입력한 동이름은 ' + u_dong);
console.log('사용자가 입력한 동이름 => ' + $(this).val());
$('#dg_zipcode').datagrid({
url:'/common/zipcodeList.st3?dong=' + $(this).val(),
columns:[[
{field:'ZIPCODE',title:'우편번호',width:100,align:'center'},
{field:'ADDRESS',title:'주소',width:300,align:'left'}
]],
singleSelect: true,
onSelect: function(index, row){
g_address = row.ADDRESS
alert(g_address);
},
onDbClickCell: function(index, field, value) {
if("ZIPCODE" === field) {
$("#mem_zipcode").textbox('setValue', value)
$("#mem_addr").textbox('setValue', g_address)
}
}
});
}
})
}) // end of ready - DOM Tree가 다 그려지고 실행됨
</script>
<!--======================= 우편번호 검색기 =======================-->
<div id="dlg_zipcode" class="easyui-dialog" title="우편번호찾기" style="width:500px;height:400px;"
data-options="iconCls:'icon-save',resizable:true,modal:true, closed:true">
<input class="easyui-textbox" id="dong" name="dong" labelPosition="top" data-options="prompt:'동이름 이나 주소정보 입력...'" style="width:210px;">
<a id="btn_search" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'">찾기</a>
<div style="margin-bottom:10px;"></div>
<table id="dg_zipcode"></table>
</div>
<!--======================= 우편번호 검색기 =======================-->
<!--======================= 회원가입 =======================-->
<div id="dlg_ins"
style="width: 100%; max-width: 480px; padding: 30px 60px;">
<form id="f_ins">
<input type="hidden" id="work" name="work" value="member" />
<div style="margin-bottom: 10px">
<input class="easyui-textbox" id="mem_id" name="mem_id" label="아이디:"
labelPosition="top" data-options="prompt:'Enter a ID'"
style="width: 110px;">
</div>
<div style="margin-bottom: 10px">
<input class="easyui-textbox" id="mem_name" name="mem_name"
label="이름:" labelPosition="top"
data-options="prompt:'Enter a Name'" style="width: 150px;">
</div>
<div style="margin-bottom: 10px">
<input class="easyui-textbox" id="mem_zipcode" name="mem_zipcode"
label="우편번호:" labelPosition="top"
data-options="prompt:'Enter a ZipCode'" style="width: 100px;">
<a id="btn_zipcode" href="#" class="easyui-linkbutton">우편번호찾기</a>
</div>
<div style="margin-bottom: 10px">
<input class="easyui-textbox" id="mem_addr" name="mem_addr"
label="주소:" labelPosition="top"
data-options="prompt:'Enter a ADDRESS'" style="width: 300px;">
</div>
<div style="margin-bottom: 10px">
<input class="easyui-textbox" id="mem_pw" name="mem_pw" label="비번:"
labelPosition="top" data-options="prompt:'Enter a PASSWORD'"
style="width: 110px;">
</div>
</form>
</div>
<div id="tb_ins" style="padding: 5px;">
<a id="btn_save" href="#" class="easyui-linkbutton"
data-options="iconCls:'icon-save'">저장</a> <a id="btn_close" href="#"
class="easyui-linkbutton" data-options="iconCls:'icon-cancel'">닫기</a>
</div>
<!--======================= 회원가입 =======================-->
</body>
</html>
<우편번호 검색기 - jsonZipcodeList.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.*, com.google.gson.Gson"%>
<%
List<Map<String,Object>> zList =
(List<Map<String,Object>>)request.getAttribute("zList");
Gson g = new Gson();
String temp = g.toJson(zList);
out.print(temp);
%>
쿠키와 세션
쿠키 → Client CSR(정적) - text에 저장 → 주소번지를 담을 수 없다, 한글처리 해야 함, 보안에 취약(로컬에 저장되니까)
세션 → Server SSR(동기화됨, 동적) - 캐시메모리에 저장(용량작음) → 주소번지를 담을 수 있다, 한글처리 안 해도 됨, 보안에 유리(서버에 저장되니까),
브라우저 안에도 세션값이 있음(쿠키에 세션 ID가 저장됨)
쿠키는 응답 헤더를 통해서 넘어간다 → flush후에 쿠키를 추가하면 안 됨
도메인마다 쿠키값이 다를 수 있다 → 그래서 getDomain() 사용
#쿠키 관련 메서드
메서드 | 리턴 타입 | 설명 |
getName() | String | 쿠키 이름을 구한다. |
getValue() | String | 쿠키 값을 구한다. |
setValue(String value) | void | 쿠키 값을 지정한다. |
setDomain(String pattern) | void | 이 쿠키가 전송될 서버의 도메인을 지정한다. |
getDomain() | String | 쿠키의 도메인을 구한다. |
setPath(String uri) | void | 쿠키를 전송할 경로를 지정한다. |
getPath() | String | 쿠키의 전송 경로를 구한다. |
setMaxAge(int expiry) | void | 쿠키의 유효시간을 초 단위로 지정한다. 음수를 입력할 경우 웹 브라우저를 닫을 떄 쿠키가 함께 삭제된다. |
getMaxAge() | int | 쿠키의 유효시간을 구한다. |
<쿠키 - cookieCreate.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키 생성하기</title>
</head>
<body>
<%
// 아래 코드는 서버가 바라보는 물리적인 위치에 들어있음
// 서버측에서 실행됨 -> html -> 다운로드(SSR) -> 동적처리(실시간)
Cookie c1 = new Cookie("notebook", "삼성갤럭시북");
c1.setMaxAge(60);
// 아래 코드를 생략하면 현재 페이지가 바라보는 물리적인 위치를 갖는다
// 여기서는 /cookie임
// c1.setPath("/");
Cookie c2 = new Cookie("hp", "아이폰14");
c2.setMaxAge(60*2);
Cookie c3 = new Cookie("coffee", "아메리카노");
c3.setMaxAge(60*3);
response.addCookie(c1);
response.addCookie(c2);
response.addCookie(c3);
%>
</body>
</html>
<쿠키 - cookieRead.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키정보 읽기</title>
</head>
<body>
<%
Cookie cs[] = request.getCookies();
int size = 0;
size = cs.length;
for(int i=0; i<size; i++){
out.print(cs[i].getName() + ", " + cs[i].getValue());
out.print("<br />");
}
%>
</body>
</html>
<쿠키 - cookieDelete.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키 삭제하기</title>
</head>
<body>
<%
Cookie cookie = new Cookie("notebook", "");
cookie.setMaxAge(0);
response.addCookie(cookie);
%>
</body>
</html>
<sts 멤버 - MemberController.java>
package com.example.demo.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.demo.logic.MemberLogic;
@Controller
@RequestMapping("/member/*")
public class MemberController {
Logger logger = LoggerFactory.getLogger(MemberController.class);
@Autowired
private MemberLogic memberLogic = null;
@GetMapping("memberInsert")
public String memberInsert(@RequestParam Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap.toString());
int result = 0; // 1이면 회원가입 성공, 0이면 실패
result = memberLogic.memberInsert(pMap);
return "redirect:memberList";
}
@GetMapping("memberList")
public String memberList(Model model) {
logger.info("memberList 호출");
return "member/memberList";
}
}
<sts 멤버 - MemberLogic.java>
package com.example.demo.logic;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.dao.MemberDao;
@Service
public class MemberLogic {
Logger logger = LoggerFactory.getLogger(MemberLogic.class);
@Autowired
private MemberDao memberDao = null;
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap.toString());
int result = 0;
result = memberDao.memberInsert(pMap);
return result;
}
}
<sts 멤버 - MemberDao.java>
package com.example.demo.dao;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
// @Repository -> MVC패턴 공부를위해 Service를 사용함
@Service
public class MemberDao {
Logger logger = LoggerFactory.getLogger(MemberDao.class);
public int memberInsert(Map<String, Object> pMap) {
logger.info("memberInsert 호출");
logger.info(pMap.toString());
int result = 0;
return result;
}
}
<sts 멤버 - memberList.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원목록 페이지</title>
</head>
<body>
<h3>회원목록 페이지</h3>
</body>
</html>
<스프링부트와 리액트 연동 - index.js>
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<>
<BrowserRouter>
<App />
</BrowserRouter>
</>
);
<스프링부트와 리액트 연동 - App.jsx>
import { Route, Routes } from 'react-router-dom';
import './App.css';
import MemberPage from './components/page/MemberPage';
function App() {
return (
<>
<Routes>
<Route path="/member" exact={true} element={<MemberPage />} />
</Routes>
</>
);
}
export default App;
<스프링부트와 리액트 연동 - MemberPage.jsx>
import React, { useEffect, useState } from 'react'
import { jsonMemberListDB } from '../../service/dbLogic'
const MemberPage = () => {
const [member, setMember] = useState({})
useEffect(()=>{
const memberList = async() => {
const res = await jsonMemberListDB(member)
console.log(res.data)
}
memberList()
},[])
return (
<div>
회원관리 페이지입니다
</div>
)
}
export default MemberPage
<스프링부트와 리액트 연동 - dbLogic.js>
import axios from "axios";
export const jsonMemberListDB = (member) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_SPRING_IP + "member/jsonMemberList",
params: member,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
<스프링부트와 리액트 연동 - Mblog1Application.java>
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class Mblog1Application {
public static void main(String[] args) {
SpringApplication.run(Mblog1Application.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:3000");
}
};
}
}
<스프링부트와 리액트 연동 - RestMemberController.java>
package com.example.demo.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.gson.Gson;
@RestController
@RequestMapping("/member/*")
public class RestMemberController {
Logger logger = LoggerFactory.getLogger(RestMemberController.class);
@GetMapping("jsonMemberList")
public String jsonMemberList(Model model) {
logger.info("jsonMemberList 호출");
String temp = null;
List<Map<String, Object>> mList = new ArrayList<>();
Map<String, Object> rmap = new HashMap<>();
rmap.put("mem_id", "tomato");
rmap.put("mem_pw", "123");
rmap.put("mem_name", "토마토");
mList.add(rmap);
Gson g = new Gson();
temp = g.toJson(mList);
return temp;
}
}
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day76 (0) | 2023.03.17 |
---|---|
국비 지원 개발자 과정_Day75 (1) | 2023.03.16 |
국비 지원 개발자 과정_Day73 (1) | 2023.03.14 |
국비 지원 개발자 과정_Day72 (1) | 2023.03.13 |
국비 지원 개발자 과정_Day71 (1) | 2023.03.10 |
댓글