요청방법 정리
JSP
표준서블릿사용(httpservlet, 자유도 x)
자바코드최소한, 마임타입에 따라 → UI역할
서블릿
확장자 자바/사용자요청 들어줌 - 상속받아서 사용(FrontMVC1같이 httpServlet상속받아서 사용)
doGet(req, res), doPost(req, res) → 웹서비스, 통신 비상태(stateless) 프로토콜 - 소통 위해 Restful API
(get-단위테스트o, 노출o, 인터셉트o, 링크공유o / post-단위테스트x, 노출x, 인터셉트x)
→ 파라미터에 요청객체 응답객체(톰캣이 주입해 줌)
URL필요 → 자바에는 URL이 없음 → web.xml로 보내서 url을 만듦(web.xml에 url 등록해 둠→url-pattern)
jsp, 서블릿엔 scope가 필요하다
→ 상태가 자꾸 끊기니까, 자바에는 scope가 없지만 웹서비스 할 때는 필요하다
→ 그냥 인스턴스화하면 scope를 누릴 수 없다(자바엔 없기에)
서블릿 scope 4가지
page / request / session / application
톰캣(WAS)
톰캣 안에 자바/servlet, jsp엔진
FrontMVC1으로 요청 들어옴(web.xml 읽어야 여기로 요청 들어옴 - *.st1으로 url이 끝날경우 여기로 들어온다)
FrontMVC1은 서블릿으로, httpServlet을 상속받았기에 override한 doGet(), doPost()가 있다
→ 사용자정의 메소드는 req, res를 주입받을 수 없다!
→ 이곳에서 메소드를 나눌 수 없다 그렇기에 URL을 통해서 업무를 나눠준다
url에 업무명/메소드명.st1
→ 업무명(member)과 메소드명(getMemberList - select사용 → forward, data유지하기 위해)을 배열 upmu[]에 담기
→ req.setAttribute해서 upmu[]를 Controller로 넘기기
→ controller의 excute()를 호출할 때 파라미터에 req, res 필요
→ FrontMVC1(서블릿)의 req, res 원본을 파라미터로 넘긴다
upmu[0] 업무명에 따라 해당 Controller로 이동
→ controller는 서블릿X! Action을 상속받았기에 req, res 없다(Action의 구현체클래스임)
→ req res를 파라미터로 받는 메소드 excute()를 오버라이드해서 사용(그냥 메소드 만들면 req res 쓰지 못함)
→ 따라서 이곳에서 메소드를 나눌 수 없다 그렇기에 if문으로 upmu[1] 메소드명에따라 거르고 Logic에 있는 해당 메소드를 호출한다
Dao는 DB서버와 통신하는 역할
요청에대한 응답도 FrontMVC1까지 와야 함
→ req res는 같기에 Controller에서 setAttribute
→ path, 응답방식에 대한 정보는 ActionForward에 있기에 그것도 담아주고 반환값으로 af 넘겨줌
→ FrontMVC1에서 af가 null이 아니라면 페이지 이동방식 설정, 이동 위치로 보내줌
업무별 Controller(MemberController)
→ 업무별 Logic(MemberLogic)
→ 업무별 Dao(MemberDao)
→ MyBatis
→ DB서버에 DML을 서버에 넘긴다
select - forward사용, 유지, 리턴타입 object, req res 필요
insert, update, delete - redirect사용, 바뀜, 리턴타입 int, res필요
<pojo step1 복습 - web.xml>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID" version="4.0">
<!--
web.xml(배치서술자 Deployment Descriptor)은 톰캣에서 제공해준다 -> 위치는 WEB-INF 아래
서버가 처음 로딩 시 web.xml 파일을 읽어들여 환경설정에 대해 어플리케이션을 배치하게 됨
-->
<!-- context-param에 등록한 정보는 톰캣서버가 기동할 때 생성되서 서버가 죽을때까지 쭉 기억한다 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4.properties</param-value>
</context-param>
<!--
자바로 웹서비스X / 서블릿은 가능 -> URL을 적을 수 있어서 -> xml에 등록가능(URL사용위해 서블릿을 배치)
서블릿과 서블릿매핑은 항상 같이있어야한다
URL을 등록(url-pattern) - URL이 있어야 요청할 수 있음
servlet보다 servlet-mapping을 먼저 읽는다
-->
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.mvc.controller.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test/test.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.day1.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/day1/hello.kh</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DeptServlet</servlet-name>
<servlet-class>com.day1.DeptManager</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DeptServlet</servlet-name>
<url-pattern>/jEasyUI/dept/insertAction.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FrontController1</servlet-name>
<servlet-class>com.pojo.step1.FrontMVC1</servlet-class>
</servlet>
<!--
*.st1하면 어떤 요청이름이 오든 확장자가 st1으로 끝나면 FrontMVC1 서블릿이 인터셉트함
web.xml을 수정하면 무조건 서버를 재기동해야 수정된 내용이 반영됨
-->
<servlet-mapping>
<servlet-name>FrontController1</servlet-name>
<url-pattern>*.st1</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
<!--
Deployment Descriptor 배치서술자 spring maven방식 빌드 -> pom.xml파일에 등록
web.xml문서를 수정하면 반드시 서버를 내렸다가 다시 기동할 것(주의!)
톰캣서버를 기동하면 가장 먼저 읽는다
이 문서를 읽으면 st1확장자에대해 알고있다
.st1으로 들어오는 모든 요청은 FrontMVC1.java가 인터셉트한다
이 요청은 브라우저의 주소창에서 하게되고
이 요청의 URL을 읽어서 upmu[] 배열에 담는다
배열에 담기는 정보는 아래와 같이 업무명/메소드명으로 이뤄진다(URL의 일부분)
dept/getDeptList -> select처리 - 화면유지필요 - forward페이지 이동 - 조회결과는 request에 담는다
dept/deptInsert -> insert처리 - 유지필요없음 - res.sendRedirect(페이지이름) -> 페이지이름은 path에 담겨있음
dept/deptUpdate -> update처리 - 유지필요없음 - res.sendRedirect(페이지이름) -> 페이지이름은 path에 담겨있음
dept/deptDelete -> delete처리 - 유지필요없음 - res.sendRedirect(페이지이름) -> 페이지이름은 path에 담겨있음
path를 관리하는 클래스명은 ActionForward.java이다
이 클래스에는 전역변수가 두 개 있다
String path - 페이지 이름
boolean isRedirect - true: res.sendRedirect();
false: RequestDispatcher view = req.getRequestDispatcher() ;
view.forward(req, res);
위 내용에 대한 설정은 어디서? -> FrontMVC1 doService()에서 했음
-->
<pojo step1 복습 - FrontMVC1.java >
package com.pojo.step1;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
// 개발자가 정의한 서블릿 - 표준서블릿이 아니다
// doService()는 요청처리에대한 창구를 일원화하기위해 개발자가 정의한 메소드임
// 따라서 request객체와 response객체를 톰캣 서버로부터 주입받을 수 없다
// 이 문제 해결을 위해서 메소드 파라미터 자리를 이용하여 doGet이나 doPost 메소드에서 주입받은
// request객체와 response객체를 넘겨받아서 사용한다
public class FrontMVC1 extends HttpServlet {
Logger logger = Logger.getLogger(FrontMVC1.class);
/*
* 이 메소드는 톰캣 서버로부터 직접 요청객체와 응답객체를 주입받을 수 없다
* 따라서 doGet메소드와 doPost메소드 안에서 doService메소드를 호출할 때 파라미터로 넘겨받는다
*/
protected void doService(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doService 호출");
String uri = req.getRequestURI(); // 주소창에 입력된 값 중 도메인과 포트번호가 제외된 값만 담음
logger.info(uri); // /dept/getDeptList.st1이 찍힘
String context = req.getContextPath(); // "/" -> server.xml에 들어있음
logger.info(context);
// 테스트: http://localhost:9000/업무폴더명/요청기능이름.st1
// 예시1: http://localhost:9000/dept/getDeptList.st1
// 예시2: http://localhost:9000/member/getMemberList.st1
// 예시3: http://localhost:9000/board/getBoardList.st1
// 예시4: http://localhost:9000/board/BoardInsert.st1
// 예시5: http://localhost:9000/board/BoardUpdate.st1
// 예시6: http://localhost:9000/board/BoardDelete.st1
String command = uri.substring(context.length() + 1); // context정보만 제외된 나머지 경로 정보를 담음
System.out.println(command); // dept/getDeptList.st1
int end = command.lastIndexOf("."); // end는 .st1 잘라내기위해 사용
System.out.println(end); // 16(dept의 경우)
command = command.substring(0, end);
System.out.println(command); // dept/getDeptList
String upmu[] = null; // upmu[0]=업무명(폴더명), upmu[1]=요청기능이름(메소드명)
upmu = command.split("/");
for (String imsi : upmu) {
System.out.println(imsi); // upmu[0]=dept, upmu[1]=getDeptList
}
// 게으른 인스턴스화 - 필요할 때 주입
// 아직 업무명이 결정되지 않았다 - 업무명(upmu[0])이 Controller클래스의 접두어이다
DeptController deptController = null;
EmpController empController = null;
ActionForward af = null;
/*
* DeptController에서 req와 res를 받을 수 있는건 execute(req, res)메소드 뿐이다 그렇기에 각 기능별로
* 사용하려면 메소드로 나누면 안될 것 같다 그럼 if문으로 분기하고 DeptLogic.java에서 메소드를 분리해보자
*/
// request 객체는 저장소이다 - setAttribute, getAttribute
// req.setAttribute로 upmu[]를 DeptController로 전달(메소드에서 사용하기위해서)
// 아래 코드는 request 저장소에 upmu[] 주소번지 원본을 저장하는 구문
req.setAttribute("upmu", upmu);
if ("dept".equals(upmu[0])) {
// 인스턴스화 -> execute() 호출하기위해 -> 안하면 NullPointerException이니까(서버쪽은 500번)
deptController = new DeptController();
// deptController는 서블릿이 아니여서 req, res를 WAS로부터 주입받을 수 없다(상속안받았기에)
// 그렇기에 execute() 호출
// 왜 DeptController는 req, res를 주입받을 수 없나? -> 서블릿이 아니라서
// 그래서 파라미터로 doGet메소드가 주입받은 req와 res 주소번지 원본을 넘겨줌
// 이렇게 넘기지 않으면 DeptController에서는 req와 res를 누릴 수 없다
// 이런 이유로 메소드를 하나만 가질 수 있다
// 다른 메소드를 정의하는 것은 자유이지만, req와 res는 주입받을 수 없다
// 입력, 수정, 삭제, 조회 4가지 메소드 필요
// DeptLogic 메소드 4가지를 호출해야하는데 upmu[1]의 정보가 필요함
// 그렇기에 요청객체에 담음 -> req.setAttribute("upmu", upmu);
af = deptController.execute(req, res);
} else if ("emp".equals(upmu[0])) {
// 인스턴스화
empController = new EmpController();
af = empController.execute(req, res);
}
// 페이지 이동 처리 공통코드로 만들기
// res.sendRedirect("/dept/getDeptList.jsp"); -> jsp(요청) - 서블릿 - jsp(응답)
// res.sendRedirect("/dept/getDeptList.st1"); -> jsp - 서블릿 - 서블릿 - jsp
if (af != null) {
if (af.isRedirect()) {
res.sendRedirect(af.getPath());
} else {
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
} // end of 페이지 이동처리에 대한 공통 코드 부분
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doGet호출"); // 브라우저의 주소창을통해 요청하는 건 모두 get방식이다 - doGet() 호출된다
doService(req, res); // doService를 호출해서 req, res를 넘겨받는다
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doPost호출"); // post는 브라우저를 통해서 단위테스트 불가(js나 포스트맨통해서 해야함)
doService(req, res);
}
}
<pojo step1 복습 - DeptController.java>
package com.pojo.step1;
import java.io.IOException;
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;
//SELECT deptno, dname, loc FROM dept
public class DeptController implements Action {
Logger logger = Logger.getLogger(DeptController.class);
// Action 인터페이스 상속 후 오버라이드한 메소드, 반환값이 ActionForward
@Override
public ActionForward execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ActionForward af = new ActionForward();
// FrontMVC1클래스에서 request객체에 저장된 배열 upmu 꺼내기
String upmu[] = (String[]) req.getAttribute("upmu"); // FrontMVC1에서 가져온 값
DeptLogic deptLogic = new DeptLogic();
String path = null;
boolean isRedirect = false;
// 부서목록 조회인 경우 - http://localhost:9000/dept/getDeptList.st1
if ("getDeptList".equals(upmu[1])) {
List<Map<String, Object>> deptList = deptLogic.getDeptList();
req.setAttribute("deptList", deptList);
//응답페이지 이름 결정
path = "getDeptList.jsp";
// redirect로 할지 forward로 할지 결정함
isRedirect = false; // false이면 forward이다 - 요청이 유지된다, 주소창은 그대로 페이지만 바뀜
}
// json 부서목록 조회인 경우 - http://localhost:9000/dept/jsonDeptList.st1
else if ("jsonDeptList".equals(upmu[1])) {
String jsonDoc = deptLogic.jsonDeptList();
req.setAttribute("jsonDoc", jsonDoc);
path = "jsonDeptList.jsp";
isRedirect = false; // false이면 forward이다 - 요청이 유지된다, 주소창은 그대로 페이지만 바뀜
}
// 부서 등록인 경우
else if ("deptInsert".equals(upmu[1])) {
// insert into dept (deptno, dname, loc) values(?, ?, ?)
int result = deptLogic.deptInsert();
}
// 부서 정보 수정인 경우
else if ("deptUpdate".equals(upmu[1])) {
int result = deptLogic.deptUpdate();
}
// 부서 삭제인 경우
else if ("deptDelete".equals(upmu[1])) {
int result = deptLogic.deptDelete();
}
af.setPath(path);
af.setRedirect(isRedirect);
return af;
}
public ActionForward getDeptList() {
// 이렇게 메소드를 만들면 req, res가 없음
// res가 없으면 res.sendRedirect();를 못함! -> res에 의존적
return null;
}
}
<pojo step1 복습 - jsonDeptList.jsp>
<%@ page language="java" contentType="application/json; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%
// jsp페이지이지만 page directive에 마임타입을 application/json으로 했기에
// 브라우저는 이 페이지를 json포맷으로 인지한다
String jsonDoc = (String) request.getAttribute("jsonDoc");
out.print(jsonDoc);
%>
<리액트 사용 - Header.jsx>
import React from 'react'
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
const Header = () => {
return (
<React.Fragment>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link>
<NavDropdown title="Dropdown" id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">
Another action
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#action/3.4">
Separated link
</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</React.Fragment>
)
}
export default Header
<리액트 사용 - Bottom.jsx>
import React from 'react'
import { Navbar } from 'react-bootstrap'
const Bottom = () => {
return (
<React.Fragment>
<Navbar fixed="bottom" className="navbar navbar-expand-sm bg-light justify-content-center" bg="dark" style={{color:'white'}}>
자바캠프 Copyright © 2023
</Navbar>
</React.Fragment>
)
}
export default Bottom
<리액트 사용 - App.jsx>
import { Route, Routes } from 'react-router-dom';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import DeptPage from './components/page/DeptPage';
import EmpPage from './components/page/EmpPage';
import HomePage from './components/page/HomePage';
function App() {
return (
<>
<Routes>
<Route path="/" exact={true} element={<HomePage />} />
<Route path="/dept/:id" exact={true} element={<DeptPage />} />
<Route path="/emp" exact={true} element={<EmpPage />} />
</Routes>
</>
);
}
export default App;
<리액트 사용 - DeptPage.jsx>
import React, { useEffect, useState } from 'react'
import { Table } from 'react-bootstrap';
import Bottom from '../include/Bottom'
import Header from '../include/Header'
const DeptPage = () => {
const [depts, setDepts] = useState([
{deptno:10, dname:"개발1팀", loc:"부산"},
{deptno:20, dname:"개발2팀", loc:"서울"},
{deptno:30, dname:"운영팀", loc:"대전"}
]);
useEffect(() => {
console.log(depts)
},[]); // 옵션에 별도의 값을 지정하지 않으면 최초 한 번만 실행됨
return (
<>
<Header />
<div>부서관리 페이지</div>
<div className="dept-list">
<Table striped bordered hover>
<thead>
<tr>
<th>부서번호</th>
<th>부서명</th>
<th>지역</th>
</tr>
</thead>
<tbody>
{Object.keys(depts).map(key => (
<tr key={key}>
<td>{depts[key].deptno}</td>
<td>{depts[key].dname}</td>
<td>{depts[key].loc}</td>
</tr>
))
}
</tbody>
</Table>
</div>
<Bottom />
</>
)
}
export default DeptPage
<리액트 사용 - HomePage.jsx>
import React from 'react'
import Header from '../include/Header'
import Bottom from '../include/Bottom'
const HomePage = () => {
return (
<React.Fragment>
<Header />
<div>
HomePage 페이지
</div>
<Bottom />
</React.Fragment>
)
}
export default HomePage
<filter함수 연습 - filter2.js>
const deptList = [
{ deptno: 10, dname: "개발1팀", loc: "부산" },
{ deptno: 20, dname: "개발2팀", loc: "서울" },
{ deptno: 30, dname: "운영팀", loc: "대전" },
];
console.log(deptList);
//[{ deptno: 10, dname: '개발1팀', loc: '부산' },
// { deptno: 20, dname: '개발2팀', loc: '서울' },
// { deptno: 30, dname: '운영팀', loc: '대전' }]
deptList.push({ deptno: 40, dname: "총무팀", loc: "인천" });
console.log(deptList);
//[{ deptno: 10, dname: '개발1팀', loc: '부산' },
// { deptno: 20, dname: '개발2팀', loc: '서울' },
// { deptno: 30, dname: '운영팀', loc: '대전' },
// { deptno: 40, dname: '총무팀', loc: '인천' }]
let other = [];
// 배열중에서 파라미터로 들어온 조건을 만족하는 원소만 새로운 배열에 담는다 - filter
other = deptList.filter((row) => row.deptno > 10);
console.log(other);
//[{ deptno: 20, dname: '개발2팀', loc: '서울' },
// { deptno: 30, dname: '운영팀', loc: '대전' },
// { deptno: 40, dname: '총무팀', loc: '인천' }]
let some = [];
some.splice(2, 1, { deptno: 30, dname: "총무1팀", loc: "인천1" });
console.log(some);
// [ { deptno: 30, dname: '총무1팀', loc: '인천1' } ]
other.splice(2, 1, { deptno: 30, dname: "총무1팀", loc: "인천1" });
console.log(other);
//[{ deptno: 20, dname: '개발2팀', loc: '서울' },
// { deptno: 30, dname: '운영팀', loc: '대전' },
// { deptno: 30, dname: '총무1팀', loc: '인천1' }]
<filter함수 연습 - filter3.js>
const array = [-1, 0, 1];
let other = [];
other = array.filter((item) => item >= 0);
console.log(other); // [0, 1]
// 조건을 갖는 함수 조건값을 변수로 치환해서 사용
const condition = (item) => item > 0;
let other1 = [];
other1 = array.filter(condition);
console.log(other1); // [1]
<useState연습 - state1.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.querySelector("#root");
const Counter = () => {
const [num, setNum] = React.useState(0);
return (
<>
<span style={{ fontSize: 78 }}>{num}</span>
<button
style={{ fontSize: 38 }}
onClick={() => {
setNum(num + 1);
}}
>
더하기
</button>
</>
);
};
const App = () => {
return (
<>
<Counter />
</>
);
};
ReactDOM.createRoot(rootElement).render(<App />);
</script>
</body>
</html>
<useState연습 - state2.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// root태그의 정보담기
const rootElement = document.querySelector("#root");
// 사용자 정의 태그 생성하기
const Counter = () => {
// 상태관리해주는 훅 -> useState()
const [num, setNum] = React.useState(0);
const handleAdd = () => {
setNum((prev) => prev + 1);
};
const handleMinus = () => {
setNum(num - 1);
};
const handleZero = () => {
setNum(0);
};
return (
<>
<span style={{ fontSize: 78 }}>{num}</span>
<button style={{ fontSize: 38 }} onClick={handleAdd}>
더하기
</button>
<button style={{ fontSize: 38 }} onClick={handleMinus}>
빼기
</button>
<button style={{ fontSize: 38 }} onClick={handleZero}>
초기화
</button>
</>
);
};
const App = () => {
return (
<>
<Counter />
</>
);
};
ReactDOM.createRoot(rootElement).render(<App />);
</script>
</body>
</html>
<useState연습 - state3.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// root태그의 정보담기
const rootElement = document.querySelector("#root");
// 사용자 정의 태그 생성하기
const Counter = () => {
// 상태관리해주는 훅 -> useState()
const [num, setNum] = React.useState(0);
const handleAdd = (event) => {
console.log(event.currentTarget);
setNum(num + 1);
// 이전 상태값을 기억해줌
setNum((prev) => prev + 1);
};
return (
<>
<span style={{ fontSize: 78 }}>{num}</span>
<button style={{ fontSize: 38 }} onClick={handleAdd}>
더하기
</button>
</>
);
};
const App = () => {
return (
<>
<Counter />
</>
);
};
ReactDOM.createRoot(rootElement).render(<App />);
</script>
</body>
</html>
<firebase 연동하기 - App.jsx>
import { Route, Routes } from 'react-router-dom';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import DeptPage from './components/page/DeptPage';
import EmpPage from './components/page/EmpPage';
import HomePage from './components/page/HomePage';
import FireDeptPage from './components/page/FireDeptPage';
function App() {
return (
<>
<Routes>
<Route path="/" exact={true} element={<HomePage />} />
{/* <Route path="/dept/:id" exact={true} element={<DeptPage />} /> */}
<Route path="/dept/:id" exact={true} element={<FireDeptPage />} />
<Route path="/emp" exact={true} element={<EmpPage />} />
</Routes>
</>
);
}
export default App;
<firebase 연동하기 - DeptRow.jsx>
import React from 'react'
const DeptRow = (props) => {
console.log(props)
const {dept} = props
return (
// 엘리먼트들을 그루핑해서 여러개 사용할때 Fragment 사용
<React.Fragment>
<tr>
<td>{dept.deptno}</td>
<td>{dept.dname}</td>
<td>{dept.loc}</td>
</tr>
</React.Fragment>
)
}
export default DeptRow
<firebase 연동하기 - FireDeptPage.jsx>
import React, { useEffect, useState } from 'react'
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.17.1/firebase-app.js";
import { getDatabase, ref, set, onValue,
} from "https://www.gstatic.com/firebasejs/9.17.1/firebase-database.js";
import { Table } from 'react-bootstrap';
import Bottom from '../include/Bottom'
import Header from '../include/Header'
import DeptRow from '../dept/DeptRow';
const firebaseConfig = {
apiKey: process.env.REACT_APP_FS_APIKEY,
authDomain: process.env.REACT_APP_FS_AUTHDOMAIN,
databaseURL: process.env.REACT_APP_FS_DATABASEURL,
projectId: process.env.REACT_APP_FS_PROJECTID,
storageBucket: process.env.REACT_APP_FS_STORAGEBUCKET,
messagingSenderId: process.env.REACT_APP_FS_MESSAGINGSENDERID,
appId: process.env.REACT_APP_FS_APPID
};
const app = initializeApp(firebaseConfig);
console.log(app)
const database = getDatabase()
const FireDeptPage = () => {
const [depts, setDepts] = useState([]);
useEffect(() => {
console.log("useEffect 호출")
console.log(database)
console.log(depts)
const starCountRef = ref(database, 'dept');
onValue(starCountRef, (snapshot) => {
const data = snapshot.val();
console.log(data)
setDepts(data)
});
},[]); // 옵션에 별도의 값을 지정하지 않으면 최초 한 번만 실행됨
// userEffect에서 초기화된 상태값 출력해보기
console.log(depts)
return (
<>
<Header />
<div>부서관리 페이지</div>
<div className="dept-list">
<Table striped bordered hover>
<thead>
<tr>
<th>부서번호</th>
<th>부서명</th>
<th>지역</th>
</tr>
</thead>
<tbody>
{Object.keys(depts).map(key => (
<DeptRow key={key} dept={depts[key]} />
))
}
</tbody>
</Table>
</div>
<Bottom />
</>
)
}
export default FireDeptPage
엘리먼트 렌더링
리액트가 비교알고리즘을 가지고 타입, 데이터를 비교해서 바뀐 사항을 체크(key값을 가지고 체크함)해서 바뀐 부분만 리렌더링 해준다
키 값은 유니크해야 하고 반복이 되는 자리에 들어간다
바벨
es5문법을 es6문법으로 바꿔줌
<리렌더링 - render1.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.querySelector("#root");
const random = () => {
const number = Math.floor(Math.random() * (10 - 1) + 1);
const element = <button>{number}</button>;
ReactDOM.render(element, rootElement);
};
setInterval(random, 1000); // 버튼부분만 리렌더링됨
</script>
</body>
</html>
<리렌더링 - render2.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.querySelector("#root");
const random = () => {
const number = Math.floor(Math.random() * (10 - 1) + 1);
const element = (
<>
{/* 버튼과 p태그 모두 리렌더링됨 */}
<button children={number}></button>
<div>
<p>{number}</p>
</div>
</>
);
ReactDOM.render(element, rootElement);
};
setInterval(random, 1000);
</script>
</body>
</html>
<리렌더링 - render3.html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const rootElement = document.querySelector("#root");
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, rootElement);
}
setInterval(tick, 1000); // 현재시간만 리렌더링됨
</script>
</body>
</html>
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day64 (0) | 2023.02.28 |
---|---|
국비 지원 개발자 과정_Day63 (0) | 2023.02.27 |
국비 지원 개발자 과정_Day61 (0) | 2023.02.23 |
국비 지원 개발자 과정_Day60 (0) | 2023.02.22 |
국비 지원 개발자 과정_Day59 (0) | 2023.02.21 |
댓글