본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day62

by 루팽 2023. 2. 24.

요청방법 정리

  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 &copy; 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>

댓글