<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>i_like_monday.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 04 Jan 2024 04:54:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>i_like_monday.log</title>
            <url>https://velog.velcdn.com/images/i_like_monday/profile/1320c6a8-fd9e-4d53-9414-e30a56412544/social_profile.gif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. i_like_monday.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/i_like_monday" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[jdk 버전이 다른 jar 파일을 실행할 때]]></title>
            <link>https://velog.io/@i_like_monday/jdk-%EB%B2%84%EC%A0%84%EC%9D%B4-%EB%8B%A4%EB%A5%B8-jar-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%8B%A4%ED%96%89%ED%95%A0-%EB%95%8C-g8kqfd9x</link>
            <guid>https://velog.io/@i_like_monday/jdk-%EB%B2%84%EC%A0%84%EC%9D%B4-%EB%8B%A4%EB%A5%B8-jar-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%8B%A4%ED%96%89%ED%95%A0-%EB%95%8C-g8kqfd9x</guid>
            <pubDate>Thu, 04 Jan 2024 04:54:12 GMT</pubDate>
            <description><![CDATA[<p>아래와 같이 적용할 jdk 버전이 있는 루트를 JAVA_HOME으로 설정해준다.
일시적으로 바꿔주는 것이므로 cmd창을 닫으면 다시 원래의 버전으로 돌아간다.</p>
<pre><code>set JAVA_HOME=C:\Program Files\Java\jdk-17
set PATH=%JAVA_HOME%\bin;%PATH%
java -version</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[정규표현식 검사(인텔리제이)]]></title>
            <link>https://velog.io/@i_like_monday/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B2%80%EC%82%AC%EC%9D%B8%ED%85%94%EB%A6%AC%EC%A0%9C%EC%9D%B4</link>
            <guid>https://velog.io/@i_like_monday/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EA%B2%80%EC%82%AC%EC%9D%B8%ED%85%94%EB%A6%AC%EC%A0%9C%EC%9D%B4</guid>
            <pubDate>Tue, 17 Oct 2023 00:29:09 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">return number.matches(&quot;[0-9]{3}-{0-9}{4}&quot;);</code></pre>
<p>위와 같이 입력 후 <code>Alt</code> + <code>Enter</code> 을 누르면 RegExp 검사가 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 Maven 프로젝트에서 jar 파일 만들기]]></title>
            <link>https://velog.io/@i_like_monday/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-Maven-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-jar-%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@i_like_monday/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-Maven-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-jar-%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Fri, 02 Jun 2023 07:53:22 GMT</pubDate>
            <description><![CDATA[<ol>
<li>pom.xml 설정<pre><code>아직 모름</code></pre></li>
</ol>
<ol start="2">
<li><p>Maven 탭에서 <code>install</code> 더블 클릭</p>
</li>
<li><p><code>clear</code> 후 install 해야되는 경우도 있음</p>
<ul>
<li><code>Maven Helper</code> 설치해서 이용하면 한번에 가능함</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cordova - 01]]></title>
            <link>https://velog.io/@i_like_monday/Cordova-01</link>
            <guid>https://velog.io/@i_like_monday/Cordova-01</guid>
            <pubDate>Wed, 10 May 2023 00:23:44 GMT</pubDate>
            <description><![CDATA[<h3 id="live-reload">Live Reload</h3>
<p>cordova-plugin-ionic-webview 라는 플러그인을 사용하여 Live Reload 기능을 활성화할 수 있습니다.</p>
<ol>
<li><p>cmd창을 열어서 프로젝트 폴더로 경로 이동 후 플러그인 설치</p>
<pre><code>cordova plugin add cordova-plugin-ionic-webview</code></pre></li>
<li><p>설치 후 라이브 리로드로 run</p>
<pre><code>cordova run android -- --livereload</code></pre></li>
</ol>
<hr>
<h3 id="notificationconfirm-활용법">notification.confirm 활용법</h3>
<ul>
<li><p><a href="https://gist.github.com/alunny/2416865">https://gist.github.com/alunny/2416865</a></p>
<pre><code class="language-javascript">&lt;script&gt;
      // process the confirmation dialog result
      function onConfirm(button) {
          alert(&#39;You selected button &#39; + button);
      }

      // Show a custom confirmation dialog
      //
      function showConfirm() {
          navigator.notification.confirm( &#39;You are the winner!&#39;,  // message
              onConfirm,              // callback to invoke with index of button pressed
              &#39;Game Over&#39;,            // title
              &#39;Restart,Exit&#39;          // buttonLabels
          );
      }

      document.addEventListener(&#39;deviceready&#39;, function (e) {
          showConfirm();
      }, false);
&lt;/script&gt;</code></pre>
</li>
</ul>
<hr>
<h3 id="자바-버전-에러">자바 버전 에러</h3>
<ol>
<li>프로젝트 터미널에서 아래 코드를 입력하면 임시로 자바 버전이 11로 변경됨.<pre><code>$env:JAVA_HOME=&quot;C:\Program Files\jdk-11&quot;</code></pre></li>
<li>그 다음, 터미널에 <code>cordova run android</code> 입력</li>
</ol>
<hr>
<h3 id="브라우저로-live-reload-확인하기">브라우저로 live reload 확인하기</h3>
<ol>
<li>browser 플랫폼 등록<pre><code class="language-cmd">PS C:\gwakProj&gt; cordova platform add browser</code></pre>
</li>
<li>browser run 실행<pre><code class="language-cmd">C:\gwakProj&gt; cordova run browser --live-reload</code></pre>
</li>
</ol>
<hr>
<p>cordova platform add android
cordova build android</p>
<p>// 애뮬레이터 실행
cordova emulate android
// 안드로이드 실행
cordova run android</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Error]]></title>
            <link>https://velog.io/@i_like_monday/React-Error</link>
            <guid>https://velog.io/@i_like_monday/React-Error</guid>
            <pubDate>Wed, 10 May 2023 00:23:08 GMT</pubDate>
            <description><![CDATA[<h3 id="scss-파일을-import-했을때-cannot-find-module-sass-오류">scss 파일을 import 했을때 <code>&#39;cannot find module &#39;sass&#39;</code> 오류</h3>
<p>터미널에서 npm install sass 입력 -&gt; sass를 설치해주면 됨.
<a href="https://dev-guardy.tistory.com/94">https://dev-guardy.tistory.com/94</a></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 07]]></title>
            <link>https://velog.io/@i_like_monday/React-07</link>
            <guid>https://velog.io/@i_like_monday/React-07</guid>
            <pubDate>Wed, 10 May 2023 00:22:56 GMT</pubDate>
            <description><![CDATA[<p><code>App7.jsx + folder5</code></p>
<h2 id="부모컴포넌트의-값을-자식컴포넌트에서-변경하기">부모컴포넌트의 값을 자식컴포넌트에서 변경하기</h2>
<p>TemperatureInput.jsx</p>
<pre><code class="language-js">// TemperatureInput.jsx

import React from &quot;react&quot;;

const scaleNames = {
  c: &quot;섭씨&quot;,
  f: &quot;화씨&quot;,
};

function TemperatureInput(props) {

  // input 태그의 값 변경 이벤트 발생 시 동작할 함수, event 객체를 매개변수로 가져옴
  const handleChange = (e) =&gt; {
    // 부모 컴포넌트에서 전달받은 props 중 키 이름이 onTemperatureChange 인 함수를 실행
    // onTemperatureChange 함수는 부모 컴포넌트의 state 값을 수정하는 함수임(자식 컴포넌트의 데이터 부모 컴포넌트로 전달)
    props.onTemperatureChange(e.target.value);
  };

  return (
    &lt;fieldset&gt;
      &lt;legend&gt;
        온도를 입력해주세요 (단위 : {scaleNames[props.scale]})
      &lt;/legend&gt;
      {/* onChange 는 input 태그의 값 변경시 발생하는 이벤트, 해당 이벤트에 handleChange 라는 함수를 연동 */}
      &lt;input value={props.temperature} onChange={handleChange}/&gt;
    &lt;/fieldset&gt;
  );
}

export default TemperatureInput;</code></pre>
<p>Calculator.jsx</p>
<pre><code class="language-js">// Calculator.jsx
import React, {useState} from &quot;react&quot;;
import TemperatureInput from &quot;./TemperatureInput&quot;;

function BoilingVerdict(props) {
  if (props.celsius &gt;= 100) {
    return &lt;p&gt;물이 끓습니다.&lt;/p&gt;;
  }
  return &lt;p&gt;물이 끓지 않습니다.&lt;/p&gt;;
}

// 매개변수로 화씨 온도를 받아서 섭씨 온도로 변환
function toCelsius(fahrenheit) {
  return ((fahrenheit - 32) * 5) / 9;
}

// 매개변수로 섭씨 온도를 받아서 화씨 온도로 변환
function toFahrenheit(celsius) {
  return (celsius * 9) / 5 + 32;
}

// 매개변수로 현재 온도와 온도 변환을 위한 함수(convert)를 받아서 사용
function tryConvert(temperature, convert) {
  const input = parseFloat(temperature);
  // 입력받은 온도가 숫자인지 아닌지 확인, 숫자가 아니면 빈 문자열 &quot;&quot; 반환
  if (Number.isNaN(input)) {
    return &quot;&quot;;
  }

  // 두번째 매개변수로 받은 변환함수(convert) 를 사용하여 표시 온도를 변환
  const output = convert(input);
  // 수학 관련 함수를 사용하여 소수점 이하 부분 처리(반올림)
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();

}

function Calculator(props) {
  const [temperature, setTemperature] = useState(&#39;&#39;);
  const [scale, setScale] = useState(&#39;C&#39;);

  const handleCelsiusChange = (temperature) =&gt; {
    setTemperature(temperature);
    setScale(&#39;c&#39;);
  };

  const handleFahrenheitChange = (temperature) =&gt; {
    setTemperature(temperature);
    setScale(&#39;f&#39;);
  };

  // 현재 온도를 표시방식에 따라서 변환
  const celsius = scale === &#39;f&#39; ? tryConvert(temperature,  toCelsius) : temperature;
  const fahrenheit = scale === &#39;c&#39; ? tryConvert(temperature,  toFahrenheit) : temperature;

  return (
    &lt;div&gt;
      {/* 자식 컴포넌트 호출 시 props에 2개의 데이터와 state 의 값을 변경할 수 있는 함수를 제공 */}
      &lt;TemperatureInput scale={&#39;c&#39;} temperature={celsius} onTemperatureChange={handleCelsiusChange} /&gt;
      &lt;TemperatureInput scale={&#39;f&#39;} temperature={fahrenheit} onTemperatureChange={handleFahrenheitChange} /&gt;
      {/* 온도를 props 를 통해서 전달, 실수로 표현하기 위해서 parseFloat() 사용 */}
      &lt;BoilingVerdict celsius={parseFloat(celsius)} /&gt;
    &lt;/div&gt;
  );
}

export default Calculator;</code></pre>
<p>App7.jsx</p>
<pre><code class="language-js">import React from &quot;react&quot;;
import Calculator from &quot;./folder5/Calculator&quot;;

function App7() {
  return (
    &lt;div&gt;
     &lt;Calculator /&gt;
    &lt;/div&gt;
  );
}

export default App7;</code></pre>
<ul>
<li>80 일때
<img src="https://velog.velcdn.com/images/i_like_monday/post/c88df0bb-ca24-4c39-ae0c-bc82b0262dbc/image.png" alt=""></li>
<li>100 일때
<img src="https://velog.velcdn.com/images/i_like_monday/post/865be9fb-7d3e-488d-a4fb-2a90f4845c20/image.png" alt=""></li>
</ul>
<hr>
<h2 id="컨텍스트">컨텍스트</h2>
<p>앱(theme 데이터) - 툴바(theme 데이터) - 테마버튼(theme 데이터) - Button(theme 데이터) 으로 값이 전달되어 dark 가 반영이 됨</p>
<h3 id="컨텍스트-사용-x">컨텍스트 사용 X</h3>
<p>App.jsx</p>
<pre><code class="language-js">// folder5/App.jsx

import React from &quot;react&quot;;
import Toolbar from &quot;./Toolbar&quot;;

function App(props) {
  // 자식 컴포넌트에 theme 라는 이름으로 데이터(&quot;dark&quot;)를 전달
  return &lt;Toolbar theme={&quot;dark&quot;}/&gt;
}

export default App;</code></pre>
<p>Toolbar.jsx</p>
<pre><code class="language-js">// folder5/Toolbar.jsx

import React from &quot;react&quot;;
import ThemedButton from &quot;./ThemedButton&quot;;

function Toolbar(props) {
  return (
    &lt;div&gt;
      {/* 자식 컴포넌트로 theme 라는 이름의 데이터를 전달 */}
      &lt;ThemedButton theme={props.theme} /&gt;
    &lt;/div&gt;
  );
}

export default Toolbar;</code></pre>
<p>ThemeButton.jsx</p>
<pre><code class="language-js">// folder5/ThemedButton.jsx

import React from &quot;react&quot;;
const styles = {
  bg: {
    background: &quot;white&quot;,
  }
}

function Button(props) {
  // 부모에게서 전달받은 theme 를 사용하고 있음
  if (props.theme == &quot;dark&quot;) {
    styles.bg.background = &#39;black&#39;;
  }
  else {
    styles.bg.background = &#39;red&#39;;
  }
  return &lt;button style={styles.bg}&gt;테마 적용 버튼&lt;/button&gt;
}

function ThemedButton(props) {
  return (
    // 자식 컴포넌트로 theme 를 전달
    &lt;Button theme={props.theme}/&gt;
  );
}

export default ThemedButton;</code></pre>
<p>App7.jsx</p>
<pre><code class="language-js">import React from &quot;react&quot;;
import Calculator from &quot;./folder5/Calculator&quot;;
import Toolbar from &quot;./folder5/Toolbar&quot;;
import App from &quot;./folder5/App&quot;;

function App7() {
  return (
    &lt;div&gt;
     &lt;Calculator /&gt;
      &lt;App/&gt;
    &lt;/div&gt;
  );
}

export default App7;</code></pre>
<hr>
<h3 id="컨텍스트-사용-o">컨텍스트 사용 O</h3>
<p>App2.jsx</p>
<pre><code class="language-js"></code></pre>
<p>// folder5/ThemeContext.jsx
// folder5/MainContent.jsx
// folder5/DarkOrLight.jsx</p>
<h2 id="리액트-라우터-dom">리액트 라우터 DOM</h2>
<p><a href="https://reactrouter.com/en/main">https://reactrouter.com/en/main</a>
최신버전 : 6.6.1</p>
<ul>
<li>자주 사용하는건 BrowserRouter, Router 
<img src="https://velog.velcdn.com/images/i_like_monday/post/d040df16-d7ee-499c-ae49-b44a22ce022b/image.png" alt=""></li>
<li>터미널에서 라우터 설치
<img src="https://velog.velcdn.com/images/i_like_monday/post/c3f9f57e-15b1-4e05-8771-7457830053b1/image.png" alt=""></li>
</ul>
<p>pages 경로 추가</p>
<p>App7.jsx 에 코드 추가</p>
<pre><code class="language-ㅓjs">import {BrowserRouter, Routes, Route} from &quot;react-router-dom&quot;;</code></pre>
<p>, return 내부 주석처리 후 코드 추가</p>
<pre><code class="language-js">import React from &quot;react&quot;;
import Calculator from &quot;./folder5/Calculator&quot;;
import Toolbar from &quot;./folder5/Toolbar&quot;;
import App from &quot;./folder5/App&quot;;
import App2 from &quot;./folder5/App2&quot;;
import MainContent from &quot;./folder5/MainContent&quot;;
import DarkOrLight from &quot;./folder5/DarkOrLight&quot;;
import App4 from &quot;./folder5/App4&quot;;

import {BrowserRouter, Routes, Route} from &quot;react-router-dom&quot;;

function App7() {
  return (
    // &lt;div&gt;
    //  &lt;Calculator /&gt;
    //   &lt;App/&gt;
    //   &lt;hr/&gt;
    //   &lt;App2/&gt;
    //   &lt;hr/&gt;
    //   &lt;DarkOrLight/&gt;
    //   &lt;App4/&gt;
    // &lt;/div&gt;
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route&gt;

        &lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App7;</code></pre>
<p>Layout.jsx</p>
<p>Home.jsx</p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/d5fd7c71-4e44-4647-818a-c4de652259fa/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 06]]></title>
            <link>https://velog.io/@i_like_monday/React-06</link>
            <guid>https://velog.io/@i_like_monday/React-06</guid>
            <pubDate>Wed, 10 May 2023 00:22:36 GMT</pubDate>
            <description><![CDATA[<p><code>App6.jsx + folder4</code></p>
<blockquote>
<p>공부할때 유용한 사이트 : <a href="https://www.w3schools.com/react/default.asp">https://www.w3schools.com/react/default.asp</a></p>
</blockquote>
<h2 id="조건부-렌더링">조건부 렌더링</h2>
<p>어떠한 조건에 따라서 화면에 표시할 렌더링이 달라지는 것
if 문 사용 시 render() 함수가 동작되는 return 부분에서 조건문을 사용하는 것이 아니라 return <u>밖에서</u> 조건문을 사용하고 return 안에서는 <u>삼항 연산자</u>를 사용함</p>
<p>자바스크립트의 true / false</p>
<ul>
<li>true : 논리형 <code>true</code>, 빈 object 타입 <code>{}</code>, 빈 배열 <code>[]</code>, <code>0이 아닌 숫자</code>, <code>빈 문자열이 아닌 문자열</code></li>
<li>false : 논리형 <code>false</code>, <code>숫자 0 또는 0.0</code>, 빈 문자열 <code>&#39;&#39;</code>, <code>&quot;&quot;</code>, <code>` `</code>, <code>null</code>, <code>undefined</code>, <code>NaN</code>(Not a Number)
Goal.jsx<pre><code class="language-js">// folder4/Goal.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;
import MadeGoal from &quot;./MadeGoal&quot;;
import MissedGoal from &quot;./MissedGoal&quot;;</p>
<p>function Goal(props) {
  const isGoal = props.isGoal;</p>
<p>  // if 문은 return 부분 바깥에서 걸어줘야함. return 안에서 진행하려면 삼항연산자 사용.
  if (isGoal) {
    return <MadeGoal/>
  }</p>
<p>  return <MissedGoal/></p>
<p>}</p>
<p>export default Goal;</p>
<pre><code>
MadeGoal.jsx
```js
// folder4/MadeGoal.jsx

import React from &quot;react&quot;;

function MadeGoal() {
  return (
    &lt;h1&gt;GOAL!!&lt;/h1&gt;
  );
}

export default MadeGoal;</code></pre><p>MissedGoal.jsx</p>
<pre><code class="language-js">// folder4/MissedGoal.jsx

import React from &quot;react&quot;;

function MissedGoal() {
  return (
    &lt;h1&gt;MISSED!!&lt;/h1&gt;
  );
}

export default MissedGoal;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/c82b14dd-7070-4be6-8fa0-926b71cf0bef/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/7e4c8331-cee1-4d17-bbae-7bed7c193005/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/1da9fd43-43bf-4bb9-abf0-5ad67fe5fe1a/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/4402631b-9f6e-4051-a461-ba40bbd0eea0/image.png" alt=""></p>
<hr>
<p>LoginControl.jsx</p>
<pre><code class="language-js">// folder4/LoginControl.jsx

import React, {useState} from &quot;react&quot;;
import button from &quot;bootstrap/js/src/button&quot;;
import Greeting from &quot;./Greeting&quot;;
import UserStatus from &quot;./UserStatus&quot;;


function LoginButton(props) {
  return (
    &lt;button onClick={props.onClick} type={&quot;button&quot;} className={&quot;btn btn-info&quot;}&gt;로그인&lt;/button&gt;
  );
}

function LogoutButton(props) {
  return (
    &lt;button onClick={props.onClick} type={&quot;button&quot;} className={&quot;btn btn-danger&quot;}&gt;로그아웃&lt;/button&gt;
  );
}


function LoginControl() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const handleLoginClick = () =&gt; {
    setIsLoggedIn(true);
  }

  const handleLogoutClick = () =&gt; {
    setIsLoggedIn(false);
  }

  // 엘리먼트 변수 : 자바스크립트 변수에 리액트 컴포넌트를 저장한 것
  // 조건부 렌더링을 사용하기 위해서 자바스크립트 변수 button 에 리액트 컴포넌트를 저장
  let button;

  // state 의 상태에 따라서 엘리먼트 변수에 저장될 리액트 컴포넌트를 변경
  if (isLoggedIn) {
    button = &lt;LoginButton onClick={handleLogoutClick}/&gt;
  }
  else {
    button = &lt;LogoutButton onClick={handleLoginClick}/&gt;
  }

  return (
    &lt;div&gt;
      &lt;Greeting isLoggedIn={isLoggedIn}/&gt;
      {/* 엘리먼트 변수에 저장된 리액트 컴포넌트가 출력됨 */}
      {/*{button}*/}

      {/* 삼항 연산자 */}
      {
        isLoggedIn ? &lt;LogoutButton onClick={handleLogoutClick}/&gt; : &lt;LoginButton onClick={handleLoginClick}/&gt;
      }
      &lt;UserStatus isLoggedIn={isLoggedIn}/&gt;
    &lt;/div&gt;
  );
}

export default LoginControl;</code></pre>
<p>UserStatus.jsx</p>
<pre><code class="language-js">// folder4/UserStatus.jsx

import React from &quot;react&quot;;
// 문제 1) 아래의 소스에서 삼항 연산자를 사용한 부분을 if ~ else 문을 사용하여 조건부 렌더링으로 수정하세요
function UserStatus(props) {

  const isLoggedIn = props.isLoggedIn;

  if (isLoggedIn) {
    return (
      &lt;div&gt;
        이 사용자는 현재 &lt;b&gt;로그인&lt;/b&gt; 상태입니다.
      &lt;/div&gt;
    );
  }
    return (
      &lt;div&gt;
        이 사용자는 현재 &lt;b&gt;로그인하지 않은&lt;/b&gt; 상태입니다.
      &lt;/div&gt;
    );

  // 삼항연산자 사용 코드
  // return (
  // &lt;div&gt;
  //    이 사용자는 현재 &lt;b&gt;{props.isLoggedIn ? &#39;로그인&#39; : &#39;로그인하지 않은&#39;}&lt;/b&gt; 상태입니다.
  // &lt;/div&gt;
  // );
}

export default UserStatus;</code></pre>
<p>Greeting.jsx</p>
<pre><code class="language-js">// folder4/Greeting.jsx

import React from &quot;react&quot;;

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;

  if (isLoggedIn) {
    return &lt;UserGreeting/&gt;
  }
  return &lt;GuestGreeting/&gt;
}

function UserGreeting() {
  return (
    &lt;h1&gt;다시 오셨군요.&lt;/h1&gt;
  );
}

function GuestGreeting() {
  return (
    &lt;h1&gt;회원 가입을 해주세요.&lt;/h1&gt;
  );
}



export default Greeting;</code></pre>
<ul>
<li>클릭 전
<img src="https://velog.velcdn.com/images/i_like_monday/post/17a8bc2d-65eb-49e5-b596-6d4f78c3c1b7/image.png" alt=""></li>
<li>클릭 후
<img src="https://velog.velcdn.com/images/i_like_monday/post/99ad566c-6550-41c7-bf88-0d344037b0c4/image.png" alt=""></li>
</ul>
<hr>
<h3 id="inline-if">inline if, &amp;&amp;</h3>
<p>inLine if : jsx 문법으로 렌더링을 진행하는 render() 함수 내부에서 if 문을 사용할 수 없기 떄문에 &#39;&amp;&amp;&#39; 연산자를 사용하거나 삼항 연산자를 사용하여 조건부 렌더링을 진행할 수 있음
<u>&#39;&amp;&amp;&#39;연산자 사용 시 주의점</u> : &amp;&amp; 연산자 오른쪽에 있는 피연산자는 왼쪽에 있는 피연산자의 값에 따라 렌더링이 결정됨
&amp;&amp; 연산자 왼쪽의 피연산자 값이 false 일 경우 오른쪽의 피연산자는 아예 동작하지 않음.(=연산 평가 자체가 동작하지 않음)
&amp;&amp; 연산자 왼쪽의 피연산자는 false 일 때라도 연산 평가가 이루어지기 때문에 해당 값이 그대로 출력이 됨.</p>
<p>MailBox.jsx</p>
<pre><code class="language-js">// folder4/MailBox.jsx

import React from &quot;react&quot;;

function MailBox(props) {
  const unreadMessages = props.unreadMessages;


  return (
    &lt;div&gt;
      &lt;h1&gt;안녕하세요&lt;/h1&gt;
      &lt;h3&gt;inline if (&amp;&amp; 연산자 테스트)&lt;/h3&gt;
      {
        unreadMessages &gt; 0 &amp;&amp;
        &lt;h2&gt;
          현재 {unreadMessages} 개의 읽지 않은 메시지가 있습니다.
        &lt;/h2&gt;
      }
    &lt;/div&gt;
  );
}

export default MailBox;</code></pre>
<hr>
<h3 id=""></h3>
<p>MainPage.jsx</p>
<pre><code class="language-js">// folder4/MainPage.jsx

import React, {useState} from &quot;react&quot;;
import WarningBanner from &quot;./WarningBanner&quot;;

function MainPage() {
  const [showWarning, setShowWarning] = useState(false);

  const handleToggleClick = () =&gt; {
    setShowWarning(prevState =&gt; !prevState);
  }

  return (
    &lt;div&gt;
      &lt;WarningBanner warning={showWarning}/&gt;
      &lt;button onClick={handleToggleClick} className={&quot;btn btn-success&quot;}&gt;{showWarning ? &#39;감추기&#39; : &#39;보이기&#39;}&lt;/button&gt;
    &lt;/div&gt;
  )
}

export default MainPage;</code></pre>
<p>WarningBanner.jsx</p>
<pre><code class="language-js">// folder4/WarningBanner.jsx

import React from &quot;react&quot;;

/*
컴포넌트 렌더링 막기
- 리액트에서 렌더링을 하고 싶지 않을 경우 null 을 사용
*/
function WarningBanner({warning}) {
  // warning 이 false 일때 if 문 실행됨
  if (!warning) {
    return null;
  }

  return &lt;div&gt;경고!!&lt;/div&gt;
}

export default WarningBanner;</code></pre>
<ul>
<li>클릭 전
<img src="https://velog.velcdn.com/images/i_like_monday/post/be82b6c1-154f-426a-9379-65be2546f29b/image.png" alt=""></li>
<li>클릭 후 
<img src="https://velog.velcdn.com/images/i_like_monday/post/3bf4f801-8eeb-486b-ac23-3cca40b1b1f4/image.png" alt=""></li>
</ul>
<hr>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/284a95b0-d658-4a5f-9385-08490c2de938/image.png" alt=""></p>
<p>자식 1-1 에서 2-2 로 바로 데이터 이동 불가능.
이것을 가능하게 해주는것이 컨텍스트이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[(study)Spring_Thymeleaf]]></title>
            <link>https://velog.io/@i_like_monday/studySpringThymeleaf</link>
            <guid>https://velog.io/@i_like_monday/studySpringThymeleaf</guid>
            <pubDate>Wed, 10 May 2023 00:22:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고자료 : 코딩의 신<br>
!youtube[YWG_huG3Gr0]</p>
</blockquote>
<hr>
<h1 id="1-환경설정">1. 환경설정</h1>
<p>자동 import : Alt + Enter
파라미터로 Model model을 추가해주면 키:값 형식으로 html파일에서 데이터를 받아올 수 있다.</p>
<blockquote>
<h3 id="controller의-required-속성">@Controller의 required 속성</h3>
</blockquote>
<hr>
<p>@RequestParam은 HTTP 요청 파라미터를 컨트롤러 메소드의 파라미터로 전달받을 때 사용된다. JSP에서 request.getParameter(); 와 비슷하다고 볼 수 있다. 이 @RequestParam이 적용된 파라미터는 필수파라미터다. 그래서 @RequestParam 어노테이션에 명시한 HTTP 요청 파라미터가 존재하지 않을 경우, 스프링 MVC는 잘못된 요청을 의미하는 400 응답 코드를 웹 브라우저에 전송하는 것이다.<br></p>
<p>1) 필수가 아닌 파라미터인 경우 required 속성 값을 주어 false로 지정해주면 된다. required 속성 값을 따로 작성안할 경우 기본 값은 true로 지정되어 있다.<br></p>
<pre><code class="language-java">@RequestParam(value=&quot;query&quot;, required=false) String query</code></pre>
<h2 id="필수가-아닌-파라미터의-값이-존재하지-않을-경우는-null-값을-할당한다-하지만-null을-할당할-수-없는-기본-데이터-타입일-경우에는-타입-변환-에러가-발생한다br">필수가 아닌 파라미터의 값이 존재하지 않을 경우는 null 값을 할당한다. 하지만 null을 할당할 수 없는 기본 데이터 타입일 경우에는 타입 변환 에러가 발생한다.<br></h2>
<p>2) RequestParam 값이 Null 일 때, 예외 처리<br>
기본 데이터 타입을 사용할 때는 요청 파라미터가 존재하지 않을 때 기본 값을 할당하는 경우가 많다. 이런 경우, defaultValue 속성을 이용해서 기본 값을 지정할 수 있다.<br></p>
<pre><code class="language-java">@RequestParam(value=&quot;page&quot;, defaultValue=&quot;1&quot;) int page</code></pre>
<p>[출처] spring controller required defaultvalue,  Controller와 RestController차이, RequestParam, RequestBody 차이|작성자 토찌</p>
<hr>
<pre><code class="language-java">@GetMapping(&quot;/greeting&quot;)
public String greeting(@RequestParam(name=&quot;name&quot;, required=false, defaultValue=&quot;World&quot;) String name, Model model) {
    model.addAttribute(&quot;name&quot;, name);
    return &quot;greeting&quot;;
    }
}</code></pre>
<p>html의 타임리프 코드 : <code>th:text=&quot;&#39;Hello, &#39; + ${name} + &#39;!&#39;</code>
여기서 html 주소에 <code>localhost:8080/greeting?name=김길동</code> 을 치면 화면에 <code>Hello, 김길동!</code> 출력됨, defaultValue=&quot;World&quot; 가 설정되어 있기때문에 <code>${name}</code> 값을 주지 않으면
Hello, World! 가 출력됨(null 값이어도 defaultValue가 있기 때문에 에러가 나지 않음)</p>
<hr>
<p>번역 확장프로그램(구글스토어) : <code>Google 번역</code> + <code>네이버 영어사전</code></p>
<p><code>href</code> 속성과 <code>th:href</code> 속성을 동시에 사용하면 html 파일만 구동했을때는 기본 href 속성이 적용된 화면이 나오고, 서버를 구동했을대는 th:href 속성이 적용된 화면이 나온다.</p>
<h3 id="fragment의-3가지-방식">fragment의 3가지 방식</h3>
<ul>
<li>insert</li>
<li>include ← 가장 많이 사용되는 방법</li>
<li>replace</li>
</ul>
<h3 id="지역변수">지역변수</h3>
<p>th:each 태그 활용</p>
<h3 id="속성-우선순위">속성 우선순위</h3>
<p>한 태그에 th: 속성이 여러 개 선언되어있을때 어떤것이 먼저 적용되는지?</p>
<ol>
<li>replace, insert</li>
<li>each</li>
<li>if, unless, switch (조건문)</li>
<li>기타 등등 속성들(if문이 먼ㄴ저</li>
</ol>
<h3 id="thtext-속성-단순화">th:text 속성 단순화</h3>
<p>기존에</p>
<pre><code class="language-html">&lt;p&gt;Hello, &lt;span th:text=&quot;${session.user.name}&quot;&gt;&lt;/span&gt;!&lt;/p&gt;</code></pre>
<p>와 같이 쓰던 th:text 속성은 아래와 같이 간단하게 사용이 가능하다.</p>
<pre><code class="language-html">&lt;p&gt;Hello, [[${session.user.name}]]!&lt;/p&gt;</code></pre>
<p><code>[[...]]</code> 는 th:text를 대신하고, <code>[(...)]</code>는 th:utext를 대신한다.</p>
<h3 id="부트스트랩-적용시-코드-순서">부트스트랩 적용시 코드 순서</h3>
<p><code>&lt;script&gt;</code> 내에 jquery - popper - bootstrap 순서로 코드를 써줘야 한다
스타터 템플릿 참고 : <a href="https://getbootstrap.com/docs/5.2/examples/starter-template/">https://getbootstrap.com/docs/5.2/examples/starter-template/</a></p>
<h3 id="fragments-활용">fragments 활용</h3>
<p>재사용할 nav 코드( <code>th:fragment</code> 사용 ) : <code>th:fragment=&quot;이름설정&quot;</code></p>
<pre><code class="language-html">&lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-dark&quot; th:fragment=&quot;menu&quot;&gt;
  ...
  ...
  ...
&lt;nav&gt;</code></pre>
<p>main이 되는 html에 사용할때( <code>th:replace</code> 사용 ) : <code>th:replace=&quot;fragments/common :: menu&quot;&gt;</code></p>
<pre><code class="language-html">&lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-dark&quot; th:replace=&quot;fragments/common :: menu&quot;&gt;
&lt;/nav&gt;</code></pre>
<ul>
<li>변수와 함께 보내는 방법
common.html<pre><code class="language-html">// th:fragment=h&quot;fragment명(변수명)&quot;
&lt;head th:fragment=&quot;head(title)&quot;&gt;
&lt;meta charset=&quot;utf-8&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
// th:text=${변수명}
&lt;title th:text=&quot;${title}&quot;&gt;Bootstrap demo&lt;/title&gt;
&lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65&quot; crossorigin=&quot;anonymous&quot;&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;/head&gt;</code></pre>
</li>
</ul>
<p>list.html</p>
<pre><code class="language-html">&lt;head th:replace=&quot;fragments/common :: head(&#39;게시판&#39;)&quot;&gt;
&lt;/head&gt;</code></pre>
<h3 id="thclassappend-속성">th:classappend 속성</h3>
<p>조건문이 true일때 원하는 속성을 추가할 수 있음
ex) nav-bar에서 사용자가 클릭한 메뉴에 색상 변화를 줄 수 있음</p>
<p>common.html의 li 태그 내에 <code>th:classappend</code> 로 조건을 줘야하는데 &lt;!!!!-- 그 전에 --!!!!&gt; menu라는 common의 fragment를 호출할때 파라미터를 함께 넘겨주면 됨</p>
<pre><code class="language-html">&lt;index.html&gt;
th:replace=&quot;fragments/common :: menu&quot; → th:fragment=&quot;menu(&#39;home&#39;)&quot;

&lt;list.html&gt;
th:replace=&quot;fragments/common :: menu&quot; → th:fragment=&quot;menu(&#39;board&#39;)&quot;  </code></pre>
<p>common.html</p>
<pre><code class="language-html">// (menu) 로 변수 넣어줌 (이 menu 변수자리에 replace자리에 넣은 변수 적용시켜주면 됨
&lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-dark&quot; th:fragment=&quot;menu(menu)&quot;&gt;</code></pre>
<p>&lt;참고&gt;
강의에서 <code>th:classappend=&quot;${menu} == &#39;home&#39;? &#39;board&#39;&quot;</code> 사용하면 된다고 나오는데 자꾸 오류가 나서 찾아보니 <code>th:classappend=&quot;${menu == &#39;board&#39; ? &#39;active&#39; :&#39;&#39; }&quot;</code> 해줘야 했다. 버전이 달라서 그런듯?</p>
<p>완성 코드 : common.html</p>
<pre><code class="language-html">&lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarColor02&quot;&gt;
      &lt;ul class=&quot;navbar-nav me-auto&quot;&gt;
        &lt;li class=&quot;nav-item&quot;&gt;
          &lt;a class=&quot;nav-link&quot; th:classappend=&quot;${menu == &#39;home&#39; ? &#39;active&#39; :&#39;&#39; }&quot; href=&quot;#&quot; th:href=&quot;@{/}&quot;&gt;홈
            &lt;span class=&quot;visually-hidden&quot; th:if=&quot;${menu} == &#39;home&#39;&quot;&gt;(current)&lt;/span&gt;
          &lt;/a&gt;
        &lt;/li&gt;
        &lt;li class=&quot;nav-item&quot; th:classappend=&quot;${menu == &#39;board&#39; ? &#39;active&#39; : &#39;&#39; }&quot;&gt;
          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot; th:href=&quot;@{/board/list}&quot; th:classappend=&quot;${menu == &#39;board&#39; ? &#39;active&#39; :&#39;&#39; }&quot;&gt;게시판&lt;/a&gt;
          &lt;span class=&quot;visually-hidden&quot; th:if=&quot;${menu} == &#39;board&#39;&quot;&gt;(current)&lt;/span&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/67e33bac-1b41-4b35-bf3e-62cd8319af4c/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/287bdfa4-252b-41a3-a706-1e603979751b/image.png" alt=""></p>
<hr>
<h2 id="jpa를-이용해-게시판-조회하기">JPA를 이용해 게시판 조회하기</h2>
<h3 id="테이블-생성">테이블 생성</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/6c596dbe-ca7a-4764-8af3-b8a9d2e3e648/image.png" alt=""></p>
<h3 id=""></h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git - 커밋 메시지 컨벤션]]></title>
            <link>https://velog.io/@i_like_monday/Git-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%BB%A8%EB%B2%A4%EC%85%98</link>
            <guid>https://velog.io/@i_like_monday/Git-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%BB%A8%EB%B2%A4%EC%85%98</guid>
            <pubDate>Tue, 04 Apr 2023 06:18:12 GMT</pubDate>
            <description><![CDATA[<h1 id="커밋-메시지-컨벤션이란">커밋 메시지 컨벤션이란?</h1>
<p>Git을 사용하여 버전 관리를 할 때, 커밋 메시지의 작성 규칙을 의미.
이 규칙에 따라 작성된 메시지는 프로젝트에 기여하는 사람들이 코드 변경 내용을 이해하고 추적하기 쉬워지며, 이를 통해 팀 내의 협업 효율성이 증가한다.</p>
<p>일반적으로 가장 많이 사용되는 git 메시지 컨벤션은 &quot;Conventional Commits&quot;이다.
이 컨벤션은 commit 메시지를 특정 형식으로 작성하도록 요구하며, 다음과 같이 구성된다.</p>
<pre><code>&lt;type&gt;[optional scope]: &lt;description&gt;

[optional body]

[optional footer(s)]</code></pre><p>위의 구조에서 
<code>&lt;type&gt;</code> 은 커밋의 종류를 나타내며, 
<code>&lt;description&gt;</code> 은 커밋의 간략한 설명이다.
<code>&lt;scope&gt;</code> 는 해당 커밋이 영향을 미친 코드 범위를 나타내는 선택적인 요소이다.
<code>&lt;body&gt;</code> 는 커밋의 자세한 내용을 작성할 수 있는 선택적인 요소이며,
<code>&lt;footer&gt;</code> 는 커밋에 대한 참조 등 추가 정보를 나타내는 선택적인 요소이다.</p>
<p>이러한 <strong>git 메시지 컨벤션</strong>을 따르면 코드 변경 사항을 추적하기 쉽고, 프로젝트에 기여하는 사람들 간에 이해하기 쉬운 공통된 언어를 사용할 수 있다.</p>
<hr>
<h1 id="commit-type">commit type</h1>
<p>Git 메시지 컨벤션에서 사용되는 대표적인 커밋 타입들은 다음과 같다.</p>
<p><code>feat</code>: 새로운 기능을 추가할 때 사용
<code>fix</code>: 버그를 수정할 때 사용
<code>docs</code>: 문서를 수정할 때 사용
<code>style</code>: 코드 포맷팅, 세미콜론 누락 등의 코드 변경이 없이 스타일만 변경했을 때 사용
<code>refactor</code>: 코드 리팩토링을 했을 때 사용
<code>test</code>: 테스트 코드를 추가하거나 수정했을 때 사용
<code>chore</code>: 빌드 업무나 패키지 매니저를 설정할 때 사용
이 외에도 <code>perf</code>(성능 개선), <code>revert</code>(이전 커밋을 되돌릴 때), <code>build</code>(빌드 관련 파일 수정), <code>ci</code>(CI 관련 설정 수정), <code>init</code>(초기화) 등 다양한 타입이 있다.</p>
<p>각 커밋 타입은 대개 _<strong>첫 단어</strong>_로 표시되며, 이를 통해 다른 사용자들이 커밋의 내용을 빠르게 파악할 수 있다. 예를 들어, <code>feat</code> 커밋 타입은 새로운 기능을 추가한 경우에 사용되므로, 다른 사용자들은 해당 커밋이 어떤 기능을 추가했는지 빠르게 파악할 수 있다.</p>
<hr>
<h2 id="예시">예시</h2>
<pre><code>feat: Add new feature to the dashboard page

This commit adds a new feature to the dashboard page that allows users to filter their data by date range. 
The date range filter is implemented using a date picker component from the UI library.

Fixes #123</code></pre><p>위의 예제에서 feat은 커밋의 타입(<code>type</code>)을 나타내며, &quot;Add new feature to the dashboard page&quot;는 커밋의 간단한 설명(<code>description</code>)이다. 
커밋 메시지의 본문(body)에는 해당 변경 사항에 대한 자세한 설명이 포함되어 있다. 또한 커밋 메시지의 마지막 부분인 <strong>Fixes #123</strong>은 해당 커밋이 이슈 #123을 해결한다는 것을 나타내는 <code>footer</code> 이다.</p>
<p>이러한 커밋 메시지를 사용하면 프로젝트의 이력(<code>history</code>)을 추적하고 관리하기 쉬워져서, 코드 변경 내역을 더욱 효율적으로 추적할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 11(Spring Security - 2)]]></title>
            <link>https://velog.io/@i_like_monday/React-11Spring-Security-2</link>
            <guid>https://velog.io/@i_like_monday/React-11Spring-Security-2</guid>
            <pubDate>Fri, 20 Jan 2023 05:07:00 GMT</pubDate>
            <description><![CDATA[<h1 id="db-연결">DB 연결</h1>
<h3 id="1-appicationproperties---데이터-베이스-접속-설정">1. appication.properties - 데이터 베이스 접속 설정</h3>
<pre><code class="language-sql"># 데이터 베이스 접속 설정
server.port=8080
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://58.239.58.243:3306/java505_team5_final
spring.datasource.hikari.username=java505_team5
spring.datasource.hikari.password=java505_team5_1234
spring.datasource.hikari.connection-test-query=SELECT 1

mybatis.configuration.map-underscore-to-camel-case=true</code></pre>
<h3 id="2-buildgradle-에서-mybatis-mysql-driver-주석-풀기---재로드">2. build.gradle 에서 mybatis, mySQL Driver 주석 풀기 - 재로드</h3>
<h3 id="3-databaseconfigurationjava-생성">3. DatabaseConfiguration.java 생성</h3>
<ul>
<li>DatabaseConfiguration.java<pre><code class="language-java">package com.bitc.securitytest.configuration;
</code></pre>
</li>
</ul>
<p>import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;</p>
<p>import javax.sql.DataSource;</p>
<p>@Configuration
@RequiredArgsConstructor
@PropertySource(&quot;classpath:/application.properties&quot;)
public class DatabaseConfiguration {</p>
<p>  private final ApplicationContext appContext;</p>
<p>  @Bean
  @ConfigurationProperties(prefix = &quot;spring.datasource.hikari&quot;)
  public HikariConfig hikariConfig() {
    return new HikariConfig();
  }</p>
<p>  @Bean
  public DataSource dataSource() throws Exception {
    DataSource dataSource = new HikariDataSource(hikariConfig());
    System.out.println(dataSource.toString());
    return dataSource;
  }</p>
<p>  @Bean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
    ssfb.setDataSource(dataSource);
    ssfb.setMapperLocations(appContext.getResources(&quot;classpath:/sql/<em>*/sql-</em>.xml&quot;));
    ssfb.setConfiguration(mybatisConfig());</p>
<pre><code>return ssfb.getObject();</code></pre><p>  }</p>
<p>  @Bean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory ssf) {
    return new SqlSessionTemplate(ssf);
  }</p>
<p>  @Bean
  @ConfigurationProperties(prefix = &quot;mybatis.configuration&quot;)
  public org.apache.ibatis.session.Configuration mybatisConfig() {
    return new org.apache.ibatis.session.Configuration();
  }
}</p>
<pre><code>
![](https://velog.velcdn.com/images/i_like_monday/post/27ec1b81-342c-479d-86d0-eeedf10123eb/image.png)
![](https://velog.velcdn.com/images/i_like_monday/post/c10b5d57-a2f8-4990-a236-82caa18bbbfb/image.png)

---

### dto 패키지 생성 - dto 파일 생성
- ClubMemberDto.java
```java
package com.bitc.securitytest.dto;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ClubMemberDto {
  private String email;
  private String password;
  private String name;
  private boolean fromSocial;
  private Set&lt;ClubMemberRole&gt; roleSet = new HashSet&lt;&gt;();

  public void addMemberRole(ClubMemberRole clubMemberRole) {
    roleSet.add(clubMemberRole);
  }
}
</code></pre><ul>
<li>dto/ClubAuthMemberDto
User 상속받아서 사용
<img src="https://velog.velcdn.com/images/i_like_monday/post/6f67310b-d2ce-4160-bd11-97c013a3ead3/image.png" alt=""></li>
</ul>
<pre><code class="language-java">@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User{

}</code></pre>
<p><code>↓</code></p>
<pre><code class="language-java">package com.bitc.securitytest.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User {
  private String email;
  private String name;
  private boolean fromSocial;

  public ClubAuthMemberDto(String username, String password, boolean fromSocial, Collection&lt;? extends GrantedAuthority&gt; auth) {
    super(username, password, auth);
    this.email = username;
    this.fromSocial = fromSocial;
  }

}
</code></pre>
<ul>
<li>dto/ClubMemberRole.java</li>
<li>*<u><code>class</code> ====&gt; <code>enum</code> 으로 수정!!</u>**<pre><code class="language-java">package com.bitc.securitytest.dto;
</code></pre>
</li>
</ul>
<p>// class -&gt; enum 
public enum ClubMemberRole {
  ADMIN, USER, MANAGER
}</p>
<pre><code>
mapper/ClubMEmberMapper.java 생성 (인터페이스)
```java
package com.bitc.securitytest.mapper;

import com.bitc.securitytest.dto.ClubMemberDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.Optional;

@Mapper
public interface ClubMEmberMapper {
  public Optional&lt;ClubMemberDto&gt; findByEmail(@Param(&quot;email&quot;) String email, @Param(&quot;social&quot;) boolean social);
}</code></pre><p>service/ClubUserDetailsService.java 생성
<code>implements UserDetailsService</code></p>
<ul>
<li>private final ClubMEmberMapper clubMEmberMapper;
alt+Enter -&gt; 메서드 구현<pre><code class="language-java">package com.bitc.securitytest.service;
</code></pre>
</li>
</ul>
<p>import com.bitc.securitytest.dto.ClubMemberDto;
import com.bitc.securitytest.dto.ClubMemberRole;
import com.bitc.securitytest.mapper.ClubMEmberMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;</p>
<p>import java.util.Optional;
import java.util.stream.Collectors;</p>
<p>@Service
@RequiredArgsConstructor
public class ClubUserDetailsService implements UserDetailsService {</p>
<p>  private final ClubMEmberMapper clubMEmberMapper;</p>
<p>  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {</p>
<pre><code>Optional&lt;ClubMemberDto&gt; result = clubMEmberMapper.findByEmail(username, false);

if (result.isEmpty()) {
  throw new UsernameNotFoundException(&quot;이메일 및 비밀번호를 확인하세요.&quot;);
}
ClubMemberDto member = result.get();
member.addMemberRole(ClubMemberRole.USER);

ClubAuthMemberDto clubAuthMemberDto = new ClubAuthMemberDto(
    member.getEmail(),
    member.getPassword(),
    member.isFromSocial(),
    member.getRoleSet().stream().map(role -&gt; new SimpleGrantedAuthority(&quot;ROLE_&quot; + role.name())).collect(Collectors.toSet())
);

clubAuthMember.setName(member.getName());
clubAuthMember.setFromSocial(member.isFromSocial());


return clubAuthMember;</code></pre><p>  }
}</p>
<pre><code>
---

resources/sql 경로 생성
- sql/sql-member.xml 생성
```xml
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org/DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;

&lt;mapper namespace=&quot;com.bitc.securitytest.mapper.ClubMEmberMapper&quot;&gt;
    &lt;select id=&quot;findByEmail&quot; parameterType=&quot;map&quot; resultType=&quot;com.bitc.securitytest.dto.ClubMemberDto&quot;&gt;
        SELECT *
        FROM club_member
        WHERE email = #{email}
          AND from_social = #{social}
    &lt;/select&gt;
&lt;/mapper&gt;</code></pre><hr>
<h2 id="주석-추가">주석 추가</h2>
<p>ClubAuthMemberDto.java</p>
<pre><code class="language-java">package com.bitc.securitytest.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;


// User 클래스를 [상속]받아 인증과 관련된 정보르 저장하는 DTO
// User 클래스 : 스프링 시큐리티에서 관리하는 사용자 정보 클래스
@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User {

  // User 클래스에서 제공하는 username, password, authorities 사용해야 함
  // username : 스프링 시큐리티에서 사용하는 사용자 ID
  // password : 스프링 시큐리티에서 사용하는 사용자 PW
  // authorities : 스프링 시큐리티에서 사용하는 사용자 권한

  private String email; // 사용자 id 로 사용되는 변수
  private String name; // 사용자 이름
  private boolean fromSocial;

  public ClubAuthMemberDto(String username, String password, boolean fromSocial, Collection&lt;? extends GrantedAuthority&gt; auth) {

    // 객체 생성 시 매개변수로 받은 사용자 id와 비밀번호, 인증 권한을 부모 클래스의 생성자를 실행하여 데이터를 저장함
    super(username, password, auth);  // 부모인 User 에 데이터 넘기기 -&gt; 자식 클래스에서 사용가능

    // User 클래스에서 사용자 id는 username 변수이므로 해당 변수를 사용자 id 역할을 하는 email 변수에 저장
    this.email = username;
    this.fromSocial = fromSocial;
  }
}</code></pre>
<ul>
<li>ClubMemberDto.java<pre><code class="language-java">package com.bitc.securitytest.dto;

</code></pre>
</li>
</ul>
<p>import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;</p>
<p>import java.util.HashSet;
import java.util.Set;</p>
<p>// 데이터 베이스의 정보를 가져오기 위한 클래스</p>
<p>@AllArgsConstructor
@NoArgsConstructor
@Data
public class ClubMemberDto {
  private String email; // 해당 DB 에서 id 의 역할을 하는 컬럼
  private String password;
  private String name;
  private boolean fromSocial;</p>
<p>  // 사용자의 권한을 저장하는 변수
  // 만약 DB에 컬럼이 따로 존재 시, 일반 String 타입으로 사용해도 됨
  private Set<ClubMemberRole> roleSet = new HashSet&lt;&gt;();</p>
<p>  // 사용자 권한을 추가하기 위한 메서드일뿐
  // 만약 데이터 베이스에 컬럼이 따로 존재 시, 해당 메서드는 필요 없음
  public void addMemberRole(ClubMemberRole clubMemberRole) {
    roleSet.add(clubMemberRole);
  }
}</p>
<pre><code>
- ClubMemberRole.jvav
```java
package com.bitc.securitytest.dto;

// 사용자의 권한을 지정해 놓은 것만 사용하도록 enum 타입으로 설정
public enum ClubMemberRole {
  ADMIN, USER, MANAGER
}</code></pre><ul>
<li>ClubUserDetailsService.java<pre><code class="language-java">package com.bitc.securitytest.service;
</code></pre>
</li>
</ul>
<p>import com.bitc.securitytest.dto.ClubAuthMemberDto;
import com.bitc.securitytest.dto.ClubMemberDto;
import com.bitc.securitytest.dto.ClubMemberRole;
import com.bitc.securitytest.mapper.ClubMEmberMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;</p>
<p>import java.util.Optional;
import java.util.stream.Collectors;</p>
<p>// UserDetailsService 사용자 정보를 데이터 베이스에서 가져올 경우 UserDetailsService 를 상속받아 loadUserByUsername() 메서드를 구현해야 함
@Service
@RequiredArgsConstructor
public class ClubUserDetailsService implements UserDetailsService {</p>
<p>  private final ClubMEmberMapper clubMEmberMapper;</p>
<p>  // DB 에서 사용자 정보를 가져올 경우 반드시 UserDetailsService 의 loadUserByUsername() 메서드를 구현해야 함
  // loadUserByUsername() 메서드는 UserDetails 인터페이스를 구현한 클래스객체를 반환해야 함
  // UserDetails 인터페이스를 상속받아 구현한 클래스의 객체를 스프링 시큐리티에서 확인하여 인증된 사용자인지 아닌지를 판단함
  // 사용자가 로그인 페이지에서 로그인 시, 스프링 시큐리티가 먼저 데이터를 받아서 loadUserByUsername() 메서드에 사용자 id 를 매개변수로 사용해서 실행
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {</p>
<pre><code>// Mybatis 의 mapper 를 이용하여 사용자 정보를 가져옴
// Optional : DB 사용 시, 데이터 조회 후 데이터가 null 이 들어올 경우 오류가 발생할 수 있는 부분을 안전하게 사용하기 위한 데이터 타입(제네릭)
Optional&lt;ClubMemberDto&gt; result = clubMEmberMapper.findByEmail(username, false);

// DB에 해당 사용자 정보가 있는지 확인
if (result.isEmpty()) {
  throw new UsernameNotFoundException(&quot;이메일 및 비밀번호를 확인하세요.&quot;);
}
// Optional 타입에 저장된 정보를 가져옴
ClubMemberDto member = result.get();  // 전체 데이터 가져오기

// ↓ 데이터베이스에 등급 권한 정보 컬럼이 있으면 필요없는 내용
// (사용자 정보에 사용 등급 권한 설정)
member.addMemberRole(ClubMemberRole.USER);

// 로그인 인증 정보를 가지고 있는 ClubAuthMemberDto 클래스 타입의 객체 생성
// 매개변수로 DB 에서 가져온 정보를 넘겨서 사용자가 입력한 사용자 id를 가지고 있는 로그인 인증된 객체가 생성됨
ClubAuthMemberDto clubAuthMember = new ClubAuthMemberDto(
    member.getEmail(),
    member.getPassword(),
    member.isFromSocial(),
    // 스프링 시큐리티에서 사용하는 권한 정보는 모두 &quot;ROLE_권한&quot; 형태로 되어 있음
    // map(매개변수 role -&gt; 인증정보 신규생성 (new SimpleGrantedAuthority))
    member.getRoleSet().stream().map(role -&gt; new SimpleGrantedAuthority(&quot;ROLE_&quot; + role.name())).collect(Collectors.toSet())
);

clubAuthMember.setName(member.getName());
clubAuthMember.setFromSocial(member.isFromSocial());


// 로그인 인증 정보를 가지고 있는 객체를 반환 시 스프링 시큐리티가 처리해 줌
return clubAuthMember;</code></pre><p>  }
}</p>
<pre><code>
---
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React - 10(Spring Security)]]></title>
            <link>https://velog.io/@i_like_monday/React-10</link>
            <guid>https://velog.io/@i_like_monday/React-10</guid>
            <pubDate>Wed, 18 Jan 2023 08:37:53 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-생성">프로젝트 생성</h1>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/3fe94287-a1c3-4cb4-968f-92b4504b7d42/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/a7e33105-b6ec-4b48-9b5d-3674663e8b01/image.png" alt=""></p>
<blockquote>
<p>스프링 공식 사이트
<a href="https://spring.io/projects/spring-boot#support">https://spring.io/projects/spring-boot#support</a>
<img src="https://velog.velcdn.com/images/i_like_monday/post/a943f5f7-3bb9-4782-b859-31f6a207abee/image.png" alt=""></p>
</blockquote>
<h3 id="스프링-버전-수정">스프링 버전 수정</h3>
<p><code>build.gradle</code>에서 <code>2.7.7</code> -&gt; <code>2.6.14</code> 로 버전 수정하기
<img src="https://velog.velcdn.com/images/i_like_monday/post/4f333515-4057-4037-8f7e-b008e6640d88/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/10b435cf-1df4-4c81-84d5-0ce2acc24d2e/image.png" alt=""></p>
<h3 id="초기설정">초기설정</h3>
<p>처음에 실행하면 오류나는 것 : DB연결문제 -&gt; 아래 코드대로 <code>build.gradle</code> 주석처리
<img src="https://velog.velcdn.com/images/i_like_monday/post/d36d33b5-7aeb-4082-ab57-0655bf7ae82d/image.png" alt=""></p>
<h3 id="스프링-시큐리티">스프링 시큐리티</h3>
<p><a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security">https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security</a>
<img src="https://velog.velcdn.com/images/i_like_monday/post/f431baf7-f519-4a30-a5f1-e3bc9fb39ae7/image.png" alt="">
dependency 추가</p>
<pre><code class="language-java">// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
    implementation &#39;org.springframework.boot:spring-boot-starter-security&#39;</code></pre>
<p>localhost:8080/login</p>
<p>id : user, pw : 콘솔에 출력된 security password
<img src="https://velog.velcdn.com/images/i_like_monday/post/b72b7da7-8dc1-43f4-8518-5d0f4149805f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/c17a0048-6384-411d-a3d7-22b4da70dcf0/image.png" alt=""></p>
<p>로그인 성공 후 다른 페이지를 볼 수 있음
<img src="https://velog.velcdn.com/images/i_like_monday/post/fe598f25-1cbe-4d19-b3c5-f28f887d585f/image.png" alt=""></p>
<p>로그아웃 : <code>localhost:8080/logout</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/9e533114-e403-4563-9678-5d6c10dd4eaa/image.png" alt=""></p>
<h2 id="configuration">configuration</h2>
<p>스프링 시큐리티를 사용하기 위해서는 configuration 을 하나 만들어야 한다.
<img src="https://velog.velcdn.com/images/i_like_monday/post/f72a5d89-80ae-42a3-8266-6836aca01ce0/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/b61c9696-e35d-42e7-ae88-f7621022634a/image.png" alt=""></p>
<p>2.7.x 버전부터는 사용법이 조금 달라짐.</p>
<h3 id="websecurityconfigureradapter-상속">WebSecurityConfigurerAdapter 상속</h3>
<p>스프링 시큐리티 설정을 위해서 <code>WebSecurityConfigurerAdapter</code> 클래스를 상속받아 사용함 (2.6.x 버전까지)</p>
<pre><code class="language-java">@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
}</code></pre>
<h3 id="비밀번호-암호화">비밀번호 암호화</h3>
<p>스프링 시큐리티에서는 반드시 비밀번호를 암호화해서 사용해야 함
BCryptPasswordEncoder 클래스를 사용하여 비밀번호를 암호화 함
@Bean 추가</p>
<pre><code class="language-java">  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }</code></pre>
<h3 id="httpsecurity-클래스">HttpSecurity 클래스</h3>
<p>스프링 시큐리티를 사용하는 웹 페이지에 접속할 경우 필요한 설정을 할 수 있음
인증과 인가에 관련된 API 를 제공하는 클래스이다.</p>
<h2 id="기능">기능</h2>
<h3 id="필요한-설정을-진행">필요한 설정을 진행</h3>
<p><code>authorizeRequests()</code> : 요청에 대한 보안 설정을 시작하는 메서드
<code>authorizeHttpRequests()</code> : http 요청에 대한 보안 설정을 시작하는 메서드
★ <code>antMatchers(url)</code> : 지정한 주소에 대한 url 주소에 대한 설정
★ <code>permitAll()</code> : 지정한 url 에 대해서 사용 인가
★ <code>hasRole(권한등급)</code> : 지정한 등급의 사용자만 지정한 url 에 대하여 사용 인가
★ <code>hasAnyRole(권한등급)</code> : 지정한 url 에 대하여 지정한 등급 중 하나 이상을 가진 사용자만 접근 가능
<code>isAnonymous()</code> : 익명 사용자만 접근
<code>isRememberMe()</code> : Remember Me 인증을 통해서 접근 시에만 인가
<code>isFullyAuthenticated()</code> : Remember Me 가 아닌 일반적인 인증 방법으로 로그인 시에만 접근
<code>denyAll()</code> : 접근 불가
<code>principal()</code> : 인증된 사용자의 사용자 정보 반환(UserDetails 인터페이스를 구현한 클래스의 객체) (잘 안씀)
<code>authentication()</code> : 인증된 사용자의 인증 정보 반환(Authentication** 인터페이스를 구현한 클래스의 객체) (잘 안씀)
<code>formLogin()</code> : 기본 스프링 시큐리티 로그인 페이지 사용</p>
<h3 id="로그인-페이지-사용-시-사용할-수-있는-추가-옵션">로그인 페이지 사용 시 사용할 수 있는 추가 옵션</h3>
<p><code>loginPage(url)</code> : 사용자 정의 로그인 페이지 지정
<code>defaultSuccessUrl(url)</code> : 로그인 성공 후 이동할 페이지 지정
<code>failureUrl(url)</code> : 로그인 실패 후 이동할 페이지 지정
<code>usernameParameter(ID)</code> : ID 파라미터명 설정
<code>passwordParameter(PW)</code> : 비밀번호 파라미터명 설정
<code>loginProcessingUrl(url)</code> : 로그인 Form Action Url 지정
<code>successHandler(loginSuccessHandler())</code> : 로그인 성공 후 동작할 내용 설정
<code>failureHandler(loginFailureHandler())</code> : 로그인 실패 후 동작할 내용 설정
<code>logout()</code> : 로그아웃 기능 활성화, 기본적으로 로그아웃 페이지는 POST 방식으로만 동작함</p>
<h3 id="로그아웃-페이지-사용-시-사용할-수-있는-추가-옵션">로그아웃 페이지 사용 시 사용할 수 있는 추가 옵션</h3>
<p><code>logoutUrl(url)</code> : 로그아웃 처리 페이지 지정
<code>logoutSuccessUrl(url)</code> : 로그아웃 성공 시 이동할 페이지 지정
<code>deleteCookies(&quot;JSESSIONID&quot;, &quot;remember-me&quot;)</code> : 로그아웃 시 쿠키 삭제
<code>addLogoutHandler(addLogoutHandler())</code> : 로그아웃 시 동작할 내용 설정
<code>logoutSuccessHandler(logoutSuccessHandler())</code> : 로그아웃 성공 시 동작할 내용 설정</p>
<p>securityConfig.java</p>
<pre><code class="language-java">package com.bitc.securitytest.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


// 스프링 시큐리티 설정을 위해서 WebSecurityConfigurerAdapter 클래스를 상속받아 사용함 (2.6.x 버전까지)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  // 스프링 시큐리티에서는 반드시 비밀번호를 암호화해서 사용해야 함
  // BCryptPasswordEncoder 클래스를 사용하여 비밀번호를 암호화 함
  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  // HttpSecurity 클래스 : 스프링 시큐리티를 사용하는 웹 페이지에 접속할 경우 필요한 설정을 할 수 있음
  // 인증과 인가에 관련된 API 를 제공하는 클래스

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 필요한 설정을 진행
    // authorizeRequests() : 요청에 대한 보안 설정을 시작하는 메서드
    // authorizeHttpRequests() : http 요청에 대한 보안 설정을 시작하는 메서드
    //★ antMatchers(url) : 지정한 주소에 대한 url 주소에 대한 설정
    //★ permitAll() : 지정한 url 에 대해서 사용 인가
    //★ hasRole(권한등급) : 지정한 등급의 사용자만 지정한 url 에 대하여 사용 인가
    //★ hasAnyRole(권한등급) : 지정한 url 에 대하여 지정한 등급 중 하나 이상을 가진 사용자만 접근 가능
    // isAnonymous() : 익명 사용자만 접근
    // isRememberMe() : Remember Me 인증을 통해서 접근 시에만 인가
    // isFullyAuthenticated() : Remember Me 가 아닌 일반적인 인증 방법으로 로그인 시에만 접근
    // denyAll() : 접근 불가
    // principal() : 인증된 사용자의 사용자 정보 반환(UserDetails 인터페이스를 구현한 클래스의 객체) (잘 안씀)
    // authentication() : 인증된 사용자의 인증 정보 반환(Authentication** 인터페이스를 구현한 클래스의 객체) (잘 안씀)
    http.authorizeRequests()
        // 보안인증 시작 → &quot;sec/all&quot; 경로에 대해서는 인증 open 하겠다.
        .antMatchers(&quot;/sec/all&quot;).permitAll() // 여기까지 쓰면 모든 페이지 접근 가능
//        .antMatchers(&quot;/sec/member&quot;).hasRole(&quot;USER&quot;)  // 여기까지 쓰면 member 접속 안됨, all 은 정상적으로 뜸, login/logout 안됨
        .antMatchers(&quot;/sec/member&quot;).hasAnyRole(&quot;ADMIN&quot;, &quot;USER&quot;)
        .antMatchers(&quot;/sec/admin&quot;).hasRole(&quot;ADMIN&quot;);  // http.logout(); 이후 추가 코드

    // formLogin() : 기본 스프링 시큐리티 로그인 페이지 사용
    // 로그인 페이지 사용 시 사용할 수 있는 추가 옵션
    // loginPage(url) : 사용자 정의 로그인 페이지 지정
    // defaultSuccessUrl(url) : 로그인 성공 후 이동할 페이지 지정
    // failureUrl(url) : 로그인 실패 후 이동할 페이지 지정
    // usernameParameter(ID) : ID 파라미터명 설정
    // passwordParameter(PW) : 비밀번호 파라미터명 설정
    // loginProcessingUrl(url) : 로그인 Form Action Url 지정
    // successHandler(loginSuccessHandler()) : 로그인 성공 후 동작할 내용 설정
    // failureHandler(loginFailureHandler()) : 로그인 실패 후 동작할 내용 설정
    http.formLogin();
    // logout() : 로그아웃 기능 활성화, 기본적으로 로그아웃 페이지는 POST 방식으로만 동작함
    // 로그아웃 페이지 사용 시 사용할 수 있는 추가 옵션
    // logoutUrl(url) : 로그아웃 처리 페이지 지정
    // logoutSuccessUrl(url) : 로그아웃 성공 시 이동할 페이지 지정
    // deleteCookies(&quot;JSESSIONID&quot;, &quot;remember-me&quot;) : 로그아웃 시 쿠키 삭제
    // addLogoutHandler(addLogoutHandler()) : 로그아웃 시 동작할 내용 설정
    // logoutSuccessHandler(logoutSuccessHandler()) : 로그아웃 성공 시 동작할 내용 설정
    http.logout();
    // 실행 -&gt; sec/member 접속 시 바로 /login 페이지로 이동됨
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser(&quot;user1&quot;).password(passwordEncoder().encode(&quot;1111&quot;)).roles(&quot;USER&quot;);
    auth.inMemoryAuthentication().withUser(&quot;admin&quot;).password(passwordEncoder().encode(&quot;1111&quot;)).roles(&quot;ADMIN&quot;);
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 09]]></title>
            <link>https://velog.io/@i_like_monday/React-09</link>
            <guid>https://velog.io/@i_like_monday/React-09</guid>
            <pubDate>Wed, 11 Jan 2023 05:50:59 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트---스프링부트-연동">리액트 - 스프링부트 연동</h1>
<p>지금은 리액트 프로젝트와 스프링 프로젝트를 별도로 만들어서 연동했지만 실제 배포할때는 그렇게 하지 않는다. 그럼 어떻게 하는지?</p>
<h3 id="1-스프링-프로젝트-생성">1. 스프링 프로젝트 생성</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/06a09e1d-8eca-4aff-bf96-eebdaaca5a61/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/0fb17718-2080-4fe5-bd49-0e45d56a9764/image.png" alt=""></p>
<p>테스트용이라 종속성은 Spring Web만 추가해준다.</p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/a1e605f4-d324-4783-a4bc-4aee9bc4998b/image.png" alt=""></p>
<pre><code class="language-java">package com.bitc.springserver.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class TestController {

  @RequestMapping(&quot;test&quot;)
  public List&lt;String&gt; test() throws Exception {
    List&lt;String&gt; result = new ArrayList&lt;&gt;();
    result.add(&quot;안녕하세요&quot;);
    result.add(&quot;리액트 연동 테스트 입니다.&quot;);

    return result;
  }
}</code></pre>
<p>여기까지 기본 서버 생성 완료.</p>
<h3 id="서버-배포-하는법">서버 배포 하는법</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/798f9e49-65b9-46df-9fd8-96232abd6bf4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/cd735478-ad5b-42dd-923e-8dcde7c78edd/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/a36e350d-9fe3-408f-86d4-1aa7912352e9/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/499fe27e-690e-4f0f-97c4-ca9c49d3b3ef/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/fd82db10-b92f-45dd-9cb2-c21e3122c101/image.png" alt=""></p>
<hr>
<h3 id="2-스프링-프로젝트의-터미널에서-리액트-프로젝트-생성">2. 스프링 프로젝트의 터미널에서 리액트 프로젝트 생성</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/c6635c20-f6c0-4ecc-bedf-501e4c46b995/image.png" alt="">
여기서 frontend는 새로 생성될 리액트 프로젝트명임
(터미널에서 안쓰고 인텔리제이에서 [파일 - 새 프로젝트 생성] 해줘도 됨)</p>
<p>리액트 폴더 생성됨!
<img src="https://velog.velcdn.com/images/i_like_monday/post/2fe9757a-b6bc-48c2-9faf-100573545248/image.png" alt=""></p>
<p>콘솔창에 <code>cd frontend</code> -&gt; <code>cd start</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/c1b497a0-6d75-406f-adb5-6844e3d03b7a/image.png" alt=""></p>
<p>리액트 서버 실행 완료
<img src="https://velog.velcdn.com/images/i_like_monday/post/3e66f7c5-2e29-40d4-9f9c-8ef6528de2ad/image.png" alt=""></p>
<p>리액트에 Test.jsx 생성
<img src="https://velog.velcdn.com/images/i_like_monday/post/88185d27-46a0-4538-b069-ff69016f8b5f/image.png" alt="">
Test.jsx</p>
<pre><code class="language-jsx">// Test.jsx

import React from &quot;react&quot;;

function Test(props) {
  return (
    &lt;div&gt;
      &lt;h3&gt;스프링 프로젝트 안에서 동작하는 리액트&lt;/h3&gt;
    &lt;/div&gt;
  );
}

export default Test;</code></pre>
<p>확인완료
<img src="https://velog.velcdn.com/images/i_like_monday/post/4fe4a7e2-9c5d-4430-b419-c874ee9042a3/image.png" alt=""></p>
<p>axios 설치
<img src="https://velog.velcdn.com/images/i_like_monday/post/45286cfa-e1aa-44a6-9947-dbe38081f2d6/image.png" alt=""></p>
<p>Test.jsx</p>
<pre><code class="language-jsx">// Test.jsx

import React, {useEffect, useState} from &quot;react&quot;;
import axios from &quot;axios&quot;;

function Test(props) {
  // state data
  const [data, setData] = useState([]);

  useEffect(() =&gt; {
    axios.get(&#39;http://localhost:8080/test&#39;)
      .then((req) =&gt; {
        // 지역변수 data
        const {data} = req;
        console.log(data);
      })
      .catch((err) =&gt; {
        console.log(&#39;통신 시 오류가 발생했습니다.&#39;)
      });
  }, []);

  return (
    &lt;div&gt;
      &lt;h3&gt;스프링 프로젝트 안에서 동작하는 리액트&lt;/h3&gt;
      &lt;ul&gt;
        {
          data.map((item, index) =&gt; {
            return &lt;li key={index}&gt;{item}&lt;/li&gt;
          })
        }
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}

export default Test;</code></pre>
<p>이대로 실행하면 <u>CORS 에러</u> 발생한다.</p>
<p>pakage.json -&gt; <code>&quot;proxy&quot;: &quot;http://localhost:8080&quot;</code> 코드 추가
<img src="https://velog.velcdn.com/images/i_like_monday/post/c790577d-1b5c-4b01-a817-6dec10d52467/image.png" alt=""></p>
<p><code>axios.get(&#39;/test&#39;)</code> 만 남겨놓음
<img src="https://velog.velcdn.com/images/i_like_monday/post/b4030a06-0ce8-48b0-93ef-5c7ff53877af/image.png" alt=""></p>
<p>에러 사라지고 정상적으로 배열이 반환됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/5e06a066-2d18-4cff-99d2-77030434c96a/image.png" alt=""></p>
<p>Test 컨트롤러</p>
<pre><code class="language-java">package com.bitc.springserver.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class TestController {

  @RequestMapping(&quot;test&quot;)
  public List&lt;String&gt; test() throws Exception {
    List&lt;String&gt; result = new ArrayList&lt;&gt;();
    result.add(&quot;안녕하세요&quot;);
    result.add(&quot;리액트 연동 테스트 입니다.&quot;);

    return result;
  }
}</code></pre>
<hr>
<p><code>useEffect</code>에 <code>setData(data);</code> 추가</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
    axios.get(&#39;/test&#39;)
      .then((req) =&gt; {
        // 지역변수 data
        const {data} = req;
        setData(data);
      })
      .catch((err) =&gt; {
        console.log(&#39;통신 시 오류가 발생했습니다.&#39;)
      });
  }, []);</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/aa2f33a1-ae5c-453f-8075-671c4ced9b92/image.png" alt=""></p>
<hr>
<p>하지만 이렇게 하면 cmd 창에서도 실행 해줘야하고, 스프링 프로젝트에서도 실행 해줘야해서 불편함</p>
<h3 id="buildgradle-코드-추가">build.gradle 코드 추가</h3>
<pre><code class="language-gradle">////////////////// 추가된 내용 ///////////////////////
// 리액트 프로젝트 폴더를 변수에 저장
def frontendDir = &quot;$projectDir/frontend&quot;

sourceSets {
  main {
    resources {
      srcDirs = [&quot;$projectDir/src/main/resources&quot;]
    }
  }
}

processResources {
  dependsOn &quot;copyReactBuildFiles&quot;
}

task installReact(type: Exec) {
  workingDir &quot;$frontendDir&quot;
  inputs.dir &quot;$frontendDir&quot;
  group = BasePlugin.BUILD_GROUP
  if (System.getProperty(&#39;os.name&#39;).toLowerCase(Locale.ROOT).contains(&#39;windows&#39;)) {
    commandLine &quot;npm.cmd&quot;, &quot;audit&quot;, &quot;fix&quot;
    commandLine &quot;npm.cmd&quot;, &quot;install&quot;
  }
  else {
    commandLine &quot;npm&quot;, &quot;audit&quot;, &quot;fix&quot;
    commandLine &quot;npm&quot;, &quot;install&quot;
  }
}

task buildReact(type: Exec) {
  dependsOn &quot;installReact&quot;
  workingDir &quot;$frontendDir&quot;
  inputs.dir &quot;$frontendDir&quot;
  group = BasePlugin.BUILD_GROUP
  if (System.getProperty(&#39;os.name&#39;).toLowerCase(Locale.ROOT).contains(&#39;windows&#39;)) {
    commandLine &quot;npm.cmd&quot;, &quot;run-script&quot;, &quot;build&quot;
  }
  else {
    commandLine &quot;npm&quot;, &quot;run-script&quot;, &quot;build&quot;
  }
}

task copyReactBuildFiles(type: Copy) {
  dependsOn &quot;buildReact&quot;
  from &quot;$frontendDir/build&quot;
  into &quot;$buildDir/resources/main/static&quot;
}

tasks.bootJar {
  dependsOn &quot;copyReactBuildFiles&quot;</code></pre>
<p>코드 추가 후 bootJar 더블클릭 -&gt; jar 파일 업데이트 됨(생성시간 확인)
build.gradle 에서 해당부분 0.0.2 로 이름 바꾼후 다시 더블클릭 하면 0.0.2 버전 새로 생김
<img src="https://velog.velcdn.com/images/i_like_monday/post/55bb2441-68a6-4f78-b77c-1a3c5e4442ad/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/22aa02b7-8978-4334-b98f-d6c16f4360cc/image.png" alt=""></p>
<p>cmd창에서 새로 만든 0.0.2 버전 실행(gradle 적용 후 버전)
<img src="https://velog.velcdn.com/images/i_like_monday/post/2fa44b7c-5c2e-4eff-b34d-8f5a699d6792/image.png" alt=""></p>
<p>기존에 돌아가던 <code>localhost:3000</code> 에서는 안돌아가고 <code>localhost:8080</code> 에서 리액트가 동작이 됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/ed532c76-2302-44f5-a053-99d8770ce029/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/78f22e16-b96b-4ac0-983e-edc5905bd814/image.png" alt=""></p>
<hr>
<p>axios 방식 쓰고싶으면 스프링 프로젝트의 컨트롤러에 @RestController 붙여줘야함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 08]]></title>
            <link>https://velog.io/@i_like_monday/React-08</link>
            <guid>https://velog.io/@i_like_monday/React-08</guid>
            <pubDate>Tue, 10 Jan 2023 15:30:14 GMT</pubDate>
            <description><![CDATA[<h2 id="url-파라미터">url 파라미터</h2>
<ul>
<li>restful 방식의 주소 형태로 구성되는 파라미터</li>
<li>기존 url 뒤에 / 와 : 파라미터명을 사용하여 데이터를 전달하는 방식</li>
<li>rest 방식에서 많이 사용함</li>
<li>연결된 컴포넌트에서 useParams 훅을 사용해야 함</li>
<li>사용법 : <code>기존 url/:파라미터1/:파라미터2</code><pre><code class="language-jsx">&lt;Route path={url/:test1/:test2} element={&lt;url/&gt;} /&gt;</code></pre>
</li>
</ul>
<h2 id="쿼리스트링">쿼리스트링</h2>
<ul>
<li>기존의 get 방식으로 웹 브라우저의 주소에 파라미터를 함께 전달하는 방식
<code>&lt;Route /&gt;</code> 컴포넌트의  path에 변경이 없음.</li>
<li>지정된 컴포넌트에서 몇가지 옵션 사항을 사용할 수 있음</li>
<li>옵션 :<ul>
<li>pathname : 쿼리 스트링을 제외한 현재 주소의 경로</li>
<li>search : 맨앞의 ? 문자를 포함한 쿼리 스트링 값</li>
<li>hash : 주소의 # 문자열 뒤의 값 (구형 브라우저에서만 사용-익스플로러)</li>
<li>state : 페이지 이동 시 임의로 추가할 상태 값</li>
<li>key : location 객체의 고유 값</li>
</ul>
</li>
<li>useLocation(), useSearchParams() 훅을 사용하여 데이터를 가져올 수 있음<ul>
<li>useLocation() 훅을 사용하여 데이터를 가져오면 원하는 파라미터 값을 가져오기 위해 직접 파싱을 진행해야 함</li>
<li>useSearchParams() 훅을 사용하여 데이터를 가져오면 파라미터 데이터만 따로 가져올 수 있음</li>
<li>사용법 :
 <code>기존 url?파라미터1=값1&amp;파라미터2=값2</code>
 <img src="https://velog.velcdn.com/images/i_like_monday/post/f9a61efb-95a8-4958-8595-1a7aa4946542/image.png" alt=""></li>
</ul>
</li>
<li>useNavigate() : Link 컴포넌트를 사용하지 않고 다른 페이지로 이동하는 훅<ul>
<li>사용법 :<pre><code class="language-jsx">const navigate = useNavigate();</code></pre>
<ul>
<li>navigate(-1) : History 를 사용하여 앞 페이지로 이동, 숫자를 음수로 입력 시 해당 뒤로 이동이고, 양수로 입력 시 앞으로 이동</li>
<li>navigate(&#39;/url&#39;) : 지정한 url 로 이동</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="예시">예시</h3>
<p>App7.jsx</p>
<pre><code class="language-jsx">&lt;Route path={&quot;board/:boardIdx&quot;} element={&lt;Board /&gt;}/&gt;
&lt;Route path={&quot;board2&quot;} element={&lt;Board2/&gt;} /&gt;</code></pre>
<p>Board1.jsx</p>
<pre><code class="language-jsx">//Board.jsx
import React from &quot;react&quot;;
import {useParams} from &quot;react-router-dom&quot;;

function Board(props){
    const boardSelect = useParams();
    return(
        &lt;div&gt;
            &lt;h2&gt;글번호 : {boardSelect.boardIdx}&lt;/h2&gt;
        &lt;/div&gt;
    )
}

export default Board;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/01cfe7d2-a168-463f-a068-af922a0c0ca1/image.png" alt=""></p>
<p>Board2.jsx</p>
<pre><code class="language-jsx">// Board2.jsx

import React from &quot;react&quot;;
import {useLocation, useNavigate, useSearchParams} from &quot;react-router-dom&quot;;

function Board2(props) {
    const param1 = useLocation();
    const [param2, setParam2] = useSearchParams();
    const navi = useNavigate();

    const goBack = () =&gt; {
        navi(-1);
    }

    const goBoard = () =&gt; {
        navi(&#39;/Board/100&#39;)
    }

    return (
        &lt;div className={&quot;container&quot;}&gt;

            &lt;p&gt;{param1.search}&lt;/p&gt;
            &lt;p&gt;{param2.get(&quot;val1&quot;)}&lt;/p&gt;
            &lt;p&gt;{param2.get(&quot;val2&quot;)}&lt;/p&gt;
            &lt;button onClick={goBack} className={&quot;btn btn-secondary&quot;}&gt;뒤로 이동&lt;/button&gt;
            &lt;button onClick={goBoard} className={&quot;btn btn-primary&quot;}&gt;Board 페이지로 이동&lt;/button&gt;
        &lt;/div&gt;
    );
}

export default Board2;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/21d35695-4475-43e3-a4f8-8544387489c2/image.png" alt=""></p>
<hr>
<h1 id="액시오스">액시오스</h1>
<p>비동기 통신을 쉽게 해줌
설치법 : <a href="https://axios-http.com/kr/docs/intro">https://axios-http.com/kr/docs/intro</a></p>
<ul>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/d7ba983f-28a8-446e-b640-a7aaa4fe72ec/image.png" alt=""></li>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/aaff083d-85ac-414f-abdc-4b324b8b2bb2/image.png" alt=""></li>
</ul>
<h3 id="파일-생성">파일 생성</h3>
<p>App8.jsx
folder6
folder6/AxiosTest.jsx</p>
<blockquote>
<h3 id="영화진흥원-openapi">영화진흥원 openAPI</h3>
<p><a href="https://www.kobis.or.kr/kobisopenapi/homepg/main/main.do">https://www.kobis.or.kr/kobisopenapi/homepg/main/main.do</a>
<img src="https://velog.velcdn.com/images/i_like_monday/post/ecf6c366-c2de-4c36-a3a0-c68a6edb7de6/image.png" alt=""></p>
</blockquote>
<pre><code class="language-txt">http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20120101</code></pre>
<br>

<h2 id="기본-axios-사용법json">기본 Axios 사용법(JSON)</h2>
<hr>
<h3 id="axios">axios</h3>
<p>node.js 에서 비동기 통신을 하기 위한 라이브러리, promise 를 사용함
axios 는 <u>rest 방식</u>을 지원함</p>
<ul>
<li>GET 방식으로 서버에 데이터를 요청함<pre><code class="language-jsx">get(url, config)</code></pre>
</li>
<li>POST 방식으로 서버에 데이터를 전달<pre><code class="language-jsx">post(url, data[, config])</code></pre>
</li>
<li>PUT 방식으로 서버에 데이터 전달<pre><code class="language-jsx">put(url, data[, config])</code></pre>
</li>
<li>DELETE 방식으로 데이터 전달<pre><code class="language-jsx">delete(url, data[, config]</code></pre>
</li>
<li>axios 객체 생성 시 설정 정보를 변경할 수 있음<pre><code class="language-jsx">options(url, config)</code></pre>
</li>
</ul>
<hr>
<p><code>then()</code> : 정상적으로 통신이 성공했을 경우 실행
<code>catch()</code> : 통신이 실패했을 경우 실행</p>
<hr>
<p>async/await 와 함께 사용 시 then(), catch() 는 필요할 경우 나중에 호출할 수 있음
async/await 와 함께 사용 시 예외처리를 <u> try/catch </u>로 처리함</p>
<hr>
<p><strong>응답 객체</strong> : 요청에 대한 응답 객체가 json 방식으로 전달됨
<code>data{}</code> : 서버에서 제공한 데이터 json 타입
<code>status</code> : 서버와의 통신 응답 신호, 200 : ok, 300 : redirect, 400 : 리소스 없음, 500 : 서버 내부 오류
<code>statusText</code> : 서버와의 응답 메시지
<code>headers{}</code> : 서버에서 응답한 헤더 정보
<code>config{}</code> : 서버에 요청 시 axios 의 설정 정보
<code>request{}</code> : 응답을 생성한 요청</p>
<ul>
<li><p>적용법 :
<code>npm install axios</code> : 사용할 프로젝트 axios 설치
<code>import axios from &#39;axios&#39;;</code> : 사용할 컴포넌트에서 axios import</p>
</li>
<li><p>axios 사용법 1</p>
<pre><code class="language-jsx">  axios.get(url)
  .then(function(req) {
      성공 시 실행할 소스;
  })
  .catch(function(err) {
      실패 시 실행할 소스;
  })</code></pre>
</li>
<li><p>axios 사용법 2</p>
<pre><code class="language-jsx">  axios({
      method: &#39;get|post|put|delete&#39;,
      url: &#39;url&#39;,
      data: {
          param1: &#39;data&#39;,
          param2: &#39;data&#39;
      },
  });</code></pre>
</li>
<li><p>axios 사용법 3</p>
<pre><code class="language-jsx">  axios.post(url, {
      param1: &#39;data1&#39;,
      param2: &#39;data2&#39;
  })
  .then(function(req) {
      성공 시 실행할 소스;
  })
  .catch(function(err) {
      실패 시 실행할 소스;
  });</code></pre>
</li>
</ul>
<p>AxiosTest.jsx</p>
<pre><code class="language-js">// AxiosTest.jsx

import React, {useEffect} from &quot;react&quot;;
import axios from &quot;axios&quot;;

function AxiosTest(props) {
    const value = 11;
    useEffect(() =&gt; {
        axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;)
            .then((req) =&gt; {
                alert(&quot;통신성공&quot;);
                console.log(req);
                console.log(req.data);
                const list = req.data.boxOfficeResult.dailyBoxOfficeList;

                for (let i = 0; i &lt; list.length; i++) {
                    let str = `순번 : ${list[i].rnum}
                    순위 : ${list[i].rank}
                    제목 : ${list[i].movieNm}
                    개봉일 : ${list[i].openDt}
                    관람객 : ${list[i].audiCnt}`
                    console.log(str + &#39;\n&#39;);
                }
            })
            .catch((err) =&gt; {
                alert(&quot;통신실패&quot;);
            });
    });
    return (
        &lt;div&gt;

        &lt;/div&gt;
    );
}

export default AxiosTest;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/35144716-1e39-4659-be3b-3f90044fdaa8/image.png" alt=""></p>
<hr>
<h3 id="axios-문제-1">axios 문제 1</h3>
<p>버튼 클릭 시 axios 통신을 통해서 데이터를 가져와서 화면의 table 에 출력하는 컴포넌트를 작성하세요
state 사용.( 화면이 업데이트 되니까), 배열로 들어오니까 map() 사용.</p>
<p>DailyBoxOffice.jsx</p>
<pre><code class="language-js">// DailyBoxOffice.jsx

import React from &quot;react&quot;;
import axios from &quot;axios&quot;;

// 문제 1. 버튼 클릭 시 axios 통신을 통해서 데이터를 가져와서 화면의 table 에 출력하는 컴포넌트를 작성하세요
// state 사용.( 화면이 업데이트 되니까), 배열로 들어오니까 map() 사용.
function DailyBoxOffice(props) {

  const loadBoxOffice = () =&gt; {
    axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;)
      .then((req) =&gt; {
        const boxOffice = req.data.boxOfficeResult.dailyBoxOfficeList;
        console.log(boxOffice);
      })
      .catch((err) =&gt; {
        console.log(&#39;통신 시 오류가 발생하였습니다.&#39;);
      });
  }


  return (
    &lt;div className={&quot;container&quot;}&gt;
      &lt;div className={&quot;row&quot;}&gt;
        &lt;div className={&quot;col-sm-8 mx-auto&quot;}&gt;
          &lt;table className={&quot;table table-striped table-hover&quot;}&gt;
            &lt;thead&gt;
            &lt;tr&gt;
              &lt;th&gt;순위&lt;/th&gt;
              &lt;th&gt;제목&lt;/th&gt;
              &lt;th&gt;개봉일&lt;/th&gt;
              &lt;th&gt;당일 관람객&lt;/th&gt;
              &lt;th&gt;누적 관람객&lt;/th&gt;
            &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;

            &lt;/tbody&gt;
          &lt;/table&gt;
          &lt;hr/&gt;
          &lt;div className={&#39;my-3 d-flex justify-content-end&#39;}&gt;
            &lt;button className={&#39;btn btn-outline-primary btn-sm&#39;} onClick={loadBoxOffice}&gt;영화 순위 조회&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default DailyBoxOffice;</code></pre>
<h3 id="해답">해답</h3>
<p>DailyBoxOffice.jsx</p>
<pre><code class="language-js">// DailyBoxOffice.jsx

import React, {useState} from &quot;react&quot;;
import axios from &quot;axios&quot;;
import BoardItem from &quot;../folder1/BoardItem&quot;;

// 문제 1. 버튼 클릭 시 axios 통신을 통해서 데이터를 가져와서 화면의 table 에 출력하는 컴포넌트를 작성하세요
// state 사용.( 화면이 업데이트 되니까), 배열로 들어오니까 map() 사용.
function DailyBoxOffice(props) {
  // boxOffice 를 state 로 설정. 주의: 초기값을 빈 배열로 넣어줘야함
  const [boxOffice, setBoxOffice] = useState([]);

  const loadBoxOffice = () =&gt; {
    axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;)
      .then((req) =&gt; {
        const DailyBoxOffice = req.data.boxOfficeResult.dailyBoxOfficeList;

        setBoxOffice(DailyBoxOffice);
        console.log(DailyBoxOffice);
      })
      .catch((err) =&gt; {
        console.log(&#39;통신 시 오류가 발생하였습니다.&#39;);
      });
  }


  return (
    &lt;div className={&quot;container&quot;}&gt;
      &lt;div className={&quot;row&quot;}&gt;
        &lt;div className={&quot;col-sm-8 mx-auto&quot;}&gt;
          &lt;table className={&quot;table table-striped table-hover&quot;}&gt;
            &lt;thead&gt;
            &lt;tr&gt;
              &lt;th&gt;순위&lt;/th&gt;
              &lt;th&gt;제목&lt;/th&gt;
              &lt;th&gt;개봉일&lt;/th&gt;
              &lt;th&gt;당일 관람객&lt;/th&gt;
              &lt;th&gt;누적 관람객&lt;/th&gt;
            &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
            {
              boxOffice.map((item) =&gt; {
                console.log(item);
                return (
                  &lt;tr key={item.rnum}&gt;
                    &lt;td&gt;{item.rank}&lt;/td&gt;
                    &lt;td&gt;{item.movieNm}&lt;/td&gt;
                    &lt;td&gt;{item.openDt}&lt;/td&gt;
                    &lt;td&gt;{item.audiCnt}&lt;/td&gt;
                    &lt;td&gt;{item.audiAcc}&lt;/td&gt;
                  &lt;/tr&gt;
                );
              })
            }
            &lt;/tbody&gt;
          &lt;/table&gt;
          &lt;hr/&gt;
          &lt;div className={&#39;my-3 d-flex justify-content-end&#39;}&gt;
            &lt;button className={&#39;btn btn-outline-primary btn-sm&#39;} onClick={loadBoxOffice}&gt;영화 순위 조회&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default DailyBoxOffice;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/1e5d2c3f-bab8-4547-a428-7cc82ed35598/image.png" alt=""></p>
<hr>
<h1 id="async-await">Async, Await</h1>
<h3 id="기본-사용-방법">기본 사용 방법</h3>
<p>async / await : 비동기 처리를 조금 더 쉽게 하기 위해서 개발됨
기본적으로 <code>promise</code> 를 사용함</p>
<p>사용법 :</p>
<pre><code class="language-jsx">async function 함수명(매개변수) {
        const 변수명 = await_promise_를_사용하는 함수();
        console.log(변수명);
    }</code></pre>
<p>AsyncAwait.jsx 생성</p>
<pre><code class="language-jsx">// AsyncAwait.jsx

import React from &quot;react&quot;;
import axios from &quot;axios&quot;;

function AsyncAwait(props) {
    const fetchItems = () =&gt; {
        return new Promise((resolve, reject) =&gt; {
            const items = [1, 2, 3];
            resolve(items);
        });
    }

    // 프로미스 사용 시 비동기 처리 방법
    const logItem1 = () =&gt; {
        fetchItems()
            .then((item) =&gt; {
                console.log(item);
            })
            .catch((err) =&gt; {
                console.log(err);
            });
    }

    // async / await 사용 시 비동기 처리 방법
    const logItem2 = async () =&gt; {
        const resultItems = await fetchItems();
        // ↓ 필요없으면 안써도됨
        // resultItems.then(() =&gt; {
        //
        // })
        //     .catch(() =&gt; {
        //
        //     });
        console.log(resultItems);
    }


    const getData = async () =&gt; {
        const {data} = await axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;);
        console.log(data.boxOfficeResult.dailyBoxOfficeList);
    }

    return (
        &lt;div&gt;
            &lt;button className={&#39;btn btn-primary&#39;} onClick={logItem1}&gt;프로미스 사용&lt;/button&gt;
            &lt;button className={&#39;btn btn-success&#39;} onClick={logItem2}&gt;async/await 사용&lt;/button&gt;
            &lt;button className={&#39;btn btn-success&#39;} onClick={getData}&gt;axios와 함께 사용&lt;/button&gt;
        &lt;/div&gt;
    );
}

export default AsyncAwait;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/29660046-92fd-4c79-83e8-7e010a759620/image.png" alt=""></p>
<p>async, await 써주면 then, catch 안써도 됨
코드가 훨씬 간결해진다. 사용하기도 편함!</p>
<hr>
<h3 id="응용-1">응용 1</h3>
<p>영화 순위 데이터 가져오는 버튼도 async, await 방식으로 만들어보자
DailyBoxOffice.jsx =&gt; asyncBoxOffice 함수</p>
<pre><code class="language-js">// DailyBoxOffice.jsx

import React, {useState} from &quot;react&quot;;
import axios from &quot;axios&quot;;

function DailyBoxOffice(props) {
  // boxOffice 를 state 로 설정. 주의: 초기값을 빈 배열로 넣어줘야함. 만약 &#39;&#39; (빈 문자열)형태로 넣으면 아래에서 map 함수가 동작하지 않음
  const [boxOffice, setBoxOffice] = useState([]);

  const loadBoxOffice = () =&gt; {
    axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;)
      .then((req) =&gt; {
        const DailyBoxOffice = req.data.boxOfficeResult.dailyBoxOfficeList;

        setBoxOffice(DailyBoxOffice);
        console.log(DailyBoxOffice);
      })
      .catch((err) =&gt; {
        console.log(&#39;통신 시 오류가 발생하였습니다.&#39;);
      });
  }

  const asyncBoxOffice = async () =&gt; {
    const {data} = await axios.get(&#39;http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&amp;targetDt=20230109&#39;);
    const DailyBoxOffice = data.boxOfficeResult.dailyBoxOfficeList;
    setBoxOffice(DailyBoxOffice);
  }


  return (
    &lt;div className={&quot;container&quot;}&gt;
      &lt;div className={&quot;row&quot;}&gt;
        &lt;div className={&quot;col-sm-8 mx-auto&quot;}&gt;
          &lt;table className={&quot;table table-striped table-hover&quot;}&gt;
            &lt;thead&gt;
            &lt;tr&gt;
              &lt;th&gt;순위&lt;/th&gt;
              &lt;th&gt;제목&lt;/th&gt;
              &lt;th&gt;개봉일&lt;/th&gt;
              &lt;th&gt;당일 관람객&lt;/th&gt;
              &lt;th&gt;누적 관람객&lt;/th&gt;
            &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
            {
              boxOffice.map((item) =&gt; {
                console.log(item);
                return (
                  &lt;tr key={item.rnum}&gt;
                    &lt;td&gt;{item.rank}&lt;/td&gt;
                    &lt;td&gt;{item.movieNm}&lt;/td&gt;
                    &lt;td&gt;{item.openDt}&lt;/td&gt;
                    &lt;td&gt;{item.audiCnt}&lt;/td&gt;
                    &lt;td&gt;{item.audiAcc}&lt;/td&gt;
                  &lt;/tr&gt;
                );
              })
            }
            &lt;/tbody&gt;
          &lt;/table&gt;
          &lt;hr/&gt;
          &lt;div className={&#39;my-3 d-flex justify-content-end&#39;}&gt;
            &lt;button className={&#39;btn btn-outline-primary btn-sm&#39;} onClick={loadBoxOffice}&gt;영화 순위 조회&lt;/button&gt;
            &lt;button className={&#39;btn btn-outline-primary btn-sm&#39;} onClick={asyncBoxOffice}&gt;영화 순위 조회(async/await 이용)&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default DailyBoxOffice;</code></pre>
<hr>
<p>AxiosRestServer.jsx 파일 생성</p>
<pre><code class="language-jsx">//AxiosRestServer.jsx
// spring과 데이터 주고받기
import React from &quot;react&quot;;
import axios from &quot;axios&quot;;
// 버튼을 매개로 요청 서버(get, post, put, delete)
function AxiosRestServer(props){

  const sendDataGet = () =&gt; {
    axios.get(&quot;http://localhost:8080/async/sendDataGet&quot;, {params: {idx: 300}})
      .then((req) =&gt; {
        console.log(&#39;Get 방식으로 통신 성공&#39;);
        console.log(req); //데이터 넘어오는지 확인
      })
      .catch((err) =&gt; {
        console.log(&#39;Get 방식 통신 오류&#39;);
      });
  }
  const sendDataPost = () =&gt; {
    axios.post(&quot;http://localhost:8080/async/sendDataPost&quot;, null, {params: {id: &quot;id&quot;, pw: &quot;pw&quot;}})
      .then((req) =&gt; {
        console.log(&#39;post 방식으로 통신 성공&#39;);
        console.log(req);
      })
      .catch((err) =&gt; {
        console.log(&#39;post 방식 통신 오류&#39;);
      });
  }
  const sendDataPut = () =&gt; {
    axios.put(&quot;http://localhost:8080/async/sendDataPut&quot;, null, {params: {idx: 100}})
      .then((req) =&gt; {
        console.log(&#39;Put 방식으로 통신 성공&#39;);
        console.log(req);
      })
      .catch((err) =&gt; {
        console.log(&#39;Put 방식 통신 오류&#39;);
      });
  }

  const sendDataDelete = () =&gt; {
    axios.delete(&quot;http://localhost:8080/async/sendDataDelete&quot;, {params: {idx: 200}})
      .then((req) =&gt; {
        console.log(&#39;delete 방식으로 통신 성공&#39;);
        console.log(req);
      })
      .catch((err) =&gt; {
        console.log(&#39;delete 방식 통신 오류&#39;);
      });
  }



  return(
    &lt;div className={&quot;container mt-5 p-5&quot;}&gt;
      &lt;h1&gt;Axios를 사용한 비동기 통신&lt;/h1&gt;
      &lt;div className={&quot;row&quot;}&gt;
        &lt;div className={&quot;col-sm-8 mx-auto&quot;}&gt;
          &lt;div&gt;

          &lt;/div&gt;
          &lt;div className={&quot;my-3 d-flex justify-content-center&quot;}&gt;
            &lt;button className = {&quot;btn btn-primary me-3&quot;} onClick={sendDataGet}&gt;get 방식&lt;/button&gt;
            &lt;button className={&quot;btn btn-success me-3&quot;} onClick={sendDataPost}&gt;post 방식&lt;/button&gt;
            &lt;button className={&quot;btn btn-info me-3&quot;} onClick={sendDataPut}&gt;put 방식&lt;/button&gt;
            &lt;button className={&quot;btn btn-danger&quot;} onClick={sendDataDelete}&gt;delete 방식&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
export default AxiosRestServer;</code></pre>
<blockquote>
<h2 id="스프링-파일-참고">스프링 파일 참고</h2>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/8f55446d-535f-40ba-9160-714dfe905499/image.png" alt=""><br></p>
</blockquote>
<p>AsyncController.java</p>
<pre><code class="language-java">package com.bitc.asynctestserveer.controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;

@CrossOrigin(origins = {&quot;http://localhost:3000&quot;, &quot;http://localhost:4000&quot;})
@RestController
public class AsyncController {

  @RequestMapping()
  public String index() {return &quot;index 페이지&quot;;}

//    @CrossOrigin(origins = &quot;http://localhost:3000&quot;)
  @RequestMapping(value = &quot;/async/data1&quot;, method = RequestMethod.POST)
  public Object data1() {
    return &quot;success&quot;;
  }

  @RequestMapping(value = &quot;/async/sendDataGet&quot;, method = RequestMethod.GET)
  public Object sendDataGet() throws Exception {
  Map&lt;String, String&gt; result = new HashMap&lt;&gt;();
  result.put(&quot;result&quot;, &quot;success&quot;);
  result.put(&quot;data&quot;, &quot;&quot;);

  return result;
}

@RequestMapping(value = &quot;/async/sendDataPost&quot;, method = RequestMethod.POST)
public Object sendDataPost(@RequestParam(&quot;id&quot;) String id, @RequestParam(&quot;pw&quot;) String pw) throws Exception {
  Map&lt;String, Object&gt; user = new HashMap&lt;&gt;();
  user.put(&quot;id&quot;, &quot;test1&quot;);
  user.put(&quot;pw&quot;, &quot;pw1234&quot;);

  Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
  result.put(&quot;result&quot;, &quot;success&quot;);
  result.put(&quot;data&quot;, user);

  return result;
}

@RequestMapping(value = &quot;/async/sendDataPut&quot;, method = RequestMethod.PUT)
public Object sendDataPut(@RequestParam(&quot;idx&quot;) int idx) throws Exception {
  Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
  result.put(&quot;result&quot;, &quot;success&quot;);

  return result;
}

@RequestMapping(value = &quot;/async/sendDataDelete&quot;, method = RequestMethod.DELETE)
public Object sendDataDelete(@RequestParam(&quot;idx&quot;) int idx) throws Exception {
  Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
  result.put(&quot;result&quot;, &quot;success&quot;);

  return result;
}
}
</code></pre>
<h2 id="cors-오류">CORS 오류</h2>
<p>CORS 오류 : Cross Origin resource sharing 의 줄임말, 동일한 주소 내에서만 리소스를 공유할 수 있음.
스프링에서는 CORS 허용을 위해서 @CrossOrigin 어노테이션을 사용하여 처리함
@CrossOrigin 어노테이션을 사용하면 지정한 도메인에 대해서 접근을 허용함
@CrossOrigin 어노테이션은 메소드, 클래스, configuration 에 설정할 수 있음
메소드에 사용 시 지정한 메소드만 접근을 허용
클래스에 사용 시 지정한 컨트롤러에 대해서만 접근을 허용
configurer 에 사용 시 모든 곳에 접근을 허용함
옵션으로 origins 에 접근할 서버의 주소를 입력함</p>
<p>이를 해결하기 위해서는 세 가지 방법이 있다.</p>
<ol>
<li><p>메인 컨트롤러에 @CrossOrigin 어노테이션으로 접근허용 url을 추가해주면 하위의 소스들에 추가 조치 없이 접근이 가능.</p>
<pre><code class="language-java">@CrossOrigin(origins = {&quot;http://localhost:3000&quot;, &quot;http://localhost:4000&quot;})
@RestController
public class AsyncController {
 실행내용
}</code></pre>
</li>
<li><p>메인 컨트롤러 하위의 소스에 개별적으로 @CrossOrigin 어노테이션을 추가해주면 좀 더 디테일하게 접근을 제한해줄수 있다.
아래의 코드대로라면 <code>&quot;/async/data1&quot;</code> 에만 <code>http://localhost:3000</code> 에서 접근이 가능하다.</p>
<pre><code class="language-java">@RestController
public class AsyncController {

@CrossOrigin(origins = &quot;http://localhost:3000&quot;)
@RequestMapping(value = &quot;/async/data1&quot;, method = RequestMethod.POST)
public Object data1() {
 return &quot;success&quot;;
}

@RequestMapping(value = &quot;/async/sendDataPut&quot;, method = RequestMethod.PUT)
public Object sendDataPut(@RequestParam(&quot;idx&quot;) int idx) throws Exception {
 Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
 result.put(&quot;result&quot;, &quot;success&quot;);

 return result;
}
}</code></pre>
</li>
<li><p>configuration 으로 설정해주는 방법
<img src="https://velog.velcdn.com/images/i_like_monday/post/139ccbc9-403a-4b93-a0ba-5f8014c3ad94/image.png" alt="">
3-1. @Configuration 어노테이션 추가
3-2. WebMvcConfigurer 상속 
<code>implements WebMvcConfigurer</code>
3-3. @Override : CorsRegistry</p>
<pre><code class="language-java">@Override
 public void addCorsMappings(CorsRegistry registry) {

 }</code></pre>
<p>3-4. <code>addMapping()</code> 을 이용해 접근 권한 확인(경로) 추가후
<code>allowedOrigins(url)</code> 을 이용해 접근 허용할 외부 url 지정(여러개 가능)
두개는 <code>.</code> 으로 연결시키기</p>
<pre><code class="language-java">registry.addMapping(&quot;/**&quot;)
         .allowedOrigins(&quot;http://localhost:3000&quot;, &quot;http://localhost:4000&quot;);</code></pre>
<p>결과</p>
<pre><code class="language-java">package com.bitc.asynctestserveer.configuration;
</code></pre>
</li>
</ol>
<p>import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;</p>
<p>@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {</p>
<pre><code>@Override
public void addCorsMappings(CorsRegistry registry) {</code></pre><p>//        addMapping() : 지정한 패턴에 맞는 페이지에 대해서 접근 권한 확인
//        allowedOrigins(url) : 접근 허용할 외부 url
        registry.addMapping(&quot;/**&quot;)
                .allowedOrigins(&quot;<a href="http://localhost:3000&quot;">http://localhost:3000&quot;</a>, &quot;<a href="http://localhost:4000&quot;">http://localhost:4000&quot;</a>);</p>
<pre><code>}</code></pre><p>}</p>
<pre><code>![](https://velog.velcdn.com/images/i_like_monday/post/80b74fb1-84f7-4537-9423-13fed6a5f3b7/image.png)![](https://velog.velcdn.com/images/i_like_monday/post/8b7eeeb1-1748-4b00-b2b6-ee73df87d2d9/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React - 05]]></title>
            <link>https://velog.io/@i_like_monday/React-05</link>
            <guid>https://velog.io/@i_like_monday/React-05</guid>
            <pubDate>Wed, 04 Jan 2023 07:25:51 GMT</pubDate>
            <description><![CDATA[<h2 id="state">state</h2>
<h3 id="state-1">state</h3>
<ul>
<li>현재 컴포넌트의 상태를 나타내는 객체, 현재 컴포넌트에서 변경 가능한 객체, state 는 직접적인 수정이 불가능함(화면 렌더링과 관련이 있음)</li>
<li><code>setState()</code> : state 의 값을 변경하는 함수</li>
</ul>
<h3 id="리액트-생명주기">리액트 생명주기</h3>
<p>컴포넌트가 생성되고 내용이 변경되고 컴포넌트가 삭제되는 상태</p>
<ul>
<li>componentDidMount : 컴포넌트가 생성되고 화면에 렌더링 된 이후 실행되는 함수</li>
<li>componentDidUpdate : 컴포넌트의 상태가 변경된 후 실행되는 함수, props 변경(=부모가 주는 값 변경), setState()함수 호출, forceUpdate()를 통한 강제 업데이트 후 동작함</li>
<li>componentWillUnmount : 부모 컴포넌트에서 더이상 해당 컴포넌트를 사용하지 않아 삭제된 후 실행</li>
</ul>
<p>folder3/LinkButton.js</p>
<pre><code class="language-js">// folder3/LinkButton.js

import React from &quot;react&quot;;

class LinkButton extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      linked: false,
    }

    this.setState({
        linked: true,
      });
  }
}</code></pre>
<h3 id="timer-예제">timer 예제</h3>
<p>folder3/Notification.jsx</p>
<pre><code class="language-js">// folder3/Notification.jsx
import React from &quot;react&quot;;

const styles = {
  wrapper: {
    margin: 8,
    padding: 8,
    display: &quot;flex&quot;,
    flexDirection: &quot;row&quot;,
    border: &quot;1px solid grey&quot;,
    borderRadius: 16,
  },
  messageText: {
    color: &quot;black&quot;,
    fontSize: 16,
  },
};

class Notification extends React.Component {
  constructor(props) {
    super(props);

    this.state = {}
  }

  render() {
    return (
      &lt;div style={styles.wrapper}&gt;
        &lt;span style={styles.messageText}&gt;
          {this.props.message}
        &lt;/span&gt;
      &lt;/div&gt;
    )
  }

}

export default Notification;</code></pre>
<p>folder3/NotificationList.jsx</p>
<pre><code class="language-js">// folder3/NotificationList.jsx

import React from &quot;react&quot;;
import Notification from &quot;./Notification&quot;;

// 서버에서 전송되어야 할 데이터, 통신 모듈이 없기 때문에 임의로 만들어준 데이터
const msgData = [
  {id: 1, message: &quot;안녕하세요. 오늘 일정입니다.&quot;},
  {id: 2, message: &quot;점심 식사 시간입니다.&quot;},
  {id: 3, message: &quot;이제 곧 미팅이 시작됩니다.&quot;},
];

// 자바스크립트 타이머 객체 정보를 저장할 변수
let timer;

// 클래스 컴포넌트 사용
class NotificationList extends React.Component {

  // 클래스 컴포넌트의 생성자
  constructor(props) {
    super(props);

    // state 객체 선언, 클래스(객체)의 멤버 변수로 선언
    this.state = {
      // 메시지를 저장할 배열
      notification: [],
    };
  }

  // 컴포넌트가 마운트된 후 실행되는 생명주기 함수
  componentDidMount() {
    // object 타입을 사용한 확장표현식. 현재는 비어있음. 타이머가 돌면서 값 들어갈 예정
    const { notification } = this.state;

    // 타이머를 사용하여 지정된 시간을 간격으로 반복 실행 (1000 = 1초)
    timer = setInterval(() =&gt; {

      // 첫번째 : 0 &lt; 3 (true)
      // 두번째 : 1 &lt; 3 (true)
      // 지연 변수인 배열 notification 크기가 전연 변수인 배열 msgData 의 크기보다 작을 경우 실행되는 코드
      if (notification.length &lt; msgData.length) {
        const index = notification.length;

        // msgData[0] 값 push
        // msgData[1] 값 push
        notification.push(msgData[index]);

        // setState() 함수를 사용하여 state 객체의 notification 을 방금 업데이트한 값으로 수정
        this.setState({
          // 전역변수로 선언된 notification : 변수로 선언한 notification
          notification: notification,
        });
      }
      else {
        // timer 삭제
        clearInterval(timer);
      }
      // 1초 후 다시 실행
    }, 1000);
  }
  // 화면을 생성(렌더링)하는 함수
  render() {
    return (
      &lt;div&gt;
        {/* map : ES6 버전에서 추가된 배열 관련 함수, 지정한 배열의 크기만큼 반복하고 결과를 배열로 반환하는 함수 */}
        {this.state.notification.map((item) =&gt; {
          return &lt;Notification key={item.id} message = {item.message} /&gt;;
        })}
      &lt;/div&gt;
    );
  }
}

export default NotificationList;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/6e875283-2e7e-408b-b256-b276fbd8bfd2/image.png" alt=""></p>
<hr>
<h3 id="strict">strict</h3>
<ul>
<li>strictMode : 자바스크립트의 문법을 엄격하게 확인한다는 의미</li>
<li>React.strictMode : 배포 버전에는 제외되고 개발 버전에서만 동작하는 엄격 모드, 몇 가지 함수를 중복 실행하여 잘못된 것이 없는지 개발자에게 확인하도록 함(생략가능)</li>
</ul>
<p>index.js</p>
<pre><code class="language-js">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;;
import &#39;./index.css&#39;;
import App from &#39;./App&#39;;
import reportWebVitals from &#39;./reportWebVitals&#39;;
import App2 from &quot;./App2&quot;;
import App3 from &quot;./App3&quot;;
import App4 from &quot;./App4&quot;;
import App5 from &quot;./App5&quot;;

const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(

  // strictMode : 자바스크립트의 문법을 엄격하게 확인한다는 의미
  // React.strictMode : 배포 버전에는 제외되고 개발 버전에서만 동작하는 엄격 모드, 몇 가지 함수를 중복 실행하여 잘못된 것이 없는지 개발자에게 확인하도록 함(생략가능)
  &lt;React.StrictMode&gt;
  {/*   &lt;App /&gt;*/}
  {/*   &lt;App2 /&gt;*/}
  {/*   &lt;App3 /&gt;*/}
  {/*   &lt;App4 /&gt;*/}
    &lt;App5 /&gt;
  &lt;/React.StrictMode&gt;
);


reportWebVitals();</code></pre>
<hr>
<h3 id="setstate">setState()</h3>
<p>countButton.jsx</p>
<pre><code class="language-js">// folder3/CountButton.jsx : setState() 익히기

import React from &quot;react&quot;;
import Button from &quot;react-bootstrap/Button&quot;;
import {logDOM} from &quot;@testing-library/react&quot;;

class CountButton extends React.Component {
  constructor(props) {
    super(props);

    // state 객체 생성
    this.state = {
      count: 0,
    }

    // 객체 멤버 변수 count 선언
    this.count = this.state.count;
  }

  // 메서드 2개 생성
  countUp = () =&gt; {
    console.log(&quot;이전 this.count : &quot; + this.count);
    this.count = this.count + 1

    // setState 함수를 실행해야만 state 의 값이 수정이 되고, state 의 값이 수정되면 render 함수가 재실행되면서 화면을 다시 그려줌
    this.setState({
      count: this.count
    });
    console.log(&quot;+ 사용 후 this.count : &quot; + this.count);
  };
  countDown = () =&gt; {
    console.log(&quot;이전 this.count : &quot; + this.count);
    this.count = this.count - 1
    this.setState({
      count: this.count
    });
    console.log(&quot;- 사용 후 this.count : &quot; + this.count);
  };


  render() {
    return (
      &lt;div&gt;
        &lt;label htmlFor=&quot;&quot; className={&quot;form-label&quot;}&gt;count : &lt;span&gt;{this.count}&lt;/span&gt;&lt;/label&gt;&lt;br/&gt;
        &lt;Button variant={&quot;primary&quot;} onClick={this.countUp} &gt; + &lt;/Button&gt;
        &lt;Button variant={&quot;danger&quot;} onClick={this.countDown}&gt; - &lt;/Button&gt;
      &lt;/div&gt;
    );
  }
}

export default CountButton;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/da5e9b59-b66c-4887-b6ca-55e7492ef8ff/image.png" alt=""></p>
<hr>
<h2 id="hooks">hooks</h2>
<p>기존의 존재하는 기능을 추가로 함께 사용하도록 하는 것, state 와 리액트 생명주기 기능을 원하는 시점에 실행할 수 있도록 하는 기능
훅을 사용 시 미리 해당 함수를 import 해서 사용해야 함</p>
<ul>
<li><code>useState</code> : state 객체를 함수 컴포넌트에서 사용할 수 있도록 하는 기능</li>
<li><code>useEffect</code> : useState() 와 함께 가장 많이 사용되는 훅, 리액트 생명주기 함수인 <u>componentDidMount</u>, <u>componentDidUpdate</u>, <u>componentWillUnmount</u> 의 기능을 하나로 합한 기능</li>
<li><code>useMemo</code> : 재 렌더링 시 연산량이 높은 작업을 반복하는 것을 피할 수 있게 하는 기능 (거의 안씀)</li>
<li><code>useCallback</code> : useMemo 와 비슷한 기능, 값이 아닌 함수를 반환함 (거의 안씀)</li>
<li><code>useRef</code> : 지정한 컴포넌트에 접근할 수 있는 객체 (거의 안씀)</li>
</ul>
<h3 id="usestate">useState</h3>
<p>클래스 컴포넌트에 존재하는 state 객체를 함수 컴포넌트에서 사용할 수 있도록 함</p>
<ul>
<li>사용법<pre><code class="language-js">const[변수명, set함수명] = useState(초기값);</code></pre>
<code>변수명</code> : state 로 지정할 지역변수명
<code>set함수명</code> : state 로 지정된 변수명에 접두사 set 을 붙여서 사용, 카멜명명법 사용
<code>useState(초기값)</code> : 확장표현식을 통해서 지정한 변수를 state 객체로 설정함, 초기값은 state 로 설정된 변수의 초기값을 말함
state 로 지정할 변수가 여러개일 경우 useState() 를 <u>변수의 수 만큼</u> 실행해야 함</li>
</ul>
<h3 id="useeffect">useEffect</h3>
<p>클래스 컴포넌트에 존재하는 생명주기 함수를 사용할 수 있도록 함
사용법</p>
<pre><code class="language-js">useEffect(이펙트함수, 의존성배열);</code></pre>
<h3 id="이펙트함수-콜백-함수">이펙트함수(= 콜백 함수)</h3>
<p>이펙트 함수는 기본적으로 컴포넌트가 처음 렌더링된 후 데이터 업데이트에 의한 재렌더링 시 실행됨
이펙트 함수를 마운트, 언마운트 시 각각 1번씩만 실행하고자 할 경우 의존성 배열에 빈 배열을 사용(componentDidMount, componentWillUnmount 기능 수행)</p>
<h3 id="의존성-배열">의존성 배열</h3>
<p>콜백 함수인 이펙트 함수가 의존하고 있는 배열, 해당 의존성 배열 안에 있는 변수 중 하나라도 값이 변경되면 실행
의존성 배열이 없을 경우 화면이 재 렌더링 된 이후에 실행(componentDidUpdate 기능 수행)</p>
<blockquote>
<p>Hook 의 규칙 :</p>
</blockquote>
<ol>
<li>Hook은 무조건 최상위 레벨에서만 호출 (if, for 문 내부에서 훅을 호출하면 안됨)</li>
<li>Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 함</li>
<li>함수 컴포넌트에서만 Hook을 사용할 수 있음
일반적인 자바스크립트 함수에서 Hook을 호출하면 안됨</li>
</ol>
<h3 id="커스텀-훅">커스텀 훅</h3>
<p>  리액트에서 제공하는 훅이 아닌 사용자가 필요에 의해서 생성하여 사용하는 훅
  <code>이름에 접두사로 use 를 사용</code>하고, 함수 내부에서 다른 훅을 호출하는 단순 자바스크립트 함수
  파라미터 및 반환값을 사용자가 직접 지정할 수 있음
  중복되는 로직을 커스텀 훅으로 설정하여 재사용하기 위함
  이름의 접두사로 <code>use</code> 를 사용하지 않을 경우 함수 내부에서 훅을 사용하는지 판단할 수 없음</p>
<p>folder3/CountButton2.jsx</p>
<pre><code class="language-js">// folder3/CountButton2.jsx : hooks 익히기

// useState() 훅을 사용하기 위해서 미리 import
import React, {useState, useEffect} from &quot;react&quot;;

// 함수 컴포넌트 사용
function CountButton2(props) {
  // 지역변수
  // let count = 0;

  // useState 를 통해서 state 객체에 추가함
  const [count, setCount] = useState(0);

  // [deps] 값 안줬으면 컴포넌트 DidMount 될때마다 동작, [deps] 값을 [count]로 줬으면 count 값이 변경될때마다 동작
  // useEffect 를 사용하여 componentDidMount, componentDidUpdate 를 구현함
  useEffect(() =&gt; {
    document.title = `총 ${count}회 클릭했습니다.`
  }/*[count]*/);


  // 함수 선언
  const countUp = () =&gt; {
    // count++; &lt;- 지역변수 사용했을때 코드.(주석처리)

    setCount(count + 1);

    console.log(`count++ : ${count}`);
  }
  const countDown = () =&gt; {
    // count--; &lt;- 지역변수 사용했을때 코드.(주석처리)

    // setCount(count - 1);

    // 콜백함수의 형태도 사용 가능
    setCount(() =&gt; {
      return count - 1;
    });

    console.log(`count-- : ${count}`);
  }

  return (
    &lt;div&gt;
      &lt;label htmlFor=&quot;&quot; className={&quot;form-label&quot;}&gt;count : &lt;span&gt;{count}&lt;/span&gt;&lt;/label&gt;&lt;br/&gt;
      &lt;button className={&quot;btn btn-outline-primary&quot;} onClick={countUp}&gt; + &lt;/button&gt;
      &lt;button className={&quot;btn btn-outline-danger&quot;} onClick={countDown}&gt; - &lt;/button&gt;
    &lt;/div&gt;
  );
}

export default CountButton2;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/04ff821c-e91f-48f5-97f5-ed7cbd2a843c/image.png" alt=""></p>
<hr>
<h3 id="커스텀-훅-1">커스텀 훅</h3>
<p>이름에 use 를 접두사로 사용
매개변수, 반환값을 사용자 마음대로 설정
내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함
props 가 아닌 initValue 인 이유 : 커스텀 훅이라서</p>
<p>folder3/UseCounter.jsx</p>
<pre><code class="language-js">// folder3/UseCounter.jsx : 커스텀 훅


import React, {useState} from &quot;react&quot;;

// 커스텀 훅
// 이름에 use 를 접두사로 사용
// 매개변수, 반환값을 사용자 마음대로 설정
// 내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함
// props 가 아닌 initValue 인 이유 : 커스텀 훅이라서

// 커스텀 훅으로 설정
// 매개변수를 마음대로 설정함
function UseCounter(initValue) {

  // state 사용을 위해서 useState 설정을 걸어줌
  const [count, setCount] = useState(initValue);

  // 함수 실행 시 state 를 수정하기 위함 setCount 를 실행
  const increaseCount = () =&gt; {
    // 자바스크립트에서 지원하는 수학 클래스의 max 함수를 사용하여 0 이하의 값을 사용할 수 없도록 제약을 걸어놓음
    setCount((count) =&gt; {return count + 1});
  }

  const decreaseCount = () =&gt; {
    setCount((count) =&gt; Math.max(count - 1, 0));
  }
  // math 사용한 코드와 동일
  // setCount((count) =&gt; {
  //   count--;
  //   if (count &lt; 0) {
  //     count = 0;
  //   }
  //   return count;
  // });
  return[count, increaseCount, decreaseCount];
}

export default UseCounter;</code></pre>
<p>Accomodate.jsx</p>
<pre><code class="language-js">// folder3/Accommodate.jsx : 커스텀 훅

import React, {useState, useEffect} from &quot;react&quot;;
// 커스텀 훅 (useCounter)
import useCounter from &quot;./UseCounter&quot;;


const MAX_CAPACITY = 10; // 최대값


function Accommodate(props) {
  // state 를 사용하기 위해 useState() 를 설정
  const [isFull, setIsFull] = useState(false);

  // 원래 코드 :
  // const[count, setCount] = useState(0); -&gt; 하위에 함수 만들어주고....
  // 해당 컴포넌트 내부에서 생성해야 할 state 객체 및 setState 를 커스텀 훅을 통해서 생성함
  // 커스텀 훅을 사용했기 때문에 재활용이 가능함
  const [count, increaseCount, decreaseCount] = useCounter(0);

  // 리액트 생명주기 함수를 사용하기 위해서 useEffect 를 설정함
  // 의존성 배열이 없을 겨우 componentDidMount, componentWillUnmount 를 실행하는 것과 같은 효과
  useEffect(() =&gt; {
    console.log(&quot;====================&quot;);
    console.log(&quot;useEffect() is called&quot;);
    console.log(`isFull : ${isFull}`);
  }, []);

  // 의존성 배열에 count 를 설정하여 count 값이 수정되면 componentDidUpdate 를 실행하는 것과 같은 효과 발생
  useEffect(() =&gt; {
    setIsFull(count &gt;= MAX_CAPACITY);
    console.log(&quot;현재 count값 : &quot; + count);
  }, [count]);

  return (
    &lt;div&gt;
      {/* 현재 state 로 설정된 count 의 값을 출력 */}
      &lt;p&gt;{`총 ${count}명 수용했습니다.`}&lt;/p&gt;
      {/* 커스텀 훅을 통해서 만들어진 사용자 입장/퇴장 함수를 버튼에 등록 */}
      &lt;button className={&quot;btn btn-warning&quot;} onClick={increaseCount} disabled={isFull}&gt;입장&lt;/button&gt;
      &lt;button className={&quot;btn btn-info&quot;} onClick={decreaseCount}&gt;퇴장&lt;/button&gt;
      {/* isFull 이 true 면 뒤에거 확인(실행), isFull 이 false 라면 &amp;&amp; 기호 뒤에는 아예 읽지도 않음 = 렌더링자체가 안됨 */}
      {isFull &amp;&amp; &lt;p style={{color: &quot;red&quot;}}&gt;정원이 가득찼습니다.&lt;/p&gt;}
    &lt;/div&gt;
  )

}

export default Accommodate;</code></pre>
<h2 id="문제1">문제1</h2>
<p>숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를 출력하는 리액트 컴포넌트를 작성하세요</p>
<ul>
<li>input 태그를 통해서 숫자를 입력(태그이름 : num1, num2, result)</li>
<li>버튼 5개 ( + - * / = )<ul>
<li>컴포넌트 이름 : Calulator.jsx</li>
</ul>
</li>
<li>useState 총 3개 사용 (함수 컴포넌트로 작성)</li>
</ul>
<p>folder/Calculator.jsx</p>
<pre><code class="language-js">// folder/Calculator.jsx

/*
문제1) 숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를 출력하는 리액트 컴포넌트를 작성하세요
- input 태그를 통해서 숫자를 입력(태그이름 : num1, num2, result)
- 버튼 5개 ( + - * / = )
  - 컴포넌트 이름 : Calulator.jsx
- useState 총 3개 사용 (함수 컴포넌트로 작성)
*/

import React, {useState} from &quot;react&quot;;

let calResult = 0;
function Calculator(props) {
  const [num1, setNum1] = useState(0);
  const [num2, setNum2] = useState(0);
  const [result, setResult] = useState(0);

  const plus = () =&gt; {
    const number1 = parseInt(num1);
    const number2 = parseInt(num2);
    calResult = number1 + number2;
  }
  const minus = () =&gt; {
    const number1 = parseInt(num1);
    const number2 = parseInt(num2);
    calResult = number1 - number2;
  }
  const multiply = () =&gt; {
    const number1 = parseInt(num1);
    const number2 = parseInt(num2);
    calResult = number1 * number2;
  }
  const division = () =&gt; {
    const number1 = parseInt(num1);
    const number2 = parseInt(num2);
    calResult = number1 / number2;
  }

  const equal = () =&gt; {
    setResult(calResult);
  }
  return (
    &lt;div&gt;
      &lt;hr/&gt;
      &lt;div className={&quot;my-3 text-center&quot;}&gt;
        &lt;span&gt;숫자1 &lt;/span&gt;&lt;input type=&quot;text&quot; value={num1} onChange={(e) =&gt; (setNum1(parseInt(e.target.value)))}/&gt;
        &lt;span className={&quot;ms-3&quot;}&gt;숫자2 &lt;/span&gt;&lt;input type=&quot;text&quot; value={num2} onChange={(e) =&gt; (setNum2(parseInt(e.target.value)))}/&gt;
      &lt;/div&gt;
      &lt;div className={&quot;text-center&quot;}&gt;
        &lt;button onClick={plus} className={&quot;btn btn-info me-5&quot;}&gt; + &lt;/button&gt;
        &lt;button onClick={minus} className={&quot;btn btn-info me-5&quot;}&gt; - &lt;/button&gt;
        &lt;button onClick={multiply} className={&quot;btn btn-info me-5&quot;}&gt; * &lt;/button&gt;
        &lt;button onClick={division} className={&quot;btn btn-info me-5&quot;}&gt; / &lt;/button&gt;
        &lt;button onClick={equal} className={&quot;btn btn-success&quot;}&gt; = &lt;/button&gt;
      &lt;/div&gt;
      &lt;div className={&quot;text-center mt-4&quot;}&gt;
      &lt;span&gt;결과 = &lt;/span&gt;&lt;input type=&quot;text&quot; value={result}/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Calculator;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/c0b604f2-d9c2-4ce1-bb46-4699f62724c0/image.png" alt=""></p>
<hr>
<h2 id="event">Event</h2>
<p>리액트는 html 과 같은 이벤트를 가지고 있음
카멜명명법을 사용하므로 <code>onclick=&quot;sum()&quot;</code> =&gt; <code>onClick={sum}</code> 으로 변경되어 사용</p>
<ul>
<li>매개변수 전달 시 이벤트 부분에 화살표 콜백함수를 사용
<code>onClick={() =&gt; sum(10)}</code></li>
<li>이벤트 사용 시 이벤트 핸들러도 매개변수로 전달이 가능함
<code>onClick={(event) =&gt; sum(10, event)}</code></li>
</ul>
<p>folder3/Events.jsx</p>
<pre><code class="language-js">//folder3/Events.jsx

import React from &quot;react&quot;;

function Events() {

  // 기본 함수를 클릭이벤트와 연동
  const click1 = () =&gt; alert(&quot;일반 클릭 이벤트&quot;);

  // 매개변수가 있는 함수를 클릭이벤트와 연동
  const click2 = (item) =&gt; alert(`매개변수 값 : ${item}, \n매개변수가 있는 이벤트`);

  // 매개변수로 이벤트 핸들러(= 이벤트 객체)를 사용하는 함수를 클릭이벤트아 연동
  const click3 = (item, event) =&gt; {
    alert(`매개변수와 event 객체가 있는 클릭 이벤트
매개변수 값 : ${item}, 이벤트 객체 : ${event.type}`);
  }


  return (
    &lt;div&gt;
      &lt;button type={&quot;button&quot;} className={&quot;btn btn-primary&quot;} onClick={click1}&gt;일반 클릭 이벤트&lt;/button&gt;
      &lt;button type={&quot;button&quot;} className={&quot;btn btn-success&quot;} onClick={() =&gt; click2(100)}&gt;매개변수가 있는 클릭 이벤트&lt;/button&gt;
      &lt;button type={&quot;button&quot;} className={&quot;btn btn-info&quot;} onClick={(event) =&gt; click3(200,event)}&gt;event 객체가 있는 클릭 이벤트&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default Events;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/6493c1ca-086b-4bf6-8d64-ce29b49bb809/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/41ffff1a-ae1b-4ebb-ab75-c0ba98731666/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/fbd1a85b-7519-4284-b887-b3265a4af1fc/image.png" alt=""></p>
<hr>
<h3 id="confirmbutton">confirmButton</h3>
<p>folder/ConfirmButon.jsx</p>
<pre><code class="language-js">// folder/ConfirmButon.jsx

import React, {useState} from &quot;react&quot;;

function ConfirmButton(props) {
  const [isConfirmed, setIsConfirmed] = useState(false);
  const handleConfirm = () =&gt; {
    setIsConfirmed((prevConfirmed) =&gt; !isConfirmed);
    setTimeout(() =&gt; {
      setIsConfirmed(false);
    }, 1000)
  };

  return (
    &lt;div&gt;
     &lt;button type={&quot;button&quot;} className={&quot;btn btn-primary&quot;} onClick={handleConfirm} disabled={isConfirmed}&gt;
       {isConfirmed ? &quot;확인 완료&quot; : &quot;확인 하기&quot;}
     &lt;/button&gt;
    &lt;/div&gt;
  );
}

export default ConfirmButton;</code></pre>
<ul>
<li>클릭 전
<img src="https://velog.velcdn.com/images/i_like_monday/post/55f458b4-a5d3-4302-84e3-91451cf18ba6/image.png" alt=""></li>
<li>클릭 후
<img src="https://velog.velcdn.com/images/i_like_monday/post/24e7479d-5c3a-4e19-a794-fa8119a98d71/image.png" alt=""></li>
<li>1초 후
<img src="https://velog.velcdn.com/images/i_like_monday/post/77cd5a17-f086-4d85-99eb-941349924da1/image.png" alt=""></li>
</ul>
<hr>
<p>App5.jsx</p>
<pre><code class="language-js">// App5.jsx

import React from &quot;react&quot;;
import NotificationList from &quot;./folder3/NotificationList&quot;;
import CountButton from &quot;./folder3/CountButton&quot;;
import CountButton2 from &quot;./folder3/CountButton2&quot;;
import Accommodate from &quot;./folder3/Accommodate&quot;;
import Calculator from &quot;./folder3/Calculator&quot;;
import Events from &quot;./folder3/Events&quot;;
import ConfirmButton from &quot;./folder3/ConfirmButton&quot;;

function App5() {
  return (
    &lt;div className={&quot;container&quot;}&gt;
      &lt;NotificationList/&gt;
      &lt;CountButton/&gt;
      &lt;CountButton2/&gt;
      &lt;Accommodate/&gt;
      &lt;Calculator/&gt;
      &lt;br/&gt;
      &lt;Events/&gt;
      &lt;br/&gt;
      &lt;ConfirmButton/&gt;
    &lt;/div&gt;
  );
}

export default App5;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 04]]></title>
            <link>https://velog.io/@i_like_monday/React-04</link>
            <guid>https://velog.io/@i_like_monday/React-04</guid>
            <pubDate>Tue, 03 Jan 2023 08:56:31 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-생성">프로젝트 생성</h1>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/f972f86b-63bf-4e4d-943e-9db6a9c0c78f/image.png" alt=""></p>
<p>2.7.0 버전 리액트 부트스트랩을 원래는 다운로드 받아서 써야 하는데 지금껏 사용해온 부트스트랩 방식과 너무 달라서 익히는데 시간이 걸림
-&gt; 부트스트랩 홈페이지에 있는 코드 붙여넣으면 사용하던 방식과 유사하게 쓸 수 있음
<a href="https://getbootstrap.com/">https://getbootstrap.com/</a>
<img src="https://velog.velcdn.com/images/i_like_monday/post/f0801b6c-a2e8-4ff1-8cb3-c5a84142d771/image.png" alt=""></p>
<ul>
<li>index.html<pre><code class="language-html">&lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD&quot; crossorigin=&quot;anonymous&quot;&gt;
  &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
  &lt;title&gt;React App&lt;/title&gt;</code></pre>
</li>
</ul>
<ul>
<li>App.js<pre><code class="language-js">import logo from &#39;./logo.svg&#39;;
import &#39;./App.css&#39;;
</code></pre>
</li>
</ul>
<p>function App() {
  return (
    <div className="App">
      &lt;button type={&quot;button&quot;}&gt;기본 버튼</button>
      &lt;button type={&quot;button&quot;} className={&quot;btn btn-primary&quot;}&gt;부트스트랩 적용 버튼</button>
      <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"
        >
          Learn React
        </a>
      </header></p>
<pre><code>&lt;/div&gt;</code></pre><p>  );
}</p>
<p>export default App;</p>
<pre><code>![](https://velog.velcdn.com/images/i_like_monday/post/107112f5-400c-4c4a-b105-77d854e2f501/image.png)

&gt;리액트 부트스트랩을 사용하고 싶다면 터미널창에서 현재 프로젝트까지 경로 설정 후 명령어 쓰기(node.js 는 폴더를 기반으로 위치를 지정하기 때문)
`npm install react-bootstrap bootstrap`
![](https://velog.velcdn.com/images/i_like_monday/post/1e3e1e93-29d3-4639-8d43-0b0d3154637b/image.png)
App.js
```js
import Button from &quot;react-bootstrap/Button&quot;;</code></pre><p>위와 같이 필요한 기능만 import 해서 사용 (사용법은 리액트 공식문서(<a href="https://ko.reactjs.org/)%EC%97%90%EC%84%9C">https://ko.reactjs.org/)에서</a> 확인)</p>
<pre><code class="language-js">&lt;button type={&quot;button&quot;}&gt;기본 버튼&lt;/button&gt;
&lt;button type={&quot;button&quot;} className={&quot;btn btn-primary&quot;}&gt;부트스트랩 적용 버튼&lt;/button&gt;
&lt;Button type={&quot;button&quot;} variant={&quot;success&quot;}&gt;react용 부트스트랩 적용 버튼&lt;/Button&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/263a13ea-9888-4402-ac22-bb17871d709a/image.png" alt=""></p>
<hr>
<h2 id="문법">문법</h2>
<h3 id="jsx">JSX</h3>
<p>자바스크립트 + html/xml, 하나의 파일에 자바스크립트와 html 을 동시에 작성</p>
<h3 id="컴포넌트">컴포넌트</h3>
<ol>
<li>함수형 컴포넌트 : 현재 많이 사용되는 방식, 사용이 간편함 (클래스형 컴포넌트의 기능을 100% 대체할 수 있음), 자바스크립트 함수를 작성하는 방식</li>
<li>클래스형 컴포넌트 : 기존에 많이 사용되던 방식, React.Component 를 상속받아 구현함, 컴포넌트 구성요소, 리액트 생명주기를 모두 포함하고 있음</li>
</ol>
<ul>
<li>리액트를 구성하는 최소 UI 단위, 데이터(props)를 입력받아 view(state)상태에 따라 화면을 출력하는 함수</li>
<li>컴포넌트의 이름은 항상 대문자로 시작 (리액트는 소문자로 시작하는 컴포넌트를 html 태그로 인식함 -&gt; html 태그는 <code>&lt;button&gt;</code>, 리액트 컴포넌트는 <code>&lt;Button&gt;</code>)</li>
<li>UI 를 재사용 가능한 개별적인 여러 조각으로 나누어서 화면 구현</li>
</ul>
<h3 id="props">props</h3>
<p>컴포넌트간의 데이터를 주고 받기 위한 JS 객체, properties 의 줄임말, 읽기 전용, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달 시 주로 사용</p>
<h3 id="state">state</h3>
<p>현재 컴포넌트의 상태를 표시하는 객체, setState() 를 통해서 데이터를 수정, 데이터 수정 시 화면에 재렌더링됨</p>
<h3 id="hooks">hooks</h3>
<p>리액트 16.8버전에서 추가된 기능, 상태 변경 및 리액트 생명주기에 관련된 함수를 사용할 수 있게 해주는 기능, 리액트 hooks 를 사용하면 클래스 컴포넌트를 사용할 필요가 없음</p>
<h3 id="컨텍스트">컨텍스트</h3>
<p>데이터 전달 객체, 컴포넌트 간의 데이터 전달 시 props 를 사용하면 순차적으로 데이터를 전달함, 컨텍스트는 위치에 상관없이 데이터를 바로 전달할 수 있음</p>
<p>전체 화면 내부에 메인화면이 있고 그 내부에 작은화면 A, B 로 나눠진다면 전체에서 B로 데이터를 바로 전달하는것이 불가능함</p>
<p>전체에서 메인으로 보내고, 메인에서 B로 보내야 함(순차적으로)</p>
<p>이것이 간단한 화면에서는 문제가 되지 않지만 화면이 복잡해지면 계속해서 함수를 만들어줘야함(사용하지 않는 화면(ex: main) 에서도)</p>
<p>이것을 쉽게 해주는것이 컨텍스트이다. 전체에서 컨텍스트로 -&gt; 컨텍스트에서 바로 B로</p>
<h3 id="react-router">react-router</h3>
<p>각 페이지의 경로를 구성하는 라이브러리.</p>
<p>리액트는 싱글 페이지이기 때문에 화면이 하나밖에 없음. 내부 컨텐츠만 바뀌면서 움직일 뿐. (= 리액트 라우터) 클릭을 하면 다른 페이지가 열린것처럼 보이지만 사실은 내부에서 컨텐츠가 움직이고 사라지고 보이게 된 것</p>
<hr>
<h2 id="jsx-문법">JSX 문법</h2>
<ol>
<li><p>반드시 1개의 부모 요소가 다른 요소를 감싸는 형태로 구성해야 함(부모 요소가 하나이기만 하면 <code>&lt;div&gt;</code>가 아니라 <code>&lt;p&gt;</code> 태그도 부모요소가 될 수 있음</p>
</li>
<li><p>자바스크립트 표현식 사용 가능</p>
</li>
</ol>
<ul>
<li><p>2-1. { } 안에 자바스크립트를 사용할 수 있음</p>
<ul>
<li><p>App.js</p>
<pre><code class="language-js">function App() {
let num1 = 10;
return (
&lt;div className=&quot;App&quot;&gt;
 &lt;button type={&quot;button&quot;}&gt;기본 버튼&lt;/button&gt;
 &lt;button type={&quot;button&quot;} className={&quot;btn btn-primary&quot;}&gt;부트스트랩 적용 버튼&lt;/button&gt;
 &lt;Button type={&quot;button&quot;} variant={&quot;success&quot;}&gt;react용 부트스트랩 적용 버튼&lt;/Button&gt;

 &lt;p&gt;{num1 + 10}&lt;/p&gt;</code></pre>
</li>
<li><p>2-2 if 문은 표현식이 아니기 때문에 JSX 에서 사용할 수 없음 (대신에 삼항연산자를 사용해야 함)
{ } 표현식 내에서 자바스크립트의 if 문을 사용할 수 없으므로 외부에서 미리 계산</p>
<ul>
<li>App.js<pre><code class="language-js">function App() {
let num1 = 10;
const flag = false;
let result;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>// {} 표현식 내에서 자바스크립트의 if 문을 사용할 수 없으므로 외부에서 미리 계산
if (flag) {
  result = <div>결과가 true</div>
} else {
  result = <div>결과가  false</div>
}
return (</p>
  <div className="App">
    <button type={"button"}>기본 버튼</button>
    <button type={"button"} className={"btn btn-primary"}>부트스트랩 적용 버튼</button>
    <Button type={"button"} variant={"success"}>react용 부트스트랩 적용 버튼</Button>

<pre><code>&lt;p&gt;{num1 + 10}&lt;/p&gt;
&lt;br/&gt;&lt;hr/&gt;
{/* if 쓰면 바로 에러남! 사용하고 싶으면 삼항연산자 사용하기*/}
{/*{if (flag) {*/}
{/*  */}
{/*}}*/}
{/* if 문 사용할 수 없어서 삼항연산자 사용함*/}
&lt;div&gt;{flag === true ? 100 : 10}&lt;/div&gt;
&lt;div&gt;{result}&lt;/div&gt;</code></pre><pre><code>
3. html 속성을 &lt;u&gt;카멜명명법&lt;/u&gt;으로 사용
    `font-size` -&gt; `fontSize` 로 사용함
    `class` -&gt; `className` 으로 사용
4. JSX 문법은 html 태그 사용 시, 반드시 시작 태그와 끝 태그를 모두 입력해야 함
     축약 형식으로 시작 태그 끝에 / 를 사용하는 방식도 상관없음</code></pre></li>
</ul>
<hr>
<h2 id="컴포넌트-1">컴포넌트</h2>
<h3 id="클래스-컴포넌트">클래스 컴포넌트</h3>
<ol>
<li>모든 컴포넌트는 React 를 import 해서 사용함</li>
<li>클래스 컴포넌트는 React.Component 상속받아 클래스를 생성</li>
<li>클래스 컴포넌트를 export default 로 설정해줘야 외부에서 사용이 가능함</li>
</ol>
<ul>
<li>ClassComponent.jsx<pre><code class="language-jsx">import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>class ClassComponent extends React.Component {</p>
<p>}</p>
<p>export default ClassComponent;</p>
<pre><code>4. 클래스 컴포넌트에는 render() 메소드가 존재함, render() 메소드에서 jsx 문법을 사용함
5. 클래스 컴포넌트는 리액트 생명주기 메소드를 사용할 수 있음
6. 생성자(constructor) 사용가능, 상태 표현을 위한  state 를 설정(없으면 안써도 됨)
- ClassComponent.jsx : 기본형
```jsx
import React from &quot;react&quot;;

class ClassComponent extends React.Component {

  constructor(props) {
    super(props);
    this.state = {}
  }

  render() {
    return (
      &lt;div&gt;&lt;/div&gt;
    );
  }

  componentDidMount() {
  }
}

export default ClassComponent;</code></pre><hr>
<h3 id="함수-컴포넌트-훨씬-사용이-간단">함수 컴포넌트 (훨씬 사용이 간단!)</h3>
<ol>
<li>모든 컴포넌트는 React 를 import 해서 사용함</li>
<li>함수 컴포넌트는 그냥 함수 생성</li>
<li>함수 컴포넌트를 export default 로 설정해줘야 외부에서 사용이 가능함</li>
<li>함수 컴포넌트에서는 return 에서 jsx 문법을 사용함</li>
</ol>
<ul>
<li>FunctionComponent.jsx<pre><code class="language-jsx">import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>function FunctionComponent(props) {
  return (
    <div>
      <p>함수 컴포넌트 사용</p>
    </div>
  );
}</p>
<p>export default FunctionComponent;</p>
<pre><code>5. state 변경 및 생명주기에 관련된 함수를 사용하기 위해서 hooks 를 사용함 
 `import {useState} from &quot;react&quot;;` 
```js
import React, {useState} from &quot;react&quot;;</code></pre><hr>
<p>App.js의 return (...) 내부에 코드 추가</p>
<pre><code class="language-js">&lt;ClassComponent/&gt;
&lt;FunctionComponent/&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/d6f5c9b5-f340-43e0-bfdb-a1eea50d8eae/image.png" alt=""></p>
<hr>
<h3 id="화면-그려보기-실습">화면 그려보기 실습</h3>
<p>App2.jsx 파일 생성</p>
<ul>
<li>App2.jsx 기본형 만들기<pre><code class="language-js">// App2.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>function App2() {
  return (
    <div></p>
<pre><code>&lt;/div&gt;</code></pre><p>  );
}</p>
<p>export default App2;</p>
<pre><code>
Index.js 의 `&lt;App/&gt;` 주석처리 후 `&lt;App2/&gt;` 추가
```js
// Index.js
root.render(
  &lt;React.StrictMode&gt;
    {/*&lt;App /&gt;*/}
    &lt;App2 /&gt;
  &lt;/React.StrictMode&gt;
);</code></pre><p>화면 열어보면 빈 화면, F12로 확인해보면 root 내부의 <code>&lt;div&gt;</code> 태그가 비어있는것 확인 가능
<img src="https://velog.velcdn.com/images/i_like_monday/post/e9ccfa1e-9e5c-4deb-a9ae-69581c1a6a30/image.png" alt=""></p>
<p>App2.jsx : 기본 페이지 만들기</p>
<pre><code class="language-js">// App2.jsx
// App2.jsx
import React from &quot;react&quot;;

// object 타입
const styles = {
  fakeImg: {
    height: 200,
    backgroundColor: &quot;#aaa&quot;,
  },
};

function App2() {
  return (
    &lt;div&gt;
      &lt;div className={&quot;p-5 bg-primary text-white text-center&quot;}&gt;
        &lt;h1&gt;컴포넌트 연습용 부트스트랩 기본 페이지 만들기 1&lt;/h1&gt;
        &lt;p&gt;반응형 웹이 지원하는 페이지임&lt;/p&gt;
      &lt;/div&gt;

      &lt;nav className={&quot;navbar navbar-expand-sm bg-dark navbar-dark&quot;}&gt;
        &lt;div className={&quot;container-fluid&quot;}&gt;
          &lt;ul className={&quot;navbar-nav&quot;}&gt;
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link active&quot;} href={&quot;#&quot;}&gt;Active&lt;/a&gt;
            &lt;/li&gt;
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link&lt;/a&gt;
            &lt;/li&gt;
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link&lt;/a&gt;
            &lt;/li&gt;
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link disabled&quot;} href={&quot;#&quot;}&gt;Disabled&lt;/a&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/div&gt;
      &lt;/nav&gt;

      &lt;div className={&quot;container mt-5&quot;}&gt;
        &lt;div className={&quot;row&quot;}&gt;
          &lt;div className={&quot;col-sm-4&quot;}&gt;
            &lt;h2&gt;About Me&lt;/h2&gt;
            &lt;h5&gt;Photo of me:&lt;/h5&gt;
            &lt;div style={styles.fakeImg}&gt;Fake Image&lt;/div&gt;
            &lt;p&gt;아무 노래나 일단 틀어~~&lt;/p&gt;
            &lt;ul className={&quot;nav nav-pills flex-column&quot;}&gt;
              &lt;li className={&quot;nav-item&quot;}&gt;
                &lt;a className={&quot;nav-link active&quot;} href=&quot;#&quot;&gt;Active&lt;/a&gt;
              &lt;/li&gt;
              &lt;li className={&quot;nav-item&quot;}&gt;
                &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link&lt;/a&gt;
              &lt;/li&gt;
              &lt;li className={&quot;nav-item&quot;}&gt;
                &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link&lt;/a&gt;
              &lt;/li&gt;
              &lt;li className={&quot;nav-item&quot;}&gt;
                &lt;a className=&quot;nav-link disabled&quot; href={&quot;#&quot;}&gt;Disabled&lt;/a&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
            &lt;hr className={&quot;d-sm-none&quot;}/&gt;
          &lt;/div&gt;
          &lt;div className={&quot;col-sm-8&quot;}&gt;
            &lt;h2&gt;제목 부분&lt;/h2&gt;
            &lt;h5&gt;부제목 부분, 2023-01-03&lt;/h5&gt;
            &lt;div style={styles.fakeImg}&gt;&lt;/div&gt;
            &lt;p&gt;점심 먹고 싶다 배고프다&lt;/p&gt;
            &lt;p&gt;리액트 수업 듣는 중... 오전 11:14&lt;/p&gt;

            &lt;h2&gt;제목 부분&lt;/h2&gt;
            &lt;h5&gt;부제목 부분, 2023-01-03&lt;/h5&gt;
            &lt;div style={styles.fakeImg}&gt;&lt;/div&gt;
            &lt;p&gt;점심 먹고 싶다 배고프다222&lt;/p&gt;
            &lt;p&gt;리액트 수업 듣는 중... 오전 11:15&lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className={&quot;mt-5 p-4 bg-dark text-white text-center&quot;}&gt;
        &lt;p&gt;Footer&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}


export default App2;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/473945cd-797d-46f6-a5c1-88c45dab3b86/image.png" alt=""></p>
<hr>
<ul>
<li>Contents.jsx 기본 코드<pre><code class="language-js">// Contents.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>function Contents() {
  return (
    <div></p>
<pre><code>&lt;/div&gt;</code></pre><p>  );
}</p>
<p>export default Contents;</p>
<pre><code>
---

### 화면 조각내기 - 1 

- App2.jsx
```jsx
// App2.jsx
import React from &quot;react&quot;;
import Contents from &quot;./Contents&quot;;
import Footer from &quot;./Footer&quot;;
import Header from &quot;./Header&quot;;
import Navigate from &quot;./Navigate&quot;;
import LinkList from &quot;./LinkList&quot;;
import Info from &quot;./Info&quot;;

// object 타입
const styles = {
  fakeImg: {
    height: 200,
    backgroundColor: &quot;#aaa&quot;,
  },
};

function App2() {
  return (
    &lt;div&gt;
      &lt;Header/&gt;
      {/* ------------------- Header.jsx 로 이동 ------------------------*/}
      {/*&lt;div className={&quot;p-5 bg-primary text-white text-center&quot;}&gt;*/}
      {/*  &lt;h1&gt;컴포넌트 연습용 부트스트랩 기본 페이지 만들기 1&lt;/h1&gt;*/}
      {/*  &lt;p&gt;반응형 웹이 지원하는 페이지임&lt;/p&gt;*/}
      {/*&lt;/div&gt;*/}

      &lt;Navigate/&gt;
      {/*------------------- Navigate.jsx 로 이동 ------------------------*/}
      {/*&lt;nav className={&quot;navbar navbar-expand-sm bg-dark navbar-dark&quot;}&gt;*/}
      {/*  &lt;div className={&quot;container-fluid&quot;}&gt;*/}
      {/*    &lt;ul className={&quot;navbar-nav&quot;}&gt;*/}
      {/*      &lt;li className={&quot;nav-item&quot;}&gt;*/}
      {/*        &lt;a className={&quot;nav-link active&quot;} href={&quot;#&quot;}&gt;Active&lt;/a&gt;*/}
      {/*      &lt;/li&gt;*/}
      {/*      &lt;li className={&quot;nav-item&quot;}&gt;*/}
      {/*        &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link&lt;/a&gt;*/}
      {/*      &lt;/li&gt;*/}
      {/*      &lt;li className={&quot;nav-item&quot;}&gt;*/}
      {/*        &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link&lt;/a&gt;*/}
      {/*      &lt;/li&gt;*/}
      {/*      &lt;li className={&quot;nav-item&quot;}&gt;*/}
      {/*        &lt;a className={&quot;nav-link disabled&quot;} href={&quot;#&quot;}&gt;Disabled&lt;/a&gt;*/}
      {/*      &lt;/li&gt;*/}
      {/*    &lt;/ul&gt;*/}
      {/*  &lt;/div&gt;*/}
      {/*&lt;/nav&gt;*/}

      &lt;div className={&quot;container mt-5&quot;}&gt;
        &lt;div className={&quot;row&quot;}&gt;
          &lt;div className={&quot;col-sm-4&quot;}&gt;
            &lt;Info/&gt;
            {/* ----------------------- Info.jsx 로 이동 -----------------------*/}
            {/*&lt;h2&gt;About Me&lt;/h2&gt;*/}
            {/*&lt;h5&gt;Photo of me:&lt;/h5&gt;*/}
            {/*&lt;div style={styles.fakeImg}&gt;Fake Image&lt;/div&gt;*/}
            {/*&lt;p&gt;아무 노래나 일단 틀어~~&lt;/p&gt;*/}

            &lt;LinkList/&gt;
            {/* ----------------------- LinkList.jsx 로 이동 -----------------------*/}
            {/*&lt;ul className={&quot;nav nav-pills flex-column&quot;}&gt;*/}
            {/*  &lt;li className={&quot;nav-item&quot;}&gt;*/}
            {/*    &lt;a className={&quot;nav-link active&quot;} href=&quot;#&quot;&gt;Active&lt;/a&gt;*/}
            {/*  &lt;/li&gt;*/}
            {/*  &lt;li className={&quot;nav-item&quot;}&gt;*/}
            {/*    &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link&lt;/a&gt;*/}
            {/*  &lt;/li&gt;*/}
            {/*  &lt;li className={&quot;nav-item&quot;}&gt;*/}
            {/*    &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link&lt;/a&gt;*/}
            {/*  &lt;/li&gt;*/}
            {/*  &lt;li className={&quot;nav-item&quot;}&gt;*/}
            {/*    &lt;a className=&quot;nav-link disabled&quot; href={&quot;#&quot;}&gt;Disabled&lt;/a&gt;*/}
            {/*  &lt;/li&gt;*/}
            {/*&lt;/ul&gt;*/}
            &lt;hr className={&quot;d-sm-none&quot;}/&gt;
          &lt;/div&gt;
          &lt;div className={&quot;col-sm-8&quot;}&gt;
            &lt;Contents/&gt;
            {/*------------Contents.jsx 로 이동----------------*/}
            {/*&lt;h2&gt;제목 부분&lt;/h2&gt;*/}
            {/*&lt;h5&gt;부제목 부분, 2023-01-03&lt;/h5&gt;*/}
            {/*&lt;div style={styles.fakeImg}&gt;&lt;/div&gt;*/}
            {/*&lt;p&gt;점심 먹고 싶다 배고프다&lt;/p&gt;*/}
            {/*&lt;p&gt;리액트 수업 듣는 중... 오전 11:14&lt;/p&gt;*/}

            &lt;Contents/&gt;
            {/*------------Contents.jsx 로 이동----------------*/}
            {/*&lt;h2&gt;제목 부분&lt;/h2&gt;*/}
            {/*&lt;h5&gt;부제목 부분, 2023-01-03&lt;/h5&gt;*/}
            {/*&lt;div style={styles.fakeImg}&gt;&lt;/div&gt;*/}
            {/*&lt;p&gt;점심 먹고 싶다 배고프다&lt;/p&gt;*/}
            {/*&lt;p&gt;리액트 수업 듣는 중... 오전 11:14&lt;/p&gt;*/}
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;Footer/&gt;
      {/*------------Footer.jsx 로 이동----------------*/}
      {/*&lt;div className={&quot;mt-5 p-4 bg-dark text-white text-center&quot;}&gt;*/}
      {/*  &lt;p&gt;Footer&lt;/p&gt;*/}
      {/*&lt;/div&gt;*/}
    &lt;/div&gt;
  );
}


export default App2;</code></pre><ul>
<li>navigate.jsx<pre><code class="language-js">// Navigate.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;</p>
<p>function Navigate() {
  return(
    <div>
      &lt;nav className={&quot;navbar navbar-expand-sm bg-dark navbar-dark&quot;}&gt;
        &lt;div className={&quot;container-fluid&quot;}&gt;
          &lt;ul className={&quot;navbar-nav&quot;}&gt;
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link active&quot;} href={&quot;#&quot;}&gt;Active</a>
            </li>
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link</a>
            </li>
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link&quot;} href={&quot;#&quot;}&gt;Link</a>
            </li>
            &lt;li className={&quot;nav-item&quot;}&gt;
              &lt;a className={&quot;nav-link disabled&quot;} href={&quot;#&quot;}&gt;Disabled</a>
            </li>
          </ul>
        </div>
      </nav>
    </div>
  );
}</p>
<p>export default Navigate;</p>
<pre><code>
- Info.jsx
```js
// Info.jsx

import React from &quot;react&quot;;

const styles = {
  fakeImg: {
    height: 200,
    backgroundColor: &quot;#aaa&quot;,
  },
};

function Info() {
  return (
    &lt;div&gt;
      &lt;h2&gt;About Me&lt;/h2&gt;
      &lt;h5&gt;Photo of me:&lt;/h5&gt;
      &lt;div style={styles.fakeImg}&gt;Fake Image&lt;/div&gt;
      &lt;p&gt;아무 노래나 일단 틀어~~&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default Info;</code></pre><ul>
<li>LinkList.jsx<pre><code class="language-js">// LinkList.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>function LinkList() {
  return (
    <div>
      &lt;ul className={&quot;nav nav-pills flex-column&quot;}&gt;
        &lt;li className={&quot;nav-item&quot;}&gt;
          &lt;a className={&quot;nav-link active&quot;} href=&quot;#&quot;&gt;Active</a>
        </li>
        &lt;li className={&quot;nav-item&quot;}&gt;
          &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link</a>
        </li>
        &lt;li className={&quot;nav-item&quot;}&gt;
          &lt;a className=&quot;nav-link&quot; href={&quot;#&quot;}&gt;Link</a>
        </li>
        &lt;li className={&quot;nav-item&quot;}&gt;
          &lt;a className=&quot;nav-link disabled&quot; href={&quot;#&quot;}&gt;Disabled</a>
        </li>
      </ul>
    </div>
  );
}</p>
<p>export default LinkList;</p>
<pre><code>
- Contents.jsx
```jsx
// Contents.jsx
import React from &quot;react&quot;;
// object 타입
const styles = {
  fakeImg: {
    height: 200,
    backgroundColor: &quot;#aaa&quot;,
  },
};

function Contents() {
  return (
    &lt;div&gt;
      &lt;h2&gt;제목 부분&lt;/h2&gt;
      &lt;h5&gt;부제목 부분, 2023-01-03&lt;/h5&gt;
      &lt;div style={styles.fakeImg}&gt;&lt;/div&gt;
      &lt;p&gt;점심 먹고 싶다 배고프다&lt;/p&gt;
      &lt;p&gt;리액트 수업 듣는 중... 오전 11:14&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default Contents;</code></pre><ul>
<li>Header.jsx<pre><code class="language-js">// Header.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;</p>
<p>function Header() {
  return(
    <div>
      &lt;div className={&quot;p-5 bg-primary text-white text-center&quot;}&gt;
        <h1>컴포넌트 연습용 부트스트랩 기본 페이지 만들기 1</h1>
        <p>반응형 웹이 지원하는 페이지임</p>
      </div>
    </div>
  );
}</p>
<p>export default Header;</p>
<pre><code>
- Footer.jsx
```jsx
// Footer.jsx

import React from &quot;react&quot;;

function Footer() {
  return(
      &lt;div className={&quot;mt-5 p-4 bg-dark text-white text-center&quot;}&gt;
        &lt;p&gt;Footer&lt;/p&gt;
      &lt;/div&gt;
  );
}

export default Footer;</code></pre><hr>
<h2 id="props-1">props</h2>
<p>자바스크립트 컴포넌트 간의 데이터 전달을 위해서 사용하는 자바스크립트 객체, 읽기 전용
부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달 시 사용
부모 컴포넌트에서 자식 컴포넌트를 호출하여 사용 시 속성을 사용하여 데이터를 전달
부모 컴포넌트에서 사용한 속성명이 자식 컴포넌트의 props 객체에 담겨서 전달이 됨
해당 속성명을 자식 컴포넌트에서 그대로 사용함</p>
<ul>
<li>App3.jsx<pre><code class="language-js">import React from &quot;react&quot;;
import ProfileList from &quot;./folder1/ProfileList&quot;;
</code></pre>
</li>
</ul>
<p>function App3() {
  return(
    <div>
      <ProfileList />
    </div>
  );
}</p>
<p>export default App3;</p>
<pre><code>
- folder1/Profile.js
```js
// folder1/Profile.jsx

import React from &quot;react&quot;;

function Profile(props) {
  return (
    &lt;div className={&quot;border rounded-2 px-3 m-4&quot;}&gt;
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-id&quot;} className={&quot;form-label&quot;}&gt;아이디 : &lt;/label&gt;
        &lt;input type={&quot;text&quot;} id={&quot;user-id&quot;} className={&quot;form-control&quot;} value={props.userId}/&gt;
      &lt;/div&gt;
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-name&quot;} className={&quot;form-label&quot;}&gt;이름 : &lt;/label&gt;
        &lt;input type={&quot;text&quot;} id={&quot;user-name&quot;} className={&quot;form-control&quot;} value={props.userName}/&gt;
      &lt;/div&gt;
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-tel&quot;} className={&quot;form-label&quot;}&gt;전화번호 : &lt;/label&gt;
        &lt;input type={&quot;tel&quot;} id={&quot;user-tel&quot;} className={&quot;form-control&quot;} value={props.userTel}/&gt;
      &lt;/div&gt;
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-email&quot;} className={&quot;form-label&quot;}&gt;이메일 : &lt;/label&gt;
        &lt;input type={&quot;email&quot;} id={&quot;user-email&quot;} className={&quot;form-control&quot;} value={props.userEmail}/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Profile;</code></pre><ul>
<li>folder2/ProfileList.jsx<pre><code class="language-js">// folder1/ProfileList.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;
import Profile from &quot;./Profile&quot;;</p>
<p>/*
props : 자바스크립트 컴포넌트 간의 데이터 전달을 위해서 사용하는 자바스크립트 객체, 읽기 전용
        부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달 시 사용
        부모 컴포넌트에서 자식 컴포넌트를 호출하여 사용 시 속성을 사용하여 데이터를 전달
        부모 컴포넌트에서 사용한 속성명이 자식 컴포넌트의 props 객체에 담겨서 전달이 됨
        해당 속성명을 자식 컴포넌트에서 그대로 사용함
*/</p>
<p>function ProfileList() {
  return (
    &lt;div className={&quot;row&quot;}&gt;
      &lt;div className={&quot;col-sm-6 mx-auto&quot;}&gt;
        &lt;Profile userId={&quot;test1&quot;} userName={&quot;테스터1&quot;} userTel={&quot;01012345678&quot;} userEmail={&quot;<a href="mailto:test1@bitc.co.kr">test1@bitc.co.kr</a>&quot;}/&gt;
      </div>
    </div>
  );
}</p>
<p>export default ProfileList;</p>
<pre><code>
![](https://velog.velcdn.com/images/i_like_monday/post/deb4089f-0ae0-4d6f-adc3-ae74f22a783c/image.png)


값을 변경하고싶다면 자바스크립트 변수 생성해서 사용
```js
  let data = props;
  let userTel = props.userTel;
  let userEmail = props.userEmail;</code></pre><ul>
<li>Profile.js<pre><code class="language-js">// folder1/Profile.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;</p>
<p>// function Profile({userId, userName, userTel, userEmail}) {...} 로도 사용 가능(확장표현식)
// 확장표현식 : 대입 연산자 오른쪽의 데이터를 연산자 왼쪽의 변수에 저장 시 () {} 에 표시된 이름에 대입
function Profile(props) {
  let data = props;
  let userTel = props.userTel;
  let userEmail = props.userEmail;</p>
<p>  return (
    &lt;div className={&quot;border rounded-2 px-3 m-4&quot;}&gt;
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-id&quot;} className={&quot;form-label&quot;}&gt;아이디 : </label>
        &lt;input type={&quot;text&quot;} id={&quot;user-id&quot;} className={&quot;form-control&quot;} value={data.userId}/&gt;
      </div>
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-name&quot;} className={&quot;form-label&quot;}&gt;이름 : </label>
        &lt;input type={&quot;text&quot;} id={&quot;user-name&quot;} className={&quot;form-control&quot;} value={data.userName}/&gt;
      </div>
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-tel&quot;} className={&quot;form-label&quot;}&gt;전화번호 : </label>
        &lt;input type={&quot;tel&quot;} id={&quot;user-tel&quot;} className={&quot;form-control&quot;} value={userTel}/&gt;
      </div>
      &lt;div className={&quot;my-3&quot;}&gt;
        &lt;label for={&quot;user-email&quot;} className={&quot;form-label&quot;}&gt;이메일 : </label>
        &lt;input type={&quot;email&quot;} id={&quot;user-email&quot;} className={&quot;form-control&quot;} value={userEmail}/&gt;
      </div>
    </div>
  );
}</p>
<p>export default Profile;</p>
<pre><code>
- ProfileList.jsx
```js
// folder1/ProfileList.jsx

import React from &quot;react&quot;;
import Profile from &quot;./Profile&quot;;

/*
props : 자바스크립트 컴포넌트 간의 데이터 전달을 위해서 사용하는 자바스크립트 객체, 읽기 전용
        부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달 시 사용
        부모 컴포넌트에서 자식 컴포넌트를 호출하여 사용 시 속성을 사용하여 데이터를 전달
        부모 컴포넌트에서 사용한 속성명이 자식 컴포넌트의 props 객체에 담겨서 전달이 됨
        해당 속성명을 자식 컴포넌트에서 그대로 사용함
*/

function ProfileList() {
  return (
    &lt;div className={&quot;row&quot;}&gt;
      &lt;div className={&quot;col-sm-6 mx-auto&quot;}&gt;
        &lt;Profile userId={&quot;test1&quot;} userName={&quot;테스터1&quot;} userTel={&quot;01012345678&quot;} userEmail={&quot;test1@bitc.co.kr&quot;}/&gt;
        &lt;Profile userId={&quot;test2&quot;} userName={&quot;테스터2&quot;} userTel={&quot;01098765432&quot;} userEmail={&quot;test2@bitc.co.kr&quot;}/&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default ProfileList;</code></pre><p><img src="https://velog.velcdn.com/images/i_like_monday/post/1066efd7-e3ce-49e5-a49c-bf88d5cfd963/image.png" alt=""></p>
<hr>
<h3 id="props-이용해서-게시판-만들어보기">props 이용해서 게시판 만들어보기</h3>
<p>App3.jsx</p>
<pre><code class="language-js">import React from &quot;react&quot;;
import ProfileList from &quot;./folder1/ProfileList&quot;;
import BoardList from &quot;./folder1/BoardList&quot;;

function App3() {
  return(
    &lt;div&gt;
      &lt;ProfileList /&gt;
      &lt;br/&gt;
      &lt;BoardList/&gt;
    &lt;/div&gt;
  );
}

export default App3;</code></pre>
<p>BoardList.jsx</p>
<pre><code class="language-jsx">// folder/BoardList.jsx

import React from &quot;react&quot;;
import BoardItem from &quot;./BoardItem&quot;;

const boardItemList = [
  {boardIdx: 100}, {boardTitle: &quot;게시판 100번 글 제목&quot;}, {boardUserId: &quot;test1&quot;}, {boardCreateDate: &quot;2023-01-03 12:40:40&quot;},
  {boardIdx: 101}, {boardTitle: &quot;게시판 101번 글 제목&quot;}, {boardUserId: &quot;test2&quot;}, {boardCreateDate: &quot;2023-01-03 12:41:40&quot;},
  {boardIdx: 102}, {boardTitle: &quot;게시판 102번 글 제목&quot;}, {boardUserId: &quot;test3&quot;}, {boardCreateDate: &quot;2023-01-03 12:42:40&quot;},
]


function BoardList() {
  return (
    &lt;div className={&quot;container mx-auto&quot;}&gt;
      &lt;table className={&quot;table table-hover table-striped&quot;}&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;글 번호&lt;/th&gt;
            &lt;th&gt;글 제목&lt;/th&gt;
            &lt;th&gt;사용자&lt;/th&gt;
            &lt;th&gt;등록 시간&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
          &lt;BoardItem idx={&quot;100&quot;} title={&quot;테스트 제목100&quot;} userId={&quot;test1&quot;} createDt={&quot;2023-01-03 12:50:00&quot;}/&gt;
          &lt;BoardItem idx={&quot;101&quot;} title={&quot;테스트 제목101&quot;} userId={&quot;test2&quot;} createDt={&quot;2023-01-04 12:50:00&quot;}/&gt;
          &lt;BoardItem idx={&quot;102&quot;} title={&quot;테스트 제목102&quot;} userId={&quot;test3&quot;} createDt={&quot;2023-01-05 12:50:00&quot;}/&gt;
        &lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;
  );
}

export default BoardList;</code></pre>
<p>BoardItem.jsx</p>
<pre><code class="language-jsx">// folder/BoardItem.jsx

import React from &quot;react&quot;;

function BoardItem({idx, title, userId, createDt}) {
  return(
    &lt;tr&gt;
      &lt;td&gt;{idx}&lt;/td&gt;
      &lt;td&gt;{title}&lt;/td&gt;
      &lt;td&gt;{userId}&lt;/td&gt;
      &lt;td&gt;{createDt}&lt;/td&gt;
    &lt;/tr&gt;
  );
}

export default BoardItem;</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/133f9e14-1b3a-466e-b54f-59446c05bb22/image.png" alt=""></p>
<p>기본적인 것 완성</p>
<hr>
<ul>
<li>BoardList.jsx<pre><code class="language-js">// folder/BoardList.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;
import BoardItem from &quot;./BoardItem&quot;;</p>
<p>const boardItemList = [
  {boardIdx: 100, boardTitle: &quot;게시판 100번 글 제목&quot;, boardUserId: &quot;test1&quot;, boardCreateDate: &quot;2023-01-03 12:40:40&quot;},
  {boardIdx: 101, boardTitle: &quot;게시판 101번 글 제목&quot;, boardUserId: &quot;test2&quot;, boardCreateDate: &quot;2023-01-03 12:41:40&quot;},
  {boardIdx: 102, boardTitle: &quot;게시판 102번 글 제목&quot;, boardUserId: &quot;test3&quot;, boardCreateDate: &quot;2023-01-03 12:42:40&quot;},
]</p>
<p>function BoardList() {
  // boardItemList.map((item) =&gt; {
  //   return <BoardItem idx={item.boardIdx} title={item.boardTitle} userId={item.boardUserId} createDt={item.boardCreateDate}/>
  // });
  return (
    &lt;div className={&quot;container mx-auto&quot;}&gt;
      &lt;table className={&quot;table table-hover table-striped&quot;}&gt;
        <thead>
          <tr>
            <th>글 번호</th>
            <th>글 제목</th>
            <th>사용자</th>
            <th>등록 시간</th>
          </tr>
        </thead>
        <tbody>
          {/* ----------------------- 아래 코드 주석 -&gt; 위 코드로 바꿔서 줄일 수 있음! ----------------------- <em>/}
          {/</em>&lt;BoardItem idx={&quot;100&quot;} title={&quot;테스트 제목100&quot;} userId={&quot;test1&quot;} createDt={&quot;2023-01-03 12:50:00&quot;}/&gt;<em>/}
          {/</em>&lt;BoardItem idx={&quot;101&quot;} title={&quot;테스트 제목101&quot;} userId={&quot;test2&quot;} createDt={&quot;2023-01-04 12:50:00&quot;}/&gt;<em>/}
          {/</em>&lt;BoardItem idx={&quot;102&quot;} title={&quot;테스트 제목102&quot;} userId={&quot;test3&quot;} createDt={&quot;2023-01-05 12:50:00&quot;}/&gt;*/}</p>
<pre><code>      {boardItemList.map(
        (item) =&gt; {
          return &lt;BoardItem idx={item.boardIdx} title={item.boardTitle} userId={item.boardUserId} createDt={item.boardCreateDate}/&gt;
        })}
    &lt;/tbody&gt;
  &lt;/table&gt;
  &lt;BoardItem/&gt;
&lt;/div&gt;</code></pre><p>  );
}</p>
<p>export default BoardList;</p>
<pre><code>
---

### 사이트 따라 만들기
https://www.w3schools.com/w3css/tryw3css_templates_gourmet_catering.htm
https://www.w3schools.com/w3css/tryw3css_templates_gourmet_catering.htm
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React - 03]]></title>
            <link>https://velog.io/@i_like_monday/React-03</link>
            <guid>https://velog.io/@i_like_monday/React-03</guid>
            <pubDate>Fri, 30 Dec 2022 06:57:07 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트-시작하기">리액트 시작하기</h1>
<h3 id="프로젝트-생성">프로젝트 생성</h3>
<p>java505_react_test2
<img src="https://velog.velcdn.com/images/i_like_monday/post/8dcdfcc4-d0e2-4cb4-be82-b5bd39835c8b/image.png" alt=""></p>
<blockquote>
<p>리액트 공식 홈페이지(Docs)
문서 : <a href="https://ko.reactjs.org/docs/getting-started.html">https://ko.reactjs.org/docs/getting-started.html</a>
자습서 : <a href="https://ko.reactjs.org/tutorial/tutorial.html">https://ko.reactjs.org/tutorial/tutorial.html</a></p>
</blockquote>
<ul>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/7c107a16-b1bd-4705-94dd-1a78784e8c69/image.png" alt=""></li>
</ul>
<p>JSX : 클래스형/함수형 두가지가 있다. 현재는 대부분 함수형을 사용함! (쉽고 편하다)</p>
<hr>
<p>Potato.jsx 파일 생성</p>
<pre><code class="language-jsx">import React from &quot;react&quot;;

function Potato() {
  return (
    &lt;h3&gt;I LOVE POTATO&lt;/h3&gt;
  );
}

export default Potato;</code></pre>
<blockquote>
<p>React에서의 가상돔 개념
<a href="https://velog.io/@mollog/React%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%80%EC%83%81%EB%8F%94-%EA%B0%9C%EB%85%90">https://velog.io/@mollog/React%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%80%EC%83%81%EB%8F%94-%EA%B0%9C%EB%85%90</a></p>
</blockquote>
<hr>
<h3 id="key-에러메세지-해결">Key 에러메세지 해결</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/6bfc76d1-abcc-4872-a9c8-09039238aea6/image.png" alt=""></p>
<pre><code class="language-js">const foodList = [
  {
    id : 1,
    name: &quot;명란구이&quot;,
    image: &quot;https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMjAzMjlfMjY0%2FMDAxNjQ4NDg2ODg0ODU1.-FRGEeH6bSjWmCQFcNs4R1Q9LourkkzdhePYtuaFNtkg.oswpp9T0mq36Y4SzZoMdKK-jdeWvxUkJmku5F7EDjj8g.JPEG.shorty_%2FIMG_5551.jpg&amp;type=a340&quot;,
  },
  {
    id : 2,
    name: &quot;스지전골&quot;,
    image: &quot;https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMjEwMjBfMjAy%2FMDAxNjY2MjYzNTA0MTU0._Kjn-tBFmMvbUWZPzD5ZhGXyIhAsFrytrYwIB1wMwkUg.pw7tmJ716ZKS6X6Tsmo-BsWRRMr11LnCv9fWjkbgc4og.JPEG.yeslhong%2FIMG_2835.jpg&amp;type=sc960_832&quot;
  },
  {
    id : 3,
    name: &quot;햄버거&quot;,
    image: &quot;https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMjEyMDRfMjg5%2FMDAxNjcwMTU1MzEwMTky.kp-QjbGuXA8-ypXo7SNodq01L0f_TqP6C1u9nt6wO7gg.aGwN6d1_SaeK21S6Vfy3obRDwH_NvpjBqqM7nsB5xsIg.JPEG.ssaounara%2F1670155306339.jpg&amp;type=sc960_832&quot;
  }
];</code></pre>
<p>위와 같이 id값 추가해주고</p>
<pre><code class="language-js">function renderFood(dish) {
  return (
    &lt;Food key={dish.id} name={dish.name} pic={dish.image} /&gt;
  );
}</code></pre>
<p><code>key={dish.id}</code>도 추가해주면 에러 메세지 사라짐</p>
<hr>
<h2 id="prop-type">prop-type</h2>
<p>터미널에서 다운로드
<img src="https://velog.velcdn.com/images/i_like_monday/post/cdee0f7b-a8e3-41b1-b1af-2fb969a61273/image.png" alt=""></p>
<ul>
<li>App.js<pre><code class="language-js">function Food({name, pic, rating}) {
console.log(name);
return (
  &lt;div&gt;
    &lt;h3&gt;I LOVE {name}&lt;/h3&gt;
    &lt;h4&gt;점수 : {rating}&lt;/h4&gt;
    &lt;img src={pic} alt=&quot;...&quot;/&gt;
    &lt;p&gt;-----------------------------------------------------------------------------------------------------------&lt;/p&gt;
  &lt;/div&gt;
);
}</code></pre>
raing의 타입을 검사 하기 -&gt; import 먼저</li>
<li>App.js<pre><code class="language-js">import PropTypes from &quot;prop-types&quot;;</code></pre>
</li>
</ul>
<p>export 바로 윗줄 (코드의 마지막쯤)</p>
<ul>
<li>App.js<pre><code class="language-js">// 매개변수로 사용되는 데이터를 체크
// 매개변수의 이름까지 체크할 수 있음
// isRequired : 해당 부분이 반드시 필요하다는 의미, 없으면 오류 발생
Food.propTypes = {
name: PropTypes.string.isRequired, // 값 안들어오면 오류체크 하라는 뜻
pic: PropTypes.string.isRequired,
rating: PropTypes.string.isRequired
}</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/f3afa67b-776c-4f17-ba6d-f95d92d84eb7/image.png" alt=""></p>
<p>데이터형이 잘못들어갔을때 이렇게 오류 확인이 가능함</p>
<p>코드 수정
<code>rating: PropTypes.string.isRequired</code> → <code>rating: PropTypes.number.isRequired</code></p>
<ul>
<li>App.js<pre><code class="language-jsx">// 매개변수로 사용되는 데이터를 체크
// 매개변수의 이름까지 체크할 수 있음
// isRequired : 해당 부분이 반드시 필요하다는 의미, 없으면 오류 발생
Food.propTypes = {
name: PropTypes.string.isRequired, // 값 안들어오면 오류체크 하라는 뜻
pic: PropTypes.string.isRequired,
rating: PropTypes.number.isRequired
}</code></pre>
</li>
</ul>
<hr>
<ul>
<li>App2.jsx<pre><code class="language-jsx">// App2.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>// 클래스 타입의 컴포넌트(잘 사용 안하지만 개념은 이해하자)
class App2 extends React.Component {
  render() {
    return(
      <div>
        <h1>클래스 컴포넌트 App2</h1>
      </div>
    );
  }
}</p>
<p>export default App2;</p>
<pre><code>

- index.jsx import
```jsx
import App2 from &#39;./App2&#39;;</code></pre><ul>
<li>index.jsx 코드 추가<pre><code class="language-jsx">root.render(
&lt;React.StrictMode&gt;
  {/* 시작 태그와 끝 태그는 jsx 문법에서 무조건 !!! 있어야 한다.*/}
  {/* &lt;App /&gt;  ==  &lt;App&gt;&lt;/App&gt; */}
  {/* jsx 문법에서 화면을 렌더링 하기 위한 태그의 이름은 반드시 첫글자가 대문자이어야 함 */}
  {/* 대문자를 사용하는 이유는 일반적으로 html 태그를 모두 소문자로 사용하기 때문에
      일반 html 태그인지 jsx 문법으로 생성된 태그인지를 구분하기 위함*/}
  &lt;App /&gt;
  &lt;br/&gt;&lt;hr/&gt;&lt;br/&gt;
  &lt;App2/&gt;    &lt;---!!!!!!!!!
&lt;/React.StrictMode&gt;
);</code></pre>
<img src="https://velog.velcdn.com/images/i_like_monday/post/e2c94508-e134-4d3b-9264-bc5d7441035c/image.png" alt=""></li>
</ul>
<hr>
<h2 id="클래스-컴포넌트">클래스 컴포넌트</h2>
<ul>
<li>App2.jsx<pre><code class="language-jsx">// App2.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>// 클래스 타입의 컴포넌트(잘 사용 안하지만 개념은 이해하자)
// 클래스 컴포넌트는 React.Component 를 상속받아 사용함
class App2 extends React.Component {
  // state : 현재 컴포넌트에서 사용하는 상태 값
  // props 는 부모 컴포넌트에서 전달되는 값이지만 state 는 현재 컴포넌트에서만 사용되는 값으로, 수정 가능함(읽기전용 X)
  // state 의 값이 변경되면 리액트는 화면을 다시 렌더링 함.
  // state의 값을 직접적으로 변경하는 것은 불가능, setState() 함수를 사용하여 값을 수정
  state = {
    count: 0,
  }</p>
<p>  plus = () =&gt; {
    this.setState({count: this.state.count + 1});
    console.log(&#39;plus&#39;);
  }</p>
<p>  minus = () =&gt; {
    console.log(&#39;minus&#39;);
    this.setState({count: this.state.count - 1});
  }</p>
<p>  // render() : 클래스 컴포넌트에서 화면을 렌더링하기 위한 메소드
  // render 은 함수 컴포넌트와 사용 방법이 동일함
  render() {
    return(
      <div>
        <h1>클래스 컴포넌트 App2</h1>
        <h3>카운트 수 : {this.state.count}</h3>
        <button onClick={this.plus}>plus</button>
        <button onClick={this.minus}>minus</button>
      </div>
    );
  }
}</p>
<p>export default App2;</p>
<pre><code>
- ![](https://velog.velcdn.com/images/i_like_monday/post/230a036e-565b-4bb7-870d-2ded7faf4ec4/image.png)

---

## 리액트의 생명주기
참고 : https://velog.io/@zezeg2/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0

#### constructor( ) 함수

#### componentDidMount( ) 함수

#### componentDidUpdate( ) 함수

#### componentWillUnmount( ) 함수


### ComponentDidMount( ) 예제
- App2.jsx 코드 추가
```jsx
constructor(props) {
    super(props);
    console.log(&quot;생성자 실행&quot;);
  }

  componentDidMount() {
    console.log(&quot;컴포넌트 생성 완료&quot;);
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(&quot;컴포넌트 업데이트 완료&quot;)
  }

  componentWillUnmount() {
    console.log(&quot;컴포넌트 제거 완료&quot;)
  }</code></pre><p>위코드만 써놓고 실행하면
<img src="https://velog.velcdn.com/images/i_like_monday/post/20e5d2a6-8d93-4fc5-a7a4-ac669e22f7f9/image.png" alt=""></p>
<hr>
<p>경로 및 파일 생성
<img src="https://velog.velcdn.com/images/i_like_monday/post/cc0e308f-2e52-4b85-ac05-c0b3b32000b4/image.png" alt=""></p>
<ul>
<li>Notification.jsx<pre><code class="language-jsx">// Notification.jsx
import React from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>const styles = {
  wrapper: {
    margin: 8,
    padding: 8,
    display: &#39;flex&#39;,
    flexDirection: &#39;row&#39;,
    border: &#39;1px solid gray&#39;,
    borderRadius: 16
  },</p>
<p>  messageText: {
    color: &#39;black&#39;,
    fontSize: 16
  },
}</p>
<p>class Notification extends React.Component {</p>
<p>  constructor(props) {
    super(props);</p>
<pre><code>this.state = {};</code></pre><p>  }</p>
<p>  render() {
    return (
      <div style={styles.wrapper}>
        <span style={styles.messageText}>{this.props.message}</span>
      </div>
    );
  }
}</p>
<p>export default Notification;</p>
<pre><code>

- NotificationList.jsx (기본형)
```jsx
// NotificationList.jsx

import React from &quot;react&quot;;
import Notification from &quot;./Notification&quot;;

const reservedNotifications = [
  {message: &#39;안녕하세요, 오늘 일정 알려드립니다.&#39;},
  {message: &#39;오후 수업 시간입니다.&#39;},
  {message: &#39;이제 곧 쉬는 시간입니다.&#39;}
];

var timer;

class NotificationList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      notifications: [],
    };
  }

  render() {
    return (
      &lt;div&gt;

      &lt;/div&gt;
    );
  }

  componentDidMount() {

  }
}

export default NotificationList;</code></pre><ul>
<li>NotificationList.jsx (수정)<pre><code class="language-jsx">// NotificationList.jsx
</code></pre>
</li>
</ul>
<p>import React from &quot;react&quot;;
import Notification from &quot;./Notification&quot;;
import {func} from &quot;prop-types&quot;;
import notification from &quot;./Notification&quot;;</p>
<p>const reservedNotifications = [
  {message: &#39;안녕하세요, 오늘 일정 알려드립니다.&#39;},
  {message: &#39;오후 수업 시간입니다.&#39;},
  {message: &#39;이제 곧 쉬는 시간입니다.&#39;}
];</p>
<p>var timer;</p>
<p>class NotificationList extends React.Component {
  constructor(props) {
    super(props);</p>
<pre><code>this.state = {
  notifications: [],
};</code></pre><p>  }</p>
<p>  render() {
    return (
      <div>
        {
          this.state.notifications.map((item) =&gt; {
            return <Notification message={item.message} />;
          })
        }
      </div>
    );
  }</p>
<p>  componentDidMount() {
    const {notifications} = this.state;</p>
<pre><code>timer = setInterval(() =&gt; {
  if (notifications.length &lt; reservedNotifications.length) {
    const index = notifications.length;
    notifications.push(reservedNotifications[index]);

    this.setState({
      notifications: notifications,
      });
  }
  else {
    clearInterval(timer);
  }
}, 2000);</code></pre><p>  }
}</p>
<p>export default NotificationList;</p>
<pre><code>
index.js 로 가서 바로 사용
- index.js
```js
root.render(
  &lt;React.StrictMode&gt;
    &lt;App2/&gt;
    &lt;NotificationList/&gt;    &lt;----!!!!!!!!!
    &lt;br/&gt;&lt;hr/&gt;&lt;br/&gt;
    &lt;App /&gt;

  &lt;/React.StrictMode&gt;
);</code></pre><p><img src="https://velog.velcdn.com/images/i_like_monday/post/0b76c5b3-c459-4467-b5e7-cbbe45fb595c/image.png" alt=""></p>
<hr>
<pre><code class="language-jsx">// NotificationList.jsx

import React from &quot;react&quot;;
import Notification from &quot;./Notification&quot;;
import notification from &quot;./Notification&quot;;

// 댓글 데이터 생성
const reservedNotifications = [
  {id: 1, message: &#39;안녕하세요, 오늘 일정 알려드립니다.&#39;},
  {id: 2, message: &#39;오후 수업 시간입니다.&#39;},
  {id: 3, message: &#39;이제 곧 쉬는 시간입니다.&#39;}
];

// 자바스크립트 타이머 객체 정보를 저장하는 변수 : 2가지(setTimer, setInterval)
// setTimeout : 1회용 타이머, 지정된 시간 이후에 1번 동작, 실행 시 타이머 정보를 반환, 타이머 삭제 시 clearTimeout() 을 사용
// setInterval : 지정된 시간마다 동작하는 타이머, 실행 시 타이머 정보를 반환, 타이머 삭제시 clearInterval() 을 사용
let timer;

class NotificationList extends React.Component {
  constructor(props) {
    super(props);

    // 현재 컴포넌트의 상태인 notifications 선언( == notifications 이 빈 배열 타입의 멤버변수가 됨)
    this.state = {
      // 빈 배열 타입인 state 가 생성됨
      notifications: [],
    };
  }

  // 화면에 처음 그려질 내용
  render() {
    return (
      &lt;div&gt;
        {
          // state 의 notifications 배열을 가지고 화면을 그려줌
          // notifications 배열의 기본값이 비었기 때문에 화면에 아무것도 그리지 않음(F5 누르면 아무것도 없다가 나중에 생기는 이유임)
          this.state.notifications.map((item) =&gt; {
            return &lt;Notification key={item.id} message={item.message} /&gt;;
          })
        }
      &lt;/div&gt;
    );
  }

  // render() 함수 실행 후 동작
  componentDidMount() {
    // object 타입의 &#39;확장 표현식&#39;을 통해서 변수 notifications 에 state 가 가지고 있는 notifications 의 데이터를 대입함
    // 확장표현식 : const/let [변수명1, 2, 3, ...] = [원본_배열] → 변수1, 변수2, 변수3, ... 생성
    // object형 확장표현식 : const/let {키이름과같은변수명_1, _2, _3, ...} = {object 타입} → 키 이름과 동일한 변수명에 값이 대입이 됨
    // 여기서 state 자체가 { } 로 감싸져있기때문에 object 타입이고, notifications 가 키이름임.
    const {notifications} = this.state; // == const notifications = this.state.notifications;
    // state 의 notifications 의 데이터가 비어있음 -------&gt; length : 0

    // 타이머를 사용하여 지정된 시간마다 동작하도록 설정함
    timer = setInterval(() =&gt; {
      if (notifications.length &lt; reservedNotifications.length) {  // 0 &lt; 3
        const index = notifications.length;
        // 배열 notifications 에 데이터 추가
        notifications.push(reservedNotifications[index]);

        // state 의 상태 수정
        this.setState({
          // this.state 에 있는 notifications 에 현재 componentDidMount 안에 있는 지역변수 notifications 의 데이터를 저장
          notifications: notifications, // 키(←state 의 notifications) : 실제데이터(←componentDidMount() 안의 배열 notifications[...])
          });
      }
      else {
        clearInterval(timer);
      }
    }, 2000);
  }
}

export default NotificationList;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 02]]></title>
            <link>https://velog.io/@i_like_monday/React-02</link>
            <guid>https://velog.io/@i_like_monday/React-02</guid>
            <pubDate>Thu, 29 Dec 2022 10:33:33 GMT</pubDate>
            <description><![CDATA[<p>※ 깃허브 레포지토리명과 프로젝트명은 같아야 함
다를 경우 : 터미널에서 <code>npm install</code> 입력, node modules 라는 폴더가 생김
그 후 프로젝트 종료
탐색기로 가서 해당 프로젝트의폴더 내의 <code>.idea</code> 폴더 -&gt; <code>프로젝트명.iml</code> 파일 복사 -&gt; 프로젝트 폴더 바깥에 붙여넣은 후 레포지토리명과 동일하게 iml파일 이름을 바꾼 후 내용 편집 -&gt; 해당 iml 파일 내부의 프로젝트명을 모두 레포지토리명과 동일하게 바꿔줌
<code>.iead</code>폴더 내부의 <code>vcs.xml</code>, <code>workspace.xml</code> 등의 모든 파일의 내용도 동일하게 확인해서 프로젝트명으로 되어있는 부분을 레포지토리명으로 바꿔줌
구성편집 -&gt; 스크립트:start</p>
<h1 id="es6-문법">ES6 문법</h1>
<h2 id="확장-표현식">확장 표현식</h2>
<p>ES6 버전에서 추가된 object 타입의 사용 방식</p>
<h4 id="es5-버전">ES5 버전</h4>
<ul>
<li>Destructuring.js<pre><code class="language-js">// Destructuring.js
</code></pre>
</li>
</ul>
<p>console.log(&#39;----- ES5 -----&#39;);
var x = 0;
var y = 0;
var obj = {x: x, y: y};
console.log(obj);</p>
<p>var randomKeyString = &#39;other&#39;;
var combined = {};</p>
<p>combined[&#39;one&#39; + randomKeyString] = &#39;some value&#39;;
console.log(combined);</p>
<pre><code>- ![](https://velog.velcdn.com/images/i_like_monday/post/e2545178-60d4-411b-8c0b-b2840cd335f4/image.png)

#### ES6 버전
- Destructuring.js
```js
console.log(&#39;\n----- ES6 -----&#39;);
var x = x;
var y = y;
// object 생성 시 키값을 설정하지 않으면 변수명이 키값으로 설정됨
var obj = {x, y}; // ES5) x --&gt; 키이름, 변수명
console.log(obj);

// object 타입 생성 시 대입 연산자 오른쪽에서 바로 키와 값을 설정하여 object 타입에 데이터를 추가하는 것이 가능
var randomKeyString = &#39;other&#39;;
var combined = {[&#39;one&#39; + randomKeyString]: &#39;some value&#39;};
console.log(combined);</code></pre><ul>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/462ba59f-b444-4536-a25f-5711c1ea29a7/image.png" alt=""></li>
</ul>
<hr>
<h4 id="es5-버전-1">ES5 버전</h4>
<ul>
<li>Destructuring.js<pre><code class="language-js">console.log(&#39;\n----- ES5 -----&#39;);
var obj2 = {
methodA: function () { console.log(&#39;A&#39;);},
methodB: function () { return 0;}
};
console.log(obj2);
obj2.methodA();
obj2.methodB();</code></pre>
</li>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/ce7d8bdc-ef9b-4dd3-b532-83a64a2728fd/image.png" alt=""></li>
</ul>
<h4 id="es6-버전">ES6 버전</h4>
<p>데이터 집어넣을때 익명함수 안써도 되고 멤버 메서드 쓰듯 바로 쓰면 됨</p>
<ul>
<li>Destructuring.js<pre><code class="language-js">console.log(&#39;\n----- ES6 -----&#39;);
</code></pre>
</li>
</ul>
<p>var obj2 = {
  methodA() { console.log(&#39;A&#39;);},
  methodB() { return 0;}
}</p>
<pre><code>- ![](https://velog.velcdn.com/images/i_like_monday/post/7c2dd8ed-923f-4746-828e-66cdf9713557/image.png)

---

### 구조분해할당
#### ES5
- Destructuring.js
```js
console.log(&#39;\n----- 구조분해할당 -----&#39;);

console.log(&#39;\n----- ES5 -----&#39;);
var list = [0, 1];
var item1 = list[0];
var item2 = list[1];
var item3 = list[2] || -1;
console.log(item1); // 0
console.log(item2); // 1
console.log(item3); // -1

// 데이터 스왑시 임시변수(tmp) 필요
var temp = item2;
item2 = item1;
item1 = temp;
console.log(item1); // 1
console.log(item2); // 0

var obj = {
  key1: &#39;one&#39;,
  key2: &#39;two&#39;
};

var key1 = obj.key1;
var key2 = obj.key2;
var key3 = obj.key3 || &#39;default key3 value&#39;;
var newKey1 = key1;
console.log(key1);  // one
console.log(key2);  // two
console.log(key3);  // default key3 value
console.log(newKey1); // one</code></pre><ul>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/33a4d3bb-6f0c-4021-8cca-6654066d71f1/image.png" alt=""> </li>
</ul>
<h4 id="es6">ES6</h4>
<ul>
<li>Destructuring.js<pre><code class="language-js">var list = [0, 1];
// 대입 연산자 왼쪽에 [] 를 사용하여 그 안에 배열을 쓰듯이 변수명을 입력하면
// 대입연산자 오른쪽의 데이터를 하나씩 꺼내어 내입 연산자 왼쪽의 변수명에 각각 저장됨
var [item1, item2, item3 = 1] = list; // = var [item1, item2, item3 = 1] = [0, 1]
console.log(item1); // 0
console.log(item2); // 1
console.log(item3); // 1
</code></pre>
</li>
</ul>
<p>// 확장 표현식을 사용하여 임시 변수 없이 2개의 변ㅅ누의 값을 서로 변경함
[item2, item1] = [item1, item2];
console.log(item1); // 1
console.log(item2); // 0</p>
<p>// 배열의 확장 표현식과 동일하게 object 타입에서도 사용이 가능함
// 대입 연산자 왼쪽에 {} 을 사용하고 변수명을 입력하면, 대입 연산자 오른쪽의 object 타입의 키와 같은 변수명에 데이터를 저장함
// 키 이름 생략 시, 변수명을 키 이름으로 사용하는 법칙 때문임
// : &lt;- 기호를 사용 시 새로운 변수명으로 적용
// = &lt;- 기호를 사용 시 기본값으로 설정
var {key1: newKey1, key2, key3 = &#39;default key3 value&#39;} = obj; // obj = { key1: &#39;one&#39;, key2: &#39;two&#39;}
console.log(newKey1); // one
console.log(key2);  // two
console.log(key3);  // default key3 value</p>
<p>var [item, ...otherItems] = [0, 1, 2];
console.log(item);  // 0
console.log(otherItems);  // [1, 2]</p>
<p>var {key1, ...others} = {key1: &#39;one&#39;, key2: &#39;two&#39;};
console.log(key1);  // one
console.log(others);  // {key2: &#39;two&#39;}</p>
<pre><code>- ![](https://velog.velcdn.com/images/i_like_monday/post/75f668f1-23c7-4c6d-8151-e7d77624a5d2/image.png)

---

## 클래스
자바의 클래스보다는 간소화 된 방식이지만 ES6 부터는 자바스크립트에서도 Class 키워드를 지원함
`constructor(매개변수)` : 자바스크립트 클래스의 생성자, 자바스크립트의 생성자는 이름을 지정할 수 없음
`extends`: 자바스크립트의 클래스도 상속을 지원함
- Class.js
```js
// Class.js

// 클래스 선언
class Shape {

  // 정적 멤버, 클래스명.정적멤버명 으로 사용
  static create(x, y) {
    return new Shape(x, y);
  }

  // 멤버변수 선언 - let, const 등 안붙여도 되는 이유 : 여기서 name 은 &#39;키&#39; 이다!
  name = &#39;Shape&#39;;

  // 생성자, 이름은 constructor 로 고정임
  // 생성자에서 this.변수명 을 입력 시, 멤버 변수가 선언됨
  constructor(x, y) { // 데이터타입은 안써도 자동으로 인식함(자바스크립트의 장점)
    this.move(x, y);  // this : 객체 자기 자신을 의미함
  }

  move(x, y) {  // 키 이름 안써도 됨 사실은 move(x: x, y: y)
    this.x = x;
    this.y = y;
  }

  area() {
    return 0;
  }
}

var s = new Shape(10, 20);
s.area();
s.move(100, 200);
console.log(s.name);
console.log(s.x);
console.log(s.y);

var s1 = Shape.create(0, 0);
s1.area();
s1.move(10, 20);
console.log(s1.name);
console.log(s1.x);
console.log(s1.y);

// 상속(extends)
class Circle extends Shape {

  constructor(x, y, radius) {
    // 부모
    super(x, y);
    // 멤버변수 생성
    this.radius = radius;
  }

  area() {
    if (this.radius === 0) {
      return super.area();
    }
    return this.radius * this.radius;  // 10 * 10
  }
}

var c = new Circle(0, 0, 10);
console.log(c.area());  // 100</code></pre><ul>
<li><img src="https://velog.velcdn.com/images/i_like_monday/post/cd6e0a41-c8cf-47b4-ac08-838cdf8f47a5/image.png" alt=""></li>
</ul>
<hr>
<h2 id="배열-함수">배열 함수</h2>
<ul>
<li><p>ES6에서 배열 관련 함수가 추가됨(forEach(), map())</p>
</li>
<li><p><code>forEach()</code> 와 <code>map()</code> 은 반환값이 있냐 없냐의 차이만 있다.</p>
</li>
<li><p><code>forEach()</code> : 지정한 배열의 요소에 callBack으로 지정한 함수의 내용을 실햄하는 함수, 반환값이 없음</p>
<ul>
<li>사용법 :<pre><code class="language-java">배열명.forEach(콜백함수(현재값이 저장될 변수, 현재 index, 현재 배열 내용) {
실행할 소스코드
})</code></pre>
</li>
</ul>
</li>
<li><p><code>map()</code> : forEach 와 같이 지정한 배열의 요소에 callBack으로 지정한 함수의 내용을 실행하고 그 결과값을 배열로 반환하는 함수</p>
<ul>
<li>사용법 :<pre><code class="language-java">배열명.map(콜백함수(현재값이 저장될 변수, 현재 index, 현재 배열 내용) {
  실행할 소스코드
  return 반환값
})</code></pre>
<h3 id="foreach-">forEach( )</h3>
</li>
</ul>
</li>
<li><p>ArrayMethod.js</p>
<pre><code class="language-js">const fruits = [&#39;사과&#39;, &#39;배&#39;, &#39;복숭아&#39;];
console.log(&#39;원본 배열 : &#39; + fruits); // 사과,배,복숭아
</code></pre>
</li>
</ul>
<p>// 기존 방식
console.log(&#39;\n----- for문 사용 시 -----&#39;);
for (let i = 0; i &lt; fruits.length; i++) {
  console.log(fruits[i]);
  // 사과
  // 배
  // 복숭아
}</p>
<p>console.log(&#39;\n----- for ~ in 문 사용 시 -----&#39;);
for (const item in fruits) {
  // 값이 아니라 key 를 넣기때문에 item을 넣어줘야함
  console.log(fruits[item])
  // 사과
  // 배
  // 복숭아
}</p>
<p>// forEach() 사용 방식
console.log(&#39;\n----- forEach 문 사용 시 -----&#39;);
fruits.forEach(function (item) {
  console.log(item);
  // 사과
  // 배
  // 복숭아
})</p>
<p>console.log(&#39;\n----- forEach 문 매개변수 여러개 -----&#39;);
fruits.forEach(function (item, index){
  console.log(<code>index : ${index}, value: ${item}</code>);
  // index : 0, value: 사과
  // index : 1, value: 배
  // index : 2, value: 복숭아
})</p>
<p>// 현재 배열 내용 값 넣기 예시
fruits.forEach(function (item, index, arrName) {
  console.log(<code>current array : ${arrName}, index : ${index}, value : ${item}</code>);
  // current array : 사과,배,복숭아, index : 0, value : 사과
  // current array : 사과,배,복숭아, index : 1, value : 배
  // current array : 사과,배,복숭아, index : 2, value : 복숭아
});</p>
<pre><code>- ![](https://velog.velcdn.com/images/i_like_monday/post/6d819f52-5203-4628-9c7b-e19374367f4a/image.png)


### map( )
```js
console.log(&#39;\n----- map 사용 -----\n&#39;);
const numbers = [4, 9, 16, 25];
console.log(`원본 배열 : `);
console.log(numbers); // [ 4, 9, 16, 25 ]

let data = numbers.map(function (item) {
  console.log(`현재 값 : ${item}`);  // 4 9 16 25
  return item * 2;
});

console.log(`map 사용 후 데이터 :`);
console.log(data);  //[ 8, 18, 32, 50 ]


console.log(&#39;\n----- map() 에서 매개변수 여러개 -----\n&#39;);

data = numbers.map(function (item, index) {
  console.log(`index : ${index}, value : ${item}`);
  // index : 0, value : 4
  // index : 1, value : 9
  // index : 2, value : 16
  // index : 3, value : 25

  return item * 2;
});
console.log(&#39;map 사용 후 데이터 : &#39;);
console.log(data);  // [ 8, 18, 32, 50 ]

data = numbers.map(function (item, index, current) {
  console.log(`current array : ${current}, index : ${index}, value : ${item}`);
  // current array : 4,9,16,25, index : 0, value : 4
  // current array : 4,9,16,25, index : 1, value : 9
  // current array : 4,9,16,25, index : 2, value : 16
  // current array : 4,9,16,25, index : 3, value : 25

  return item * 2;
});
console.log(&#39;map 사용 후 데이터 : &#39;);
console.log(data);  // [ 8, 18, 32, 50 ]</code></pre><h2 id="화살표-함수">화살표 함수</h2>
<ul>
<li><p>화살표 함수 : ES6 버전부터 사용되는 함수를 선언하는 새로운 방식
ES5 버전의 익명함수를 사용하여 함수를 생성하는 방식에서 변경되어 <em>function 키워드를 생략_하고, 매개변수와 코드블럭 사이에 <code>=&gt;</code> 기호를 사용하는 방식
소스코드가 반환값 **_한 줄</em>** 일 경우 코드블럭을 생략할 수 있음
매개변수가 <strong><em>1개</em></strong> 인 경우, 매개변수의 괄호를 생략할 수 있음</p>
</li>
<li><p>사용법 1 :</p>
<pre><code class="language-js">변수명 = (매개변수1, 매개변수2, ...) =&gt; {
 실행할 소스코드
 return 반환값
}</code></pre>
</li>
<li><p>사용법 2 : 리턴 키워드, 중괄호 생략</p>
<pre><code class="language-js">변수명 = (매개변수1, 매개변수2, ...) =&gt; 반환값;</code></pre>
</li>
<li><p>사용법 3 : 매개변수가 1개일때 소괄호까지 생략</p>
<pre><code class="language-js">변수명 = 매개변수 =&gt; 반환값;</code></pre>
</li>
<li><p>사용법 4 : 매개변수가 없을때 () 로 대체함</p>
<pre><code class="language-js">변수명 = () =&gt; 반환값;</code></pre>
</li>
<li><p>ArrowFunction.js</p>
<pre><code class="language-js">// 기존 함수 선언 방식 1
function es5Func1(a, b) {
console.log(a + b);
return a + b;
}
</code></pre>
</li>
</ul>
<p>// 기존 함수 선언 방식 2
var es5Func2 = function (a, b) {
  console.log(a + b);
  return a + b;
}</p>
<p>es5Func1(10, 20); // 30
es5Func2(100, 200); // 300
let data = es5Func1(10, 20) // 30
console.log(data);  // 30
data = es5Func2(100, 200); // 300
console.log(data);  // 300</p>
<p>console.log(&#39;\n----- es6 방식 -----&#39;)
// 화살표 함수 선언
const es6Func1 = (a, b) =&gt; {
  console.log(a + b);
  return a + b;
}</p>
<p>data = es6Func1(10, 20);  // 30
console.log(<code>리턴 받은 값 : ${data}</code>);  // 30</p>
<p>// 생략 방법 1
// 생략 전
const es6Func22 = (a, b) =&gt; {
  return a + b
}
// --&gt; 생략 후
const es6Func2 = (a, b) =&gt;  a + b;
data = es6Func2(100, 200);
console.log(<code>리턴 받은 값 : ${data}</code>); // 300</p>
<p>const es6Func3 = (a, b) =&gt; console.log(a + b);
es6Func3(10, 20);</p>
<p>// 매개변수가 1개일때 : 소괄호도 생략가능
const es6Func4 = (a) =&gt; a * 2;
data = es6Func4(10);
console.log(<code>리턴 받은 값 : ${data}</code>);</p>
<p>const es6Func5 = a =&gt; a * 2;
data = es6Func5(10);
console.log(<code>리턴 받은 값 : ${data}</code>);</p>
<p>// 매개변수가 없을때 : = =&gt; 와 같이 아예 생략은 안되고 = () =&gt; 와 같이 사용
const es6Func6 = () =&gt; console.log(&#39;esFunc5() 실행&#39;);
es6Func6();</p>
<pre><code>
---

## 모듈(export, import)

- 하나의 자바스크립트 파일에서 다른 자바스크립트의 내용을 사용하기 위한 방식.
자바스크립트는 원래 html 을 지원하기 위해서 개발된 언어였기 때문에 html 문서 내에서 `&lt;script&gt;&lt;/script&gt;` 또는 `&lt;script src=&#39;파일경로&#39;&gt; &lt;/script&gt;` 형태로 다른 자바스크립트의 내용을 사용했음.

- 자바스크립트가 발전하면서 자바스크립트를 단순히 웹에서만 사용하는 것이 아니게 되어,
외부 자바스크립트 내용을 사용하기 위한 방법이 여러가지로 개발됨.

- ES6 버전부터 export, import 를 지원함.
자바스크립트 파일 하나하나를 모듈이라고 함.
하나의 모듈에는 하나의 export default 가 존재함.


- html 문서 내에서 import 를 사용하고자 할 경우 `&lt;script type=&quot;module&quot;&gt;`  을 사용하여 해당 파일이 모듈을 사용한다는 것을 알려줘야 함
 ```js
&lt;script type=&quot;module&quot; src=&quot;파일경로&gt;&lt;script&gt;</code></pre><ul>
<li><p>node.js 가 ES6 버전이 나오기 이전부터 모듈화 시스템을 사용하고 있었음.
node.js 가 기본적으로 사용하던 방식이 CommonJS 방식의 모듈 시스템을 사용하고 있었음
ES6 가 발표되면서 [ import/export ] 를 지원하게 됨.
기본 방식은 CommonJS 으로 사용되고 ES6 방식으로 사용하려면
<u>확장자를 .mjs 로 변경하여 사용하던지</u>, 
<u>package.json 파일에 type: &quot;module&quot; 을 추가하여 설정</u>을 해야 함.</p>
</li>
<li><p><code>export</code> : 변수, 함수, 클래스를 다른 자바스크립트 파일에서 사용할 수 있도록 설정
사용법 :</p>
<pre><code class="language-js">export 함수명;  // &lt;-- 함수명이라고 썼지만 변수/함수 모두 사용 가능
export {함수명1, 함수명2, ...};
export default 함수명;</code></pre>
</li>
<li><p><code>import</code> : 다른 자바스크립트 파일이 제공하는 변수, 함수, 클래스를 불러와서 사용하도록 설정
사용법 :
↓↓ 일반 export 로 지정된 것을 불러올 때 ↓↓</p>
<pre><code class="language-js">import {함수명1, 함수명2, ...} from 파일경로; // &lt;-- 함수명이라고 썼지만 변수/함수 모두 사용 가능</code></pre>
<p>↓↓ export default 로 지정된 것을 불러올 때 ↓↓</p>
<pre><code class="language-js">import 함수명 from 파일경로;</code></pre>
</li>
<li><p>export, import 하는 파일 모두 확장자명을 .js가 아니라 .mjs로 수정해야 한다.</p>
</li>
<li><p><img src="https://velog.velcdn.com/images/i_like_monday/post/8204f5ff-9ccf-4317-ac8f-b4a62f9cf18e/image.png" alt=""></p>
</li>
<li><p>터미널에서 실행할때도 파일명 끝에 .mjs 붙여줘야 정상적으로 실행이 됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/7b5ce43c-0d43-4c64-b6d8-8d2179d41c91/image.png" alt=""></p>
</li>
<li><p>파일생성(설정방법 2가지) : MyMod1.mjs, MyMod2.mjs
  파일생성(사용) : ModuleUse.mjs
  방법1(MyMod1) : <strong><em>외부로 출력하고자 하는 변수 및 함수, 클래스에 export 를 접두사로 붙임</em></strong>
  방법2(MyMod2) : _*<em>변수, 함수, 클래스를 기존에 사용하듯이 다 선언하고, 파일의 끝에서 export 를 *</em>_진행
방법2를 보편적으로 사용함</p>
</li>
<li><p>MyMod1.mjs</p>
<pre><code class="language-js">// MyMod1.mjs
</code></pre>
</li>
</ul>
<p>// export 로 설정 방법은 두 가지.
// export 로 설정하는 방법 1
// 외부로 출력하고자 하는 변수 및 함수, 클래스에 export 를 접두사로 붙임</p>
<p>export const name = &#39;아이유&#39;;
export const gender = &#39;여성&#39;;
export const job = &#39;가수&#39;;</p>
<p>export const getInfo = () =&gt; console.log(<code>이름 : ${name}, 성별 : ${gender}, 직업 : ${job}</code>);</p>
<pre><code>- MyMode2.mjs
```js
// MyMod2.mjs

// export 로 설정하는 방법 2
// 변수, 함수, 클래스를 기존에 사용하듯이 다 선언하고, 파일의 끝에서 export 를 진행

const num1 = 10;
const num2 = 20;

const sum = () =&gt; num1 + num2;

const sub = () =&gt; num1 - num2;

export {num1, num2, sum};
export default sub;</code></pre><ul>
<li>ModuleUse.js<pre><code class="language-js">// ModuleUse.mjs
</code></pre>
</li>
</ul>
<p>import {name, job, gender, getInfo} from &quot;./MyMod1.mjs&quot;;
import {num1, num2, sum} from &quot;./MyMod2.mjs&quot;;
import sub from &quot;./MyMod2.mjs&quot;;</p>
<p>console.log(name);  // 아이유
console.log(gender);  // 여성
console.log(job); // 가수
getInfo();  // 이름 : 아이유, 성별 : 여성, 직업 : 가수</p>
<p>console.log(<code>첫번째 숫자 : ${num1} + 두번째 숫자 : ${num2}의 값 : ${sum()}</code>); 
// 첫번째 숫자 : 10 + 두번째 숫자 : 20의 값 : 30
console.log(<code>sub() 실행 : ${sub()}</code>); // -10</p>
<pre><code>- ![](https://velog.velcdn.com/images/i_like_monday/post/72efb0d7-ed3b-4d2b-8669-783a9bb8186c/image.png)



---

## Promise (비동기 처리)
- Promise : 자바스크립트에서 &lt;u&gt;비동기 함수의 동기 처리&lt;/u&gt;를 하기 위해서 사용하는 객체
자바스크립트는 기본적으로 `1 쓰레드` `비동기 처리` 방식을 사용하고 있음 (특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것이 비동기 처리이다.)
자바스크립트에서 순차적으로 실행을 하고 싶을 경우 `콜백 함수`를 이용하여야 함
순차적으로 실행할 것이 많아지게 되면 `콜백지옥`이라고 불리는 형태가 만들어지게 됨
&lt;u&gt;프로미스는 이러한 콜백 지옥을 해결하기 위해서 사용하는 객체&lt;/u&gt;임

- promise 에는 `pending`, `fulfilled`, `rejected` 3가지 &lt;u&gt;**상태**&lt;/u&gt;가 존재함
   - &lt; 상태정보는 실제로 쓰는것은 아니고 상태정보일 뿐임 &gt;
`pending` : &lt;u&gt;대기 상태&lt;/u&gt;, 비동기 함수가 실행되고, 아직 처리가 완료되지 않은 상태
`fulfilled` : &lt;u&gt;완료 상태&lt;/u&gt;, 비동기 함수가 실행된 후 정상적으로 처리가 완료된 상태
`rejected` : &lt;u&gt;거부 상태&lt;/u&gt;, 비동기 함수가 실행된 후 오류가 발생한 상태

- promise 에는 실행 시 콜백함수가 실행되고, 해당 콜백함수의 매개변수로 `resolve()`, `reject()` 이라는 함수를 제공함
   - `resolve(매개변수)` : 프로미스 실행 후 fulfilled 상태일 경우 실행하는 함수로 나중에 `then()` 함수를 제공함
   - `reject(매개변수)` : 프로미스 실행 후 rejected 상태일 경우 실행하는 함수로 나중에 `catch()` 함수를 제공함

- 프로미스에는 완료 및 오류처리를 위해서 `then()`, `catch()` 함수를 제공하고 있음
`then(매개변수)` : 비동기 함수 실행이 &lt;u&gt;완료&lt;/u&gt;된 후 실행되는 함수
`catch(매개변수)` : 비동기 함수 실행이 &lt;u&gt;거부&lt;/u&gt;된 후 실행되는 함수

- 사용법
   - 선언 :
 ```js
function 프로미스를 사용할 함수명(매개변수) {
   return new Promise(function(resolve, reject) {
       비동기 통신 소스...
       비동기 통신 결과에 따라 resolve(), reject();
   });
}</code></pre><ul>
<li>실행 :<pre><code class="language-js">프로미스를 사용한 함수명()
.then(function(매개변수) {
  성공 시 실행할 내용
})
.catch(function(매개변수) {
  실패 시 실행할 내용
});</code></pre>
</li>
</ul>
<blockquote>
<p>자바스크립트의 이벤트 처리 방식 참고 : <a href="https://manbalboy.github.io/front/javascript01.html">https://manbalboy.github.io/front/javascript01.html</a><br>
V8에서 한줄씩 실행을 하다가 이벤트를 만나면 이벤트Queue에 넘기고 V8은 다시 실행해야되는 코드로 돌아가서 그 다음 줄 부터 하나씩 실행함<br>
이벤트 Queue 에서는 먼저 들어온것부터 Event Loop 돌림
워크 쓰레드에서 실행 후 결과를 이벤트 큐로 다시 보냄 -&gt; 이벤트 큐가 다시 실행 -&gt; 끝나면 이벤트 처리가 완전히 끝냈을때 실행하는 부분에 알려줌 (= 콜백)
ajax에서의 success: function() &lt;- function 이 콜백함수임
이것이 비동기 처리임<br>
단점은 콜백지옥이 생김
<a href="https://velog.io/@seul06/JavaScript-%EC%BD%9C%EB%B0%B1-%EC%A7%80%EC%98%A5">https://velog.io/@seul06/JavaScript-%EC%BD%9C%EB%B0%B1-%EC%A7%80%EC%98%A5</a><br>
이것을 해결하고자 만들어진 것이 promise 이다.</p>
</blockquote>
<ul>
<li>프로미스 사용 전 준비
jquery 추가 : 터미널에 <code>npm install jquery</code> 입력
<img src="https://velog.velcdn.com/images/i_like_monday/post/6415d727-35ac-4a19-b1bc-3f696c4ec50c/image.png" alt=""></li>
</ul>
<p>App.js </p>
<pre><code class="language-js">import &#39;./App.css&#39;;
import {getData, getData1} from &quot;./es6/Promise&quot;;

function App() {
  getData();

  return (
    &lt;div className=&quot;App&quot;&gt;

    &lt;/div&gt;
  );
}

export default App;</code></pre>
<ul>
<li>Promise.js<pre><code class="language-js">// import
import React from &quot;react&quot;;
import $ from &#39;jquery&#39;;
</code></pre>
</li>
</ul>
<p>// 프로미스 객체를 사용할 함수
function getData() {
  // 콜백함수 resolve, reject
  return new Promise(function (resolve, reject) {
    const data = 100;
    // resolve 를 실행하게되면 .then ~ 부분이 실행이 되고, reject 를 실행하게 되면 .catch ~ 부분이 실행이 됨
    resolve(data);
    // reject(data);
  });
}</p>
<p>getData()
  .then(function (data) {
    console.log(<code>프로미스 사용 반환값 : ${data}</code>);
  })
  .catch(function (err) {
    console.log(<code>프로미스 사용 오류 시 출력 : ${err}</code>)
  });</p>
<p>const getData1 = function () {
  return new Promise(function (resolve, reject) {
    $.ajax({
      url: &#39;<a href="http://localhost:8080/async/data1&#39;">http://localhost:8080/async/data1&#39;</a>,
      type: &#39;post&#39;,
      success: function (data) {
        console.log(&#39;통신 성공&#39;);
        resolve(data);
      },
      error: function () {
        reject(&#39;오류 발생!&#39;);
      }
    });
  });
};</p>
<p>// export 선언
export {getData, getData1};</p>
<pre><code>
- App.js에 코드 추가
```js
  getData1()
    .then(function (data) {
      console.log(data);
    })
    .catch(function (err) {
      console.log(err);
    });</code></pre><ul>
<li>ajax를 실행하기 위한 서버 생성
<img src="https://velog.velcdn.com/images/i_like_monday/post/56f19e7a-e90c-4ca9-8141-5292c8a85930/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/5f5cfbf4-3ed3-4288-9823-7ab9248feb1b/image.png" alt=""></li>
</ul>
<ul>
<li>port 번호가 서로 다를때 생기는 오류
<img src="https://velog.velcdn.com/images/i_like_monday/post/5be6623b-008c-431a-aee0-0c9e4c1cb3f1/image.png" alt="">
서버 또는 리액트 에서 옵션을 하나 더 넣어야 함
<code>@CrossOrigin(origins = &quot;http://localhost:3000&quot;)</code> 어노테이션 추가<pre><code class="language-java">@CrossOrigin(origins = &quot;http://localhost:3000&quot;)
@ResponseBody
@RequestMapping(value = &quot;/async/data1&quot;, method = RequestMethod.POST)
public String asyncData1() {
  return &quot;서버와 통신 성공&quot;;
}</code></pre>
</li>
</ul>
<blockquote>
<p>자바스크립트 비동기 처리와 콜백 함수 쉽게 이해하기
<a href="https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/">https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/</a></p>
</blockquote>
<blockquote>
<p>자바스크립트 Promise 쉽게 이해하기
<a href="https://joshua1988.github.io/web-development/javascript/promise-for-beginners/">https://joshua1988.github.io/web-development/javascript/promise-for-beginners/</a></p>
</blockquote>
<ul>
<li>리액트 개발툴 설치 필수
크롬 웹스토어 - 확장프로그램 : React Developer Tools 설치
<img src="https://velog.velcdn.com/images/i_like_monday/post/2caf7691-5011-4475-a6e0-9e0d3fae8da8/image.png" alt="">
실행화면
<img src="https://velog.velcdn.com/images/i_like_monday/post/f8561884-19b8-419f-ba00-34411418446d/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 01]]></title>
            <link>https://velog.io/@i_like_monday/React-01</link>
            <guid>https://velog.io/@i_like_monday/React-01</guid>
            <pubDate>Wed, 28 Dec 2022 08:28:15 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트-설치">리액트 설치</h1>
<ol>
<li><p>node.js가 필요하다. (yarn 이라는 것도 있으나 요즘은 node.js를 많이 쓰는 추세)</p>
<ul>
<li>1-1. NVM 설치 (NVM : node.js 버전 매니저 - 워낙 node.js 업데이트가 빨라서...)</li>
<li>1-2. node.js 최신버전(정확하게는 LTS버전) 설치</li>
</ul>
</li>
<li><p>React 프로젝트 생성</p>
<ul>
<li>2-1. create - react - app 툴 사용(node.js를 설치하면 자동으로 설치되는 툴)</li>
</ul>
</li>
<li><p>JS ES6 버전</p>
<ul>
<li>3-1. React는 자바스크립트 ES6 버전에 대한 이해도가 있어야 사용이 가능함.</li>
</ul>
</li>
</ol>
<h3 id="현재-설치된-nvm-버전-확인">현재 설치된 NVM 버전 확인</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/1d0ce38c-d0f1-4392-a675-c95140dff8d2/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/3ddfc7fc-de13-4936-b255-bb78c41d3b8b/image.png" alt=""></p>
<h3 id="nvm-lts버전-설치업데이트">NVM lts버전 설치/업데이트</h3>
<p><a href="https://github.com/coreybutler/nvm-windows">https://github.com/coreybutler/nvm-windows</a></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/00487858-9a32-479d-b32e-439f0c39890d/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/55d54810-98b9-49a1-915b-faf04b6a175a/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/e3c6bc7e-7b24-40eb-be65-743d6b2cb933/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/585a9bab-f0a4-4b3e-9ad5-d26799a488bc/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/2800440c-a2f9-434c-9de7-dc0c99837005/image.png" alt=""></p>
<p>여기까지 하면 node.js 사용 준비 완료.</p>
<hr>
<p>프로젝트 폴더 잡기
<img src="https://velog.velcdn.com/images/i_like_monday/post/400d88ba-3bb2-4f7a-96b2-0f9e564dedb9/image.png" alt="">
react_test1 프로젝트 생성
<img src="https://velog.velcdn.com/images/i_like_monday/post/49302a48-16f9-4b55-9721-5a1f17ce359c/image.png" alt="">
위 코드에서 로딩이 다 끝난 후 입력
<img src="https://velog.velcdn.com/images/i_like_monday/post/12dcc961-f83c-4bf1-b196-f797c7a1a55b/image.png" alt="">
완료되면 아래와같이 웹 페이지 새로 뜸
<img src="https://velog.velcdn.com/images/i_like_monday/post/e86414dd-7c07-488f-9265-074694314797/image.png" alt="">
Ctrl + C 두세번 누르면 벗어날 수 있음
<img src="https://velog.velcdn.com/images/i_like_monday/post/0e6c7d1f-8a7d-471c-85da-676ae2529dc6/image.png" alt=""></p>
<hr>
<h3 id="인텔리제이에서-리액트-프로젝트-생성">인텔리제이에서 리액트 프로젝트 생성</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/d86535f4-9c7a-4b49-8d24-25834673321c/image.png" alt=""></p>
<h4 id="구성편집에-npm이-안잡혀있다면">구성편집에 npm이 안잡혀있다면</h4>
<p>새 구성 추가 -&gt; npm -&gt; 이름:아무거나(리액트 동작) -&gt; 명령어: run -&gt; pakage.json은 잡혀있으면 그대로 쓰면 되고, 아니면 pakage.json이 있는 폴더를 찾아서 해당 폴더 지정해주면됨(프로젝트 폴더 내에 있음) -&gt; 스크립트: start -&gt; 나머지는 자동으로 불러온거 쓰면 됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/9fda48c8-0ba6-4bc6-9f26-25345564640f/image.png" alt="">
서버 실행해보면 처음에는 느리게 뜨지만 두번째 실행부터는 빨리 뜸</p>
<hr>
<h3 id="기본-구조">기본 구조</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/abb0a5a4-b1e4-4427-b604-ceb9e3f11203/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/fbb5adeb-617a-4a2f-a5ab-8828282960ab/image.png" alt=""></p>
<p>index.html은 거의 건들지 않고
index.js, app.js를 주로 만진다</p>
<hr>
<h1 id="es6-이론">ES6 이론</h1>
<ul>
<li>index.html 에서 <code>&lt;html lang=&quot;ko&quot;&gt;</code> en -&gt; ko로 수정</li>
</ul>
<ul>
<li>src - es6 경로 추가 - TemplateString.js 파일 생성 
<img src="https://velog.velcdn.com/images/i_like_monday/post/5b6d44a9-6238-4403-935f-8bb5fb0505a8/image.png" alt=""></li>
</ul>
<h3 id="es5-방식">ES5 방식</h3>
<ul>
<li>TemplateString.js<pre><code class="language-js">// TemplateString.js
</code></pre>
</li>
</ul>
<p>// ES6 에서 새로 추가된 문자열 사용 방식
// <code>(백틱) 기호 안에 문자열을 입력하는 방식
//</code> 안에 ${변수명} 을 사용하여 변수의 내용을 바로 출력하는 것이 가능
// ${ } 내부에서 간단한 연산도 가능함</p>
<p>// 기존
console.log(&#39;-----ES5 버전 -----&#39;);
var string1 = &#39;안녕하세요&#39;;
var string2 = &#39;반갑습니다.&#39;;</p>
<p>var greeting = string1 + &#39; &#39; + string2;
console.log(greeting);</p>
<p>var product = {name: &#39;도서&#39;, price: &#39;4200원&#39;};
var message = &#39;제품 &#39; + product.name + &#39;의 가격은 &#39; + product.price + &#39;입니다.&#39;;
console.log(message);</p>
<p>var multiLine = &#39;문자열1\n문자열2&#39;;
console.log(multiLine);</p>
<p>var value1 = 1;
var value2 = 2;
var boolValue = false;
var operator1 = &#39;곱셈값은 &#39; + value1 * value2 + &#39;입니다.&#39;;
console.log(operator1);
var operator2 = &#39;불리언 값은 &#39; + (boolValue ? &#39;참&#39; : &#39;거짓&#39;) + &#39;입니다.&#39;;
console.log(operator2);</p>
<pre><code>
- 인텔리제이에서 터미널 실행
![](https://velog.velcdn.com/images/i_like_monday/post/d2950126-5267-4c6d-bfb7-04ec02b2ed87/image.png)

---
### ES6 방식
ES6 에서 새로 추가된 문자열 사용 방식
`` ` ` `` (백틱) 기호 안에 문자열을 입력하는 방식
`` ` ` `` 안에 `${변수명}` 을 사용하여 변수의 내용을 바로 출력하는 것이 가능
`${ }` 내부에서 간단한 연산도 가능함
- TemplateString.js
```js
console.log(&#39;----- ES6 버전 -----&#39;)

var string1 = &#39;안녕하세요&#39;;
var string2 = &#39;반갑습니다&#39;;
var greeting = `${string1} ${string2}`;
console.log(greeting);

var product = {name: &#39;도서&#39;, price: &#39;4200원&#39;};
var message = `제품 ${product.name}의 가격은 ${product.price}`;
console.log(message);

var multiLine = `문자열1
문자열2`;
console.log(multiLine);

var value1 = 1;
var value2 = 2;
var boolValue = false;
var operator1 = `곱셈값은 ${value1 * value2}입니다.`
console.log(operator1);
var operator2 = `불리언 값은 ${boolValue ? &#39;참&#39; : &#39;거짓&#39;}입니다.`
console.log(operator2);</code></pre><ul>
<li>인텔리제이에서 터미널 실행
<img src="https://velog.velcdn.com/images/i_like_monday/post/f801e935-33ab-4808-97ec-1e7add70958f/image.png" alt=""></li>
</ul>
<hr>
<h3 id="변수">변수</h3>
<p>Variables.js (변수)</p>
<p>ES6 에서 <code>let</code>, <code>const</code> 키워드가 추가됨</p>
<ul>
<li><p><code>var</code> : ES5 버전까지 자바스크립트에서 변수를 선언하는 유일한 방식</p>
<ul>
<li>var 변수의 문제점 1 ) 변수의 스코프(범위)가 함수를 기준으로 하고 있음 </li>
</ul>
</li>
<li><blockquote>
<p>for 문 / if 문 / switch ~ case 내에서 선언한 변수라도 해당 코드블럭을 벗어나서 사용이 가능함</p>
</blockquote>
<ul>
<li>var 변수의 문제점 2 ) 중복 선언이 가능함</li>
</ul>
</li>
<li><p><code>let</code> : ES6에서 추가된 변수 선언 방식</p>
<ul>
<li>변수의 스코프가 코드 블럭( <code>{  }</code> )을 기준으로 함</li>
<li><blockquote>
<p>for 문 / if 문 / switch ~ case 의 코드블럭 내에서 선언된 변수는 해당 코드 블럭을 벗어나면 메모리에서 삭제됨</p>
</blockquote>
</li>
<li>중복 선언이 불가능함(자바의 변수선언 방식과 유사)</li>
</ul>
</li>
<li><p><code>const</code> : ES6에서 추가된 상수 선언 방식</p>
<ul>
<li>변수의 스코프가 코드 블럭( <code>{ }</code> ) 을 기준으로 함</li>
<li><blockquote>
<p>for 문 / if 문 / switch ~ case 의 코드블럭 내에서 선언된 변수는 해당 코드 블럭을 벗어나면 메모리에서 삭제됨</p>
</blockquote>
</li>
<li>중복 선언이 불가능함</li>
<li>지정된 변수의 데이터 수정이 불가능함</li>
<li>const 로 선언된 변수에 배열, object 와 같은 데이터를 저장했을 경우, 해당 배열 및 object 의 요소의 데이터를 변경할 수 있음</li>
</ul>
</li>
<li><p>Variable.js</p>
<pre><code class="language-js">// Variables.js (변수)
</code></pre>
</li>
</ul>
<p>// const는 상수이기 때문에 값을 직접 수정하고자 하면 오류 발생
const num = 1;
console.log(num);
// 상수이기때문에 처음 선언한 이후 다시 데이터를 넣으면 오류 발생
// num = 100;</p>
<p>const str = &#39;문자&#39;;
console.log(str);
// 상수이기때문에 처음 선언한 이후 다시 데이터를 넣으면 오류 발생
// str = &#39;변경된 문자&#39;;</p>
<p>const arr = [];
console.log(arr);
// 상수로 선언된 배열이라도 배열의 요소의 값을 수정하는 것은 문제가 없음
arr[0] = 10;
// 배열에 요소를 추가하는 append 함수 사용 시 오류남
// arr.append(30);</p>
<p>const arr1 = [1, 2, 3];
console.log(arr1);
// 배열에 요소를 추가하는 append 함수 사용 시 오류남
// arr1.append(4);</p>
<p>// 상수로 선언된 배열이라도 배열의 요소의 값을 수정하는 것은 문제가 없음
arr1[0] = 100;
arr1[1] = 200;
arr1[2] = 300;
console.log(arr1);</p>
<p>// object 타입에 데이터 추가 시 오류 발생
const obj = {};
// obj = {name: &#39;이름&#39;};</p>
<p>// object 가 가지고 있는 요소의 내용을 수정하는 것은 문제가 없음</p>
<p>const obj1 = {num: 1, name: &#39;아이유&#39;};
console.log(obj1);
obj1.num = 10;
obj1.name = &#39;유재석&#39;;
console.log(obj1);</p>
<p>console.log(&#39;\n\n&#39;);</p>
<p>// const 로 생성한 배열 혹은 object 에 내장 함수 사용 시 데이터 추가, 삭제가 가능함
// 하지만 무결성 제약조건을 위반하게 되므로 사용하지 않아야 함
const arr2 = [];
console.log(arr2);
arr2.push(1);
console.log(arr2);
arr2.splice(0, 0, 0);
console.log(arr2);
arr2.pop();
console.log(arr2);</p>
<p>const obj2 = {};
console.log(obj2);
obj2[&#39;name&#39;] = &#39;아이유&#39;;
console.log(obj2);
Object.assign(obj2, {name: &#39;유재석&#39;});
console.log(obj2);
delete obj2.name;
console.log(obj2);</p>
<p>console.log(&#39;\n\n&#39;);</p>
<p>// const 사용 시 내용을 수정하고자 하면 새로운 const 변수를 선언하고 데이터를 입력하는 방식으로 사용
const num1 = 1;
const num2 = num1 * 3;</p>
<p>const str1 = &#39;문자&#39;;
const str2 = str1 + &#39;추가&#39;;</p>
<p>const arr3 = [];
const arr4 = arr3.concat(1);
console.log(arr3);
console.log(arr4);</p>
<p>const arr5 = [...arr4, 2, 3];
console.log(arr5);</p>
<p>// slice : 잘라내는데 원본은 그대로 두고 잘라낸 배열만 새로 만들어 반환해줌
const arr6 = arr5.slice(0, 1);
console.log(arr5);
console.log(arr6);</p>
<p>// 전개연산법 : &#39;[first, ...arr7]&#39; 이 하나의 변수명이 됨, 뒤의 arr5는 배열 or Object
// arr5의 첫 값은 first라는 변수에 들어가고 나머지 값은 arr7에 들어감
const [first, ...arr7] = arr5;
console.log(first);
console.log(arr7);</p>
<p>const obj3 = {name: &#39;아이유&#39;, age: 30};
const obj4 = {...obj3, name: &#39;유재석&#39;};
const {name, ...obj5} = obj4;
console.log(obj3); // {name: 아이유, age: 30}
console.log(obj4); // {name: 유재석, age: 30}
console.log(name); // 유재석
console.log(obj5); // {age: 30}</p>
<pre><code>
---

### 전개연산법(1)
- 배열에서 전개연산자 사용하기
- 전개 연산자 ( ... )
  나열형 자료를 추출하거나 연결할 때 사용
  배열, 객체, 변수명 앞에 ... 기호를 사용하여 사용
  배열 객체 함 수 인자 표현식( [ ] ) 안에서만 동작함
- SpreadOperator1.js
```js
// SpreadOperator1.js

console.log(&#39;----- ES5 -----&#39;);

var array1 = [&#39;one&#39;, &#39;two&#39;];
var array2 = [&#39;three&#39;, &#39;four&#39;];

var combined = [array1[0], array1[1], array2[0], array2[1]];
// 배열의 각 요소를 하나씩 추출하여 새로운 배열에 대입
console.log(combined);

// array1 배열에 concat으로 array2 배열 붙이기
var combined = array1.concat(array2);
console.log(combined);

// 빈 배열에 concat 으로 배열 2개 붙이기
var combined = [].concat(array1, array2);
console.log(combined);

console.log(array1);  // [ &#39;one&#39;, &#39;two&#39; ]
var first = array1[0];
var second = array1[1];
// array1의 2번 index의 값이 없으면 기본적으로 false가 뜨지만 || 연산자를 넣어서 기본값 설정을 해주면 false일때 empty가 출력됨
var three = array1[2] || &#39;empty&#39;;
console.log(first);   // one
console.log(second);  // two
console.log(three);   // empty

console.log(&#39;----- ES6 -----&#39;);

var array1 = [&#39;one&#39;, &#39;two&#39;];
var array2 = [&#39;three&#39;, &#39;four&#39;];

// array1이 가진 모든것, array2가 가진 모든것 을 집어넣어서 하나의 배열로 생성
var combined = [...array1, ...array2];
console.log(combined); // [ &#39;one&#39;, &#39;two&#39;, &#39;three&#39;, &#39;four&#39; ]

// array1의 첫번째, 두번째, 세번째 요소가 각각 first, second, three 라는 변수에 들어감
// 하지만 array1에는 세번째 요소가 없으므로 &#39;= empty&#39; 로 기본값 설정해준 empty 가 들어감
// ...others는 남아있는 데이터 모두를 의미하지만 현재 배열에서는 남은것이 없으므로 빈 배열이 others라는 변수에 들어감
var [first, second, three = &#39;empty&#39;, ...others] =array1;
console.log(first); // one
console.log(second);  // two
console.log(three); // empty
console.log(others);  // []

// 잘못 사용한 예
// var wrongArr = ...array1; &lt;- 대괄호( [] ) 안에서만 전개연산자 사용할 수 있음!

// 전개연산자를 이용한 데이터 스와핑 간략화
var [first, second] = [second, first];

// 데이터 스와핑의 기존 방식(매우 번거로움!)
var first = 10;
var second = 20;
var tmp = 0;

tmp = first;
first = second;
second = tmp;</code></pre><hr>
<h3 id="전개연산법2">전개연산법(2)</h3>
<p>Object 타입에서 전개 연산자 사용하기</p>
<ul>
<li>SpreadOperator2.js<pre><code class="language-js">// SpreadOperator2.js
// Object 타입에서 전개 연산자 사용하기
</code></pre>
</li>
</ul>
<p>console.log(&#39;----- ES5 -----&#39;);</p>
<p>var objOne = {one: 1, two: 2, other: 0};
var objTwo = {three: 3, four: 4, other: -1};</p>
<p>var combined = {
  one: objOne.one,
  two: objOne.two,
  three: objTwo.three,
  four: objTwo.four
};
console.log(combined); // { one: 1, two: 2, three: 3, four: 4 }</p>
<p>// assign() 함수를 이용하여  object 타입의 변수를 하나로 합함
// 첫번째 매개변수는 합쳐질 대상, 두번째와 세번째 매개변수는 첫번째 매개변수에 합함
// 동일한 키를 사용한 데이터가 있을 경우 &#39;뒤쪽&#39;에 나오는 키의 값으로 합쳐짐
var combined = Object.assign({}, objOne, objTwo);
console.log(combined); // { one: 1, two: 2, other: -1, three: 3, four: 4 }</p>
<p>// 위에 있는 함수와 매개변수의 순서가 다르기 때문에 결과도 달라짐
var combined = Object.assign({}, objTwo, objOne);
console.log(combined); // { three: 3, four: 4, other: 0, one: 1, two: 2 }</p>
<p>var others = Object.assign({}, combined);
console.log(others);  // { three: 3, four: 4, other: 0, one: 1, two: 2 }</p>
<p>delete others.other;
console.log(others);  // { three: 3, four: 4, one: 1, two: 2 }</p>
<p>console.log(&#39;\n----- ES6 -----\n&#39;);</p>
<p>var combined = { ...objOne, ...objTwo};
console.log(combined);  // { one: 1, two: 2, other: -1, three: 3, four: 4 }</p>
<p>var combined = { ...objTwo, ...objOne};
console.log(combined);  // { three: 3, four: 4, other: 0, one: 1, two: 2 }</p>
<p>var {other, ...others} = combined;
console.log(other); // 0
console.log(others);  // { three: 3, four: 4, one: 1, two: 2 }
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Python - 08]]></title>
            <link>https://velog.io/@i_like_monday/Python-08</link>
            <guid>https://velog.io/@i_like_monday/Python-08</guid>
            <pubDate>Tue, 20 Dec 2022 08:44:45 GMT</pubDate>
            <description><![CDATA[<h2 id="flask-wtf-설치">Flask-WTF 설치</h2>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/d5ce6fbd-ce35-4f14-8cae-f48b08d7abf2/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/ace6e2e7-31b0-47a5-bc5f-a80c25c6e4dc/image.png" alt=""></p>
<hr>
<h1 id="페이징-처리">페이징 처리</h1>
<h3 id="페이징-처리를-위해-백데이터-넣기">페이징 처리를 위해 백데이터 넣기</h3>
<p>들여쓰기, commit 전 enter 2번 주의
<img src="https://velog.velcdn.com/images/i_like_monday/post/4fe46fcc-8ee1-474f-a174-b4819d931352/image.png" alt=""></p>
<pre><code class="language-py">from pybo import db
from pybo.models import Question
from datetime import datetime


for i in range(300):
    q = Question(subject=&#39;테스트 데이터입니다:[%03d]&#39; % i, content=&#39;내용무&#39;, create_date=datetime.now())
    db.session.add(q)
db.session.commit()</code></pre>
<p>↓ 백데이터 생성됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/82d9b15a-c0f5-448e-9916-39dbd983b173/image.png" alt=""></p>
<p>페이징 처리는 보안이 필요없기 때문에 Get방식으로 넘어가면 됨
<img src="https://velog.velcdn.com/images/i_like_monday/post/61fdc93a-604a-4718-b651-c3bdcba9784a/image.png" alt=""></p>
<p>question_views.py</p>
<pre><code class="language-py">@bp.route(&#39;/list&#39;)
def _list():
    # 페이징 처리를 위해 추가한 코드 1 ↓
    page = request.args.get(&#39;page&#39;, type=int, default=1)    # 페이징

    # question_list = &quot;select * from question order by id desc&quot; (sql)
    question_list = Question.query.order_by(Question.create_date.desc())

    # 페이징 처리를 위해 추가한 코드 2 ↓
    question_list = question_list.paginate(page=page, per_page=10)   # 페이징

    return render_template(&#39;question/question_list.html&#39;, question_list=question_list)</code></pre>
<h3 id="페이징-처리의-원리">페이징 처리의 원리</h3>
<p>total : 735
page per : 10
block per : 15
total page : 735 / 10 -&gt; 73.5 -&gt; 절상 : 74 -&gt; 74페이지까지 있어야 함
total Block : 74 / 15 -&gt; 4.93 -&gt; 절상 : 5 -&gt; 마지막 블럭은 5개 있어야 함
now page : 1(default)</p>
<p>mySQL = limit 사용
limit 0, 10 : 시작번호 0부터 10개 (1페이지)
limit 10, 10 : 10 : 시작번호 10부터 10개(2페이지)
limit 20, 10 : 시작번호 20부터 10개 (3페이지)</p>
<p>이중 for문을 돌려서 jsp에서 뿌려주면 됨</p>
<hr>
<h3 id="datetime-format">datetime format</h3>
<p><code>__init__.py</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/89aeccc7-f852-467a-bd25-dbc9502f2b2b/image.png" alt="">
<code>filter.py</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/7e06faa8-451d-4e5b-8785-5fb44b94c069/image.png" alt="">
<code>question_detail.html</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/b662c4fd-623f-41e6-a998-34fc766256a7/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/d452c12b-82ba-4e74-aeb0-a5abe0591a78/image.png" alt=""><img src="https://velog.velcdn.com/images/i_like_monday/post/02a4be31-5326-4d9f-b1c2-4013fcb7a8e6/image.png" alt=""></p>
<hr>
<h3 id="db--테이블-생성">DB : 테이블 생성</h3>
<ul>
<li><p>models.py 코드추가</p>
<pre><code class="language-py">class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
email = db.Column(db.String(150), unique=True, nullable=False)</code></pre>
</li>
<li><p>테이블 update
<img src="https://velog.velcdn.com/images/i_like_monday/post/b59d6cc4-cd7a-4556-b835-28e0932ba15d/image.png" alt=""></p>
</li>
<li><p>forms.py import 추가</p>
<pre><code class="language-py">from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, PasswordField, EmailField
from wtforms.validators import DataRequired, Length, EqualTo, Email</code></pre>
</li>
<li><p>forms.py 코드 추가</p>
<pre><code class="language-py">class UserCreateForm(FlaskForm):
username = StringField(&#39;사용자이름&#39;, validators=[DataRequired(), Length(min=3, max=25)])
password1 = PasswordField(&#39;비밀번호&#39;, validators=[DataRequired(), EqualTo(&#39;password2&#39;, &#39;비밀번호가 일치하지 않습니다.&#39;)])
password2 = PasswordField(&#39;비밀번호확인&#39;, validators=[DataRequired])
email = EmailField(&#39;이메일&#39;, validators=[DataRequired(), Email()])</code></pre>
</li>
<li><p>pip install validator
<img src="https://velog.velcdn.com/images/i_like_monday/post/c5ee7350-3067-49d2-a166-25f2ee7370f6/image.png" alt=""></p>
</li>
<li><p>views/auth_views.py 생성</p>
<pre><code class="language-py"># views/auth_views.py : 인증 및 인가 기능
</code></pre>
</li>
</ul>
<p>from flask import Blueprint, url_for, render_template, flash, request
from werkzeug.security import generate_password_hash
from werkzeug.utils import redirect</p>
<p>from pybo import db
from pybo.forms import UserCreateForm
from pybo.models import User</p>
<p>bp = Blueprint(&#39;auth&#39;, <strong>name</strong>, url_prefix=&#39;/auth&#39;)</p>
<p>@bp.route(&#39;/signup/&#39;, methods=(&#39;POST&#39;, &#39;GET&#39;))
def signup():
  form = UserCreateForm()
  if request.method == &#39;POST&#39; and form.validate_on_submit():
    # db에서 username을 검색해서 첫번째 레코드를 출력,
    user = User.query.filter_by(username=form.username.data).first()
    # 만약 데이터가 없다면
    if not user:
      # hash 값으로 저장 : 암호화
      user = User(username=form.username.data, password=generate_password_hash(form.password1.data), email=form.email.data)
      db.session.add(user)
      db.session.commit()
      return redirect(url_for(&#39;main.index&#39;))
    else:
      flash(&#39;이미 존재하는 사람입니다.&#39;)
  return render_template(&#39;auth/signup.html&#39;, form=form)</p>
<pre><code>
- __init__.py 코드 추가
1. auth_views
2. app.register_blueprint(auth_views.bp)
```py
from .views import main_views, question_views, answer_views, **auth_views**
    app.register_blueprint(main_views.bp)
    app.register_blueprint(question_views.bp)
    app.register_blueprint(answer_views.bp)
    **app.register_blueprint(auth_views.bp)**</code></pre><ul>
<li><p>navbar.html 코드 수정
<code>href=&quot;#&quot;</code> → <code>href=&quot;{{ url_for(&#39;auth.signup&#39;) }}&quot;</code></p>
<pre><code class="language-html">&lt;a class=&quot;nav-link&quot; href=&quot;{{ url_for(&#39;auth.signup&#39;) }}&quot;&gt;계정생성&lt;/a&gt;</code></pre>
</li>
<li></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Python - 07]]></title>
            <link>https://velog.io/@i_like_monday/Python-07</link>
            <guid>https://velog.io/@i_like_monday/Python-07</guid>
            <pubDate>Mon, 19 Dec 2022 07:11:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h1 id="장고django란">장고(Django)란?</h1>
<br>
장고(Django, FAQ 발음으로는 "쟁고"(IPA: [ˈdʒæŋgoʊ]))는 파이썬으로 작성된 오픈 소스 웹 프레임워크로, 모델-템플릿-뷰(MTV) 패턴을 따르고 있다. 현재는 장고 소프트웨어 재단에 의해 관리되고 있다.<br>
고도의 데이터베이스 기반 웹사이트를 작성하는 데 있어서 수고를 더는 것이 장고의 주된 목표이다. 장고는 콤포넌트의 재사용성(reusability)과 플러그인화 가능성(pluggability), 빠른 개발 등을 강조하고 있다. 또한, "DRY(Don't repeat yourself: 중복배제)" 원리를 따랐다. 설정 파일부터 데이터 모델에까지 파이썬 언어가 구석구석에 쓰였다.<Br>
인스타그램, NASA, 빗버킷, Disqus, 모질라에서 장고를 사용하는 것으로 알려져있다.
</blockquote>
<blockquote>
<h1 id="플라스크flask란">플라스크(Flask)란?</h1>
<p>플라스크(Flask)는 파이썬으로 작성된 마이크로 웹 프레임워크의 하나로, Werkzeug 툴킷과 Jinja2 템플릿 엔진에 기반을 둔다. BSD 라이선스이다.<br>
플라스크의 최신 안정판은 2017년 5월 기준으로 1.1.1이다.[2] 플라스크 프레임워크를 사용하는 애플리케이션에는 핀터레스트,[3] 링크드인,[4] 플라스크 자체를 위한 공동체 웹 페이지를 포함한다.<br>
플라스크는 특별한 도구나 라이브러리가 필요 없기 때문에 마이크로 프레임워크라 부른다. 데이터베이스 추상화 계층, 양식 유효성 확인, 기타 기존의 서드파티 라이브러리가 공통 기능을 제공하는 구성 요소가 없다. 그러나 플라스크는 플라스 자체에서 구현된 것처럼 애플리케이션 기능을 추가할 수 있는 확장 기능을 지원한다. 확장 기능은 객체 관계 매퍼, 양식 유효성 확인, 업로드 관리, 다양한 개방형 인증 기술, 여러 공통 프레임워크 관련 도구들을 위해 존재한다. 확장 기능들은 코어 플라스크 프로그램에 비해 훨씬 더 정기적으로 업데이트된다.<br></p>
</blockquote>
<h3 id="예시">예시</h3>
<pre><code class="language-py">from flask import Flask
app = Flask(__name__)
@app.route(&quot;/&quot;)
def hello():
    return &quot;Hello World!&quot;
if __name__ == &quot;__main__&quot;:
    app.run()</code></pre>
<hr>
<h3 id="파이썬-버전확인-가상환경-설정하기">파이썬 버전확인, 가상환경 설정하기</h3>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/3cc9c0d9-d745-44d1-b1d3-b4d256ee220e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/f4178c7b-2ef4-4458-a24b-ad1fb802769a/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/d3fd5fad-0919-44e3-abf0-46ece4c6d337/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/c273e028-0c33-4fcd-bb69-709810f8f03a/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/990afc5e-1df8-4f8c-a369-469ff332a5a4/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/c8a222b5-ee10-44b3-9c1f-1e2db42ca293/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/9c93c7b0-64f9-4d76-a1bf-3ca1d44cb619/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/c4337241-be48-40dc-b837-892cf6238868/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/fe2cbd54-506e-4127-a72a-60deccf545fe/image.png" alt=""></p>
<ul>
<li>환경변수 설정
<img src="https://velog.velcdn.com/images/i_like_monday/post/2c2f9865-9eaa-4b82-adac-b4018fa2786e/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/18dc3e11-21cb-42d8-8485-9128f867f0ad/image.png" alt=""></p>
<ul>
<li>bat 파일 생성
<img src="https://velog.velcdn.com/images/i_like_monday/post/1d72f2ec-1b0b-4a35-b06a-c16c55849214/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/dd3f089f-3f93-4d9b-b96f-1bbb04b6ee8e/image.png" alt=""><pre><code class="language-cmd">C:\users\admin\myproject</code></pre>
해당 코드 콘솔창에 쳐주면 자동으로 path 인식해서 가상환경으로 들어감</li>
<li>실행완료화면
<img src="https://velog.velcdn.com/images/i_like_monday/post/26c64d4b-d18b-4e08-b64c-b5f3ca99eea8/image.png" alt=""></li>
</ul>
<hr>
<p>파이참 -&gt; open -&gt; [project - myproject]</p>
<ul>
<li>myproject 영역에서는 코딩만 진행</li>
<li>파이썬 가상실행환경 [vens - myproject - Scripts] 를 이용해서 서버 실행</li>
</ul>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/c8e5a01c-7cbc-4590-b46d-ce1e3180d2d4/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/4eb27303-fc80-4f7d-89d4-6e56b69a8cdd/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/7660a2aa-7eaa-4c8a-800c-c85b4b4d1bf4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/d5e6617a-7b5a-4746-93e1-f549f7cd374a/image.png" alt=""></p>
<p>  main.py -&gt; pybo.py 이름변경
<img src="https://velog.velcdn.com/images/i_like_monday/post/53746b1e-ccf0-463c-87d2-0ec3821fc031/image.png" alt=""></p>
<hr>
<h3 id="플라스크-run오류">플라스크 run오류</h3>
<p>  계속 cmd창에서 flask run 입력시 오류가 나서 전체 경로를 넣어서 run 해줘야했다.
  <code>C:\venvs\myproject\Scripts\python -m flask run</code>
  <img src="https://velog.velcdn.com/images/i_like_monday/post/adc6bb35-edcb-44a5-941b-3cf59c2c61ce/image.png" alt=""></p>
<hr>
<p>최종 경로 확인<br><img src="https://velog.velcdn.com/images/i_like_monday/post/ef9be4ee-c6e0-4dad-981c-d87092e2f49f/image.png" alt=""></p>
<hr>
<p><strong>init</strong>.py</p>
<pre><code class="language-py">  # __init__.py

from flask import Flask

def create_app():
  app = Flask(__name__)

  from .views import main_views
  app.register_blueprint(main_views.bp)

  return app</code></pre>
<p>  main_views.py</p>
<pre><code class="language-py">  # main_views.py

from flask import Blueprint

bp = Blueprint(&#39;main&#39;, __name__, url_prefix=&#39;/&#39;)

@bp.route(&#39;/&#39;)
def index():
    return &#39;Hello, index&#39;

@bp.route(&#39;/hello&#39;)
def hello_pybo():
    return &#39;Hello, 하이부~~~&#39;</code></pre>
<hr>
<h2 id="sql-lite-설치">sql-lite 설치</h2>
<p>  <a href="https://sqlitebrowser.org/dl/">https://sqlitebrowser.org/dl/</a>
  <img src="https://velog.velcdn.com/images/i_like_monday/post/3068048f-d4c4-40fe-8aeb-a487ca5da3ca/image.png" alt=""></p>
<hr>
<h1 id="플라스크-orm-라이브러리-사용하기">플라스크 ORM 라이브러리 사용하기</h1>
<p>  ORM, FLASK 
<img src="https://velog.velcdn.com/images/i_like_monday/post/37637e94-cb3f-4ad2-90c9-6e82774b9c6a/image.png" alt="">
<code>(myproject) C:\projects\myproject&gt;C:\venvs\myproject\Scripts\python -m pip install flask-migrate</code>
<img src="https://velog.velcdn.com/images/i_like_monday/post/e35b70db-29c5-4177-9087-0da1375f481e/image.png" alt="">
<code>(myproject) C:\projects\myproject&gt;C:\venvs\myproject\Scripts\python -m flask db init</code></p>
<p><strong>init</strong>.py</p>
<pre><code class="language-py">  # __init__.py

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

import config

db = SQLAlchemy()
migrate = Migrate()

def create_app():
  app = Flask(__name__)
  app.config.from_object(config)

  #ORM
  db.init_app(app)
  migrate.init_app(app, db)

  from .views import main_views
  app.register_blueprint(main_views.bp)

  return app</code></pre>
<p>config.py</p>
<pre><code class="language-py">  # myproject/config.py
# DB 연동에 대한 파일

import os

BASE_DIR = os.path.dirname(__file__)
SQLALCHEMY_DATABASE_URI = &#39;sqlite:///{}&#39;.format(os.path.join(BASE_DIR, &#39;pybo.db&#39;))
SQLALCHEMY_TRACK_MODIFICATIONS = False</code></pre>
<hr>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/b02c9807-7992-4aec-a74a-04b478ee8033/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/ad81d8f3-2852-45cb-842e-302a282e3b33/image.png" alt=""></p>
<p>  int.py</p>
<pre><code class="language-py">
 # __init__.py

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

import config

db = SQLAlchemy()
migrate = Migrate()

def create_app():
  app = Flask(__name__)
  app.config.from_object(config)

  #ORM
  db.init_app(app)
  migrate.init_app(app, db)
  from . import models

  from .views import main_views
  app.register_blueprint(main_views.bp)

  return app</code></pre>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/ea1989c0-24e0-4b85-952e-d40168368ab8/image.png" alt="">
-&gt; mysql lite 열어보면 
  <img src="https://velog.velcdn.com/images/i_like_monday/post/1f76f7a1-54b9-4257-a03d-47bcb01b51f6/image.png" alt=""></p>
<hr>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/c0af701f-6c4f-4590-861b-7f71c798e1cf/image.png" alt="">
<code>C:\venvs\myproject\Scripts\python -m flask shell</code></p>
<hr>
<p><img src="https://velog.velcdn.com/images/i_like_monday/post/b4b04a02-0758-428e-ab06-2b543b89442c/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/b1fda96b-4f7c-4120-a14e-a6f614618723/image.png" alt=""></p>
<hr>
<p>  컬럼 데이터 변경
<img src="https://velog.velcdn.com/images/i_like_monday/post/81bae394-4893-451b-85b8-aaf0957e642d/image.png" alt=""></p>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/ff47159f-000e-459b-ab5b-4a25244274b6/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/c17ff4f6-734f-423f-96e4-f33bce171aae/image.png" alt=""></p>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/48569036-6f12-461d-91cf-542838710635/image.png" alt=""></p>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/6040eab2-38cc-41e4-b5f8-eb8955355f6e/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_like_monday/post/6e8d6b7a-67b7-448d-9478-1def44577b6c/image.png" alt=""></p>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/a13d933b-d156-45fc-a0e4-edc6ede5fded/image.png" alt="">
  <img src="https://velog.velcdn.com/images/i_like_monday/post/c172501f-761d-4c84-a421-1b10f29316b2/image.png" alt=""></p>
<hr>
<p>flask run</p>
<p>main_views.py</p>
<pre><code class="language-py">  # main_views.py

from flask import Blueprint, render_template
from pybo.models import Question

bp = Blueprint(&#39;main&#39;, __name__, url_prefix=&#39;/&#39;)

@bp.route(&#39;/&#39;)
def index():
    # 원래는
    # question_list = &#39;select * from question order by id desc&#39;
    question_list = Question.query.order_by(Question.create_date.desc())
    return render_template(&#39;question/question_list.html&#39;, question_list=question_list)

@bp.route(&#39;/hello&#39;)
def hello_pybo():
    return &#39;Hello, 하이부~~~&#39;</code></pre>
<p>  question_list.html</p>
<pre><code class="language-html">  &lt;!-- question_list.html --&gt;

&lt;!-- 타임리프의 확장자는 HTML --&gt;
{% if question_list %}
    &lt;ul&gt;
        {% for question in question_list %}
            &lt;li&gt;&lt;a href=&quot;/detail/{{ question.id }}&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;
        {% endfor%}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
<hr>
<p>  <img src="https://velog.velcdn.com/images/i_like_monday/post/a0b1c6c8-310b-48a7-8b60-01bd3b893a10/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>