Spring Boot 3 & Spring Framework 6 - Section 14 : React 공부

이정수·2024년 9월 3일

Full Stack Architecture :

。REST API를 구축해두면, Flexibility가 확보되어 REST API의 Business logic을 각각의 Application( React App, Mobile App... etc)에 중복하여 구현할 필요가 없이 쉽게 재사용하여 구축이 가능.

Full-Stack Application :

  • Front-End : ReactJavaScript의 Framework으로 활용하여 Front-End Application 구축.
  • Back-End : Spring Boot를 활용하여 REST API 구축
  • DataBase : H2 DB 사용 후 Postgres DB로 전환.
  • Security : JWT Token을 사용한 Spring Security 활용
    ▶ Spring Boot로 구축된 REST API에 연결되는 React Application을 구축하여 브라우저에서 실행하기.
  • ES : ( ECMAScript ) :
    。ECMA International의 ECMA-262 표준을 따르는 Scirpt 언어
    ▶ JavaScript는 해당 표준을 구현한 Script 언어! ( JavaScript = ECMAScript )
    ▶ interface와 구현한 Class의 관계.

  • Node.js
    V8 JavaScript Engine으로 빌드 된 JavaScript Runtime.
    JavaScript웹 브라우저로부터 독립하여 서버환경 구현
    ▶ JS 코드를 브라우저 없이 기기에서 바로 실행 가능.
    Runtime : 특정 언어가 구동되는 환경

  • npm ( Node Package Manager ) :
    Node.js에서 JS로 개발된 각종 사용가능한 모듈의 package를 설치, 업데이트 , 제거과정을 자동화하면서 관리 할 수 있는 도구.

  • npx ( Node Package Executer )
    JavaScript package를 설치할 필요 없이 직접 실행하게하는 패키지 실행기.

package.json :
JavaScript의 Dependency를 정의하는 파일
Package dependency 정보를 정의할 경우, npm에서 자동으로 다운로드.
▶ Spring의 Maven or Gradle과 유사.
ex ) Maven의 pom.xml에 dependency를 정의할 경우 Maven이 자동으로 다운로드.

npm을 활용하여 라이브러리 설치하기.
。npm을 이용하면 dependency를 직접 다운로드 및 버전관리 할 필요 없이 라이브러리를 package.json파일에 dependency로서 정의하여 자동으로 설치 및 관리.

PowerShell을 실행한 후 cd "생성할폴더경로"를 통해 폴더 경로를 정의 후 npm init을 입력하여 프로젝트 생성.
package.json파일이 생성되며, package 이름 및 각종 설정을 설정한 후 ( 입력하지 않으면 default값이 적용 ) yes를 입력하여 마무리.

。이후 pwd를 눌러 현재 작업디렉토리를 확인한 후 이동 시 package.json이 생성되있음을 확인 가능.

package.jsonVisual Studio Code로 드래그할 경우 내용이 처음 설정한대로 정의되있음을 확인 가능.

  • package.json에 JavaScript Library를 dependency로 추가하는 방법
    jQuery Library를 추가할 경우, PowerShellnpm install jquery를 입력하여 설치될 경우 자동으로 package.json에서 해당 dependency로서 정의됨.
    。또한, 작업디렉토리에 생성된 폴더 node_modules에서 jquery 폴더가 생성되있음을 확인 가능.



    package.jsonjQuery dependency가 추가됨. ( "jquery": "^3.7.1" )
    ▶ JavaScript 코드에서 바로 jQuery를 사용 가능!

React.js :
。Meta(현 Facebook)에서 오픈소스로 공개한 UI( User Interface )를 구축하기 위한 Front-End JavaScript Library
▶ 주로 Web SPA Application 구축에 사용됨.

。동일 성격의 다른 Library로는 Angular, Vue.js가 존재.
React Component의 조합으로 Application을 제작.
React Component의 재사용으로 개발과정을 단순화 및 유지보수가 용이함.

  • SPA ( Single Page Applications ) :
    。하나의 HTML로 구성된 Web Application으로서, 서버에서 필요한 데이터만 비동기로 받아와서 동적으로 현재 화면에 다시 Rendering하는 방식.
    ex) To-do Web Application에서 To-do를 하나 추가할 경우, 전체 페이지가 새로고침되어 변경되는것이 아닌, 페이지에서 변경되는 해당 부분만 새로고침되어 변경됨.

    。Application과 상호작용 할때마다 서버에 요청하며 전체 HTML 화면을 받아오는게 아닌, 화면 렌더링을 Local PC에서 생성하므로 빠르게 화면전환이 가능!

  • React Native
    。Facebook에서 제작한 오픈소스 모바일 application framework.
    。다른 언어를 사용할 필요없이 JavaScript로 IOS와 Android 모바일 Application을 동시에 구축이 가능한 크로스 플랫폼.
    ▶ 기본 Android Application은 Kotlin, Java를 사용하고, IOS는 Obect-CSwift의 언어로 개발하는게 일반적이었음.


  • React의 Background Logic
    Virtual DOM : HTML을 가상으로 만든 UI표현
    • 1. 브라우저에서 Page 로딩 시 React는 해당 Page의 Virtual DOM을 생성하여 메모리에 저장

    • 2. React Component에서 State를 활용해 해당 Virtual DOM을 Update하는 코드를 작성
      。실제 HTML의 DOM을 업데이트하는게 아닌, Virtual DOM을 업데이트.
      。기존 Virtual DOM에서 수정된 버전의 Virtual DOM이 생성됨.
      ▶ 단일 React Component에서 2개의 Virtual DOM이 생성.

    • 3. React가 기존의 Virtual DOM과 수정되어 생성된 Virtual DOM간의 변경사항을 파악하여 HTML DOM과 동기화하여 HTML Page에 반영.
      React ComponentStatus가 조금이라도 변경되면 ReactVirtual DOM으로부터 변경사항을 감지하여 실제 HTML의 DOM와 동기화를 수행.

      DOM ( Document Object Model ) : 문서객체모델 Javascript-DOM
      。Browser가 HTML문서를 해석 시 생성하는 Tree구조의 객체 모델.
      ▶ HTML요소를 동적으로 제어하여 JavaScript 객체처럼 조작할 수 있는 Model
      DOM tree : HTML ElementJavascript객체처럼 사용 시 객체를 트리구조로 표현.

React Component
React로 구축된 Application의 UI를 구성하는 독립으로 재사용가능한 최소 단위.
HTML , CSS , JavaScript를 하나의 최소단위로 묶어서 효율적으로 관리 가능.
▶ 보통 React Application은 Component를 수백 개 포함.

각 Page( = View )는 Menu, Header, Footer, Log-in 등 여러 Page가 존재.
React Application을 JS파일로 모듈화하여 각 Page를 React Component로 분리하여 재사용성을 높임.

。React Application에서 처음 load되는 React Componentsrc/index.js<App /> ( App() Component ).
▶ 다른 React Component를 생성 시 보통 해당 src/app.js에서 구현된 App() Component의 자식 Component으로 생성.

  • React Component 고려사항
    • Class Component보다는 Functional Component + Hook를 사용
    • 각 자식 Component는 목적( Menu, Header, Footer, Log-in )에 따라 각 Module에 구분되어 분리되어야 좋다.
    • React Component의 이름은 항상 대문자로 시작해야한다 : PastelCase
      HTML 태그는 (ex. <div> ) 항상 소문자를 사용.

    • JSX는 닫는 태그가 필수적( </태그> )
    • JSX에서의 최상위 태그는 오직 한개만 허용
    • return (JSX코드) 에서 2줄 이상의 JSX를 반환하는 목적으로 소괄호 사용.
    • <div className="CSS클래스명"> : JSX에서는 태그에 Class 속성 지정 시 className=""로 지정
      HTML에서는 class="" 속성과 동일.
      ▶ 상위태그요소에 CSS 적용 시 내부의 자식태그에도 해당 CSS Styling이 상속되어 적용됨.


  • React Component의 종류
    • Functional Component
      。가장 많이 사용하는 방식.
      useState , useEffect등의 Hook를 사용 가능.
      Hook를 사용하여 State를 구현 가능.

      Hook : Functional Component에서 State를 추가 및 Life-Cycle를 관리할 수 있게 하는 기능.
      React 16.8에서 도입되어 Class Component보다 더 간단하고 직관적인 코드를 작성.

      useNavigate() , useContext()등의 react hook를 사용하여 객체 생성 시 Component 내부 Scope에서 선언.
      Component의 특정함수 내에서 객체를 선언하면 안된다.
    function First(){
      return (
          <div className="First">
            First() Component
          </div>
      )
    }
     const First => (){
                      return (
          <div className="First">
            First() Component
          </div>
      )
                      }

    ▶ 화살표함수로 구현이 가능하다

      • Functional Component에서 자식 Component를 매개변수로 받아서 return하는 방법
        활용 : <Context객체.Provider> , React 보호
        。자식Component를 Component의 { 자식요소 }Object객체로 매개변수로 받아들인다.
      function First(){
        return (
            <div>
              <Parent>
                <Child/>
              </Parent>
            </div>
        )
      }
      function Parent( { children } ){
                 return ( <div> Parent / { children }</div> )
      }
      function Child(){
                 return ( <div>child</div> )
      }

      <Parent><Child/></Parent>에서 Child ComponentParent Component의 자식 Component이므로, Parent Component의 매개변수로 <div>child</div>를 가져올 수 있다.



    • Class Component
      import {Component} from 'react'를 통해 React Framework에서 Component를 import한 후 Class에 상속하여 사용.
      React 16.8 이전에는 State를 사용가능한 장점으로 자주 사용됨.
      ▶ 현재는 Functional Component + Hook로 대체.
    import {Component} from 'react'
    class Second extends Component{
      render() {
        return(
          <div className="Second">
            Second() Component
          </div>
        )
      };
    }


  • React Component의 특징
    • 재사용성 , 독립성
      。기존 Web Application은 MVC 방식으로 각 Model, View, Controller간 의존성이 높아 재사용이 어려운 단점이 존재.
      React ComponentMVCView를 독립적으로 구성하여 재사용이 가능하며, 해당 React Component를 통해 새로운 React Component를 구축 가능.

    • 동적 렌더링
      propsstate를 활용하여 동작.

    • JSX( Javascript XML ) :
      const element = <h1>Hello, world!</h1>;
      React에서 VIEW( UI )를 작성하기 위해 Javascript를 확장한 문법.
      Javascript의 모든 기능이 포함됨.

      Javascript 코드 내부에서 HTML 태그를 작성하여 동적기능 부여
      ▶ React Component가 HTML, JS를 별도 파일로 인위적으로 분리하는게 아닌, 둘다 포함하는 특성을 통해 사용 가능.

      <div className="CSS클래스명"> : JSX에서는 태그에 Class 속성 지정 시 className=""로 지정
      HTML에서의 class="" 속성과 동일.
      ▶ 해당 태그에 CSS 적용 시 내부의 태그에도 해당 CSS Styling이 상속되어 적용됨.

      JSX는 브라우저에서 실행할 수 없으므로 Babel을 통해 Javascript로 변환하여 브라우저에 전달.
      JSX는 닫는 태그( </태그> )가 필수적이고, Functional Component에서 return하는 최상위 태그는 오직 한개만 허용
      ▶ 복수 이상의 태그를 return 하는게 아닌, 단일 태그 내부에 복수 태그를 정의하여 return을 수행.
    return ( 
       // JSX 코드 시작
        <> // 복수의 태그를 단일 태그로 묶어서 return하며 빈태그 <>도 사용가능.
           { true && <div>true</div>} // true이므로 <div>true</div>를 반환.
           <div></div>
           <div></div>
           <FirstComponent/>
        </>
      );

    return ( JSX ) 코드에서 소괄호 ()를 사용하는 이유?
    ▶ 한줄만 작성할 경우 없어도되나, 2줄 이상의 복잡한 JSX를 반환하는 목적.

    JSX 에서는 빈태그 ( <> )도 사용 가능.

    { boolean && JSX코드 }
    Componentreturn ()에서 해당 booleantrue 인 경우, JSX코드를 반환하는 구문.
    ex) return ( { true && <div>true</div>} ) → Page에 true를 반환.

    Babel : Babeljs
    。최신 JavaScript를 브라우저가 이해할 수 있는 Javascript 코드로 변환할 수 있는 compiler.
    JSX에 대해서도 Javascript코드로 변환할 수 있는 기능을 제공.

    。오래된 브라우저에서도 최신버전의 Javascript 또는 JSX를 실행 가능.
    ▶ 브라우저에 대하여 버전별 Javascript의 호환성을 보장.

    JSX 코드로 작성된 내용을 Javascript 내용으로 변환.



  • React Component의 요소
    • View
      。React Component가 실제로 렌더링하는 UI
      JSX 코드로 작성되며 사용자에게 보이는 요소로 구성.

    • Logic
      。React Component가 Data를 처리하고 동작하는 부분
      JSX로 작성하여 event handler , API호출등의 Logic들이 구현.

      onClick : <button onClick={increment}>increment</button>
      JSX에서 <button> 태그의 onClick 이벤트를 통해 Javascript 함수 사용 시 onClick={JS함수명}으로 작성.
      ▶ 기존 HTML에서는 HTML태그에 Javascript 함수 사용 시 onclick="JS함수명()"으로 문자열과 매개변수를 같이 표현해야했으나, JSX에서는 중괄호에 JS함수명만 기입하여 활용.

    • Styling ( CSS )
      CSS를 사용하여 ComponentJSX를 통해 구현된 HTML 태그에 디자인을 적용.

      JSX에 CSS를 적용하는 방법

      • JSX 태그의 style 속성에 정의
        • 태그의 style={스타일링Object객체}에 직접 스타일링 코드를 작성.
        <button style={{backgroundColor:"#00a5ab"}}>속성에 직접명시</button>
        • Object객체로서 스타일링 코드를 작성하여 style={Object객체}으로 적용.
          value는 문자열 형식으로 작성.
          key에서 설정할 스타일명은 기존 CSS의 스타일명에서 -부분 뒤에 있는 문자열을 대문자로 표현.
          ex) font-sizefontSize
        const btnStyle={
          fontSize:"16px",
          backgroundColor:"red"
        }  
        <button style={btnStyle}>style속성에 배열로명시</button>



      • CSS파일을 import하여 className="css클래스명" 정의
        。CSS 코드를 작성하여 JSX 모듈에서 import하여 css코드를 활용. ( import 'CSS경로' )
          /* Counter.css  */
       .countBtn{
        text-align:cetner;
        background-color:green;
        font-size:16px;
        padding : 15px;
        margin:10px;
        color:white;
        border-radius: 30px;
      }
          // test.jsx
      import './Counter.css'
      export default function test(){
          return(
              <button className="countBtn">increment</button>
         )
      }

      JSX모듈에 import한 Counter.css 내부의 .countBtn{스타일링정보}를 통해 button에 스타일링을 적용.



    • State :
      。특정 React Component 내부에서 관리되는 동적데이터
      。과거의 React에서는 Class Component에서만 State를 포함할 수 있었지만, 현재는 Hook 기능을 통해 Function Component에서도 State를 포함할 수 있다.
      ▶ React에서는 useState Hook를 사용하여 상태를 변경 가능.

      React Component instance는 고유의 State를 가지며 여러개의 State를 가질 수 있다.
      React ComponenState 공유가 가능하다.
      Controlled Method : 사용자가 Form element로 입력한 변수 값을 Component State로서 유지 가능.

      Hook : Functional Component에서 State를 추가 및 Life-Cycle를 관리할 수 있게 하는 기능.
      React 16.8에서 도입되어 Class Component보다 더 간단하고 직관적인 코드를 작성.
      • useState(초기값) : Const [ 현재State값, StateUpdate함수 ] = useState(초기값)
        import {useState} from 'react';를 통해 React Framework에서 import.
        ReactHook의 일종으로서, 매개변수로 State초기값을 전달하여 Component의 State를 추가 및 관리 시 사용.
        Functional Component에서도 State를 관리하도록 설정.

        StateUpdate함수(값) : 현재State값을 특정값으로 설정.
        StateUpdate함수(0)으로 선언하여 Component State의 초기화를 수행 가능.

        Functional Component에서 useState 활용 시 함수 내부에서 Component를 지칭하기위해 this를 선언할 필요가 없다.

        현재State값StateUpdate함수를 반환하며 StateUpdate함수현재State값 변경 시 해당 State의 Component가 다시 비동기로 Rendering.
        현재state값은 직접 수정하면 안되고 반드시 StateUpdate함수를 활용하여 수정.
      // useState(초기값)을 통해 구조분해로 [현재State값 , StateUpdate함수] 도출
      const [state,StateFunc] = useState(0) 
        StateFunc(
          state+1 // state는 1로 update.
        ) 

      State값의 변경은 즉시 적용이 아닌 비동기적으로 처리됨.
      ▶ 버튼을 2번 눌러 2번 StateUpdate함수를 호출해도 비동기적으로 처리됨으로 현재state값은 한번만 증가할 수 있다.

      • State를 사용하는 이유와 React 표현 원리
        • StateUpdate함수를 통해 State값을 Update하면 자동으로 React Component (= VIEW)도 Update
          React Component에서 State를 사용하는 경우 State 변경 시 React에 의해 초기 Page loading 시 생성된 Virtual DOMState에 의해 변경되어 생성된 Virtual DOM간의 변경사항을 감지한 후 실제 HTMLDOM과 동기화하면서 React Component를 자동 Update
          ▶ 기존의 HTML elementHTML Page를 표현하는 DOM Tree의 각각의 Node로 표현되며 해당 HTML Element를 Update할 경우, DOM도 함께 Update하는 코드를 작성해야하는 비효율로 코드의 복잡성 증가.


      • useReducer(reducer , 초기값State)
        const [state, dispatch] = useReducer(reducer, initialState);

        useState()처럼 ReactState를 관리하는 Hook
        useState보다 복잡한 State logic을 관리 시 사용.

        reducer : State Update 함수
        (state, action)=>{new State}

        action : 어떤 action을 수행할 지 결정하는 Object.
        type, payload를 포함.

        dispatch : reducer에 Action을 보내는 함수
        action에 해당하는 type을 전달 시 해당 actionstate값이 전달됨.
        dispatch({ type: "ACTION_TYPE", payload : 전달값 })
        • 예제 : Global State 관리
        import React, { createContext, useReducer, useContext } from "react";
        const initialState = { count: 0 };
        const CounterContext = createContext();
                      // State Update 함수
        function reducer(state, action) {
          switch (action.type) {
            case "INCREMENT":
              return { count: state.count + 1 };
            case "DECREMENT":
              return { count: state.count - 1 };
            default:
              return state;
          }
        }
        export function CounterProvider({ children }) {
          const [state, dispatch] = useReducer(reducer, initialState);
             // 자식 Component에게 state와 dispatch를 전달
             // 
          return (
            <CounterContext.Provider value={{ state, dispatch }}>
              {children}
            </CounterContext.Provider>
          );
        }
        export function useCounter() {
          return useContext(CounterContext);
        }

        ContextGlobal State 지정

        import React from "react";
        import { CounterProvider } from "./CounterContext";
        import Counter from "./Counter";
        export default function App() {
          return (
            <CounterProvider>
              <Counter />
            </CounterProvider>
          );
        }

        。Component 위계설정

        import React from "react";
        import { useCounter } from "./CounterContext";
        export default function Counter() {
          const { state, dispatch } = useCounter();
          return (
            <div>
              <h2>Count: {state.count}</h2>
              <button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
              <button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
            </div>
          );
        }

        자식 Component에서 Context를 통해 State의 Update 수행



    • Props : Property :
      React Component 간 데이터를 동적으로 전달하는 Read-only 속성
      One-Way Data Flow : 부모 Component에서 자식 Component로 단일방향 데이터 전달
      읽기전용 : 부모 Component에서 전달되는 props를 자식 Component에서는 수정이 불가능.
      동적전달 : 부모 Component에서 전달된 props가 변경된 경우 자식 Component로 전달된 props도 자동으로 update.

      。단일 React Component는 여러개의 Props를 가질 수 있다.
      ▶ 다수의 Props를 다른 React Component로 전달 가능.

      Props는 부모 Component에서 구현된 function을 자식 Component로 전송할 수 있다
      ▶ 자식 Component는 부모 Component의 함수를 참조하지 못하므로, Property를 통해 전달하여 자식 Component에서 사용이 가능하다.
    function App() { 
      function Func1(){
      	return (
      		<div>Parent Component Function</div>
      	)
      }
      return (
        <div className="App">
          // 부모 Component : Property 전달
          // Component에 props로 전달할 값을 태그 속성으로 작성.
          <PracticingProp property1="lee" property2={Func1}/> 
        </div>
      );
    }
           // 자식 Component : 부모 Component에서 전달한 문자열과 함수를 포함한 Property 수신
    function PracticingProp({ property1, property2 }){ 
     // 부모 Component(= App())에서 전달된 props를 구조분해하여 매개변수로 할당.
      return <div>
        {property1} // lee
        {property2} // <div>Parent Component Function</div>
        </div>
    }

    。부모 Component에서 props 작성 시 <Component명 key1="value1" , .../> 형식으로 자식 Component로 전달.
    ▶ 부모 Component의 Props객체( Object ) 형태로 전달된다. { key1: 'value1', ...}

    。자식 Component에서 function Component명(임의의props이름){ 임의의props이름.key1 }로 매개변수에 부모Component의 객체 Props를 수신.
    구조분해( Deconstruction ) 활용 : function Component명({key1,key2, ...}){ key1 }로 매개변수에 중괄호( { } )를 활용하여 Object객체가 아닌 구조분해된 key:value로 가져오는게 좋다

    • 특정Component.propTypes :
      。React에서 Component가 전달받는 props의 값형식을 정의하기 위해 사용.
      Component의 특정 Props에 들어갈 수 있는 값의 Type을 제한.
      특정Component.propTypes = { props변수명 : PropTypes.제한조건 }Object객체로 설정하여 해당 Component의 Props의 제한조건 설정.

      PropTypes.제한조건 : 해당 프로퍼티를 통해 Component props의 조건을 제한할 수 있다.
      import PropTypes from 'prop-types'로 import 하여 사용.
    import PropTypes from 'prop-types';
    const MyComponent = ({ name, age, isActive }) => {
        return (
            <div>
                <h1>{name}</h1>
                <p>Age: {age}</p>
                <p>Status: {isActive ? 'Active' : 'Inactive'}</p>
            </div>
        );
    };
    // Component property의 제한조건을 추가.
    MyComponent.propTypes = {
        name: PropTypes.string.isRequired,  // 문자열 (필수)
        age: PropTypes.number,              // 숫자 (선택사항)
        isActive: PropTypes.bool            // 불리언 (선택사항)
    };
    • 이후 부모 Component쪽에서 <MyComponent name="문자열" age="문자열2" isActive={true} />로 전달 시 propTypes.number에 의해 오류 발생.

      PropTypes 종류
      PropTypes.string : 문자열
      PropTypes.number : 숫자
      PropTypes.bool : boolean
      PropTypes.array : 배열
      PropTypes.object : 객체
      PropTypes.func : 함수
      PropTypes.any : 모든 타입



    • 특정Component.defaultProps :
      。React에서 Component가 전달받는 props의 초기값을 설정하기위해 사용.
    MyComponent.defaultProps = {
        name: "lee",  
        age: 27,              
        isActive: true          
    };

    ▶ 이후 부모 Component쪽에서 <MyComponent/> 만 전달해도 Props의 초기값이 자동으로 반영.



  • Controlled Component : 활용
    React ComponentState를 이용하여 StateHTML Form element( <input> 등 )의 DOM value를 동기화하여 제어하는 Component.
    。입력 field에 입력한 value가 React State로서 관리되며 value가 변화할때마다 onChange 이벤트를 통해 State도 같이 동기화되어 Update.
    Controlled Component를 설정하면서 사용자가 Form element로 입력한 변수 값을 Component State로서 유지 가능.
    DOM value : HTML태그로서 <input value="값">Form 자체의 value

    。사용자가 입력 field에 입력할때마다 onChange 이벤트로 State를 상시 Update할 수 있다.
    • Controlled Component 선언하는 방법 Input 속성
      。Component의 state<input>onChange , value 속성을 활용해야한다.
      import {useState} from 'react'를 선언하여 State사용.

      const [ 현재State값, StateUpdate함수 ] = useState(초기값)을 선언하여 JSX의 입력 Form element <input value={현재State값}>을 설정하여 Binding.
      <input onChange={함수}>를 설정하여 해당 onChange event를 매개변수로 받는 함수를 구현 및 내부에 StateUpdate함수를 활용해 event객체.target.value를 설정하여 현재State값이 변경되도록 설정.
      event객체.target : onChange 이벤트를 수행한 <input>의 JS객체를 지시.
    // Contorlled Component로 설정하기위해 State를 선언 및 Form element와 Binding을 수행.
    // [현재State값, StateUpdate함수] = useState(초기값)
    const [ username, setUsername ] = useState("wjdtn");
    // 현재State값을 <input>의 onChange event로 trigger되어 변경하는 함수 생성.
    function changeUsername(evt){
      // StateUpdate함수로 state값 설정.
      // evt.target.value : `onChange` 이벤트를 수행한 `<input>`의 value
      setUsername(evt.target.value)
      console.log(evt.target.value)
    }
    return (
        {/*  State와 Form Element를 Binding 하기위해 Form element의 value와 onChange 이벤트를 State와 관련된 기능으로 구현.  */}
        <input type="text" value={username} onChange={changeUsername} />
    )


    onChange 이벤트를 통해 입력 field의 값을 변경할때마다 State도 동기화되어 즉각 Update가 수행됨.

React Context : Context 활용
。 React에서 Context API를 통해 Component Tree 전체에 대하여 Global State를 전달
React Component들이 props를 전달하지 않아도 데이터를 공유할 수 있게하는 역할을 수행.
Props Drilling 방지

Props Drilling : 중간 Component가 불필요한 props 전달.

ContextGlobal State를 통해 React ApplicationLogin State 유지 등의 작업을 수행 가능.

Context 관련 기능
import { Context관련기능 } from 'react'

  • createContext() :
    export const Context객체 = createContext();
    。React에서 Global State를 관리할때 사용하는 Context 객체를 생성하는 Hook
    ▶ 해당 Context 객체는 Component Tree 전체에 Global State를 전달하기 위한 Context API가 구현됨.
    ▶ 다른 Component에서 Context를 참조하기위해 Context객체 앞에 export를 선언.

  • <Context객체.Provider> :
    Global State를 제공
    • value={{ State변수 }} :
      Context를 통해 데이터를 Component Tree로 전달하는 역할을 수행.
      value 속성에 전달할 데이터를 정의하면 모든 자식 Component가 데이터에 전역적으로 접근할 수 있음.

      value 속성 설정 시 다른 Component에서 useContext(전달된Context객체)를 통해 새로운 Context객체 생성 시 { State변수 : State변수값 } 으로 생성됨.


  • useContext(전달된Context객체) :
    Context의 데이터를 직접 가져오는 역할의 React Hook
    ▶ 다른 JS파일로부터 export된 Context객체를 매개변수로 전달받아 새로운 Context객체를 생성.

    。 새로 생성된 Context객체<기존Context객체.Provider value={{State변수}}>를 통해 전달된 State값을 Object객체 형식으로 포함. ( { State변수 : State변수값 }
    ▶ 해당 방식으로 모든 자식 Component에서 Context의 데이터를 전역 데이터로서 접근 가능.

    Context를 전달 시 props를 활용한 전달방식을 사용하지 않아도된다.
  • React에서 Routing
    。Client가 브라우저에서 URL을 전달 시 해당 URL에 Mapping된 Component를 Rendering하는 기능을 의미.
    SPA상에서는 페이지를 새로고침하지 않고도 페이지 전환처럼 보이게 할 수 있다.
    • React Router DOM :
      。React Application에서 URL에 따라 Component를 Rendering하는 Library
      Client-side Routing으로 페이지 새로고침 없이 빠르게 페이지 전환.
      。URL 경로에 따라 Component를 Rendering.
      History API를 활용해 브라우저의 뒤로가기 / 앞으로가기 지원

      PowerShell 실행 후 React SPA Application이 존재하는 경로로 설정 후 npm install react-router-dom을 입력하여 dependency를 설치하여 활용.

      React SPA Application 경로의 package.json에 다음처럼 "react-router-dom": "^7.2.0" dependency가 구현.

useEffect(콜백함수, [ Dependency List ]) : API 호출 시 활용 사례
import { useEffect } from 'react'
Functional Component에서 Side effect를 처리하는데 사용하는 React Hook
▶ Component의 Rendering 후 실행되는 코드를 정의 시 사용.

API 호출 , Event Listener 추가, Timer 설정 , State 변화 감지 등 작업에 활용.

의존성배열 : Dependency List :
useEffect의 2번째 인자에 선언.

정의안하는경우 : 모든 Rendering 시 마다 콜백함수 실행.
▶ 해당 API 호출 등의 작업을 Rendering할때마다 반복하므로 무한으로 사용.
빈 배열 [] : React Component가 처음 렌더링 시 한번만 콜백함수 실행.
[ 특정 변수 ] : 특정 변수가 변경시마다 콜백함수 실행.

useRef() :
React Hook 종류 중 하나로서, Component의 Rendering과 무관하게 값 유지 및 DOM Element를 직접 참조 시 사용됨.
▶ 값을 저장하는 역할을 수행하지만, 값을 변경해도 Component는 Re-Rendering하지 않는다.

npm 명령어
PowerShell에서 React Application의 Directory 경로에서 실행
。주로 React, Vue , Next.js등의 Framework에서 활용됨.

  • npm start :
    Node.js에서 개발모드로 Application을 실행.
    。개발 모드로 실행시, 변경사항을 바로 적용하여 Feedback을 확인 가능.

  • npm test :

    。watch (감시) mode에서 Unit Test를 실행.

  • npm run build :
    Node.js에서 src/에 존재하는 소스코드( Javascript, Typescript, CSS 등 )를 압축(minified) 및 최적화하여 배포(Production Deployable) 가능한 상태로 build하여 package 생성.
    ▶ React Application 개발 시 사용
    ▶ build 된 경우 dist/ 또는 build/ 폴더에 결과물 생성.



    。디렉토리 내 많은 js , css 파일이 존재하여 배포 될 경우 생성된 build 디렉토리 내 단 3개의 js , css 파일로 생성됨.
    ▶ 해당 3개의 js, css 파일만으로 Application을 배포하여 실행이 가능.

  • npm install :
    Node.js에서 해당 Project에 특정 dependency를 설치.
    ex) npm install --save react-router-dom

Visual Studio Code 명령어

  • Toggle Explorer : Ctrl + B
    。Explorer( 좌측 폴더트리구조 )창을 토글하여 숨기고 코드부분을 확장이 가능.
  • 파일 검색 : Ctrl + P

React Application 생성하기
Create React App
。React SPA Application 생성 시 사용.
Node.js의 최신버전을 사용해야 사용가능.

  • PowerShell에서 package.json, node_modules, package-lock.json이 존재하는 폴더에 경로 설정
    cd "C:\Users\LG\Desktop\Web Programming\FullStack"

  • npx create-react-app Application이름 을 입력하여 React Application 생성
    。해당 경로에 Create React App package를 실제로 설치하지 않았지만, npx를 이용하여 실행이 가능.
    npx ( Node Package Executer ) : JavaScript package를 설치할 필요 없이 직접 실행하게하는 패키지 실행기.


    。다음 내용이 도출되면 성공적으로 React Application이 구축되었으며 경로내에 해당 Application이름의 Directory가 생성.

    cd Application이름경로를 입력하여 디렉토리 내로 경로를 변경 후 npm start 입력 시 생성된 React Applicaton이 실행되며 브라우저에서는 로컬 URL http://localhost:3000/로 연결.


    ctrl + c를 눌러서 Server를 종료할 수 있다.

    Application 생성 중 발생하는 에러

    • ~\Roaming\npm ERR Path Error 발생
      package.json가 설치된 해당 경로를 환경변수로 추가.

    • network read ECONNRESET이 발생할 때 해결 방법
      PowerShell에 다음 구문을 입력 후 재시도.
    npm config set registry http://registry.npmjs.org/


  • Create React App을 통해 생성된 React Application 둘러보기
    Visual Studio Code를 실행한 후 해당 Application의 디렉토리로 경로 설정.
    npm start로 실행 후 코드를 변경하면 npm이 자동으로 Create React App을 통해 Application을 Build하여 브라우저에서 Rendering.
    。 public 폴더 내 index.html 실행 시 실행중인 http://localhost:3000/ Page의 html을 확인 가능.
    ex ) <title>Todo App</title>로 수정 시 해당 Local URL tab의 이름이 변경됨.
    • React Application 구조

      Package.json : dependency를 정의하는 파일
      node_modules : package.json에 의해 정의된 dependency를 npm이 다운로드하여 저장하는 디렉토리.
      index.html , index.js , App.js : React Application을 구현 시 활용되는 파일.
      • public/index.html :
        。초기 HTML Page를 정의하여 React Application 실행 시 웹 브라우저에 먼저 로드됨.
        <div id="root"></div>를 포함하며 해당 태그에 App Component로서 index.js가 호출됨.

      • src/index.js :
        。React Application을 초기화 및 App Component를 렌더링하는 Module( = js파일 ).
        App Component로서 index.html에 의해 호출되어 Application을 동적으로 렌더링
        src/index.css : Application의 VIEW 전체에 대한 Styling 정보를 포함.
      const root = ReactDOM.createRoot(document.getElementById('root')); // index.html의 <div id="root"></div>로 접근.
      root.render(
        <React.StrictMode> // App Component 정의
          <App />  // App.js 의 App() Component를 가져옴.
        </React.StrictMode>
      );

      <App /> :
      src/App.jsApp() Component가 load되는 부분.
      ▶ 해당 태그의 양식은 <Component이름/>으로 작성
      src/App.js 모듈의 App() Component와 자식 Component는 해당 구문을 통해 브라우저에 표현됨.

      • src/App.js:
        App() Component의 코드를 포함하는 Module.
        ▶ Page에 load되는 실제 내용이 포함.

        。다른 Component 생성 시 App Component의 자식으로 생성하는 방식.
        function App() Component의 자식 Component 생성 및 App() Component의 계층구조에 포함!

        src/App.css : 해당 App Component에 대한 Styling 정보를 포함.
        src/App.test.js : 해당 App Component에 대해서 Unit Test를 수행하는 Module.
        ▶ Spring의 경우, src/main/javaproduction code, src/test/javatest code가 따로 분리되어 존재하지만, JavaScript는 production codetest code가 함께 존재
      import logo from './logo.svg';
      import './App.css';
      function App() {
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <p>
                Edit <code>src/App.js</code> and save to reload.
              </p>
              <a
                className="App-link"
                href="https://reactjs.org"
                target="_blank"
                rel="noopener noreferrer">
               My Todo Application Updated
              </a>
            </header>
          </div>
        );
      }
      export default App;

      。해당 Component의 <div>태그 내용 수정 시 다음처럼 수정됨.



  • React Component 생성하기
    App.js Module 내에서 React Component 작성해보기.
    ▶ 차후 Component를 개별의 모듈로 구분.

    。React를 코드 작성 시 JSX 코드로 작성.

    。CSS스타일링 시 Component의 태그는 className="CSS클래스명"으로 설정.
    <div className="App"> 설정 시 src/App.css에 존재하는 .App{css코드} 적용
    ▶ 해당 태그에 CSS 적용 시 내부의 태그에도 해당 CSS Styling이 상속되어 적용됨.

    ▶ 이때 Compile 시 발생하는 오류는 Node.jsnpm start를 실행한 콘솔창에 지시됨.

    App.js 내부에서 App()Component의 자식 Component 생성 및 표현
    。생성할 React Component는 function App() Component의 자식 Component.

    • 자식 Component : 함수형 Component function 함수명(){내용} 생성
      。함수형 Component 구축 시, function을 선언하여 함수형식으로 작성.
      。Component의 return(html태그요소)은 소괄호 내부에 html태그요소를 작성하여 해당 Component의 일부로서 return.
    // App() Component의 자식 Function Component
    // Component 생성 시 function으로 선언.
    function First(){
      return (
          <div>
            First() Component
          </div>
      )
    }
    • 자식 Component : Class형 Compoennt Class 클래스명 extends Component {내용}
      React FrameworkComponent를 상속하는 Class 형식으로 작성.
      import {Component} from 'react'를 통해 Component를 import.

      。Method에 return(html태그요소)를 구현하여 html태그요소App() Component에 return.
      render method : Component의 일부로 Page에 표현할 내용을 return.

      ▶ 현재는 함수형 Component + Class형 Component 로 대체됨
    import {Component} from 'react'
    // App() Component의 자식 Class Component
    // React Framework의 Component를 상속.
    // Rendering Method를 통해 태그요소를 return.
    class Second extends Component{
      render() {
        return(
          <div>
            Second() Component
          </div>
        )
      };
    }
    • App() Component
      。자식 Component를 VIEW에 표현하려면 App() Component의 계층 구조에 포함되어야함.
      App() Component의 <div></div> 내부에 <자식Component이름/> 선언.

      <div className="App"> 설정 시 src/App.css에 존재하는 .App{css코드} 적용
      ▶ 상위태그요소에 CSS 적용 시 내부의 자식태그( <First/>, <Second/> )에도 해당 CSS Styling이 상속되어 적용됨.
      ▶ CSS Styling 지정 시 HTML태그에 스타일링을 지정해야하며, 자식 Component는 CSS Styling이 적용이 안된다
    // App() Component
    import './App.css';
    function App() { 
      return (
        <div className="App">
          App() Component
          <First/>
          <Second/>
        </div>
      );
    }
    import './App.css';
    import {Component} from 'react'
    // App() Component
    function App() { 
      return (
        <div className="App">
          App() Component
          <First/>
          <Second/>
        </div>
      );
    }
    // App() Component의 자식 Function Component
    // Component 생성 시 function으로 선언.
    function First(){
      return (
          <div>
            First() Component
          </div>
      )
    }
    // App() Component의 자식 Class Component
    // React Framework의 Component를 상속.
    // Rendering Method를 통해 태그요소를 return.
    class Second extends Component{
      render() {
        return(
          <div>
            Second() Component
          </div>
        )
      };
    }
    export default App;


    。다음처럼 App.jsApp() Component에 자식 Component가 포함되어 Page에 표현됨.

    。이때 각각의 자식 Component를 모두 App().js Module에 작성하는게 아닌, JS Module별로 구분하여 사용하는게 좋다.
    。또한 Class형 Component보다는 Functional Component를 사용하는게 좋다

    React Component 고려사항

    • Class Component보다는 Functional Component + Hook를 사용
    • 각 자식 Component는 목적( Menu, Header, Footer, Log-in )에 따라 각 Module에 구분되어 분리되어야 좋다.
    • React Component의 이름은 항상 대문자로 시작해야한다 : PastelCase
      HTML 태그는 (ex. <div> ) 항상 소문자를 사용.

    • JSX는 닫는 태그가 필수적( </태그> )
    • JSX에서의 최상위 태그는 오직 한개만 허용
    • return (JSX코드) 에서 2줄 이상의 JSX를 반환하는 목적으로 소괄호 사용.
    • <div className="CSS클래스명"> : JSX에서는 태그에 Class 속성 지정 시 className=""로 지정
      HTML에서는 class="" 속성과 동일.
      ▶ 상위태그요소에 CSS 적용 시 내부의 자식태그에도 해당 CSS Styling이 상속되어 적용됨.


  • 경로상 각 Component를 src/components/ 경로 아래에 .jsx파일 생성하여 구분
    。각각의 자식 Component는 목적( Menu, Header, Footer, Log-in )에 따라 각 Module로 구분하여 분리.
    ▶ 분리된 모듈에서 export default Component명을 작성하여 다른 모듈에서 사용가능하도록 설정.
    • React Component 코드를 포함할 .jsx Module 생성 및 App.js에서 활용
      src/components/특정디렉토리 경로 아래에 React Component 코드를 포함한 .jsx파일을 생성.
      export Component명 , import Export한Component명 from '컴포넌트경로' 활용.
     //         src/components/practice1/First.jsx 
     // export를 통해 해당 Component를 다른 모듈에서 import하여 사용가능하도록 설정.
    export default function First(){
      return (
          <div>
            First() Component
          </div>
      )
    }
     //         src/components/practice2/Second.jsx
    // export를 통해 해당 Component를 다른 모듈에서 import하여 사용가능하도록 설정.
    export default function Second(){
      return (
        <div>
          Second() Component
        </div>
      )
    }

    export Component명의 선언하는 위치는 자유.
    export function Component명(){} 또는 function Component명(){} export Component명

    import './App.css';
            // 다른 React Component를 포함하는 모듈에서 Component를 import. 
    import First from './components/practice1/First';
    import Second from './components/practice2/Second';
    // App() Component
    function App() { +
      return (
        <div className="App">
          App() Component
          <First/>
          <Second/>
        </div>
      );
    }
    export default App;

    。이때, Import할 Component의 수가 많아지면 상단의 구문이 매우 복잡하므로, 이를 중계해서 처리하는 .jsx 파일을 하나 더 생성하여 처리하면 깔끔하다.

     // Assemble.jsx
    // Named Import
    import {First, Second} from './practice1/First'
    // Default Import
    import Third from './practice2/Third'
    import Fourth from './practice2/Fourth'
    export default function Assemble(){
      return(
        <div>
          <First/>
          <Second/>
          <Third/>
          <Fourth/>
        </div>
      )
    }
      // App.js
    import './App.css';
    import Assemble from './components/Assemble'
    // App() Component
    function App() { 
      return (
        <div className="App">
          <Assemble/>
        </div>    
      );
    }
    export default App;

    App.js에 import한 Component들을 중간에 Assemble.jsx 모듈을 하나 생성하여 정리하여 깔끔하게 정리.

    • Component의 export , import 활용 례
      Component를 포함하는 Module에서 선언하여 해당 Component를 다른 module에서 사용가능하도록 설정.
      • export default Component명 :
        。모듈에서 기본적으로 export할 Default Component 선언.
        ▶ 다른 모듈에서 참조 시 import Component명 from '모듈경로'로 중괄호 { } 생략.
        // 단일 Component를 Export.
      export default function First(){ 
        return (
            <div>
              First() Component
            </div> 
        )
      }
      • export Component명 :
        。모듈에서 다른 모듈로 복수 이상의 Component를 Export할 경우의 Component에 선언.
        。 동일한 Module 이내에 존재하는 Component를 참조 시에도 해당 Component는 export가 선언되어있어야한다.
        ▶ 다른 모듈에서 받을때는 import { Compnent명, ... } 의 중괄호로 받아서 활용.
       // 복수의 Component를 Export.
      export function First(){ 
        return (
            <div>
              First() Component
            </div>
        )
      }
      export function Second(){
        return (
          <div>
            Second() Component
          </div>
        )
      }

      .

      • import Export한Component명 from '컴포넌트경로' :
        defalut import : import Export한Component명 from '컴포넌트경로'
        。특정모듈의 export default가 선언된 Component를 Export할 경우의 import를 정의.
      import First from './components/practice1/First';
      • named import : import { Component명1, Component명2 , ... } from '모듈경로' :
        。특정 Module의 복수 이상의 Component를 import할 경우 중괄호 ( { } ) 로 수신.
        import에서 중괄호를 사용하지 않는 경우 default import로서 export default가 선언된 Component만 import.
        { Component명 }이 선언된 경우 export default Component를 import하지 못한다.
      import {First, Second} from './components/practice1/First';


    • JSXJS 변수 전달하여 표현하기
      。동적인 값을 표현하기 위한 방법.
      ▶ JS 변수를 중괄호( { } )를 이용해 <div>{변수명}</div>JSX코드 내부에서 표현.
    import './App.css';
    const kv = {
      name:'KF16',
      address:{
        address1:'강원도 원주시'
      },
      properties:["naver","kakao","instagram"],
      func : ()=>{
        kv.properties.map((property)=>{
          console.log(property)
        })
      }
    }
    function App() { 
      return (
        <>
          <div className="App">
            {kv.name}
          </div>
          <div>
            {kv.address.address1},
            {kv.func()}
          </div> 
        </>   
      );
    }
    export default App;

    const kv라는 key:value 배열을 생성 후, JSX코드에서 중괄호 안에 key의 value를 표현.
    ▶ JavaScript의 key:value 배열은 JSON Format과 유사.
    key:value배열은 함수를 포함할 수 있다.

    Javascript 배열 메소드

    • 배열.map(람다식) :
      。람다식의 매개변수를 배열의 요소로 받은 후 순회하면서 람다식에서 구현된 기능을 수행.
      ▶ 실무에서 자주사용.
    const kv = {
      properties:["naver","kakao","instagram"],
      func : kv.properties.map((property)=>{
        return property;
      })
    }
    kv.func
  • 리액트 실습 : CSSState를 활용하여 카운터 Application 구축
    css 코드를 import하여 className="CSS클래스명"으로 CSS 스타일링 적용.
.countBtn{
  text-align:cetner;
  background-color:green;
  font-size:16px;
  padding : 15px;
  margin:10px;
  color:white;
  border-radius: 30px;
  border-color:black;
  border-width:5px;
}
.count{
  font-size:150px;
  padding:20px;
}

onClick : <button onClick={increment}>increment</button>
JSX에서 <button> 태그의 onClick 이벤트를 통해 Javascript 함수 사용 시 onClick={JS함수명}으로 작성.
▶ 기존 HTML에서는 HTML태그에 Javascript 함수 사용 시 onclick="JS함수명()"으로 문자열과 매개변수를 같이 표현해야했으나, JSX에서는 중괄호에 JS함수명만 기입하여 활용.

State : Hook( = useState )를 활용한 Functional Component에서 상태( = 데이터 )를 저장.
useState(초기값)을 통해 반환되는 현재State값StateUpdate함수에서 StateUpdate함수로 State 변경 시 해당 State의 Component가 다시 Rendering
현재state값은 직접 수정하면 안되고 반드시 StateUpdate함수를 활용하여 수정.

import './Counter.css'
import {useState} from 'react'
export default function Counter(){
    // useState(초기값)을 통해 구조분해로 [현재State값 , StateUpdate함수] 도출
  const [state,StateFunc] = useState(0)
  function increment(){
    StateFunc(
      state+1
    )
  } ;
  function decrement(){
    StateFunc(
      state-1
    )
  }
  return (
    <div className="Counter">
      <div className="count">{state}</div>
      <button className="countBtn" onClick={increment}>increment</button>
      <button className="countBtn" onClick={decrement}>decrement</button>
    </div>
  )
}                        

JSX에 변수는 중괄호 { }로 반영.
현재State값의 변경은 즉시 적용이 아닌 비동기적으로 처리됨.

    • React Component instance는 고유의 State를 갖는다
      。동일한 Component를 instance로서 2개 구현 시 독립적인 State값을 가진다.
     function App() { 
      return (
        <div className="App">
          <Counter/>
          <Counter/> // 자식 Component의 instance를 2개 구현
        </div>
      );
    }
    export default App;



  • Props 활용하여 각각의 Component Instance의 증감값을 다르게 설정.
    Props : React Component 간 데이터를 동적으로 전달하는 Read-only 속성
    。부모 Component에서 props 작성 시 <Component명 key1="value1" , .../> 형식으로 자식 Component로 전달.
    ▶ 부모 Component의 Props객체( Object ) 형태로 전달된다. { key1: 'value1', ...}

    。자식 Component에서 function Component명(임의의props이름){ 임의의props이름.key1 }로 매개변수에 부모Component의 객체 Props를 수신.
    구조분해( Deconstruction ) 활용 : function Component명({key1,key2, ...}){ key1 }로 매개변수에 중괄호( { } )를 활용하여 Object객체가 아닌 구조분해된 key:value로 가져오는게 좋다.
import './App.css';
import Counter from "./components/counter/Counter"
function App() { 
  return (
    <div className="App">
      <Counter number={1}/>
      <Counter number={2}/>
      <Counter number={3}/>
    </div>
  );
}
export default App;

。부모 Component( = App() )에서 각각의 자식 Component instance( = Counter() )에 재각각 다른 Props 값을 전달.

// Counter.js
 import './Counter.css'
import {useState} from 'react'
    // 부모 Component에서 전달한 Property를 매개변수에 구조분해하여 수신.
export default function Counter({number}){
    // useState(초기값)을 통해 구조분해로 [현재State값 , StateUpdate함수] 도출
  const [state,StateFunc] = useState(0)
  function increment(){
    StateFunc(
      state+number
    )
  } ;
  function decrement(){
    StateFunc(
      state-number
    )
  }
  return (
    <div className="Counter">
      <div className="count">{state}</div>
      <button className="countBtn" onClick={increment}>{"+"+String(number)}</button>
      <button className="countBtn" onClick={decrement}>{"-"+String(number)}</button>
    </div>
  )
}

。부모 Component에서 전달한 propsnumber를 매개변수로 구조분해를 통해 전달 받은 후 증감값에 반영

  • 。부모 Component의 Propsnumber의 값을 각각 다르게 설정하여 동일한 자식 Component로 전달하여 각각의 instance 생성.
    동적으로 Props값 전달 시 각각의 Component instance의 증감값이 다르게 반영됨.

    。 각각의 Component instance의 State는 고유하므로, 현재State값이 각각 독립적으로 저장됨.
    PropsRead-only이므로, 부모 Component에서 전달된 Props의 값을 자식 Component에서 수정이 불가능.
    • Props
      React Component 간 데이터를 동적으로 전달하는 Read-only 속성
      One-Way Data Flow : 부모 Component에서 자식 Component로 단일방향으로 데이터 전달.
      읽기전용 : 부모 Component에서 전달된 props를 자식 Component에서는 수정이 불가능.
      동적전달 : 부모 Component에서 전달된 props가 변경된 경우 자식 Component로 전달된 props도 자동으로 update.


  • Component의 Props에 들어갈 수 있는 값 형식 제한 / 초기값 설정
    특정Component.propTypes :
    。React에서 Component가 전달받는 props의 값형식을 정의하기 위해 사용.
    Component의 특정 Props에 들어갈 수 있는 값의 Type을 제한.
    특정Component.propTypes = { props변수명 : PropTypes.제한조건 }Object객체로 설정하여 해당 Component의 Props의 제한조건 설정.

    PropTypes.제한조건 : 해당 프로퍼티를 통해 Component props의 조건을 제한할 수 있다.
    import PropTypes from 'prop-types'로 import 하여 사용.

    특정Component.defaultProps :
    。React에서 Component가 전달받는 props의 초기값을 설정.
    ▶ 부모 Component에서 props를 정의 안해도 자식 Component에서 props 초기값이 자동으로 적용.
import {useState} from 'react'
import PropTypes from "prop-types" // PropTypes import
export default function Counter({num}){
  const [state,StateFunc] = useState(0)
  function increment(){
    StateFunc(
      state+num
    )
  } ;
  function decrement(){
    StateFunc(
      state-num
    )
  }
  return (
    <div className="Counter">
      <div className="count">{state}</div>
      <button className="countBtn" onClick={increment}>{"+"+String(num)}</button>
      <button className="countBtn" onClick={decrement}>{"-"+String(num)}</button>
    </div>
  )
}
// Counter Component의 number props의 값형식을 숫자형으로 제한
Counter.propTypes = {
  num:PropTypes.number.isRequired,
}
// Counter Component의 props의 초기값 설정
Counter.defaultProps = {
  num : 1,
};

▶ 이후 App() Component에서 <Counter number="문자열"/> 전달시 오류 발생.

  • Component를 계층적 구조로 표현 및 전체 카운트를 재는 Component를 추가
    。Component의 위계를 App() - TotalCounter() - Counter()로 설정.
    。자식 Component에서는 부모 Component의 field를 참조할 수 없다.
    ▶ 부모 Component에서 Props를 통해 자식 Component로 Function을 전달.
    <자식Component 함수명={함수명}>
     // App.jsx
import './App.css';
import TotalCounter from "./components/counter/Counter"
function App() { 
  return (
    <div className="App">
      <TotalCounter/>
    </div>
  );
}
export default App;
     // Counter.jsx
import './Counter.css'
import {useState} from 'react'
export default function TotalCounter() {
  // TotalCounter 고유의 State
  const [totalstate,totalFunc] = useState(0);
  function TotalCount(num){
    totalFunc(totalstate+num)
  }
  return ( // 부모Component의 함수를 property로서서 자식Component 전달
      <>   
        <div style={{fontSize:"100px",color:"blue"}}>{totalstate}</div>
        <Counter num={1} TotalCount={TotalCount}/> 
        <Counter num={2} TotalCount={TotalCount}/>
        <Counter num={3} TotalCount={TotalCount}/>
      </>
    );
  };
  // 부모 Component로부터 증감값과 함수를 포함한 Property를 매개변수에 구조분해하여 전달
function Counter({num, TotalCount}){
  // Counter의 고유의의 State
  function increment(){
    TotalCount(num) // 부모 Component State값 감소
  } ;
  function decrement(){
    TotalCount(-num) // 부모 Component State값 감소소
  }
  return (
    <div className="Counter">
      <button className="countBtn" onClick={increment}>{"+"+String(num)}</button>
      <button className="countBtn" onClick={decrement}>{"-"+String(num)}</button>
    </div>
  )
};

。자식 Component( = Counter() )의 State는 필요 없으므로 관련 구문은 모두 삭제.
。부모 Component ( = TotalCounter() )의 함수 TotalCount(num)property를 이용해 함수명={함수명}을 입력하여 자식 Component로 전송.
。 자식 Component ( = Counter() )에서 해당 함수와 증감값을 property로서 매개변수에 구조분해하여 수신하여 활용.
▶ 자식 Component에서 부모 Component의 함수를 사용하면서 부모 Component의 State값이 변화.
。최상단 부모 Component( = App() )은 자식Component ( = TotalCounter() )에서 Return한 VIEW를 표현.

  • 부모 (TotalCounter()) Component의 State값 ( totalstate )이 자식 ( Counter() ) Component에서 자식 Component에서 property로 전달한 부모 Component의 함수 ( TotalCount(num) )이 동작하면서 변화.
    • Chrome : React Developer Tools
      Chrome에서 React Component의 계층구조를 확인하는 목적으로 사용.
      。각 Component의 PropsState를 확인 및 편집이 가능.

      검사-Components 탭에서 다음 사항들을 확인할 수 있다.
      !
      。Rendering된 React Component의 계층정보를 확인 가능.
      。추가적으로 propsstate 정보를 확인 할 수 있으며 해당 값을 편집 가능.
      ▶ 해당 사진은 props로서 부모 Component로 부터 props로 전달받은 증감값(num)과 함수(TotalCount())를 지시.

      react-dom@19.0.0( Virtual DOM ) ▶ createRoot()AppTotalCounterCounter 순으로 렌더링 되고있음을 관측 가능.


  • Component의 현재State값을 0으로 초기화하기
    StateUpdate함수(0)을 선언 시 0으로 초기화가 가능.
import './Counter.css'
import {useState} from 'react'
import Counter from "./Counter2"
export default function TotalCounter() {
  // TotalCounter 고유의 State
  const [totalstate,totalFunc] = useState(0);
  function TotalCount(num){
    totalFunc(totalstate+num)
  }
  function init(){
    // 현재 state값을 0으로 설정
    totalFunc(0) 
  }
  // 부모Component의 함수를 property로서서 자식Component 전달
  return ( 
      <>   
        <div style={{fontSize:"100px",color:"blue"}}>{totalstate}</div>
        <Counter num={1} TotalCount={TotalCount}/> 
        <Counter num={2} TotalCount={TotalCount}/>
        <Counter num={3} TotalCount={TotalCount}/>
        <button className="count" onClick={init}>init</button>
      </>
    );
  };

。 초기화 함수 init()을 구현 후 내부에 StateUpdate함수 totalFunc(0) 선언.
▶ 이후 <button onClick={init}>을 통해 버튼을 누를 경우 해당 함수를 작동


  • prop을 통해 전달된 부모 Component의 매개변수를 포함하는 함수를 JSX에서 화살표함수를 활용해 직접 호출
export default function Counter({num, TotalCount}){
		function increment(){
      		TotalCount(num) // 부모 Component State값 감소
    	} ;
	return (
          <button className="countBtn" onClick={increment}>{"+"+String(num)}</button>
    )
}

。기존 자식 Component에서는 부모 Component로부터 property로 전달된 매개변수를 포함하는 함수( = TotalCount(num) )를 사용하기 위해 추가적으로 함수 increment()를 구현하여 안에 포함했어야 했다.
따로 함수를 추가 구현할 필요없이 JSX에서 직접 사용하기 위해서는 화살표함수를 사용.
onClick={()=>함수명(매개변수)}

export default function Counter({num, TotalCount}){
          return ( 
          <button className="countBtn" onClick={()=>TotalCount(num)}>{"+"+String(num)}</button>
    ) 		
}

JSX코드에서 onClick에 직접 람다식을 통해 매개변수가 포함된 함수를 추가.

export default function Counter({num, TotalCount}){
    return (
      <div className="Counter">
        <button className="countBtn" onClick={()=>TotalCount(num)}>{"+"+String(num)}</button>
        <button className="countBtn" onClick={()=>TotalCount(-num)}>{"-"+String(num)}</button>
      </div>
    )
}; 

부모Component{children}을 전달하고 매개변수로 { children }를 전달받는 이유?
React Component Tree에서 부모Component 내부에 포함된 다른 하위 Component의 Rendering을 수행하기 위한 목적.

children : React에서 Component의 JSX 내부에 포함된 요소를 의미하는 props

const MapComponent = ({ children }) => {
    return (
        <div id="map" style={{ width: '100%', height: '500px' }}>
            {children} {/* App() Component의 <button>이 여기 렌더링됨 */}
        </div>
    );
};
const App = () => {
    return (
        <MapComponent>
            <button style={{ position: 'absolute', top: 10, left: 10 }}>Zoom In</button>
        </MapComponent>
    );
};

MapComponent 의 내부에 버튼이 추가.

MapComponent{children}App Component<button>으로 설정되어 버튼이 추가됨.
▶ 원하는 UI요소를 children을 통해 전달 가능.

MapComponent 내부에 Context객체.Provider를 활용 시 하위 Component들이 ProviderContext로 전달하는 객체에 쉽게 접근 가능.

 const MapComponent = ({ children }) => {
    const [mapObj, setMapObj] = useState(null);
    useEffect(() => {
        const map = new OlMap({
            target: 'map',
            layers: [new TileLayer({ source: new OSM() })],
            view: new View({ center: fromLonLat([127.0, 37.0]), zoom: 10 }),
        });
        setMapObj(map);
        return () => map.setTarget(null);
    }, []);
    return (
        <MapContext.Provider value={{ mapObj }}>
            <div id="map" style={{ width: '100%', height: '500px' }}>{children}</div>
        </MapContext.Provider>
    );
};
const Marker = () => {
    const { mapObj } = useContext(MapContext);
    useEffect(() => {
        if (mapObj) {
            const marker = new Feature(new Point(fromLonLat([127.0, 37.0])));
            const vectorLayer = new VectorLayer({ source: new VectorSource({ features: [marker] }) });
            mapObj.addLayer(vectorLayer);
        }
    }, [mapObj]);
    return null; // JSX로 아무것도 렌더링하지 않음 (맵에만 마커 추가)
};
const App = () => {
    return (
        <MapComponent>
            <Marker />
        </MapComponent>
    );
};

Map instance가 구현 및 Context로 전달되는 Component 내부에서 <Context.Provider>를 통해 Map instance를 하위 Component로 전달 시 하위 Component에서 Map instance에 접근하여 Map instanceglobal state로서 공유 및 제어 가능.
참고

React & Openlayers 연동
npm install ol을 통해 OpenLayers Dependency를 정의

import React ,{useContext} from 'react'
export const MapContext = React.createContext({});
export const useMapContext = ()=>{
  return useContext(MapContext)
}

OpenlayersMap 객체를 Global State로서 관리하기 위한 Context 생성.

import React, { useEffect,useState, useReducer} from 'react';
import 'ol/ol.css';
import { Map, View } from 'ol';
import { Tile } from 'ol/layer';
import { XYZ } from 'ol/source'
import '../css/all4runner.css'
import { MapContext } from '../Context/MapContext'
import { Graticule ,Vector as VectorLayer} from 'ol/layer'
const MapComponent = ({children})=>{
  const [map,setMap]= useState()
  // state update 함수 => useReducer 적용용도
  const reducer = (state,action)=>{
    switch(action.type){
      case "returnmap":
        console.log(map)
        return map
        // graticule 설정정
      case "setgraticule":
        const gridMapLayer = new Graticule({
          map:map,
          showLabels:true,
          zIndex:4
        });
      default:
        return undefined;
    }
  }
  // useReducer State 비동기 해결용용
  const [mapstate, mapdispatch]=useReducer(reducer,undefined)
  // 구조분해로 받기
  useEffect(() => {
    const Mapinstance = new Map({
      target: 'map',  // 하위 요소 중 id 가 map 인 element가 있어야함.
      layers: [
          new Tile({
            source : new XYZ({ 
              url : "https://api.vworld.kr/req/wmts/1.0.0/45C98863-0CE9-39F9-881C-ED4E6DB5B3EE/Base/{z}/{y}/{x}.png" }),
            zIndex:0
        })
      ],
      view: new View({
        projection : 'EPSG:4326',
        center: [127.05994054600626, 37.58400223316397],  // 맵의 초기 위치
        zoom: 10,  // 초기 줌 레벨
      }),
    })
    setMap(Mapinstance)
    mapdispatch({type:"setgraticule"})
    return () => Mapinstance.setTarget(undefined)
  }, []);
  return (
  <MapContext.Provider value={{mapstate,mapdispatch}}>
    {children}
  </MapContext.Provider>
  )};
export default MapComponent;

useState()를 활용한 비동기적 State 관리가 아닌, useReducer() 함수를 이용해 동기적으로 map 객체를 Context로 전달하는 역할을 수행.
useState()가 비동기적으로 동작함으로 인해 발생됐던 문제들을 해결.
(ex. 자식 Component쪽에서는 undefined로 전달받는다.)
▶ 본인 시도에서 useEffect()등의 Hook로 비동기 문제를 해결하려고 했음에도 해결이 힘듬.

useReducer()를 통해 MapComponent에서만 사용될 StateContext를 통해 하위 Component에 대해서만 Global State가 작용하여 전역적으로 관리를 수행.

profile
공부기록 블로그

0개의 댓글