<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>wamt_2787.log</title>
        <link>https://velog.io/</link>
        <description>IT 학습과정중 학습내용을 복기하기 위해 사용하는 블로그 입니다.</description>
        <lastBuildDate>Sun, 12 Mar 2023 14:22:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>wamt_2787.log</title>
            <url>https://velog.velcdn.com/images/wamt_2787/profile/f53aa192-2eec-441c-b111-2364363a18f9/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. wamt_2787.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wamt_2787" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React 이미지 비율조정]]></title>
            <link>https://velog.io/@wamt_2787/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B9%84%EC%9C%A8%EC%A1%B0%EC%A0%95</link>
            <guid>https://velog.io/@wamt_2787/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B9%84%EC%9C%A8%EC%A1%B0%EC%A0%95</guid>
            <pubDate>Sun, 12 Mar 2023 14:22:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Canvas에서 이미지를 출력하니 크기가 제각각 이였다 때문에 비율을 구해서 이미지를 resize하는 모듈을 찾아냈다.</p>
</blockquote>
<p>참고링크
<a href="https://www.npmjs.com/package/react-image-file-resizer">https://www.npmjs.com/package/react-image-file-resizer</a></p>
<pre><code class="language-js">const resizeFile = (file) =&gt;
        new Promise((resolve) =&gt; {
            Resizer.imageFileResizer(
                file,
                300,
                300,
                &#39;JPEG&#39;,
                100,
                0,
                (uri) =&gt; {
                    resolve(uri);
                },
                &#39;base64&#39;
            );
        });</code></pre>
<p>async 함수 </p>
<pre><code class="language-js">const onChange = async (event) =&gt; {
  try {
    const file = event.target.files[0];
    const image = await resizeFile(file);
    console.log(image);
  } catch (err) {
    console.log(err);
  }
};</code></pre>
<p>맨위의 코드와 같이 함수를 만들어주면 base64의 형태로 입력한 이미지가 resize되어 사용할수 있게된다.이후 base64의 코드로 출력된 resize이미지를 url을 받기위해 아래와같은 함수를 사용해준다.</p>
<pre><code class="language-js"> function base64toFile(base_data, filename) {
        var arr = base_data.split(&#39;,&#39;),
            mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]),
            n = bstr.length,
            u8arr = new Uint8Array(n);

        while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }

        return new File([u8arr], filename, { type: mime });
    }</code></pre>
<p>내 코드의 onChange 비동기 함수 부분은 아래와 같다.</p>
<pre><code class="language-js">onChange={async (e) =&gt; {
               e.preventDefault(); //모든 이벤트를 스탑하겠다
               let reader = new FileReader();
               let file = e.target.files[0];

               reader.onloadend = async () =&gt; {
               const image = await resizeFile(file);
               const imgfile = base64toFile(await image, &#39;newImg.jpg&#39;);

                setImg({
                        file: imgfile,
                        previewURL: await image,
                        });
                    };
               reader.readAsDataURL(file);</code></pre>
<p>여기서 눈여겨 봐야할 부분은 먼저 예제와 같이 resize를 사용한 부분이고 두번째로
base64toFile을 선언한 부분이다 imgfile이라는 변수를 선언해서 값을 넣었기 때문에
결과적으로 imgfile에 최종 resize된 이미지가 저장이되고 이후 previewUrl 로 우리의
canvas에 그려지게된다 즉 imgfile에 서버로 가는 이미지가 담기는 것</p>
<p>base64toFile 참고링크
<a href="https://bcdragonfly.tistory.com/74">https://bcdragonfly.tistory.com/74</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 다듬기 (상)]]></title>
            <link>https://velog.io/@wamt_2787/React-%EC%B4%88%EA%B8%89-%EC%8B%A4%EC%A0%84</link>
            <guid>https://velog.io/@wamt_2787/React-%EC%B4%88%EA%B8%89-%EC%8B%A4%EC%A0%84</guid>
            <pubDate>Sat, 25 Feb 2023 19:05:43 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프로젝트-생성--bootstrap다운">1. 프로젝트 생성 &amp; Bootstrap다운</h2>
<blockquote>
</blockquote>
<ol>
<li>새로운 프로젝트를 생성해보자     <code>npx create-react-app $작명</code></li>
<li><code>npm start</code> npm을 사용해서 개발할것이다. 앱을 실행 시켜보자.</li>
<li>페이지 디자인을 위한 <code>bootstrap</code> 이라는 라이브러리를 설치해보자.
<a href="https://react-bootstrap.github.io/getting-started/introduction">https://react-bootstrap.github.io/getting-started/introduction</a></li>
<li><code>index.html</code> 의 <code>head태그</code> 안에 link된 bootstrap 위의 정보를 넣어주자. </li>
<li>bootStrap 에서 원하는 <code>UI</code>를 복사 붙여넣기 해주자</li>
<li>사용할 <code>UI</code>를 import 로 선언해주자. (태그 앞대문자 선언)<pre><code class="language-java">import { Navbar,Container,Nav } from &#39;react-bootstrap&#39;;</code></pre>
🤨 : 선언한 <code>UI</code>의 스타일 수정은 ClassName을 사용해 css변경<img src="https://velog.velcdn.com/images/wamt_2787/post/e1f58010-c4cc-46ca-96cd-0b9c5f863555/image.png" alt=""> <img src="https://velog.velcdn.com/images/wamt_2787/post/59da89ed-dc2a-47a3-8a9e-2e831d983dd8/image.png" alt=""></li>
</ol>
<h2 id="2-이미지-넣기--public폴더">2. 이미지 넣기 &amp; Public폴더</h2>
<blockquote>
</blockquote>
<ol>
<li>대문사진을 하나 넣어보자.<code>div태그</code>를 생성후 <code>classaname</code> 선언</li>
<li><code>Img 폴더</code>를 생성후 그안에 대문사진 이미지를 넣어주자.</li>
<li>css로 이동 <code>background-image: url(&#39;&#39;)</code>주소와 css 코드 입력</li>
<li>상품컬럼 3개를 만들고 이미지주소를 통해 이미지를 붙이자. (링크를 참조)
<a href="https://react-bootstrap.netlify.app/layout/grid/#rb-docs-content">https://react-bootstrap.netlify.app/layout/grid/#rb-docs-content</a></li>
<li>이미지를 많이 쓸땐 Public에 관리하는것이 좋다 하지만 경로가 바뀌면 오류가 발생할수 있음으로 다음과 같은 방법으로 코드를 짜면된다.
<code>&lt;img src={process.env.PUBLIC_URL + &#39;/img/logo.png&#39;}</code> 
🤨 :html에서 이미지를 넣을땐 import 로 이름과 경로를 선언해주어야함
<img src="https://velog.velcdn.com/images/wamt_2787/post/6a0b6c44-041b-4021-8f76-0c4317827fdf/image.png" alt=""></li>
</ol>
<h2 id="3-import-export를-이용">3. import export를 이용</h2>
<blockquote>
</blockquote>
<h4 id=""></h4>
<p>상품컬럼에 대한 정보를 서버를 통해 가져왔다 가정하고 진행하겠다.</p>
<ol>
<li>useState를 사용     <code>let [art] = useState(data);</code></li>
<li>data.js 라는 상품컬럼 정보 에대한 파일을 만들어 <code>export</code>해준다.</li>
<li>다시 App.js로 돌아와서 data.js를 <code>import</code> 해준다.</li>
<li>data.js 의 구조는 <code>array</code>안의 <code>object</code>구조이기 때문에 선언할떄 유의한다.</li>
<li>즉 첫번째 상품의 제목을 알고싶으면 <code>art[0].title</code> 이런식으로 써야한다.<img src="https://velog.velcdn.com/images/wamt_2787/post/e1975198-7fb1-4887-b26c-cbececd4b83e/image.png" alt=""><img src="https://velog.velcdn.com/images/wamt_2787/post/7e6d58b6-23b6-4be1-8376-71ad6307fa9d/image.png" alt=""><img src="https://velog.velcdn.com/images/wamt_2787/post/85498a8f-0906-4ef1-adbe-ec548770b3bf/image.png" alt=""></li>
</ol>
<h2 id="4-컴포넌트-만들기">4. 컴포넌트 만들기</h2>
<blockquote>
<h4 id="컴포넌트-함수를-만들어서-원래의-코드를-더-간단하게-표현하자">컴포넌트 함수를 만들어서 원래의 코드를 더 간단하게 표현하자.</h4>
</blockquote>
<ol>
<li>기존의 <code>App함수</code>가 아닌 외부에 <code>MidArt</code>라는 컴포넌트 함수를 만들것이다.</li>
<li>상품컬럼 3개가 동일한 형식이기 때문에 이를 컴포넌트로 대체한다.</li>
<li>컴포넌트 함수안에 동일한 형식인 상품컬럼 한가지의 코드 내용을 복붙 한다.</li>
<li>🚨문제 발생 외부의 함수에서 <code>state</code>변수 사용으로 코드가 동작하지 않는다.</li>
<li><code>props</code>를 사용하여 부모함수에서 <code>state</code>변수에대한 정보를 가져오게 한다.</li>
<li><code>props</code>선언으로 인해 컴포넌트를 사용하는 부분에 다음과 같이 state 이름과 작명을 해주고<code>&lt;MidArt art={art[0]}&gt;&lt;/MidArt&gt;</code> 컴포넌트 함수에선 state 사용 부분에 <code>props.art.title</code> 이와같이 <code>props.</code>을 붙여준다.</li>
<li>6번까지가 <code>상품컬럼[0]</code> 에대한 컴포넌트 생성이고 나머지 두개도 진행해준다. <h4 id="app함수">App함수<img src="https://velog.velcdn.com/images/wamt_2787/post/55087e9f-c9bc-45d2-80c0-a0130d4f5ccb/image.png" alt=""><img src="https://velog.velcdn.com/images/wamt_2787/post/d25ccdda-cf53-40e1-96b7-7ab4bb3d377c/image.png" alt=""></h4>
</li>
</ol>
<h2 id="4-2-maps-활용">4-2. Maps 활용</h2>
<blockquote>
<h4 id="map을-사용해-3개의-위-반복코드를-더-간단하게-만들어줄것이다">Map을 사용해 3개의 위 반복코드를 더 간단하게 만들어줄것이다.</h4>
</blockquote>
<ol>
<li>map 함수는 반복문이며 map 앞의 변수의 크기만큼 반복한다.</li>
<li><code>art.map</code> 은 art의 배열수만큼 반복실행한다. 함수로 만들어보자.<pre><code class="language-js">{art.map(() =&gt; {
 return&lt;MidArt art={art[0]}&gt;&lt;/MidArt&gt;;
})} </code></pre>
</li>
<li>위와 같은 코드는<code>art[0]</code> 상품컬럼을 <code>art</code>배열의 개수인 3번만큼 반복한다.</li>
<li>때문에  반복문이 돌때마다 1씩 증가하는 파라미터 <code>i</code>를 사용하여 해결한다.<pre><code class="language-js">{art.map((a,i) =&gt; {
 return&lt;MidArt art={art[i]}&gt;&lt;/MidArt&gt;;
})} </code></pre>
</li>
</ol>
<h2 id="5-라우팅">5. 라우팅</h2>
<h4 id="리액트는-html파일-하나만-사용하여-여러페이지를-보여준다때문에-사용자가-다른-페이지를-요청하면-내부의-div들을-갈아치워서-보여주는데-react-router-dom은-이것을-간편하게-해준다">리액트는 html파일 하나만 사용하여 여러페이지를 보여준다.때문에 사용자가 다른 페이지를 요청하면 내부의 <code>&lt;div&gt;</code>들을 갈아치워서 보여주는데 <code>react-router-dom</code>은 이것을 간편하게 해준다.</h4>
<blockquote>
<ol>
<li>react-router-dom 설치 터미널 입력 <code>npm install react-router-dom@6</code></li>
<li>index.js세팅 import 이후 BrowserRouter로 App을 감싼다.
<code>import { BrowserRouter } from &#39;react-router-dom&#39;;</code></li>
</ol>
</blockquote>
<pre><code>root.render(
  &lt;React.StrictMode&gt;
      &lt;BrowserRouter&gt;
          &lt;App/&gt;
      &lt;/BrowserRouter&gt;
  &lt;/React.StrictMode&gt;
  );</code></pre><ol start="3">
<li>이제 메인페이지와 어바웃페이지 2개를 만들어보자.</li>
<li>먼저 상단에서 여러가지 컴포넌트를 import 해 <code>Routes</code>안에 <code>Route</code>를 작성</li>
<li>그리고 <code>path</code>엔 경로 <code>element</code>엔 보여줄 html을 작성하면된다.<pre><code>import {Routes,Route,Link} om &#39;react-router-dom&#39;
function App(){
return( (생략)
&lt;Routes&gt;
 &lt;Route path=&quot;/&quot; element={&lt;div&gt;메인&lt;/div&gt; }/&gt;
 &lt;Route path=&quot;/detail&quot; element={&lt;div&gt;상세&lt;/div&gt; }/&gt;
&lt;/Routes&gt;
)
}</code></pre></li>
<li>이제 <code>nav</code>를 제외한 기존의 <code>view</code>들을 <code>element</code>메인자리 에 넣어주자.</li>
<li><code>detail</code>엔 Detail.js 파일에 컴포넌트 함수를 만들어 <code>element</code>에 넣어주자.</li>
<li>이제 <code>&lt;Link</code>를 이용해서 메인과 상세 페이지를 이동할수있도록 하면 끝<pre><code>&lt;Link to=&quot;/&quot;&gt;홈&lt;/Link&gt;
&lt;Link to=&quot;/Detail&quot;&gt;상세페이지&lt;/Link&gt;</code></pre>😃😃😃
<code>path</code>경로에 <code>&#39;/&#39;</code>  를 경로로 지정하면 초기 url의 경로가 나오며 만약 
<code>path</code>경로에<code>&#39;*&#39;</code> 를 경로로 지정하면 앞서 선언한 라우터들을 제외한 나머지 경로에 대한 페이지를 만들수있다 예를들어 <code>404페이지</code> 같은 에러페이지..<pre><code>&lt;Route path=&quot;*&quot; element={&lt;div&gt;없는페이지임&lt;/div&gt;} /&gt;</code></pre></li>
</ol>
<h2 id="6-navigat와-routes의-기능들">6. Navigat와 routes의 기능들</h2>
<h4 id="link태그-보다-페이지-더깔끔하게-페이지-이동이가능한-방법을-알아보고-비슷한-여러페이지의-생성과-분류하는-방법을-알아보자">Link태그 보다 페이지 더깔끔하게 페이지 이동이가능한 방법을 알아보고 비슷한 여러페이지의 생성과 분류하는 방법을 알아보자.</h4>
<blockquote>
<ol>
<li>새로운 기능 useNavigate 와 Outlet 을 import 하는것을 잊지말자</li>
</ol>
</blockquote>
<pre><code class="language-js"> import{ Routes, Route, Link,useNavigate,Outlet} from &#39;react-router-dom&#39;</code></pre>
<ol start="2">
<li>Link태그와 같은 기능을 하지만 더 깔끔하게 함수를 사용가능한<code>useNavigate</code> 를 만들어보자. 먼저 변수를 만들어 선언해주고 사용되는 태그의 <code>event함수</code>에 <code>navigate</code> 선언과 <code>경로</code>를 지정해준다.navigate 안에 <code>-1</code>을 넢으면 뒤로가기 <code>1</code>을 넣으면 앞으로 가는등 여러가지 기능 활용이 가능하다.<pre><code class="language-js">function App(){
let navigate = useNavigate();
return( 
------------(생략)
&lt;button onClick={()=&gt;{navigate(&#39;/Detail&#39;)}&gt;상세페이지&lt;/button&gt;
  &lt;button onClick={()=&gt;{navigate(-1)}&gt;뒤로가기&lt;/button&gt;)    ;}</code></pre>
</li>
<li>서브경로를 만들수 있는 <code>nested Routes</code>를 사용해보자.
<code>회사정보</code> 라는 페이지 안에 <code>회사멤버</code>,<code>회사위치</code>라는 두개의 페이지를 만들자<pre><code>&lt;Route path=&#39;/about&quot; element{&lt;About/&gt;} &gt;
&lt;Route path=&#39;member&#39; element{&lt;div&gt;멤버들&lt;/div&gt;}/&gt;
&lt;Route path=&#39;location&#39; element{&lt;div&gt;위치&lt;/div&gt;}/&gt;
&lt;/Route&gt;</code></pre><ol start="4">
<li>이렇게 생성하면 이제 <code>&#39;about&#39;</code>이란 경로 하위에 <code>&#39;member&#39;,&#39;location&#39;</code>
이 생성되고 about 페이지를 공통으로 보여주며 member나 location의 페이지또한 보여줄수있다.이렇듯 Route 안에 Route를 넣는 방식을  nested
Route 라고 하며 상위 페이지를 고정시키고 하위 박스들만 변경할때 사용된다.
<code></code></li>
<li>하지만 여기서 실제로 <code>about/member</code>위치로 가보면 멤버들 이라는 div가 안보이는데 이는 상위 경로에서 하위 route를 보여주는 위치를 지정하지 않았기 때문이다.때문에 about 컴포넌트로 돌아가<code>Outlet</code>이라는 태그를 선언해준다.<pre><code>funciton About() {
return(
&lt;div&gt;
&lt;h4&gt;어바웃 페이지&lt;/h4&gt;
&lt;Outlet&gt;&lt;/Outlet&gt;
&lt;/div&gt; )
}</code></pre></li>
</ol>
</li>
</ol>
<h2 id="7-url-파라미터를-통한-페이지제작">7. URL 파라미터를 통한 페이지제작</h2>
<h4 id="여러-routes를-만드는-방법을배우고-detail-이란-상세-페이지를-만들었으니-props로-state를-넘겨-하위-텍스트-들을-채워보자">여러 Routes를 만드는 방법을배우고 Detail 이란 상세 페이지를 만들었으니 Props로 state를 넘겨 하위 텍스트 들을 채워보자.</h4>
<blockquote>
<ol>
<li>props를 전송하는 방법은 동일하다 사용하는 컴포넌트의 위치에 아래처럼 작명</li>
</ol>
</blockquote>
<pre><code>&lt;Route path=&quot;/detail&quot; element={ &lt;Detail shoes={shoes}/&gt; }/&gt;</code></pre><ol start="2">
<li>이후 Detail.js 로 돌아가 아래 코드처럼 사용해주면 props를 사용가능하다. <pre><code>&lt;h4 className=&quot;pt-5&quot;&gt;{props.shoes[0].title}&lt;/h4&gt;
   &lt;p&gt;{props.shoes[0].content}&lt;/p&gt;
   &lt;p&gt;{props.shoes[0].price}원&lt;/p&gt;</code></pre></li>
<li>그럼 페이지를 여러개 만들면 어떻게 해야함? 아래처럼 무식한 방법으로 만듬??<pre><code>&lt;Route path=&quot;/detail/0&quot; element={ &lt;Detail shoes={shoes}/&gt; }/&gt;
&lt;Route path=&quot;/detail/1&quot; element={ &lt;Detail shoes={shoes}/&gt; }/&gt;
&lt;Route path=&quot;/detail/2&quot; element={ &lt;Detail shoes={shoes}/&gt; }/&gt; </code></pre></li>
<li>저렇게 하지않기 위해 URL파라미터라는 문법을 사용 아래와 같은방법으로 path 에 <code>/:id</code>를 입력하면 말그대로 사용자가 url로 입력한 <code>detail/</code>다음의 parm을 가져와 보여준다는 뜻<pre><code>&lt;Route path=&quot;/detail/:id&quot; element={ &lt;Detail shoes={shoes}/&gt; }/&gt;</code></pre></li>
<li>근데 여기서 문제발생 2번 처럼 shoes[0]의 자료만 하드코딩 해왔기때문에 
3번출력하면 결국 3개의 같은 페이지만 생성된다. 어떻게 해결?</li>
<li>useParam 이라는 훅을 사용하면 url파라미터에 입력된 숫자를 가져올수있음<pre><code class="language-js">import { useParams } from &#39;react-router-dom&#39;;
(생략)
let {id} = useParams();
return(
(생략)
&lt;h4 className=&quot;pt-5&quot;&gt;{props.shoes[id].title}&lt;/h4&gt;</code></pre>
</li>
<li>문제!! 근데 만약 상품의 순서가 바뀌면 어떻게 상세페이지를 보여줌???
1) 먼저 data.js에 상품명,타이틀 등과함께 구분지을수있는 id 값이 존재해야함
2) 이후 자바스크립트의 find()문법을 사용하여 배열의 오브젝트값인 id 를색출
3) <code>array자료.find(()=&gt;{ return 조건식 })</code> 이렇게 쓰면 조건식에 맞는 자료를 찾아서 이 자리에 남겨줌<pre><code class="language-js">let { id } = useParams();
let 찾은상품 = props.shoes.find(function(x){
 return x.id == id        
 // let 찾은상품 = props.art.find((x) =&gt; x.id == id);
});
return(
(생략)
&lt;h4&gt;{찾은상품.title}&lt;/h4&gt;
&lt;p&gt;{찾은상품.content}&lt;/p&gt;
&lt;p&gt;{찾은상품.price}원&lt;/p&gt;</code></pre>
</li>
<li>find()는 array 뒤에 붙일 수 있으며 return 조건식 적으면 됨 그럼 조건식에 맞는 자료 남겨줌 </li>
<li>find() 콜백함수에 파라미터 넣으면 array자료에 있던 자료를 뜻함.  x라고 작명해봤음 </li>
<li>x.id == id 라는 조건식을 써봄. 그럼 array자료.id == url에입력한번호 일 경우 결과를 변수에 담아줌</li>
</ol>
<h2 id="8-styled-components">8. styled-components</h2>
<h4 id="styled-components-는-css-사용없이-js파일-내에서-스타일을-변경하기-쉽기해주는-라이브러리이다-따라서-사용하고-말고는-사용자-마음이며-간단한-개인-프로젝트에-사용하는걸-추천한다">styled-components 는 css 사용없이 js파일 내에서 스타일을 변경하기 쉽기해주는 라이브러리이다. 따라서 사용하고 말고는 사용자 마음이며 간단한 개인 프로젝트에 사용하는걸 추천한다.</h4>
<blockquote>
<ol>
<li>터미널 에서 <code>npm install styled-components</code></li>
<li>사용할 컴포넌트 에서 <code>import styled from &#39;styled-components&#39;</code></li>
<li>사용법 상단에서 변수와 변수명을 생성후 <code>styled.태그명</code>을 붙여준다.</li>
<li>이후 ` 백틱을 사용하여 열고닫아 내부에서 css처럼 스타일을 변형시켜준다.</li>
</ol>
</blockquote>
<pre><code class="language-js">let Box = styled.div`
  padding : 20px;
  color : grey
`;</code></pre>
<ol start="5">
<li><p>이제 사용을 원하는 부분에 변수명을 컴포넌트로 선언해준다. <code>&lt;Box&gt;&lt;/Box&gt;</code></p>
</li>
<li><p>Props를 사용하여 비슷한 컴포넌트의 색깔 스타일만 바꿀수도있다.</p>
<pre><code class="language-js">import styled from &#39;styled-components&#39;;
let YellowBtn = styled.button`
background : ${ props =&gt; props.bg };  // 이부분
color : black;
padding : 10px;
`;
function Detail(){
return (
 &lt;div&gt;
     &lt;YellowBtn bg=&quot;orange&quot;&gt;오렌지색 버튼임&lt;/YellowBtn&gt;
     &lt;YellowBtn bg=&quot;blue&quot;&gt;파란색 버튼임&lt;/YellowBtn&gt;
 &lt;/div&gt;
)
}</code></pre>
<p>Q. 저거 ${ } 이거 무슨 문법임?
A. 자바스크립트 `` 백틱 따옴표 안에 적어도 문자를 만들 수 있는데
백틱으로 만든 문자 중간에 변수같은걸 넣고 싶을 때 ${ 변수명 } 이렇게 쓸 수 있습니다. </p>
<h4 id="장점">장점</h4>
</li>
<li><p>CSS 파일 오픈할 필요없이 JS 파일에서 바로 스타일넣을 수 있습니다.</p>
</li>
<li><p>여기 적은 스타일이 다른 JS 파일로 오염되지 않습니다. </p>
</li>
<li><p>페이지 로딩시간 단축됩니다.</p>
<h4 id="단점">단점</h4>
</li>
<li><p>JS 파일이 매우 복잡해집니다.</p>
</li>
<li><p>컴포넌트가 styled 인지 아니면 일반 컴포넌트인지 구분도 어렵습니다.</p>
</li>
<li><p>CSS 담당하는 디자이너가 있다면 협업시 styled-components 문법을 모른다면 그 사람이 CSS로 짠걸 styled-components 문법으로 다시 바꾸거나 그런 작업이 필요함</p>
<h2 id="9-생애주기와-useeffect">9. 생애주기와 useEffect</h2>
<h4 id="컴포넌트는-크게-3가지의-생애주기lifecycle-이-존재한다-이를-알아야하는-이유는-생애주기-사이사이에서-간섭하여-이벤트를-발생시킬수있다는-뜻이다">컴포넌트는 크게 3가지의 생애주기(LifeCycle) 이 존재한다. 이를 알아야하는 이유는 생애주기 사이사이에서 간섭하여 이벤트를 발생시킬수있다는 뜻이다.</h4>
<p>1.생성이 될수있다. (Mount)
2.재렌더링이 될수있다. (Updata)
3.삭제가 될수있다. (Unmount)
크게 3개의 생애주기 사이에서 hook 으로 코드실행을 개입시키는것을
Lifecylce hook이라고 한다 ex) <code>A컴포넌트</code>가 실행될때 <code>Go</code> 라는 hook코드를 실행</p>
<blockquote>
<ol>
<li>react에선 이러한 Lifecyclehook을 사용하기위해 <code>useEffect</code>를 사용한다.</li>
<li>사용법 </li>
</ol>
</blockquote>
<pre><code class="language-js">import {useState, useEffect} from &#39;react&#39;;
function Detail(){
useEffect(()=&gt;{
 //여기적은 코드는 컴포넌트 로드 &amp; 업데이트 마다 실행됨
 console.log(&#39;안녕&#39;)
});
let [count, setCount] = useState(0)
return (
 &lt;button onClick={()=&gt;{ setCount(count+1) }}&gt;버튼&lt;/button&gt;
)
}</code></pre>
<ol start="3">
<li>useEffect 안에 있는 consolelog 안녕은 랜더 될때마다 실행된다 때문에
button의 onClick 이벤트로 인한 count state 변화에 의해 안녕이 계속 출력됨</li>
<li>근데 <code>useEffect</code>외부에 console.log를 하여도 똑같이 동작한다 다만 다른점은 내부에서 동작시 html이 먼저 동작하고 이후 내부코드가 돌아가기 때문에 시간절약이 가능하다 때문에오래걸리는 반복연산, 서버에서 데이터가져오는 작업, 타이머다는거 이런건 useEffect 안에 많이 적는다.</li>
</ol>
</li>
</ol>
<h2 id="9-1-생애주기와-useeffect2">9-1. 생애주기와 useEffect2</h2>
<h4 id="useeffect에-대한-이해를-위해-2초동안-보여주는-레이아웃을-만들어보고-실행조건과-렌더링-에대한-이해를-해보자">useEffect에 대한 이해를 위해 2초동안 보여주는 레이아웃을 만들어보고 실행조건과 렌더링 에대한 이해를 해보자</h4>
<blockquote>
<ol>
<li>먼저 두가지 상태에 따라 바뀌는 UI 제작을 위해 State만들어주자.</li>
<li>State 값이 true 일때만<code>&lt;div&gt;</code>의 내용을 볼수있게 삼항연산자를 사용해주자.<ol start="3">
<li><code>useEffect</code>를 사용하여 내부에 <code>setTimout</code>이라는 함수를 넣어주자 </li>
</ol>
</li>
</ol>
</blockquote>
<pre><code class="language-js">function Detail(){

  let [alert, setAlert] = useState(true)
  useEffect(()=&gt;{
    setTimeout(()=&gt;{ setAlert(false) }, 2000)
  }, [])

  return (
  {
    alert == true
    ? &lt;div className=&quot;alert alert-warning&quot;&gt;
        2초이내 구매시 할인
      &lt;/div&gt;
    : null
  } ) }</code></pre>
<blockquote>
<p>여기서 보면 잘 이해가지 않는 부분이있다. 바로 useEffect 의 <code>[]</code>부분
<code>useEffect()</code>의 둘째 파라미터로 <code>[ ]</code> 를 넣을 수 있는데 거기에 변수나 <code>state</code>같은 것들을 넣을 수 있다.그렇게 하면 <code>[ ]</code>에 있는 변수나 <code>state</code> 가 변할 때만 <code>useEffect</code> 안의 코드를 다음과 같이 실행한다.
<code>useEffect(()=&gt;{ 실행할코드 }, [count])</code> 
만약 <code>[]</code> 값에아무것도 안넣으면 컴포넌트 mount시 (로드시) 
1회 실행하고 영영 실행해주지 않는다.<code>useEffect(()=&gt;{ 실행할코드 }, [])</code></p>
</blockquote>
<h3 id="clean-up-function">Clean up function</h3>
<blockquote>
<p>useEffect 동작하기 전에 특정코드를 실행하고 싶으면 <code>return ()=&gt;{}</code> 안에 넣을 수 있다.  <code>clean up function</code> 이라고 부른다. 예를들어 <code>setTimeout()</code> 쓸 때마다 브라우저 안에 타이머가 하나 생깁니다.
근데 useEffect 안에 썼기 때문에 컴포넌트가 mount 될 때 마다 실행되고
잘못 코드를 짜면 타이머가 100개 1000개 생길 수도 있다.나중에 그런 버그를 방지하고 싶으면<code>useEffect</code>에서 타이머 만들기 전에 기존 타이머를 싹 제거하라고 코드를 짜면 되는데 그런거 짤 때 return ()=&gt;{} 안에 짜면 된다. </p>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{ 
  let a = setTimeout(()=&gt;{ setAlert(false) }, 2000)
  return ()=&gt;{
    clearTimeout(a)
  }
}, [])</code></pre>
<blockquote>
</blockquote>
<h4 id="useeffect를-사용해서-숫자만-입력가능한-input을-만들어보자">useEffect를 사용해서 숫자만 입력가능한 input을 만들어보자</h4>
<pre><code class="language-js">function Detail(){
  let [num, setNum] = useState(&#39;&#39;)
  useEffect(()=&gt;{
    if (isNaN(num) == true){
      alert(&#39;그러지마세요&#39;)
    }
  }, [num])
  return (
    &lt;input onChange((e)=&gt;{ setNum(e.target.value) }) /&gt;
  )
}</code></pre>
<p>isNaN 은 String 이 들어가면 true 를 int 가들어가면 false를 뱉는 함수이다.
이를 통해 input 의 target.value에 문자가 들어가면 if 문이 동작한다.</p>
<h2 id="10-서버와의-ajax-통신">10. 서버와의 ajax 통신</h2>
<h4 id="서버란-유저가-데이터를-요청하면-데이터를-보내주는-프로그램이다서버에-데이터를-요청할-때는-정확한-규격에-맞춰서-요청해야하는데-아래와-같은-사항을-잘-기재해야-한다">서버란? 유저가 데이터를 요청하면 데이터를 보내주는 프로그램이다.서버에 데이터를 요청할 때는 정확한 규격에 맞춰서 요청해야하는데 아래와 같은 사항을 잘 기재해야 한다</h4>
<ol>
<li>어떤 데이터인지 (URL 형식으로)</li>
<li>어떤 방법으로 요청할지 (GET or POST)
출처:코딩애플<img src="https://velog.velcdn.com/images/wamt_2787/post/17767981-60e1-4dac-b594-d1eb9224dac3/image.png" alt=""></li>
</ol>
<p>GET,POST 요청을 그냥 보내게 되면 브라우저가 새로고침 된다 그렇기 때문에 우리는 Ajax 라는 브라우저 기능을 사용한다. Ajax를 사용하면 새로고침 없이 서버로 정보를 보내거나 가져오는 기능을 만들수있다. Ajax로 GET/POST를 요청하는 방법은 여러개가 있는데 그중 axios같은 외부 라이브러리를 이용하는 방법이 가장편하다.</p>
<p><code>npm install axios</code></p>
<blockquote>
<ol>
<li>axios 사용할 위해 상단에서 import 해주자 </li>
<li><code>axios.get(URL)</code> 이러한 방식으로 요청이 가능하다.</li>
<li>데이터 가져온 결과는 then이후의 지정되는 <code>result.data</code>안에 들어있습니다. </li>
<li>여러사항으로 실패했을 때 실행할 코드는 <code>.catch()</code> 안에 적는다.</li>
</ol>
</blockquote>
<pre><code class="language-js">import axios from &#39;axios&#39;

function App(){
  return (
    &lt;button onClick={()=&gt;{
      axios.get(&#39;https://codingapple1.github.io/shop/data2.json&#39;)
      .then((result)=&gt;{
        console.log(result.data)
      })
      .catch(()=&gt;{
        console.log(&#39;실패함&#39;)
      })
    }}&gt;버튼&lt;/button&gt;
  )
}</code></pre>
<blockquote>
<p>응용 문제</p>
</blockquote>
<h4 id="버튼을-누르면-서버에서-상품데이터-3개를-가져와서-메인페이지에-상품카드-3개를-더-생성해보자">버튼을 누르면 서버에서 상품데이터 3개를 가져와서 메인페이지에 상품카드 3개를 더 생성해보자.</h4>
<ol>
<li>서버에서 가져온 result.data를 확인하면 배열의 형태로 들어와있다.</li>
<li>이는 기존의 data.js에 있는 상품카드의 데이터 정보와 같은 형태이다.</li>
<li>그렇기때문에 기존의 상품카드 state에 새로운 result.data를 추가해준다.<pre><code class="language-js">//(생략) 바로 위의 코드와 같음
   .then((result)=&gt;{
        let copy = [...art, ...result.data];
       setArt(copy);
     console.log(art);
   })
//(생략) 바로 위의 코드와 같음</code></pre>
<h4 id="만약-버튼을-2회-누를때-789번-상품도-가져오려면">만약 버튼을 2회 누를때 7,8,9번 상품도 가져오려면?</h4>
</li>
<li>버튼의 입력횟수를 카운트 할 state를 하나 생성해야한다.</li>
<li>삼항연산자를 사용하여 버튼입력횟수 마다 각각 다른 데이터 정보를 가져온다.</li>
<li>세번이상 버튼을 누르면 알림 을 띄워 메시지를 보낸다.<pre><code class="language-js">btn &lt; 1
 ? axios.get(&#39;https://codingapple1.github.io/shop/data2.json&#39;)
          .then((result) =&gt; {setBtn(btn + 1);
                // (생략 위의 코드와 같음)
:
btn &lt; 2
 ? axios.get(&#39;https://codingapple1.github.io/shop/data3.json&#39;)
           .then((result) =&gt; {setBtn(btn + 1);
                // (생략 위의 코드와 같음)
: alert(&#39;추가할 상품이없습니다).                                                        </code></pre>
</li>
</ol>
<h2 id="10-1-서버와의-ajax-통신-2">10-1. 서버와의 ajax 통신 2</h2>
<h4 id="ajax의-다른-요청방법들과-json에-대해-간략하게-알아보자">ajax의 다른 요청방법들과 json에 대해 간략하게 알아보자</h4>
<blockquote>
<h4 id="1-post-요청방법">1. post 요청방법?</h4>
</blockquote>
<pre><code class="language-js">axios.post(&#39;URL&#39;, {name : &#39;kim&#39;})</code></pre>
<p>이거 실행하면 서버로 { name : &#39;kim&#39; } 자료가 전송된다.
완료시 특정 코드를 실행하고 싶으면 이것도 역시 .then() 뒤에 붙이면 된다.</p>
<h4 id="2동시에-ajax-여러-요청">2.동시에 AJAX 여러 요청</h4>
<pre><code class="language-js">Promise.all( [axios.get(&#39;URL1&#39;), axios.get(&#39;URL2&#39;)] )</code></pre>
<p>이러면 URL1, URL2로 GET요청을 동시에 해준다.
둘 다 완료시 특정 코드를 실행하고 싶으면 .then() 뒤에 붙이면 된다.</p>
<h4 id="3-배열을-원래는-못가져온다">3. 배열을 원래는 못가져온다?</h4>
<p>object/array 이런거 못주고받는다.
근데 방금만해도 array 자료 받아온 것 같은데 그건 어떻게 한거냐면 
object/array 자료에 따옴표를 쳐놓으면 된다.
<code>&quot;{&quot;name&quot; : &quot;kim&quot;}&quot;</code> 이걸 JSON 이라고 한다.
JSON은 문자 취급을 받기 때문에 서버와 자유롭게 주고받을 수 있다.
그래서 실제로 결과.data 출력해보면 따옴표쳐진 JSON이 나와야하는데
axios 라이브러리는 JSON -&gt; object/array 변환작업을 자동으로 해줘서 
출력해보면 object/array가 나온다. </p>
<h4 id="4-자주하는-실수">4. 자주하는 실수</h4>
<ol>
<li>ajax요청으로 데이터를 가져와서 </li>
<li>state에 저장하라고 코드를 짜놨고</li>
<li>state를 html에 넣어서 보여달라고 <code>&lt;div&gt; {state.어쩌구} &lt;/div&gt;</code>
이렇게 코드 짰는데.잘 될 것 같은데 이 상황에서 state가 텅 비어있다고 에러가 나는 경우가 많다.
이유는 ajax 요청보다 html 렌더링이 더 빨라서 그럴 수 있다. 
state안에 뭐가 들어있으면 보여달라고 if문 같은걸 추가하거나 그러면 된다. </li>
</ol>
<h2 id="11-리액트에서-탭-ui만들기">11. 리액트에서 탭 UI만들기</h2>
<h4 id="여러-사이트에서-흔히-볼수있는-탭-ui를-만들어보자-버튼-3개를-미리-만들고-버튼을-누를-때마다-그에맞는-박스를-보여주는-탭-ui가-필요하다">여러 사이트에서 흔히 볼수있는 탭 UI를 만들어보자 버튼 3개를 미리 만들고 버튼을 누를 때마다 그에맞는 박스를 보여주는 탭 UI가 필요하다</h4>
<blockquote>
<p>간단한 작업이라도 3Step 을 통해 천천히 진행하자</p>
</blockquote>
<ol>
<li>html css로 디자인 미리 완성해놓고</li>
<li>UI의 현재 상태를 저장할 state 하나 만들고</li>
<li>state에 따라서 UI가 어떻게 보일지 작성하면 된다고 했다. </li>
</ol>
<p>1)디자인은 react-bootStrap 의 nav 를 검색하여 참고했다</p>
<pre><code>&lt;Nav variant=&quot;tabs&quot;  defaultActiveKey=&quot;link0&quot;&gt;
    &lt;Nav.Item&gt;   //defaultActiveKey 여기는 페이지 로드시 어떤 버튼에 눌린효과를 줄지 결정하는 부분입니다. 
      &lt;Nav.Link eventKey=&quot;link0&quot;&gt;버튼0&lt;/Nav.Link&gt;
    &lt;/Nav.Item&gt;
    &lt;Nav.Item&gt;
      &lt;Nav.Link eventKey=&quot;link1&quot;&gt;버튼1&lt;/Nav.Link&gt;
    &lt;/Nav.Item&gt;
    &lt;Nav.Item&gt;
      &lt;Nav.Link eventKey=&quot;link2&quot;&gt;버튼2&lt;/Nav.Link&gt;
    &lt;/Nav.Item&gt;
&lt;/Nav&gt;
&lt;div&gt;내용0&lt;/div&gt;
&lt;div&gt;내용1&lt;/div&gt;
&lt;div&gt;내용2&lt;/div&gt; </code></pre><p>2)  UI의 현재 상태를 저장할 state 하나 만들기
0,1,2 3가지의 상태가 필요하기 때문에 초기값은 0으로 선언</p>
<pre><code>function Detail(){
  let [탭, 탭변경] = useState(0)
  (생략)
}</code></pre><p>3) state에 따라서 UI가 어떻게 보일지 작성
삼항연산자,컴포넌트,배열 어떤 방식으로 하든지 자유다. 여러 방식대로 한번 짜보자
 3-1) function외부에 컴포넌트로 만들기 
     props.tab 으로 상속 받지않아도<code>{탭}</code> 으로 바로 state를 가져올수있다.
    그리고 외부에 컴포넌트로 사용하면<code>return</code>을 사용해야한다.</p>
<pre><code class="language-js">function Detail(){
  let [탭, 탭변경] = useState(0)
  return (
    &lt;TabContent/&gt;
  )
}
function TabContent({탭}){
  if (탭 === 0){
   return &lt;div&gt;내용0&lt;/div&gt;
  }
  if (탭 === 1){
   return &lt;div&gt;내용1&lt;/div&gt;
  }
  if (탭 === 2){
   return &lt;div&gt;내용2&lt;/div&gt;
  }
}</code></pre>
<p>3-2) array 를 사용해서 if 문없이 만들어보자
아래와 같은 형식은 <code>[A배열] [B배열]=상수</code> 형태임으로 상수 배열 B를 통해
A배열의 위치를 특정해 가져올수있다.</p>
<pre><code class="language-js">function TabContent(props){
  return [ &lt;div&gt;내용0&lt;/div&gt;, &lt;div&gt;내용1&lt;/div&gt;, &lt;div&gt;내용2&lt;/div&gt; ]
          [props.탭]
}</code></pre>
<h2 id="12-컴포넌트-전환-애니메이션-주는-법-transition">12. 컴포넌트 전환 애니메이션 주는 법 (transition)</h2>
<h4 id="컴포넌트를-사용할때-애니메이션을-통해-좀더-매끄러운-표현이-가능하다-이는-누군가-만들어놓은-라이브러리를-이용하여도-괜찮지만-그래도-간단한-애니메이션은-css-를-통하여-대체-할수있다">컴포넌트를 사용할때 애니메이션을 통해 좀더 매끄러운 표현이 가능하다 이는 누군가 만들어놓은 라이브러리를 이용하여도 괜찮지만 그래도 간단한 애니메이션은 css 를 통하여 대체 할수있다.</h4>
<blockquote>
<p>애니메이션을 만들기위한 step
애니메이션 만들고 싶으면 </p>
</blockquote>
<ol>
<li>애니메이션 동작 전 스타일을 담을 className 만들기 </li>
<li>애니메이션 동작 후 스타일을 담을 className 만들기 </li>
<li>transition 속성도 추가</li>
<li>원할 때 2번 탈부착
탭의 내용이 서서히 등장하는 fade in 애니메이션을 만들어보자</li>
</ol>
<p>1) 애니메이션 동작 전 2. 애니메이션 동작 후 className 만들기 </p>
<pre><code class="language-css">.start {
  opacity : 0
}
.end {
  opacity : 1;
}</code></pre>
<p>3) transition 추가
transition은 &quot;해당 속성이 변할 때 서서히 변경해주셈~&quot; 이라는 뜻이다. 그럼 이제 원하는 <code>&lt;div&gt;</code> 요소에 start 넣어두고 end 를 탈부착할 때 마다 fade in이 된다. </p>
<pre><code class="language-css">.start {
  opacity : 0
}
.end {
  opacity : 1;
  transition : opacity 0.5s;
}</code></pre>
<p>4) 원할 때 end 부착
동작원리를 알아보면 start가 실행되고 end가 나중에 뒤에 붙어 순간적으로
<code>className=&#39;start end&#39;</code>가 되었을때 opacity 투명도가 fade in되는 것이다.
이제 &quot;버튼을 누를 때 className에 end를 부착해주세요&quot; 라고 코드짜면 애니메이션 동작한다.<code>useEffect</code> 를통해 활용할수 있을거 같다.
useEffect 쓰면 특정 state 아니면 props가 변할 때 마다 코드실행이 가능하다, 
그래서 &quot;탭이라는 state가 변할 때 end를 저기 부착해주세요&quot; 라고 코드를 짜면 좋을듯</p>
<pre><code class="language-js">function TabContent({탭}){

  let [fade, setFade] = useState(&#39;&#39;)

  useEffect(()=&gt;{
    setFade(&#39;end&#39;)
  }, [탭])

  return (
    &lt;div className={&#39;start &#39; + fade}&gt;
      { [&lt;div&gt;내용0&lt;/div&gt;, &lt;div&gt;내용1&lt;/div&gt;, &lt;div&gt;내용2&lt;/div&gt;][탭] }
    &lt;/div&gt;
  )
}</code></pre>
<p>5) 첫번째 동작에러
위의 코드가 동작하지 않는 이유는 start -&gt; start end의 과정처럼 떼었다 붙여져야 하는데 바로 붙은 상태가 되버리기 때문이다. 이를 위해 우리는 <code>setFade(&#39;&#39;)</code> 를 작성
해주어야한다 이때 이전에 배운 <code>cleanUp</code>코드를 활용하면 아주좋다.</p>
<pre><code class="language-js"> return () =&gt; {
            setFade(&#39;&#39;);</code></pre>
<p>6) 두번째 동작에러
<code>clean up</code> 으로 end 를 떼어줘도 동작하지 않는다? 이는 react 의
 <code>automatic batch</code> 라는 기능때문인데 쉽게 말해 state의 변경 함수가 연달아 여러개 처리 될때 부하를 막기위해 계속 렌더링 하는것이 아닌 모두 처리된 이후의 마지막 state 상태만 렌더링 하는 기능이다. 이때문에 우리는 state에 약간의 딜레이를 부여해 렌더링이 겹치는걸 방지해야 한다. 저번에 사용했던 TimeOut 함수를 써보자.</p>
<pre><code class="language-js"> function TabContent({탭}){

  let [fade, setFade] = useState(&#39;&#39;)

  useEffect(()=&gt;{
    let a = setTImeout(()=&gt;{ setFade(&#39;end&#39;) }, 100)
  return ()=&gt;{
    setFade(&#39;&#39;)
    clearTimeout(a);
  }
  }, [탭])

  return (
    &lt;div className={&#39;start &#39; + fade}&gt;
      { [&lt;div&gt;내용0&lt;/div&gt;, &lt;div&gt;내용1&lt;/div&gt;, &lt;div&gt;내용2&lt;/div&gt;][탭] }
    &lt;/div&gt;
  )
}</code></pre>
<h2 id="13-redux-1--redux-toolkit-설치-장바구니-페이지-만들기">13. Redux 1 : Redux Toolkit 설치 (장바구니 페이지 만들기)</h2>
<h4 id="장바구니-페이지를-만들어보고-props-를통해-state를-이동하는것이-아닌-redux라는-라이브러리를-사용해-state-의-사용을-편하게-해보자">장바구니 페이지를 만들어보고 Props 를통해 state를 이동하는것이 아닌 Redux라는 라이브러리를 사용해 state 의 사용을 편하게 해보자.</h4>
<blockquote>
</blockquote>
<ol>
<li>새로운 페이지를 만들기위해 먼저 <code>Route</code>와 <code>nav</code>에 추가해주자.
<code>&lt;Route path=&quot;/cart&quot; element={ &lt;Cart/&gt; } /&gt;</code></li>
<li>Route에 담기에 페이지가 크기 때문에 컴포넌트로 분류하고 import 해주자.</li>
<li>편하게 제작하기위해 <code>bootStrap</code>에서 <code>Table</code>에 대한 양식을 가져와주자.</li>
<li><code>&lt;thead&gt;</code>태그로 가로를 <code>th</code> 태그로 컬럼을 나누고 <code>td</code>태그로 속성을 구분<pre><code>&lt;Table&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;tr&gt;
  &lt;td&gt;1&lt;/td&gt;
  &lt;td&gt;안녕&lt;/td&gt;
  &lt;td&gt;안녕&lt;/td&gt;
  &lt;td&gt;안녕&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/Table&gt; </code></pre></li>
<li>장바구니 기능을 사용하려면 상단의 App.js에서 State를 가져와야하는데 이를 편하게 하기위해 Redux를 배워볼것이다.</li>
</ol>
<blockquote>
<h3 id="redux란">Redux란?</h3>
<p> Redux는 props 없이 state를 공유할 수 있게 도와주는 라이브러리이다.
 <img src="https://velog.velcdn.com/images/wamt_2787/post/21018f24-6c45-4c40-8f09-2f75853864ee/image.png" alt=""> js 파일 하나에 여러 state들을 보관할수있어 모든 컴포넌트가 직접 꺼내쓸수있다.
 <code>npm install @reduxjs/toolkit react-redux</code> redux 를 설치해주자.
 근데 설치하기 전에 package.json 파일을 열어 <code>react``react-dom</code> 항목의 버전을 확인하자. 두개가 18.1.x 이상이면 사용가능하다,.</p>
</blockquote>
<h3 id="redux-세팅">Redux 세팅</h3>
<pre><code class="language-js">import { configureStore } from &#39;@reduxjs/toolkit&#39;
export default configureStore({
  reducer: { }
}) </code></pre>
<p>아무데나 store.js 파일을 만들어서 위 코드를 넣어주자  아까 말했던 state들을 보관하는 파일이다.. </p>
<pre><code class="language-js">import { Provider } from &quot;react-redux&quot;;
import store from &#39;./store.js&#39;
const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(
  &lt;React.StrictMode&gt;
    &lt;Provider store={store}&gt;
      &lt;BrowserRouter&gt;
        &lt;App /&gt;
      &lt;/BrowserRouter&gt;
    &lt;/Provider&gt;
  &lt;/React.StrictMode&gt;
); </code></pre>
<p><code>index.js</code> 파일가서 <code>Provider</code> 라는 컴포넌트와 아까 작성한 파일을 import 해옵니다. 그리고 밑에 <Provider store={import해온거}> 이걸로 <App/> 을 감싸면 된다.이제 <App>과 그 모든 자식컴포넌트들은 
<code>store.js</code>에 있던 <code>state</code>를 맘대로 꺼내쓸 수 있다.</p>
<h2 id="14--redux-2--store에-state-보관하고-쓰는-법">14.  Redux 2 : store에 state 보관하고 쓰는 법</h2>
<h4 id="본격적으로-redux를-사용하기전에-우리는-왜-state를-사용한-props-들을-왜-쓴지-부터-알아야한다-2050개의-컴포넌트를-사용하는-중대형-프로젝트의-경우-redux사용을-통해-복잡한-코드의-양을-줄일수있지만">본격적으로 Redux를 사용하기전에 우리는 왜 State를 사용한 props 들을 왜 쓴지 부터 알아야한다. 20~50개의 컴포넌트를 사용하는 중대형 프로젝트의 경우 Redux사용을 통해 복잡한 코드의 양을 줄일수있지만</h4>
<h4 id="몇개안되는-컴포넌트의-사용과-한-파일구조내에서-이동하는-state들을-전부-redux로-표현한다면-오히려-코드양이-많아지기때문에-적절하게-타협을-보면서-두가지-방법을-사용해야한다">몇개안되는 컴포넌트의 사용과 한 파일구조내에서 이동하는 State들을 전부 Redux로 표현한다면 오히려 코드양이 많아지기때문에 적절하게 타협을 보면서 두가지 방법을 사용해야한다.</h4>
<blockquote>
<h3 id="redux-store에-state를-보관해보자">Redux store에 State를 보관해보자.</h3>
</blockquote>
<ol>
<li><p>store.js 를열어 createSlice()를 먼저 import 해준다.</p>
</li>
<li><p>createSlice 에 <code>{ name : &#39;state이름&#39;, initialState : &#39;state값&#39; }</code> </p>
</li>
<li><p>2번과 같이 선언해주면 state 하나가 생성가능하게된다.</p>
</li>
<li><p>configureStore() 라는 변수도 import 해준다.</p>
</li>
<li><p>configureStore() 안에 <code>{작명 : createSlice만든거.reducer}</code>선언</p>
<pre><code class="language-js">import { configureStore, createSlice } from &#39;@reduxjs/toolkit&#39;
</code></pre>
</li>
</ol>
<p>let user = createSlice({
  name : &#39;user&#39;,
  initialState : &#39;kim&#39;
})</p>
<p>export default configureStore({
  reducer: {
    user : user.reducer
  }
}) </p>
<pre><code> &gt; 
  ### Redux store에 있던 state를 가져다 써보자
  아무 컴포넌트에서 `useSelector((state) =&gt; { return state } )` 쓰면 store에 있던 모든 state가 그 자리에 남는다.

```js
  (Cart.js)

import { useSelector } from &quot;react-redux&quot;

function Cart(){
  let a = useSelector((state) =&gt; { return state } )
  console.log(a)

  return (생략)
}</code></pre><blockquote>
<h3 id="응용문제">응용문제</h3>
<p>  아래와 같은 오브젝트형 데이터를 Store에 담으려면 똑같이 <code>name</code>을 설정하고
  <code>initialState</code>선언에 오브젝트 그대로 넣어주면 state로 선언이 가능하고
 컴포넌트에서 사용시 <code>{a.name[0]?.id}</code> 이러한 형식으로 배열을 불러서 사용</p>
</blockquote>
<pre><code class="language-js">  [
  {id : 0, name : &#39;White and Black&#39;, count : 2},
  {id : 2, name : &#39;Grey Yordan&#39;, count : 1}
] </code></pre>
<h2 id="15-redux-3--store의-state-변경하는-법">15. Redux 3 : store의 state 변경하는 법</h2>
<p> 1.store.js에 state변경해주는 함수를 만들고 
  2.export 한다. 
  3. 필요할때 import 해서 사용하면 되는데 dispatch()로 함수를 감싸서 사용해야한다.</p>
<blockquote>
<h3 id="1-storejs-안에-state를-수정해주는-함수를-생성한다">1. store.js 안에 state를 수정해주는 함수를 생성한다.</h3>
</blockquote>
<pre><code class="language-js">  let user = createSlice({
  name : &#39;user&#39;,
  initialState : &#39;kim&#39;,
  reducers : {
    changeName(state){
      return &#39;john &#39; + state
    }
  }
}) </code></pre>
<p>  <code>slice</code> 안에 <code>reducers :{}</code> 를 열고 그안에 함수를 생성하면 된다.
  함수 작명은 원하는대로 가능하며 파라미터 하나를 작명해 기존<code>state</code>를 사용할수있다 return 우측에 새로운 <code>state</code>를 입력하면 
  그 값으로 기존 <code>state</code>를 갈아치워준다.</p>
<blockquote>
<h3 id="2-다른곳에서-쓰기좋게-export-해준다">2. 다른곳에서 쓰기좋게 export 해준다.</h3>
<p>  <code>export let { changeName } = user.actions</code>
  이런 코드를 store.js 밑에 추가하면 된다. <code>slice이름.actions</code>
  라고 적으면 state 변경함수가 전부 그자리에 출력된다.
  그걸 변수에 저장했다가 export 하라는 코드이다.</p>
</blockquote>
<blockquote>
<h3 id="3--import-해서-사용한다-근데-dispatch로-감싸서">3.  import 해서 사용한다. 근데 dispatch()로 감싸서</h3>
</blockquote>
<pre><code class="language-js">  (Cart.js)
import { useDispatch, useSelector } from &quot;react-redux&quot;
import { changeName } from &quot;./../store.js&quot;
(생략) 
&lt;button onClick={()=&gt;{
  dispatch(changeName())
}}&gt;버튼임&lt;/button&gt; </code></pre>
<p>  <code>Cart.js</code> 에서 버튼을 만들어 <code>ChangeName 함수</code>를 실행하는 코드이다.
  <code>store.js</code> 에서 원하는 <code>state변경함수</code>를 가져오고 
  <code>useDispatch</code>라는 것도 라이브러리에서 가져온다 그리고 <code>dispatch(state변경함수())</code> 이런식으로 감싸서 실행하면 state가 변경된다.</p>
<blockquote>
<h3 id="왜이런식으로-할까">왜이런식으로 할까??</h3>
<p>  <img src="https://velog.velcdn.com/images/wamt_2787/post/03abde3f-67e5-42be-96ec-51a11a1a68e0/image.png" alt="">
  컴포넌트 100개에서 직접 &#39;kim&#39; 이라는 state 변경하다가 갑자기 &#39;kim&#39;이 123이 되어버리는 버그가 발생할수있다.범인 찾으려고 컴포넌트 100개를 다 뒤져야하는데 만약 state 수정함수를 store.js에 미리 만들어두고
컴포넌트는 그거 실행해달라고 부탁만 하는 식으로 코드를 짜놓으면 &#39;kim&#39;이 123이 되어버리는 버그가 발생했을 때 범인찾기가 수월할것이다.
범인은 무조건 store.js에 있으니까. (수정함수를 잘 만들어놨다면)</p>
</blockquote>
<h2 id="16-redux-4--state가-objectarray일-경우-변경하는-법">16. Redux 4 : state가 object/array일 경우 변경하는 법</h2>
<h4 id="store에-저장된-state가-arrayobject-자료인-경우-state-변경을-편리하게-할수있다">store에 저장된 state가 array,object 자료인 경우 state 변경을 편리하게 할수있다</h4>
<blockquote>
<h3 id="redux-state가-arrayobject-인-경우-변경하려면">redux state가 array/object 인 경우 변경하려면?</h3>
</blockquote>
<ol>
<li><p><code>{name:&#39;kim&#39;,age:20}</code> 이렇게 생긴 자료를 state 로 만들어보자</p>
</li>
<li><p><code>kim</code>이라는 내용을 <code>park</code>으로 변경하는 함수를 만들고싶으면 state변경함수는 아래처럼 만든다.</p>
<pre><code class="language-js">let user = createSlice({
name : &#39;user&#39;,
initialState : {name : &#39;kim&#39;, age : 20},
reducers : {
changeName(state){
 return {name : &#39;park&#39;, age : 20}
}
}
}) </code></pre>
<p>이러한 코드를 사용하면 <code>changeName()</code>함수 사용시 당연히변경된다 
그러면 아래처럼 state를 직접 수정하게 하면 어떻게 될까</p>
<pre><code class="language-js">let user = createSlce({
(생략)
reducers : {
changeName(state){
 state.name = &#39;park&#39;
}
}
}) </code></pre>
<p>이러한 코드를 사용해도 <code>immer.js</code>라이브러리 를 통해 잘 동작한다.
결론은 <code>array/object</code> 자료의 경우 state변경은 직접 수정해도 되니까
직접 수정하자 때문에 state를 만들때 문자나 숫자하나만 필요해도 redux에선 일부러 object 아니면 array에 담는 경우가 있는데 이는 수정이 편리해서이다.</p>
<blockquote>
<h3 id="응용-문제-1">응용 문제 1</h3>
<p>사이트에 버튼을 만들어 클릭시 age 항목이 +1 되게 만들어보자</p>
</blockquote>
<pre><code class="language-js">let user = createSlice({
name : &#39;user&#39;,
initialState : {name : &#39;kim&#39;, age : 20},
reducers : {
increase(state){
 state.age += 1
}
}
})</code></pre>
<p>이렇게 increase라는 함수 만들어 export 하고 
필요한 곳에서 import 해서 dispatch(increase()) 하면 +1 된다.</p>
<blockquote>
<h3 id="응용문제2">응용문제2</h3>
<p>그러면 변경함수가 여러개 필요하면 여러 함수를 전부 redux에 넣음??
동작은 같으나 횟수나 인자가 다를경우 우리는 <code>파라미터 문법</code>을 사용할수있다.
아래처럼 코드를 사용하면 action이라는 파라미터를 주어 함수를 사용하는 컴포넌트에서 값을 설정해 원하는 만큼 함수의 실행을 도울수있다. 
이때 꼭 파라미터 뒤에는 <code>.payload</code>를 사용해주자</p>
</blockquote>
<pre><code class="language-js">let user = createSlice({
name : &#39;user&#39;,
initialState : {name : &#39;kim&#39;, age : 20},
reducers : {
increase(state, a){
 state.age += a.payload
}
}
}) </code></pre>
<p>파라미터를 사용해주면 <code>increase(10)</code>하면 10을 더하고
<code>increase(100</code>하면 100을 더할것이다. </p>
<h2 id="17-redux-5--장바구니-기능-만들기--응용문제">17. Redux 5 : 장바구니 기능 만들기 &amp; 응용문제</h2>
<h4 id="여러-응용문제를-통해-장바구니-기능을-활성화-시켜보자">여러 응용문제를 통해 장바구니 기능을 활성화 시켜보자</h4>
<blockquote>
<h3 id="응용문제1">응용문제1</h3>
</blockquote>
<h4 id="장바구니-오브젝트에서-수량1-기능을-만들어보자">장바구니 오브젝트에서 수량+1 기능을 만들어보자</h4>
<pre><code class="language-js">let cart = createSlice({
name : &#39;cart&#39;,
initialState : [
{id : 0, name : &#39;White and Black&#39;, count : 2},
{id : 2, name : &#39;Grey Yordan&#39;, count : 1}
],
reducers : {
addCount(state, action){
 state[action.payload].count++
}
}
}) </code></pre>
<p><code>addCount</code> 라는 함수를 만들어 파라미터로 <code>action</code>을 주어 <code>addCount(0)</code>하면 0번째 상품이 +1 <code>addCount(1)</code>하면 1번째 상품이 +1 된다. export해서
필요할때 사용하면 될거같고 장바구니 컴포넌트 에선 대충 이렇게 쓰면 될거같다.</p>
<pre><code class="language-js">&lt;button onClick={()=&gt;{ dispatch(addCount(i)) }}&gt;+&lt;/button&gt;</code></pre>
<p>어짜피 map을 통해 반복 되니까 파라미터 i를 써서 열마다 새로운 i값을 받을수있다. 0번째 버튼을 누르면 <code>addCount(0)</code> 실행 이런식으로</p>
<blockquote>
<h3 id="응용문제-1심화">응용문제 1(심화)</h3>
<p>그런데 만약 테이블의 정렬 순서가 바뀌면?? 
저러한 방식으로 코드를 사용하면 오브젝트안의 데이터 순서가 바뀔경우 
버그가 발생할것이다. 그렇기때문에 우리는 데이터마다 가진 
고유의 id 값을 확인하여 count++ 를 해줄것이다.
먼저 장바구니 컴포넌트의 코드를 조금 수정해주자.</p>
</blockquote>
<pre><code class="language-js">&lt;button
onClick={()=&gt;{ dispatch(addCount(state.cart[i].id)) }}&gt;
+&lt;/button&gt;</code></pre>
<p>이런식으로 코드를 짜면 dispatch안의 addCount 내용은 <code>state.cart[i]에 대한 id 값</code> 즉 사용자가 선택한 테이블의 id 값을 가지고있을것이고 그 값은 action.payload 로 전송됨.</p>
<blockquote>
</blockquote>
<h3 id="이제-addcount-함수를-수정해보자">이제 addCount 함수를 수정해보자</h3>
<p><code>&quot;payload와 같은 id를 가진 상품을 찾아서 +1 해달라~&quot;</code> 이 말을
js로 풀어서 써넣어야한다. <code>state 오브젝트</code> 안에서 <code>payload</code> 와 같은 <code>id</code> 를 찾아서 그 값을 가진 테이블의 <code>count 1증가</code>해달라 해야한다 그럼 코드를 보자</p>
<pre><code class="language-js">let cart = createSlice({
name : &#39;cart&#39;,
initialState : [
{id : 0, name : &#39;White and Black&#39;, count : 2},
{id : 2, name : &#39;Grey Yordan&#39;, count : 1}
],
reducers : {
addCount(state, action){
 let 번호 = state.findIndex((a)=&gt;{ return a.id === action.payload })
 state[번호].count++
}
}
}) </code></pre>
<h4 id="array-자료에서-원하는-항목을-찾으려면-반복문-find-findindex-사용">array 자료에서 원하는 항목을 찾으려면 반복문, find(), findIndex() 사용!!!!</h4>
<ul>
<li><code>findIndex()</code>는 array 뒤에 붙일 수 있다.</li>
<li>안에 콜백함수넣고 return 뒤에 조건식도 넣으면 된다.</li>
<li><code>a</code>라는 파라미터는 array 안에 있던 하나하나의 자료다.</li>
<li>array에 있던 자료를 다 꺼내서 조건식에 대입해보는데 
조건식이 참이면 그게 몇번째 자료인지 알려준다.<blockquote>
</blockquote>
그래서 위의 코드는 <code>a.id</code>와 <code>payload</code>가 같으면 그게 몇번째 자료인지 
변수에 저장하라는 소리다. </li>
</ul>
</li>
</ol>
<blockquote>
<h3 id="응용문제-2">응용문제 2</h3>
</blockquote>
<h4 id="주문버튼누르면-state에-새로운-상품추가">주문버튼누르면 state에 새로운 상품추가</h4>
<p> 상세페이지의 주문하기 버튼을 누르면  장바구니 state에 항목이 
  하나 추가되는 기능을 만들어보자 이것도 state 변경함수 만들고 
  export하고 import해서 사용한다.</p>
<pre><code class="language-js">  let cart = createSlice({
  name : &#39;cart&#39;,
  initialState : [
    {id : 0, name : &#39;White and Black&#39;, count : 2},
    {id : 2, name : &#39;Grey Yordan&#39;, count : 1}
  ],
  reducers : {
    addCount()
  (생략)
    addItem(state, action){
      state.push(action.payload)
    }
  }
}) </code></pre>
<p> <code>addItem()</code>이라는 함수를 만들어 state 오브젝트에 새로운 배열을
  <code>action.payload</code> 에 가져와 <code>push</code> 하는 과정을 진행해보자.</p>
<blockquote>
</blockquote>
<h3 id="detailjs">(Detail.js)</h3>
<pre><code>&lt;div className=&quot;col-md-6&quot;&gt;
  &lt;h4 className=&quot;pt-5&quot;&gt;{찾은상품.title}&lt;/h4&gt;
  &lt;p&gt;{찾은상품.content}&lt;/p&gt;
  &lt;p&gt;{찾은상품.price}원&lt;/p&gt;
  &lt;button className=&quot;btn btn-danger&quot; onClick={()=&gt;{
    dispatch(addItem( {id : 1, name : &#39;Red Knit&#39;, count : 1} ))
  }}&gt;주문하기&lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt; </code></pre><p> 상세페이지(Detail.js)에서도 <code>useDispatch</code>를 import 해주고
 만든 <code>addItem()</code>함수도 import 해주자. 그리고 위와같이 
  <code>id</code>,<code>name</code>,<code>count</code>등을 넣어주면 새로운 데이터가 배열안에 들어간다.
  우리는 찾은 상품의 <code>id</code>,<code>title</code>등을 알고있으니 저런식으로 하드코딩이아닌</p>
<pre><code class="language-js">  addItem({id: finded.id, name: finded.title, count: 1,})</code></pre>
<p>이와 같은 방법으로 <code>state</code> 를 연결해주면 더 좋을거같다.</p>
<blockquote>
<h3 id="응용문제-3">응용문제 3</h3>
</blockquote>
<h4 id="표의-행마다-삭제버튼-만들고-그거-누르면-상품이-삭제되게-만들려면">표의 행마다 삭제버튼 만들고 그거 누르면 상품이 삭제되게 만들려면?</h4>
<p>  문제를 해결하려면 일단 배열에서 특정 값을 제거하는 함수를 알아야한다
 검색으로 <code>pop</code>과 <code>splice</code>라는 함수를 알게되었다 이중 응용1번 처럼 
  장바구니의 값을 정렬할때 위치가 바뀔수도 있으니 고유 번호인 <code>id</code>값을
  기준으로 진행하게 할려면 아래와 같이 코드를 사용할수 있을것이다.</p>
<pre><code class="language-js">  DeleteItem(state, action) {
            let paynum = state.findIndex((a) =&gt; {
                return a.id === action.payload;
            });
            state.splice(paynum, 1);
        },</code></pre>
<p>  <code>state.spicle(paynum,1)</code>은 이제 배열의 paynum에 해당하는
  객체부터 ~1만큼 제거하겠다는 뜻이다 즉 paynum에 해당하는 객체만 삭제</p>
<pre><code class="language-js"> &lt;button
 onClick={() =&gt; {dispatch(DeleteItem(state.cartdata[i].id));}}</code></pre>
<p>  사용하는 <code>cart.js</code> 부분에도 응용 1과 같이 <code>cartdata.id</code>값을 보내주면 끝.</p>
<blockquote>
<h3 id="응용문제-4">응용문제 4</h3>
</blockquote>
<h4 id="주문하기-버튼-누를-때-이미-상품이-state안에-있으면-추가가-아니라-기존-항목-수량증가만">주문하기 버튼 누를 때 이미 상품이 state안에 있으면 추가가 아니라 기존 항목 수량증가만?</h4>
<p>   기존의<code>addItem</code>에서 if 문을 사용해 장바구니에 이미 존재하는 id 는
   <code>count++</code>하고 없는 id 만 add 해주는 과정이 필요할꺼같다.</p>
<pre><code class="language-js">   addItem(state, action) {   
// id 를 확인해 이미 장바구니에 있는 id면 count+ 아니면 새로 push
            let paynum = state.findIndex((a) =&gt; a.id === action.payload.id);
            if (paynum &gt;= 0) {
                state[paynum].count++;
                console.log(paynum);
            } else {
                state.push(action.payload);
                console.log(paynum);
            }
        },</code></pre>
<p>   여기서 중요하게 봐야할 부분은 <code>paynum &gt;=0</code> 부분이다 <code>paynum</code>에
   어떤 내용이 뜨는지 정확히 보기위해 <code>console.log</code>로 확인해보니
   장바구니에 있는 값이면 그 id값의 배열 번호가 뜨고 만약 없으면 
   <code>-1</code>음수가 나왔다 그러므로 if 문을 사용하여 0보다 크면 count++
   작으면 새로 push하는 코드를 작성했다.</p>
<pre><code class="language-js">onClick={() =&gt; { dispatch(
    addItem({
        id: finded.id,
        name: finded.title,
        count: 1,
            })
        );</code></pre>
<h2 id="18-localstorage로-만드는-최근-본-상품-기능1">18. localStorage로 만드는 최근 본 상품 기능1</h2>
<blockquote>
<p>새로고침하면 모든 state 데이터는 리셋된다. 왜냐면 새로고침하면
  브라우저는 html css js 파일들을 첨부터 다시 읽기 때문이다.
 이게 싫다면 state 데이터를 서버로 보내서 DB에 저장하거나 하면 된다.
내가 서버나 DB 지식이 없다면 localStorage를 이용해도 된다.
유저의 브라우저에 몰래 정보를 저장하고 싶을 때 쓰는 공간이다. 
   <img src="https://velog.velcdn.com/images/wamt_2787/post/33bb40c3-718b-4508-a29d-25d372265260/image.png" alt="">
크롬개발자 도구에서 Application 탭 들어가면 볼수있으며  사이트마다 5MB정도의 문자 데이터를 저장할 수 있다.
object 자료랑 비슷하게 key/value 형태로 저장하며
유저가 브라우저 청소를 하지 않는 이상 영구적으로 남아있는다.
밑에 있는 Session Storage도 똑같은데 브라우저 끄면 삭제되는 휘발성.</p>
</blockquote>
<h3 id="사용법">사용법</h3>
<p>  js 파일 아무데서나 다음 문법을 쓰면 localStorage에 
   데이터 입출력 할 수 있다. 차례로 추가, 읽기, 삭제 문법이다.</p>
<pre><code class="language-js">   localStorage.setItem(&#39;데이터이름&#39;, &#39;데이터&#39;);
localStorage.getItem(&#39;데이터이름&#39;);
localStorage.removeItem(&#39;데이터이름&#39;)</code></pre>
<h3 id="object-는-못함">object 는 못함?</h3>
<p><code>localStorage에 array/object</code> 자료를 저장하려면 문자만 저장할 수 있는 공간이라 <code>array/object</code>를 바로 저장할 수는 없다. 강제로 저장
해보면 문자로 바꿔서 저장해주는데 그럼 <code>array/object</code> 자료가 깨진다.
그래서 편법이 하나 있는데 <code>array/object -&gt; JSON</code> 이렇게 변환해서 
저장하면 된다.JSON은 문자취급을 받아서 그렇다. JSON은 그냥 따옴표친 array/object 자료다. </p>
<pre><code class="language-js">localStorage.setItem(&#39;obj&#39;, JSON.stringify({name:&#39;kim&#39;}) );</code></pre>
<p>이런식으로 코드를 만들면 <code>obj</code>라는 key를 가지고 <code>name:kim</code>이라는 
 object 값의 <code>value</code>를 가진 값을 저장할수있다.</p>
<pre><code class="language-js">   var a = localStorage.getItem(&#39;obj&#39;);
var b = JSON.parse(a)</code></pre>
<p>  당연히 꺼낼때도 그냥 꺼내면 JSON값을 그대로 가져오기 때문에
   <code>JSON.parse</code>를 사용해서 array/object값으로 다시 변경해줘야한다.</p>
<h2 id="응용문제-1">응용문제 1</h2>
<p>   localStorage 를 사용해서 main페이지에서 최근 열람한 상품들을 간단하게
   보여주는 ui를 만들어보자 그러기 위해선 id 값들을 가져와야한다.
   1.먼저 최상단의 App.js 에서 시작시 localStorage에 배열을 넣어 초기화하자.</p>
<pre><code class="language-js">   useEffect(()=&gt;{
    localStorage.setItem(&#39;watched&#39;, JSON.stringify( [] ))
  },[]) </code></pre>
<p>   2.<code>Detail.js</code>에서 get 을 통해 <code>watched</code>배열을 가져와보자.
   3. 가져온 배열에서 현재있는 상품 페이지의 id를 넣는다.
   4. set으로 다시 배열을 localstorage 에 저장하자.</p>
<pre><code class="language-js">   (Detail.js)
useEffect(()=&gt;{
  let 꺼낸거 = localStorage.getItem(&#39;watched&#39;)
  꺼낸거 = JSON.parse(꺼낸거)
  꺼낸거.push(찾은상품.id)
  localStorage.setItem(&#39;watched&#39;, JSON.stringify(꺼낸거))
}, [])</code></pre>
<ol start="5">
<li>작동은 잘하지만 이미본 상품도 계속 추가되는 버그가 있다 고쳐보자<ol start="6">
<li>상품 id에 이미 같은 값이 있으면 추가하지말라고 if문을 사용할수있지만
<code>set</code>이란 문법을 통해 중복을 제거하고 다시<code>array</code>로 감싸는 방법을 사용해보자 아래의 두 문장을 중간에 넣어주면 잘 동작 할거다.<pre><code class="language-js">(생략)
꺼낸거 = new Set(꺼낸거)
꺼낸거 = Array.from(꺼낸거)
(생략)</code></pre>
</li>
<li>근데 사이트 새로고침시 배열이 전부 사라지게 되는데 이는
우리가 처음에 선언한 <code>app.js</code>의 <code>watched</code>에 대한 <code>useEffect</code>
때문이다.if문을 통해 배열이 <code>null</code>값이 아니라면 실행하지 않겠다고 선언하자.</li>
</ol>
</li>
</ol>
<h2 id="19-실시간-데이터가-중요하면-react-query">19. 실시간 데이터가 중요하면 react-query</h2>
<blockquote>
<p> ajax 요청하다보면 아래와 같은 기능들이 필요해진다.</p>
</blockquote>
<ul>
<li><p>몇초마다 자동으로 데이터 다시 가져오게 하려면?</p>
</li>
<li><p>요청실패시 몇초 간격으로 재시도?</p>
</li>
<li><p>다음 페이지 미리가져오기?</p>
<ul>
<li>ajax 성공/실패시 각각 다른 html을 보여주려면?</li>
</ul>
<p>이러한 기능들을 <code>react-quert</code>를 사용하면 쉽게 넣을수있다.</p>
<blockquote>
<h3 id="react-query-세팅">react-query 세팅</h3>
</blockquote>
<ol>
<li>설치: <code>npm install @tanstack/react-query</code></li>
<li>세팅: index.js 로 가서 아래의 코드 Import
<code>import { QueryClient, QueryClientProvider, useQuery } 
from &#39;@tanstack/react-query&#39;</code></li>
<li>원래의 <code>index.js</code> 코드에 2번을 import 하고 <code>QueryClient</code>태그로 감싸준다.<pre><code>&lt;QueryClientProvider client={queryClient}&gt; 
&lt;Provider store={store}&gt;
&lt;BrowserRouter&gt;
 &lt;App /&gt;
&lt;/BrowserRouter&gt;
&lt;/Provider&gt;
&lt;/QueryClientProvider&gt;</code></pre></li>
<li><a href="https://codingapple.com/course-status/">https://codingapple.com/course-status/</a> 기능</li>
</ol>
<h2 id="20-성능-개선-1-개발자도구--lazy-import">20. 성능 개선 1 개발자도구 &amp; lazy import</h2>
<blockquote>
<h3 id="크롬-확장프로그램--react-developer-tools">크롬 확장프로그램 : React Developer Tools</h3>
<p>props를 보냈는데 출력이 안된다거나 이미지를 넣었는데 안보이는 
버그같은게 생기면 개발자도구를 켜서 <code>Elements 탭</code> 살펴보면 되는데
여기선 짠 코드가 실제 <code>html css</code>로 변환되어서 보인다. 
그게 싫고 컴포넌트로 미리보고 싶으면 리액트 개발자도구를 설치해서 켜보면 됨. </p>
</blockquote>
<blockquote>
<p><img src="https://velog.velcdn.com/images/wamt_2787/post/4061a242-094c-4ea1-9a6a-277ada6cecc3/image.png" alt=""> 위의 확장 프로그램을 사용하여 컴포넌트를 미리 확인 할수있으며
왼쪽상단 아이콘눌러서 컴포넌트 찍어보면 거기 있는 
state, props 들을 조회가 가능하다.
Profiler 탭에서는 녹화버튼을 누르고 앱에서의 동작 버튼클릭 페이지이동
들을 해본후 녹화를 종료하면 방금 렌더링된 모든 컴포넌트의 렌더링 시간을
측정해준다 이를통해 딜레이 걸리는 문제들을 확인이 가능하다.
Redux전용 <code>developer tools</code>도 있으니 나중에 사용해보자.</p>
</blockquote>
<blockquote>
<h3 id="lazy-import">lazy import</h3>
<p>react 를 다짜고 나서 배포를 할려고 <code>build</code>하는 순간 우리가 짠
모든 코드의 내용이 하나의 <code>js</code> 에 묶여서 배포된다. 이렇게되면
<code>js</code>의 용량이 크기때문에 사용자가 처음 접속시 시간이 오래걸리것이며
이를 자원을 크게 잡아 먹는다고 말할수있다. 그렇기 때문에 우리는 
<code>lazy import</code>라는 것을 사용해 메인에서 바로 처리하지 않는 컴포넌트를 사용하기 전까지 자원을 받지 않게 할것이다.
예를들어 아래의 코드는 App.js의 시작시 사용되지않음으로 </p>
</blockquote>
<pre><code class="language-js">(App.js)
import Detail from &#39;./routes/Detail.js&#39;
import Cart from &#39;./routes/Cart.js&#39;</code></pre>
<p>다음과 같이 바꿔줄것이다.</p>
<pre><code class="language-js">(App.js)
import {lazy, Suspense, useEffect, useState} from &#39;react&#39;
const Detail = lazy( () =&gt; import(&#39;./routes/Detail.js&#39;) )
const Cart = lazy( () =&gt; import(&#39;./routes/Cart.js&#39;) )</code></pre>
<p>물론 <code>lazy import</code>도 사용이 가능한데 이는 선언한 컴포넌트들이
필요하면 그때 load    해주세요 라는 의미를 담고있다. 
lazy를 사용하면 당연히 Detail 컴포넌트 로드까지 지연시간이 발생한다.</p>
<ol>
<li>Suspense 라는거 import 해오고</li>
<li>Detail 컴포넌트를 감싸면 Detail 컴포넌트가 로딩중일 때 대신 보여줄 html 작성도 가능하다. 귀찮으면 <code>&lt;Suspense&gt;</code>이걸로 <code>&lt;Routes&gt;</code> 전부 감싸도 된다. <pre><code>&lt;Suspense fallback={ &lt;div&gt;로딩중임&lt;/div&gt; }&gt;
&lt;Detail shoes={shoes} /&gt;
&lt;/Suspense&gt;</code></pre></li>
</ol>
<p>##21. 성능 개선2 재렌더링 막는 memo, useMemo</p>
<blockquote>
<p>컴포넌트가 재렌더링되면 거기 안에 있는 자식컴포넌트는 항상 함께 재렌더링된다.리액트는 그렇게 대충 무식하게 동작하는데 평소엔 별 문제가 없겠지만 자식컴포넌트가 렌더링시간이 1초나 걸리는 무거운 컴포넌트면 곤란해진다. 
부모컴포넌트에 있는 버튼 누를 때 마다 1초 버벅이는 불상사가 발생하기때문 
그럴 땐 자식을 <code>memo</code>로 감싸놓으면 된다.</p>
</blockquote>
<pre><code class="language-js">function Child(){
console.log(&#39;재렌더링됨&#39;)
return &lt;div&gt;자식임&lt;/div&gt;
}
</code></pre>
</li>
</ul>
<p>function Cart(){ 
  let [count, setCount] = useState(0)</p>
<p>  return (
    <Child />
    &lt;button onClick={()=&gt;{ setCount(count+1) }}&gt; + </button>
  )
}</p>
<pre><code> &gt;테스트용 자식 컴포넌트 를 하나 만들어보았다. 이제 만약 `cart`컴포넌트가
   재렌더링 될때 `child`컴포넌트 또한 재렌더링 될것이다. 만약 `child`가 이처럼 가벼우면 상관없는데 1초 정도 걸리는 동작이 들어가면 버튼 하나
   누를때 마다 동작이 더뎌질것이기 떄문에 memo를 통해 꼭 필요할때만
   재렌더링 되게 바꿔줄수있다.
   위의 코드에서 child 함수부분만 살짝바꿔줘 보자

```js
   let Child = memo( function(){
  console.log(&#39;재렌더링됨&#39;)
  return &lt;div&gt;자식임&lt;/div&gt;
})</code></pre><blockquote>
<p>이제 cart가 재렌더링 될때 같이 재렌더링 되는게아닌 child자체의
   state가 변경 될때만 <code>console</code>이 찍힐것이다. 즉 <code>button</code>을
   누를 때만 작동된다는 뜻이다.import 도 까먹지 말고 해주자
   <code>import {memo, useState} from &#39;react&#39;</code>
   그렇다고 memo를 전부 사용하면 문제가 발생되는데 이는 memo의 동작
   안에 기존의 state와 이후 state의 변경점을 확인하는 연산이 있기 때문에
   많은 사용은 오히려 부하를 주기 때문이다.</p>
</blockquote>
<p>   ##22. 성능 개선 3 useTransition</p>
<blockquote>
<p>렌더링시간이 매우 오래걸리는 컴포넌트가 있다고 했을때
버튼클릭, 타이핑할 때 마다 그 컴포넌트를 렌더링해야한다면
이상하게 버튼클릭, 타이핑 반응속도도 느려진다 개선방법을 알아보자. 
당연히 그 컴포넌트 안의 html 갯수를 줄이면 대부분 해결되는데
근데 그런게 안되면 useTransition 기능을 쓰면 된다.</p>
</blockquote>
<pre><code class="language-js">   import {useState} from &#39;react&#39;

let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState(&#39;&#39;)

  return (
    &lt;div&gt;
      &lt;input onChange={ (e)=&gt;{ setName(e.target.value) }}/&gt;
      {
        a.map(()=&gt;{
          return &lt;div&gt;{name}&lt;/div&gt;
        })
      }
    &lt;/div&gt;
  )
}</code></pre>
<blockquote>
<p>위처럼 사용자가 input에 입력한 정보를 <code>map</code>을 통한 10000개의
   값을 가진 배열에 집어넣어 <code>&lt;div&gt;</code>안에 보여주는 무식한 코드를
   만들어보자 유저가 <code>&lt;input&gt;</code>에 타이핑하면 그 글자를 <code>&lt;div&gt;</code> 
   1만개안에 집어넣어줘야하는데 <code>&lt;div&gt;</code> 1만개 렌더링해주느라 
  <code>&lt;input&gt;</code>도 많은 지연시간이 발생한다.</p>
</blockquote>
<pre><code class="language-js">   import {useState, useTransition} from &#39;react&#39;
   let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState(&#39;&#39;)
  let [isPending, startTransition] = useTransition()

  return (
    &lt;div&gt;
      &lt;input onChange={ (e)=&gt;{ 
        startTransition(()=&gt;{
          setName(e.target.value) 
        })
      }}/&gt;

      {
        a.map(()=&gt;{
          return &lt;div&gt;{name}&lt;/div&gt;
        })
      }
    &lt;/div&gt;
  )</code></pre>
<blockquote>
<p><code>useTransition()</code> 쓰면 그 자리에 <code>[변수, 함수</code>]가 남는다. 
우측에 있는 <code>startTransition()</code> 함수로 state변경함수 같은걸 
 묶으면 그걸 다른 코드들보다 나중에 처리해준다.
그래서 <code>&lt;input&gt;</code> 타이핑같이 즉각 반응해야하는걸 우선 처리해줄 수 있다.
물론 근본적인 성능개선이라기보단 특정코드의 실행시점을 뒤로 옮겨주는 것일 뿐이다.</p>
</blockquote>
<blockquote>
<h4 id="ispending은-어디다-쓰냐면">isPending은 어디다 쓰냐면</h4>
</blockquote>
<pre><code class="language-js">   {
  isPending ? &quot;로딩중기다리셈&quot; :
  a.map(()=&gt;{
    return &lt;div&gt;{name}&lt;/div&gt;
  })
} </code></pre>
<p><code>startTransition()</code> 으로 감싼 코드가 처리중일 때 <code>true</code>로 변하는 변수다.그래서 이런 식으로 코드짜는 것도 가능 위의 코드는 <code>useTransition</code>으로 감싼게 처리완료되면 <code>&lt;div&gt;{name}&lt;/div&gt;</code> 이게 보이겠군요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) 로그인 데이터 저장하기]]></title>
            <link>https://velog.io/@wamt_2787/RN-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wamt_2787/RN-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 24 Feb 2023 10:41:34 GMT</pubDate>
            <description><![CDATA[<p>import datamanger</p>
<p>DataManage 에 JSON 값을 넣기위해 string 으로 변환해서 넣어준다.</p>
<pre><code class="language-tsx">const jsonDataTostring = JSON.stringify(data.result);
DataManager.storage.set(&quot;loginInfo&quot;, jsonDataToString);
navigation.navigate(&#39;home&#39;)</code></pre>
<p>home 으로 넘어가 userToken을 get 해본다.
돌아와서 값을 받을때 제대로 출력하기 위해 다시 Json으로 바꾸어준다.</p>
<pre><code class="language-tsx">const loginInfo = DataManager.storage.getString(&#39;loginInfo&#39;)
const loginInfoToJson = JSON.parese(loginInfo);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter 를 배우기위한 Dart 기초]]></title>
            <link>https://velog.io/@wamt_2787/Flutter-%EB%A5%BC-%EB%B0%B0%EC%9A%B0%EA%B8%B0%EC%9C%84%ED%95%9C-Dart-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@wamt_2787/Flutter-%EB%A5%BC-%EB%B0%B0%EC%9A%B0%EA%B8%B0%EC%9C%84%ED%95%9C-Dart-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 05 Feb 2023 08:53:08 GMT</pubDate>
            <description><![CDATA[<h1 id="dart">Dart?</h1>
<blockquote>
<p>Dart 는 ui에 최적화된 개발환경을 가지고있으며 상당히 빠르다는 장점이있다.
Dart 의 종류에는 dart web , dart native 가 존재한다.
Dart 를 사용하여 IOS,Android,Windows,Linux,Mac 으로 컴파일이 가능하다.</p>
</blockquote>
<h2 id="장점">장점</h2>
<blockquote>
<p>기존의 C++,C,GO 와 같은 언어들로 코딩할때는 AOT 방법으로 입력된 언어를 기계어로 컴파일 해야 했지만 이는 UI 에 대한 작업을 할때 하나의 버튼 수정만해도 계속해서 처음부터 컴파일해야 하기 때문에 Dart 는 JIT 방법 또한 채택 하였다. 
이는 Dart Vm을 사용해 개발자가 쓴 코드의 결과를 바로 화면에 보여준다.하지만 결국 개발이후 배포 과정에선 AOT를 이용한 컴파일을 해야한다.</p>
</blockquote>
<h2 id="체험">체험</h2>
<blockquote>
<p>Dart 를 체험하기 위해 <a href="dartpad.dev">dartpad.dev</a> 링크로 들어가 시작해본다.</p>
</blockquote>
<p>🚨🚨 주의사항 🚨🚨
1.Dart는 main mathod 를 중점으로  내부코드를 작성해 동작한다. 
2.Dart 에서 세미콜론을 안쓰는 기능이 있기때문에 세미콜론이 자동적으로 
 찍히지않아 유의해야한다.</p>
<h1 id="variables">VARIABLES</h1>
<h3 id="변수생성">변수생성</h3>
<p>메소드 내부에서 사용할때 var를 통해 자동적으로 명시되는 경우</p>
<pre><code class="language-dart">var name = &#39;이름&#39; ;
name = &#39;김김김&#39;;</code></pre>
<p>class 에서 변수나 property를 선언할때 타입을 명시하는 경우 </p>
<pre><code class="language-dart">String name = &#39;이름&#39;;
name = &#39;김김김&#39; ;</code></pre>
<h3 id="dynamic"><strong>dynamic</strong></h3>
<p>var와 동일하게 동작되는 동적타입 변수이다.
외부에서 데이터를 받았을때 그 데이터를 필터링 할수있는 느낌이다.</p>
<pre><code class="language-dart">dynamic name;
if(name is String){
 name.메소드
}
if(name is boolean){
 name.메소드
}</code></pre>
<h3 id="nullable"><strong>nullable</strong></h3>
<p>null이 될수도 있는 값을 뜻한다.
첫번째의 경우 에러가 발생해 <code>String</code> 뒤에 <code>?</code> 를 명시하여 nullable 를 나타낸다. 
두번째와 세번째의 코드는 동일하게 작동하며 세번째의 코드가 Dart의 장점을 활용해 
단축 시켰다 볼수있다.</p>
<pre><code class="language-dart">String name =&#39;김김김&#39;;
name = null;</code></pre>
<pre><code class="language-dart">String? name =&#39;김김김&#39;;
name = null;
if (name != null){
    nico.isNotEmpty;
}</code></pre>
<pre><code class="language-dart">String? name =&#39;김김김&#39;;
name = null;
name?.isNotEmpty</code></pre>
<h3 id="final-variables"><strong>Final Variables</strong></h3>
<p>var 대신 final 로 변수를 지정해주면 변수타입을 변경할수 없게 설정이 가능하다.</p>
<pre><code class="language-dart">final name =&#39;김김김&#39;;
name = null;</code></pre>
<h3 id="late-variables"><strong>Late Variables</strong></h3>
<p>late 는 final 이나 var 앞에 붙을수있는 수식어이다.
late는  초기 데이터 없이 변수를 선언할 수 있게 해준다.
예를들어 변수를 미리 선언하고 후에 api등으로 데이터를 가져올때 유용하다.
데이터가 들어오지 않은 상태에서 변수 사용이 불가능 하기때문에 실수 방지가 가능하다.</p>
<pre><code class="language-dart">late final String name ;
// do something. go to api
print(name);   // 에러!!
name = &#39;김김김&#39;;
print(name);   // 정상작동!</code></pre>
<h3 id="constant-variables"><strong>Constant Variables</strong></h3>
<p>Dart 의 const 는 Js 의 const등과 다르다 오히려 <code>final</code> 이 유사하다.
사용자의 동작에 따라 api값을 받아 변수에 저장할때는 final 을 사용하고
const는 앱을 배포하기전 컴파일 할때 알고있는 값에 사용하는 것이다.</p>
<h1 id="data-types">DATA TYPES</h1>
<h3 id="basic-data-types"><strong>Basic Data Types</strong></h3>
<p>여러 데이터 타입이 존재한다.(num 은 double과 int의 부모 타입)</p>
<pre><code class="language-dart">String name=&quot;김김김&quot;;
int age = 12;
double money = 22.22;
num x =12;
x = 1.1;</code></pre>
<h3 id="list--collection-if"><strong>List &amp; Collection If</strong></h3>
<p>List 는 배열이고 <code>Colliection If</code>는 Dart에서 사용되는 간단한 조건문이다.원래는 <code>numbers.add(5)</code>와 같은 if안에 문법을 사용해야하는것을 간단히 작성 가능하다.</p>
<pre><code class="language-dart"> var numbers = [1,2,3,4,5,];
 List&lt;int&gt; numbers2 =[1,2,3,4,5,];  // 동일한 동작</code></pre>
<pre><code class="language-dart"> var giveFive = true;
 var numbers = [1,2,3,4,if(giveFive) 5,]; //결과 1,2,3,4,5</code></pre>
<h3 id="string-interpolation"><strong>String Interpolation</strong></h3>
<p>변수값 을 보여주기 위해서 <code>$</code>를 사용한다.만약 변수값의 계산을 하고싶으면
<code>${}</code> 를 사용하여 나타내준다.</p>
<pre><code class="language-dart">var age = 24;
var name=&#39;김김김&#39;;
var write= &quot;Hello $name I\&#39;m ${age+3}&quot; ;
print(write);            //결과 Hello 김김김 I&#39;m 27</code></pre>
<h3 id="collection-for"><strong>Collection For</strong></h3>
<p><code>Collection if</code> 와 마찬가지로 <code>list</code>와 같은 변수내용에 직접적으로 간단하게
변화를 줄수있는 방법이다.<code>for(var ooo in xxx)&quot;😃 $ooo&quot;,</code> 이 한줄의 
코드로 인해 newBro의 리스트에 oldBro를 포함 시키고 구분짓는다.</p>
<pre><code class="language-dart">var oldBro=[&#39;이이이&#39;,&#39;감감감&#39;];
var newBro=[&#39;니니니&#39;,&#39;누누누&#39;,&#39;나나나&#39;,
    for(var bro in oldBro) &quot;😃 $bro&quot;,
];
print(newBro);</code></pre>
<h3 id="maps"><strong>Maps</strong></h3>
<p><code>map</code>을 사용해 <code>object형태</code>의 변수틀 을 만들수있다. 
<code>var 타입</code>으로 생성하면 역시 자동으로 <code>key,value</code> 형태의 타입을 설정하고
<code>map</code> 을사용해 직접적으로 데이터 타입을 명시할수도 있다.(List 와 혼합가능)</p>
<pre><code class="language-dart">var player = {
 &#39;name&#39;: &#39;김김김&#39;,
 &#39;xp&#39;: 12.22,
 &#39;seqw&#39;: false,
 };</code></pre>
<pre><code class="language-dart">Map&lt;String,Object&gt; player = {
  ddd:&#39;nico&#39;,
  eee: 1234,
  ccc: false,
 };
 player.containsKey() //메소드</code></pre>
<h3 id="sets"><strong>Sets</strong></h3>
<p><code>set</code>은 <code>List</code>와 비슷하지만 요소가 하나씩 있어야 되는 경우에 사용한다 이유는 <code>unique</code>한 속성을 가지기 때문에 중복이 어렵기 때문이다. 
<code>{}</code> 를 사용한다면 <code>set</code> 이고 <code>[ ]</code> 를 사용한다면 <code>List</code>이다.</p>
<pre><code class="language-dart">set&lt;int&gt; numbers ={1,2,3,4,};
var numbers2 ={1,2,3,4,};    
 // 위 set 아래 list
List&lt;int&gt; numbers3 = [1,2,3,4,];
var numbers4 =[1,2,3,4,];</code></pre>
<h1 id="functions">FUNCTIONS</h1>
<h3 id="defining-a-function"><strong>Defining a Function</strong></h3>
<p>다른 함수에서 <code>parameter</code>값을 가져올때 보통의경우 아래와 같이 사용한다.</p>
<pre><code class="language-dart">String sayHello(String name) {
 return &quot;Hello $name nice to meet you!&quot;;
 }
void main() {
 print(sayHello(&#39;김김김&#39;));
}</code></pre>
<p>하지만 만약 복잡하지 않은 간단한 함수라면 <code>fat arrow syntax</code>방법으로 표현이 가능</p>
<pre><code class="language-dart">num sayNum(num a,num b ) =&gt; a+b;
void main() {
 print(sayNum(1,2));
}</code></pre>
<h3 id="name-parameters"><strong>Name Parameters</strong></h3>
<p>Parameters의 값이 여러개일때 코드가 복잡해질수 있기 때문에 정리를하여
나열 하는 <code>name argument</code>를 사용한다. (🚨🚨 반드시 매개변수에 <code>{}</code>)</p>
<pre><code class="language-dart">String SayHello({String name, int age, String country}){
    return&quot;$name , $age, $country&quot;;
}
void main() {
 print(sayHello(
  age:12,
  country:&#39;korea&#39;,
  name:&#39;김김김&#39;,)
  );}</code></pre>
<p>하지만 이런 경우 요소에 값을 넣지 않으면 <code>nullSafety</code> 가 발생한다.때문에 아래 <code>Default Value</code>나 <code>required modifier</code>의 방법중 하나를 선택해야한다.
<code>required modifier</code>방법은 요소중의 하나라도 선언하지 않으면 에러가 발생하기 떄문에 null값을 방지 할수있다,</p>
<pre><code class="language-dart">/// Default Value : 기본값 설정
String SayHello(
String name=&#39;good&#39;, 
int age=14, 
String country=&#39;korean&#39;
) {
    return&quot;$name , $age, $country&quot;;
}
void main() {
 print(sayHello());}</code></pre>
<pre><code class="language-dart">/// required modifier 방법
String SayHello(
required String name,
required int age, 
required String country&#39;
) {
    return&quot;$name , $age, $country&quot;;
}
void main() {
 print(sayHello());} // error 발생!!!</code></pre>
<h3 id="optional-positonal-parameters"><strong>Optional Positonal Parameters</strong></h3>
<p><del>생략</del><br><a href="https://nomadcoders.co/dart-for-beginners/lectures/4110">영상</a>
이유: 가시성이 떨어지고 name arguments 보다 이점이 없음</p>
<h3 id="qq-operator"><strong>QQ Operator</strong></h3>
<p>기존의 코드에서 매개변수에 대한 조건연산은 삼항연산자 조건문으로 많이 사용되었다.</p>
<pre><code class="language-dart">String capitalizeName(String? name) =&gt;
name!= null ? name.toUpperCase(): &#39;NON&#39;;
 //null값이 아닐경우 대문자변환 맞을경우 NON 
void main(){
 capitalizeName(&#39;ddd&#39;);
 capitalizeName(null);
}</code></pre>
<p>여기서 <code>?</code>의 특성을 활용하면 <code>null값</code>과 <code>null이 아닌값</code>을 구분할수있다.</p>
<pre><code class="language-dart">String capitalizeName(String? name) =&gt;
name?.toUpperCase() ?? &#39;NON&#39;;
 //null값이 아닐경우 대문자변환 맞을경우 NON 
void main(){
 capitalizeName(&#39;ddd&#39;);
 capitalizeName(null);
}</code></pre>
<p><code>fat arrow</code> 와 <code>?</code>의 특성 그리고 <code>QQ Operator</code>를 이용해 기존의 다른 언어들의 
코드보다 간결하게 표현이 가능하며 아래와 같은 코드도 가능해진다.</p>
<pre><code class="language-dart">void main() {
 String? name;      //null = x
 name ??= &#39;qqq&#39;;     //if(name=null)=&gt; name=&#39;qqq&#39;
 name = null;       //if(name!=null)=&gt; name= null
 name ??= &#39;ddd&#39;;     //if(name=null)=&gt; name=&#39;ddd&#39;
 print(name);
}</code></pre>
<h3 id="typedef"><strong>Typedef</strong></h3>
<p>자료형을 간편하게 작성할수있게 <code>alias</code> 쉽게말해 자료형의 별명을 만들어준다.
예를들어 아래와 같이 <code>list</code> 값을 받아 반대로 저장하는 함수가 있을때 새로운
<code>Typdef</code> 의 <code>alias</code> 를 만들어 대체하여 사용이 가능하다.</p>
<pre><code class="language-dart">List&lt;int&gt; reverse(List&lt;int&gt; list) {
 var reversed = list.reversed;
 return reversed.toList();
}
void main() { print(reverse([1,2,3,]));}</code></pre>
<pre><code class="language-dart">Typedef Lint = List&lt;int&gt;; // Lint 로 대체

Lint reverse(Lint list) {
 var reversed = list.reversed;
 return reversed.toList();
}
void main() { print(reverse([1,2,3,]));}</code></pre>
<h1 id="classes">CLASSES</h1>
<h3 id="firsd-dart-class"><strong>Firsd Dart Class</strong></h3>
<p><code>Flutter</code> 에서 중요하게 사용되는 Dart 의 Class 부분을 살펴보자 
<code>Class</code> 에서 <code>Property</code> 를 선언할 때는 <code>var</code> 가아닌 꼭 타입을 사용해서 정의한다.
함수에서 Class 를 호출할때 <code>new</code>를 사용하지 않아도 된다.
Class 내에서의 함수에서 변수를 호출할때 <code>this</code> 를 사용하지 않아도된다.</p>
<pre><code class="language-dart">class People{
    final String name = &#39;김김김&#39;;
    int age = 14;

  void DD() {
   print(&#39;hi $name i\&#39;m $age&#39;);
  }
}
void main() {
 var people = People();
 print(people.name);
 people.age = 16;
}</code></pre>
<h3 id="constructors생성자"><strong>Constructors(생성자)</strong></h3>
<p>위의 코드에서 argument 로 people의 이름과 나이를 전달해서 새로운 People을
만드는 코드를 짜보자 이를 위해선 Constructors 가 필요하다.</p>
<pre><code class="language-dart">class People{
 late final String name;
 late int age;

  People(String name, int age){
    this.name = name;
    this.age = age
  }
   void DD () {
    print(&#39;hi $name i\&#39;m $age&#39;);
   }
}
void main() {
 var people = People(&#39;이이이이&#39;,15);
  people.DD();
}</code></pre>
<p>이와 같이 코드를 짜면 일단 초기값이 없기때문에 <code>late</code> 를 사용해야하고 생성자의
크기가 길어지며 이미 선언한 이름과 나이의 자료형을 한번 더 선언해 코드가 길어진다. 이를 방지 하기위해 아래와 같이 코드를 간단하게 작성 가능하다.</p>
<pre><code class="language-dart">class People{
 final String name;
 int age;

  People(this.name, this.age)

   void DD () {
    print(&#39;hi $name i\&#39;m $age&#39;);
   }
}
void main() {
 var people = People(&#39;이이이이&#39;,15);
  people.DD();
}</code></pre>
<p>🚨🚨 이때 주의 해야할 점은 argument의 위치다.</p>
<h3 id="named-constructor-parameters"><strong>Named Constructor Parameters</strong></h3>
<p>위와 같은 Positional Parameters는 결국 arguments의 위치가 바뀌면 에러가 발생하고
또한 Class의 크기가 커지면 통제가 어렵다는 큰 단점이 존재한다. 따라서 우리는 Flutter
를 사용할때 앞서 배웠던 Named Parameters를 생성자에 적용 시킬것이다.</p>
<pre><code class="language-dart">class People{
 final String name;
 int age;
 String team,country;

  People({
  required this.name, 
  required this.age,
  required this.team,
  required this.country,})   //null값 방지를 위한 required와 {} 사용

   void DD () {
    print(&#39;hi $name i\&#39;m $age&#39;);
   }
}
void main() {
 var people = People(
 name : &#39;이이이이&#39;,
 team : &#39;red&#39;,
 country :&#39;korea&#39;,
 age : 15,
 );
  people.DD();
}</code></pre>
<h3 id="named-constructor"><strong>Named Constructor</strong></h3>
<p>비슷하지만 조금 다르게 동작하는생성자를 가지고싶은 경우에 사용하는 방법이다.
두가지의 경우에 대해서 알아볼것이다. 첫번째 <code>blueTeam</code> 이라는 이름의 생성자는
Named parameter를 사용해 만들것이고 두번째     <code>redTeam</code>생성자는 Position 
parameter를 사용해 만들것이다. </p>
<pre><code class="language-dart">class People{
 String name;
 int age;
 String team,country;

  People({
  required this.name, 
  required this.age,
  required this.team,
  required this.country,})   //null값 방지를 위한 required와 {} 사용

  People.blueTeam({required String name,required int age,}) 
  :                                             // : 콜론 매우 중요!!!
  this.age = age,
  this.name = name,
  this.country = &#39;korea&#39;,
  this.team = &#39;blue&#39;;                    //name parameter

  People.redTeam(String name, int age) 
  :                                          // : 콜론 매우 중요!!!
  this.age = age,
  this.name = name,
  this.team = &#39;red&#39;,
  this.countty = &#39;china&#39;,
   void DD () {
    print(&#39;hi $name i\&#39;m $age&#39;);
   }
}                                        //position parmeter
void main() {
 var bluepeople = People.blueTeam(
 name : &#39;이이이이&#39;,
 age : 15,                                        //named parmeter
 );
 var redpeople = People.redTeam(&#39;김김김김&#39;,15);    //position parmeter
  bluepeople.DD();
  redpeople.DD();
}</code></pre>
<p>코드가 계속 길어져 알아보기 힘들지만 결국 중요한 내용은 name parmeter로 만든 생성자든 position으로 만든 생성자 이든 초기화를 위한 <code>:</code>을 써줘야하고 name 은
required 를 position은 arguments 의 위치를 신경써야한다.</p>
<h3 id="cascade-notation"><strong>Cascade Notation</strong></h3>
<p>Cascade 는 생성한 변수의 arguments 값이나 이후 선언할때 변수명을 <code>..</code>으로 대체할수있는 장점을 가진다. 사용법은 class 바로 뒤에 <code>;</code>을 붙이지 않고 사용한다.</p>
<pre><code class="language-dart"> var redpeople = People.redTeam(&#39;김김김김&#39;,15,&#39;blue&#39;,)
 ..name = &#39;이이이&#39;                    // redpeople.name =&#39;이이이&#39;;
 ..age = 16                            // redpeople.age = 16;
 ..team = &#39;red&#39;;
 ..sayHello();</code></pre>
<h3 id="enums"><strong>Enums</strong></h3>
<p>Enums는 개발자가 실수를 줄일수있도록 만들어주는 일종의 틀 이다.
enum으로 설정한 변수에는 오직 선언한 값만 들어갈수있다.</p>
<pre><code class="language-dart">enum Team {red,blue} ;
 //생략

..team = Team.blue;</code></pre>
<h3 id="abstract-classes"><strong>Abstract Classes</strong></h3>
<p>추상화 클래스는 다른 클래스들이 직접 구현 해야하는 메소드들을 모아놓은 일종의 &#39;청사진&#39; 이라 보면 된다. 여러 클래스들에 대해서 공통적으로 상속받는 메소드들을 
추상화 클래스로 사용이 가능하다.</p>
<pre><code class="language-dart">abstract class Human {
 void walk();
}
class Player extedns Human{
 void walk(){
 print(&quot;working!&quot;);
 }
}</code></pre>
<h3 id="inheritance상속"><strong>Inheritance(상속)</strong></h3>
<p>부모 class 에서 <code>Human({required this.name});</code> 와같이 객체 형태의 파라미터로 선언된 경우에 상속받은 자식 Class 에서 <code>super</code>를 사용해 생성자를 구성할때 <code>(name:name)</code>와 같은 형태로 만들어 <code>main</code>함수에서 값을 받을수있다. 또한 <code>override</code>를 통해 부모 클래스의 객체를 받아올수 있는데 이때도 <code>super</code>를 사용한다</p>
<pre><code class="language-dart">class Human {
 final String name;
 Human({required this.name});
 void sayHello() {
  print (&#39;Hi $name &#39;);
 }}

  enum Team {blue,red};

 class Player extends Human {
   final Team team;
  Player({
     required this.team,
     required String. name
  }) : super(name: name);                //super 사용해서 값 보내기

  @override
  viod sayHello(){
   super.sayHello();
   print(&#39;and I play for ${team}&#39;);
  }

 }
 void main() {
  var player = Player(team :Team.red,name:&#39;nnn&#39;);
 }
</code></pre>
<h3 id="mixins"><strong>Mixins</strong></h3>
<p>Mixin은 생성자가 없는 클래스를 의미하며 <code>extends</code>가 아닌 <code>with</code>를 통해
여러 클래스의 Mixin 내부의 프로퍼티와 메소드 들을 가져온다. 이는 상속이아닌
빌리는 개념이며 또한 여러 클래스에 재사용이 가능하다.</p>
<pre><code class="language-dart">class Strong{
    final int power = 100;
}
class Karisma{
    void karis(){
    print(&quot;ouuuuuuu&quot;);
    }
}

class Bigman with Strong,Karisma (){

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) Android.display Name 충돌(한국어 오류)]]></title>
            <link>https://velog.io/@wamt_2787/RN-Android.display-Name-%EC%B6%A9%EB%8F%8C%ED%95%9C%EA%B5%AD%EC%96%B4-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@wamt_2787/RN-Android.display-Name-%EC%B6%A9%EB%8F%8C%ED%95%9C%EA%B5%AD%EC%96%B4-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Wed, 28 Dec 2022 06:40:56 GMT</pubDate>
            <description><![CDATA[<p>string.xml 한글 타이틀 입력시 오류가 발생
&#39;&#39;&#39;
<resources>
    <string name="app_name">오글</string>
   <string name="appCenterCrashes_whenToSendCrashes" moduleConfig="true" translatable="false">DO_NOT_ASK_JAVASCRIPT</string>
<string name="appCenterAnalytics_whenToEnableAnalytics" moduleConfig="true" translatable="false">ALWAYS_SEND</string>
    <!-- <string moduleConfig="true" name="CodePushDeploymentKey">seqwFt3ITMFsvjgt1vssqbd1dbcdHci5lAcZt</string> -->
</resources></p>
<p>&#39;&#39;&#39;</p>
<p>해결법</p>
<pre><code>FLIPPER_VERSION=0.145.0</code></pre><p>플립 버전 변경</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) 리액트 네이티브 세팅]]></title>
            <link>https://velog.io/@wamt_2787/RN-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@wamt_2787/RN-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Sun, 11 Dec 2022 07:21:26 GMT</pubDate>
            <description><![CDATA[<p>개발환경
1.Node,Watchman 설치 (Node 14 이상인지 확인)</p>
<pre><code class="language-bash">brew install node
brew install watchman</code></pre>
<p>2.JDK 설치
Homebrew 를 사용하여 Azul Zulu 라는 OpenJDK 설치하는 것이 좋다 . 
터미널에서 다음 명령을 실행합니다.</p>
<pre><code class="language-bash">brew tap homebrew/cask-versions
brew install --cask zulu11</code></pre>
<p>JDK를 설치한 경우 JDK 11을 권장한다.</p>
<p>3.Android Studio 설치
Android 스튜디오를 다운로드하여 설치한다. Android Studio 설치 마법사에서 
다음 항목 옆의 상자가 모두 선택되어 있는지 확인해야한다.</p>
<pre><code class="language-bash">Android SDK
Android SDK Platform
Android Virtual Device</code></pre>
<p>3-1. Android SDK 설치
Android Studio는 기본적으로 최신 Android SDK를 설치한다. 네이티브 코드로 
React Native 앱을 빌드하려면 Android 12 (S)특히 SDK가 필요하다.
Android SDK는 Android Studio의 SDK Manager를 통해 설치할 수 있다.
Android Studio를 열고 &quot;추가 작업&quot; 버튼을 클릭한 다음 &quot;SDK 관리자&quot;를 선택한다.<img src="https://velog.velcdn.com/images/wamt_2787/post/ea155735-e469-4d1a-b213-0bd43b072c41/image.png" alt="">
SDK 관리자에 들어가서 먼저 확장해야 할것은 <code>Android 12 (s)</code> 를 확인해야한다.
두번째로 API build tools level을 확인한다 <code>31.0.0</code>
<img src="https://velog.velcdn.com/images/wamt_2787/post/9db353f1-0a41-4d8d-ae0e-1db5579127ba/image.png" alt=""></p>
<p>3-2. ANDROID_SDK_ROOT 환경 변수 구성
<code>zsh</code> 를 사용한다면 Home 에서 <code>vim ~/.zshrc</code> 를 사용해서 안의 환경 변수를 아래와 같이 적어준다.</p>
<pre><code class="language-bash">export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools</code></pre>
<p>애플리케이션 만들기</p>
<p>먼저 전에 글로벌 <code>react-native-cli</code>패키지를 설치한 경우 예기치 않은 문제가 발생할 수 있으므로 제거해야한다.
아래의 명령어로 새로운 RN프로젝트를 만든다.
<code>npx react-native init 프로젝트이름</code></p>
<p>이후 우리는 실행을 돕기위해 <code>Yarn</code> 이라는 자바스크립트 패키지 매니저를 설치해 
사용할거다. <code>brew install yarn --ignore-dependencies</code> 
이미 설치되어 있는 패키지는 무시하는 <code>--ignore-dependencies</code>를 꼭 붙여줘야한다.이후 생성한 프로젝트 경로에 들어가 <code>yarn</code> 을 터미널에 입력후
<code>yarn run android</code> 로 시뮬레이터를 실행하면 아래와 같이 앱이 만들어진다.
<img src = https://velog.velcdn.com/images/wamt_2787/post/7cf1a848-03ba-45f9-9e8c-f902bcf97f93/image.png width = "40%" height = "40%">  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) 앱 아이콘 추가]]></title>
            <link>https://velog.io/@wamt_2787/RN-%EC%95%B1-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@wamt_2787/RN-%EC%95%B1-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Sun, 11 Dec 2022 06:20:46 GMT</pubDate>
            <description><![CDATA[<p>앱 아이콘을 만들기위해 일단 이미지가 필요하다 예를들어</p>
<p><img src="https://velog.velcdn.com/images/wamt_2787/post/57710121-fba1-4b95-865a-f6194f557362/image.png" alt="">
이와 같이 큰 이미지가 있을때 먼저 아이콘에 맞는 사이즈인 1024x1024 로 변경해야한다.
<a href="https://www.appicon.co/">https://www.appicon.co/</a>
링크된 사이트에서 간편하게 변경이 가능하다.
<img src="https://velog.velcdn.com/images/wamt_2787/post/f7abae01-5238-47cd-8597-ca5abeebeecd/image.png" alt="">
이제 변경된 아이콘 사이즈를 Android 의 규정에 맞는 round 로 바꿔야한다
<a href="http://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=image&amp;foreground.space.trim=1&amp;foreground.space.pad=0.25&amp;foreColor=rgba(96%2C%20125%2C%20139%2C%200)&amp;backColor=rgb(255%2C%20255%2C%20255)&amp;crop=0&amp;backgroundShape=square&amp;effects=none&amp;name=ic_launcher">http://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=image&amp;foreground.space.trim=1&amp;foreground.space.pad=0.25&amp;foreColor=rgba(96%2C%20125%2C%20139%2C%200)&amp;backColor=rgb(255%2C%20255%2C%20255)&amp;crop=0&amp;backgroundShape=square&amp;effects=none&amp;name=ic_launcher</a>
위의 사이트에서 배경색과 같이 아이콘을 변경해서 저장할수있다.
<img src="https://velog.velcdn.com/images/wamt_2787/post/1698274d-7d14-4c98-9441-4ed136cc19bf/image.png" alt="">
이제 여러 형식의 앱 아이콘을 받을수있는데 이를 <code>android/app/src/main/res</code> 경로에 이름을 동일하게 해서 넣으면 끝이난다.
<img src="https://velog.velcdn.com/images/wamt_2787/post/ac81b637-74e4-45a7-b1e1-f9ec1fece203/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) 앱 이름 변경]]></title>
            <link>https://velog.io/@wamt_2787/RN-%EC%95%B1-%EC%9D%B4%EB%A6%84-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@wamt_2787/RN-%EC%95%B1-%EC%9D%B4%EB%A6%84-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Sun, 11 Dec 2022 06:08:23 GMT</pubDate>
            <description><![CDATA[<p>앱이름 변경</p>
<p><strong>Android</strong></p>
<ol>
<li>app.json 의 displayName 수정</li>
<li>android/app/src/main/res/values/strings.xml 코드를 수정합니다.<pre><code>&lt;resources&gt;
 &lt;string name=&quot;app_name&quot;&gt;새로운 앱 이름&lt;/string&gt;
&lt;/resources&gt;</code></pre></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[RN) 앱 출시 방법]]></title>
            <link>https://velog.io/@wamt_2787/%EC%95%B1-%EC%B6%9C%EC%8B%9C-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@wamt_2787/%EC%95%B1-%EC%B6%9C%EC%8B%9C-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 27 Nov 2022 09:42:25 GMT</pubDate>
            <description><![CDATA[<h2 id="1-안드로이드-서명-키-생성">1. 안드로이드 서명 키 생성</h2>
<p>Mac에서 터미널 프로그램을 열고 RN(react native) 프로젝트 폴더에 android/app 폴더로 이동합니다.
<code>cd [my path]/android/app</code></p>
<p>아래에 명령어를 통해 안드로이드(Android)용 서명 키(Signing Key)를 발급합니다.</p>
<pre><code class="language-html">keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

keytool -genkey -v -keystore [key-name].keystore -alias [key alias] -keyalg RSA -keysize 2048 -validity 10000

Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:
What is the name of your organizational unit?
  [Unknown]:
What is the name of your organization?
  [Unknown]:
What is the name of your City or Locality?
  [Unknown]:
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]:
Is CN=*****, OU=Unknown, O=Unknown, L=*****, ST=*****, C=***** correct?
  [no]:

Enter key password for &lt;my-key-alias&gt;
    (RETURN if same as keystore password)</code></pre>
<p>  전부 입력을 하고 나면 RN(react native) 프로젝트 폴더 하위에 android/app 폴더에 my-release-key.keystore 파일이 생성된 것을 확인할 수 있습니다.</p>
<h2 id="2서명-키-설정">2.서명 키 설정</h2>
<p> 서명 키(Signing Key)가 생성되면 gradle에 키를 설정해야합니다. android/gradle.properties 파일을 열고 아래에 코드를 추가합니다.</p>
<pre><code class="language-html">MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****</code></pre>
<p>아래에 코드를 android/app/build.gradle 파일에 추가합니다.</p>
<pre><code class="language-html">...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty(&#39;MYAPP_RELEASE_STORE_FILE&#39;)) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
...
</code></pre>
<h2 id="3빌드">3.빌드</h2>
<p>RN(react native)가 있는 프로젝트 폴더에서 android 폴더로 이동한 후 아래에 명령어로 빌드합니다.</p>
<pre><code class="language-html"># cd android
./gradlew bundleRelease</code></pre>
<h2 id="4-플레이-스토어-앱생성">4. 플레이 스토어 앱생성</h2>
<p><a href="https://yannichoongs.tistory.com/141?category=797849">https://yannichoongs.tistory.com/141?category=797849</a> 참고</p>
<p>앱생성 진행을 하다보면 개인정보 처리 방침이란 곳에서 막히게된다.
먼저 아래의 사이트에 들어가 개인정보 처리 방침에대한 관리 여부와 URL생성을 위한 작업을 진행해준다.
<a href="https://www.privacy.go.kr/a3sc/per/inf/perInfStep01.do">https://www.privacy.go.kr/a3sc/per/inf/perInfStep01.do</a></p>
<p>이후 자신의 블로그나 개인 URL에 생성한 개인정보 처리방침HTML을 업로드 해둔URL 을 개인정보 처리방침 URL에 등록해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TodaySmileWide 개인정보처리방침]]></title>
            <link>https://velog.io/@wamt_2787/TodaySmileWide-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%B9%A8</link>
            <guid>https://velog.io/@wamt_2787/TodaySmileWide-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EB%B0%A9%EC%B9%A8</guid>
            <pubDate>Sun, 27 Nov 2022 09:22:58 GMT</pubDate>
            <description><![CDATA[<p>&lt; AlphaNo &gt;(&#39;<a href="https://velog.io/@wamt_2787&#39;%EC%9D%B4%ED%95%98">https://velog.io/@wamt_2787&#39;이하</a> &#39;TodaySmileWide&#39;)은(는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다.
○ 이 개인정보처리방침은 2022년 1월 1부터 적용됩니다.
제1조(개인정보의 처리 목적)
&lt; AlphaNo &gt;(&#39;<a href="https://velog.io/@wamt_2787&#39;%EC%9D%B4%ED%95%98">https://velog.io/@wamt_2787&#39;이하</a> &#39;TodaySmileWide&#39;)은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다.</p>
<ol>
<li>홈페이지 회원가입 및 관리</li>
</ol>
<p>회원 가입의사 확인 목적으로 개인정보를 처리합니다.</p>
<p>제2조(개인정보의 처리 및 보유 기간)</p>
<p>① &lt; AlphaNo &gt;은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다.</p>
<p>② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.</p>
<p>1.&lt;홈페이지 회원가입 및 관리&gt;
&lt;홈페이지 회원가입 및 관리&gt;와 관련한 개인정보는 수집.이용에 관한 동의일로부터&lt;지체없이 파기&gt;까지 위 이용목적을 위하여 보유.이용됩니다.
보유근거 : 없음
관련법령 :
예외사유 :</p>
<p>제3조(처리하는 개인정보의 항목)</p>
<p>① &lt; AlphaNo &gt;은(는) 다음의 개인정보 항목을 처리하고 있습니다.</p>
<p>1&lt; 홈페이지 회원가입 및 관리 &gt;
필수항목 : 생년월일
선택항목 :</p>
<p>제4조(개인정보의 파기절차 및 파기방법)</p>
<p>① &lt; AlphaNo &gt; 은(는) 개인정보 보유기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다.</p>
<p>② 정보주체로부터 동의받은 개인정보 보유기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다.</p>
<ol>
<li>법령 근거 :</li>
<li>보존하는 개인정보 항목 : 계좌정보, 거래날짜</li>
</ol>
<p>③ 개인정보 파기의 절차 및 방법은 다음과 같습니다.</p>
<ol>
<li><p>파기절차
&lt; AlphaNo &gt; 은(는) 파기 사유가 발생한 개인정보를 선정하고, &lt; AlphaNo &gt; 의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다.</p>
</li>
<li><p>파기방법</p>
</li>
</ol>
<p>전자적 파일 형태의 정보는 기록을 재생할 수 없는 기술적 방법을 사용합니다</p>
<p>제5조(정보주체와 법정대리인의 권리·의무 및 그 행사방법에 관한 사항)</p>
<p>① 정보주체는 AlphaNo에 대해 언제든지 개인정보 열람·정정·삭제·처리정지 요구 등의 권리를 행사할 수 있습니다.</p>
<p>② 제1항에 따른 권리 행사는AlphaNo에 대해 「개인정보 보호법」 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 AlphaNo은(는) 이에 대해 지체 없이 조치하겠습니다.</p>
<p>③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다.이 경우 “개인정보 처리 방법에 관한 고시(제2020-7호)” 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.</p>
<p>④ 개인정보 열람 및 처리정지 요구는 「개인정보 보호법」 제35조 제4항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다.</p>
<p>⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다.</p>
<p>⑥ AlphaNo은(는) 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다.</p>
<p>제6조(개인정보의 안전성 확보조치에 관한 사항)</p>
<p>&lt; AlphaNo &gt;은(는) 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.</p>
<ol>
<li>개인정보에 대한 접근 제한
개인정보를 처리하는 데이터베이스시스템에 대한 접근권한의 부여,변경,말소를 통하여 개인정보에 대한 접근통제를 위하여 필요한 조치를 하고 있으며 침입차단시스템을 이용하여 외부로부터의 무단 접근을 통제하고 있습니다.</li>
</ol>
<p>제7조(개인정보를 자동으로 수집하는 장치의 설치·운영 및 그 거부에 관한 사항)</p>
<p>AlphaNo 은(는) 정보주체의 이용정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용하지 않습니다.</p>
<p>제8조(행태정보의 수집·이용·제공 및 거부 등에 관한 사항)</p>
<p>① &lt;개인정보처리자&gt;은(는) 서비스 이용과정에서 정보주체에게 최적화된 맞춤형 서비스 및 혜택, 온라인 맞춤형 광고 등을 제공하기 위하여 행태정보를 수집·이용하고 있습니다.</p>
<p>② &lt;개인정보처리자&gt;은(는) 다음과 같이 행태정보를 수집합니다.</p>
<ol start="8">
<li>행태정보의 수집·이용·제공 및 거부 등에 관한 사항 제공을 위해 수집하는 행태정보의 항목, 행태정보 수집 방법, 행태정보 수집 목적, 보유·이용기간 및 이후 정보처리 방법을 입력하기 위한 표입니다.
수집하는 행태정보의 항목    행태정보 수집 방법    행태정보 수집 목적    보유·이용기간 및 이후 정보처리 방법
&lt;온라인 맞춤형 광고 등을 위해 제3자(온라인 광고사업자 등)가 이용자의 행태정보를 수집·처리할수 있도록 허용한 경우&gt;
③ &lt;개인정보처리자&gt;은(는) 다음과 같이 온라인 맞춤형 광고 사업자가 행태정보를 수집·처리하도록 허용하고 있습니다.</li>
</ol>
<ul>
<li><p>행태정보를 수집 및 처리하려는 광고 사업자 : ○○○, ○○○, ○○○, ○○○,</p>
</li>
<li><p>행태정보 수집 방법 : 이용자가 당사 웹사이트를 방문하거나 앱을 실행할 때 자동 수집 및 전송</p>
</li>
<li><p>수집·처리되는 행태정보 항목 : 이용자의 웹/앱 방문이력, 검색이력, 구매이력</p>
</li>
<li><p>보유·이용기간 : 00일</p>
</li>
</ul>
<p>④ &lt;개인정보처리자&gt;은(는) 온라인 맞춤형 광고 등에 필요한 최소한의 행태정보만을 수집하며, 사상, 신념, 가족 및 친인척관계, 학력·병력, 기타 사회활동 경력 등 개인의 권리·이익이나 사생활을 뚜렷하게 침해할 우려가 있는 민감한 행태정보를 수집하지 않습니다.
⑤ &lt;개인정보처리자&gt;은(는) 만 14세 미만임을 알고 있는 아동이나 만14세 미만의 아동을 주 이용자로 하는 온라인 서비스로부터 맞춤형 광고 목적의 행태정보를 수집하지 않고, 만 14세 미만임을 알고 있는 아동에게는 맞춤형 광고를 제공하지 않습니다.</p>
<p>⑥ &lt;개인정보처리자&gt;은(는) 모바일 앱에서 온라인 맞춤형 광고를 위하여 광고식별자를 수집·이용합니다. 정보주체는 모바일 단말기의 설정 변경을 통해 앱의 맞춤형 광고를 차단·허용할 수 있습니다.</p>
<p>‣ 스마트폰의 광고식별자 차단/허용</p>
<p>(1) (안드로이드) ① 설정 → ② 개인정보보호 → ③ 광고 → ③ 광고 ID 재설정 또는 광고ID 삭제</p>
<p>(2) (아이폰) ① 설정 → ② 개인정보보호 → ③ 추적 → ④ 앱이 추적을 요청하도록 허용 끔</p>
<p>※ 모바일 OS 버전에 따라 메뉴 및 방법이 다소 상이할 수 있습니다.</p>
<p>⑦ 정보주체는 웹브라우저의 쿠키 설정 변경 등을 통해 온라인 맞춤형 광고를 일괄적으로 차단·허용할 수 있습니다. 다만, 쿠키 설정 변경은 웹사이트 자동로그인 등 일부 서비스의 이용에 영향을 미칠 수 있습니다.</p>
<p>‣ 웹브라우저를 통한 맞춤형 광고 차단/허용</p>
<p>(1) 인터넷 익스플로러(Windows 10용 Internet Explorer 11)</p>
<ul>
<li><p>Internet Explorer에서 도구 버튼을 선택한 다음 인터넷 옵션을 선택</p>
</li>
<li><p>개인 정보 탭을 선택하고 설정에서 고급을 선택한 다음 쿠키의 차단 또는 허용을 선택</p>
</li>
</ul>
<p>(2) Microsoft Edge</p>
<ul>
<li><p>Edge에서 오른쪽 상단 ‘…’ 표시를 클릭한 후, 설정을 클릭합니다.</p>
</li>
<li><p>설정 페이지 좌측의 ‘개인정보, 검색 및 서비스’를 클릭 후 「추적방지」 섹션에서 ‘추적방지’ 여부 및 수준을 선택합니다.</p>
</li>
<li><p>‘InPrivate를 검색할 때 항상 &quot;&quot;엄격&quot;&quot; 추적 방지 사용’ 여부를 선택합니다.</p>
</li>
<li><p>아래 「개인정보」 섹션에서 ‘추적 안함 요청보내기’ 여부를 선택합니다.</p>
</li>
</ul>
<p>(3) 크롬 브라우저</p>
<ul>
<li><p>Chrome에서 오른쪽 상단 ‘⋮’ 표시(chrome 맞춤설정 및 제어)를 클릭한 후, 설정 표시를 클릭합니다.</p>
</li>
<li><p>설정 페이지 하단에 ‘고급 설정 표시’를 클릭하고 「개인정보」 섹션에서 콘텐츠 설정을 클릭합니다.</p>
</li>
<li><p>쿠키 섹션에서 ‘타사 쿠키 및 사이트 데이터 차단’의 체크박스를 선택합니다.</p>
</li>
</ul>
<p>52 | 개인정보 처리방침 작성지침 일반</p>
<p>⑧ 정보주체는 아래의 연락처로 행태정보와 관련하여 궁금한 사항과 거부권 행사, 피해 신고 접수 등을 문의할 수 있습니다.</p>
<p>‣ 개인정보 보호 담당부서</p>
<p>부서명 : ○○○ 팀</p>
<p>담당자 : ○○○</p>
<p>연락처 : &lt;전화번호&gt;, &lt;이메일&gt;, &lt;팩스번호&gt;</p>
<p>제9조(추가적인 이용·제공 판단기준)</p>
<p>&lt; AlphaNo &gt; 은(는) ｢개인정보 보호법｣ 제15조제3항 및 제17조제4항에 따라 ｢개인정보 보호법 시행령｣ 제14조의2에 따른 사항을 고려하여 정보주체의 동의 없이 개인정보를 추가적으로 이용·제공할 수 있습니다.
이에 따라 &lt; AlphaNo &gt; 가(이) 정보주체의 동의 없이 추가적인 이용·제공을 하기 위해서 다음과 같은 사항을 고려하였습니다.
▶ 개인정보를 추가적으로 이용·제공하려는 목적이 당초 수집 목적과 관련성이 있는지 여부</p>
<p>▶ 개인정보를 수집한 정황 또는 처리 관행에 비추어 볼 때 추가적인 이용·제공에 대한 예측 가능성이 있는지 여부</p>
<p>▶ 개인정보의 추가적인 이용·제공이 정보주체의 이익을 부당하게 침해하는지 여부</p>
<p>▶ 가명처리 또는 암호화 등 안전성 확보에 필요한 조치를 하였는지 여부</p>
<p>※ 추가적인 이용·제공 시 고려사항에 대한 판단기준은 사업자/단체 스스로 자율적으로 판단하여 작성·공개함</p>
<p>제10조(가명정보를 처리하는 경우 가명정보 처리에 관한 사항)</p>
<p>&lt; AlphaNo &gt; 은(는) 다음과 같은 목적으로 가명정보를 처리하고 있습니다.</p>
<p>▶ 가명정보의 처리 목적</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>▶ 가명정보의 처리 및 보유기간</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>▶ 가명정보의 제3자 제공에 관한 사항(해당되는 경우에만 작성)</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>▶ 가명정보 처리의 위탁에 관한 사항(해당되는 경우에만 작성)</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>▶ 가명처리하는 개인정보의 항목</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>▶ 법 제28조의4(가명정보에 대한 안전조치 의무 등)에 따른 가명정보의 안전성 확보조치에 관한 사항</p>
<ul>
<li>직접작성 가능합니다.</li>
</ul>
<p>제11조 (개인정보 보호책임자에 관한 사항)</p>
<p>① AlphaNo 은(는) 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.</p>
<p>▶ 개인정보 보호책임자
성명 :이준호
직책 :학생
직급 :학생
연락처 :01026614393, <a href="mailto:gnflfh@gmail.com">gnflfh@gmail.com</a>,
※ 개인정보 보호 담당부서로 연결됩니다.</p>
<p>▶ 개인정보 보호 담당부서
부서명 :
담당자 :
연락처 :, ,
② 정보주체께서는 AlphaNo 의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. AlphaNo 은(는) 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다.</p>
<p>제12조(국내대리인의 지정)</p>
<p>정보주체는 ｢개인정보 보호법｣ 제39조의11에 따라 지정된 &lt; AlphaNo &gt;의 국내대리인에게 개인정보 관련 고충처리 등의 업무를 위하여 연락을 취할 수 있습니다. &lt; AlphaNo &gt;은(는) 정보주체의 개인정보 관련 고충처리 등 개인정보 보호책임자의 업무 등을 신속하게 처리할 수 있도록 노력하겠습니다.</p>
<p>▶ &lt; AlphaNo &gt; 은(는) ｢개인정보 보호법｣ 제39조의11에 따라 국내대리인을 지정하였습니다.</p>
<ul>
<li><p>국내대리인의 성명 : [대리인 성명_직접입력] (법인의 경우 법인명, 대표자의 성명)</p>
</li>
<li><p>국내대리인의 주소 : [대리인 주소_직접입력] (법인의 경우 영업소 소재지)</p>
</li>
<li><p>국내대리인의 전화번호 : [대리인 전화번호_직접입력]</p>
</li>
<li><p>국내대리인의 전자우편 주소 : [대리인 전자우편_직접입력]</p>
</li>
</ul>
<p>제13조(개인정보의 열람청구를 접수·처리하는 부서)
정보주체는 ｢개인정보 보호법｣ 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다.
&lt; AlphaNo &gt;은(는) 정보주체의 개인정보 열람청구가 신속하게 처리되도록 노력하겠습니다.</p>
<p>▶ 개인정보 열람청구 접수·처리 부서
부서명 :
담당자 :
연락처 : , ,</p>
<p>제14조(정보주체의 권익침해에 대한 구제방법)</p>
<p>정보주체는 개인정보침해로 인한 구제를 받기 위하여 개인정보분쟁조정위원회, 한국인터넷진흥원 개인정보침해신고센터 등에 분쟁해결이나 상담 등을 신청할 수 있습니다. 이 밖에 기타 개인정보침해의 신고, 상담에 대하여는 아래의 기관에 문의하시기 바랍니다.</p>
<ol>
<li>개인정보분쟁조정위원회 : (국번없이) 1833-6972 (<a href="http://www.kopico.go.kr">www.kopico.go.kr</a>)</li>
<li>개인정보침해신고센터 : (국번없이) 118 (privacy.kisa.or.kr)</li>
<li>대검찰청 : (국번없이) 1301 (<a href="http://www.spo.go.kr">www.spo.go.kr</a>)</li>
<li>경찰청 : (국번없이) 182 (ecrm.cyber.go.kr)</li>
</ol>
<p>「개인정보보호법」제35조(개인정보의 열람), 제36조(개인정보의 정정·삭제), 제37조(개인정보의 처리정지 등)의 규정에 의한 요구에 대 하여 공공기관의 장이 행한 처분 또는 부작위로 인하여 권리 또는 이익의 침해를 받은 자는 행정심판법이 정하는 바에 따라 행정심판을 청구할 수 있습니다.</p>
<p>※ 행정심판에 대해 자세한 사항은 중앙행정심판위원회(<a href="http://www.simpan.go.kr">www.simpan.go.kr</a>) 홈페이지를 참고하시기 바랍니다.</p>
<p>제15조(영상정보처리기기 운영·관리에 관한 사항)
① &lt; AlphaNo &gt;은(는) 아래와 같이 영상정보처리기기를 설치·운영하고 있습니다.</p>
<p>1.영상정보처리기기 설치근거·목적 : &lt; AlphaNo &gt;의</p>
<p>2.설치 대수, 설치 위치, 촬영 범위 :
설치대수 : 대
설치위치 :
촬영범위 :
3.관리책임자, 담당부서 및 영상정보에 대한 접근권한자 :</p>
<p>4.영상정보 촬영시간, 보관기간, 보관장소, 처리방법
촬영시간 : 시간
보관기간 : 촬영시부터
보관장소 및 처리방법 :
5.영상정보 확인 방법 및 장소 :</p>
<p>6.정보주체의 영상정보 열람 등 요구에 대한 조치 : 개인영상정보 열람.존재확인 청구서로 신청하여야 하며, 정보주체 자신이 촬영된 경우 또는 명백히 정보주체의 생명.신체.재산 이익을 위해 필요한 경우에 한해 열람을 허용함</p>
<p>7.영상정보 보호를 위한 기술적.관리적.물리적 조치 :</p>
<p>제16조(개인정보 처리방침 변경)</p>
<p>① 이 개인정보처리방침은 2022년 1월 1부터 적용됩니다.</p>
<p>② 이전의 개인정보 처리방침은 아래에서 확인하실 수 있습니다.</p>
<p>예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)</p>
<p>예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)</p>
<p>예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ToyProject_앱 개발(React)_TSW]]></title>
            <link>https://velog.io/@wamt_2787/ToyProject%EC%95%B1-%EA%B0%9C%EB%B0%9CReactTSW</link>
            <guid>https://velog.io/@wamt_2787/ToyProject%EC%95%B1-%EA%B0%9C%EB%B0%9CReactTSW</guid>
            <pubDate>Sat, 05 Nov 2022 05:32:36 GMT</pubDate>
            <description><![CDATA[<h1 id="목차">목차</h1>
<ol>
<li>앱소개</li>
<li></li>
</ol>
<h1 id="오늘의-싱글벙글-todaysmilewide">오늘의 싱글벙글 (TodaySmileWide)</h1>
<blockquote>
<h1 id="앱소개">앱소개</h1>
<p>친구들과 앱 으로 만든 ToyProject 를 개발하기로 했다. 앱 개발은 대학교 과제로 만든 Android Stuodio 기반으로 코로나&amp;날씨 현황 앱 말고는 만들어 본적이 없고 React Native도 익숙한 언어가 아니기 때문에 배운다는 마음으로 임했다.목표 개발기간 12일 (6주간의 주말만)</p>
</blockquote>
<h2 id="주제">주제</h2>
<ul>
<li>이 각박한 세상에서 피식하는 웃음을 줄 수 있는 뇌빼고 볼 수 있는 웃음 커뮤니티</li>
</ul>
<h2 id="기능">기능</h2>
<ul>
<li>(백그라운드) 푸시알림으로 밈 던져주기</li>
<li>싱글벙글 지구촌 갤러리나 giphy에서 가져온 인기 컨텐츠를 랜덤으로 가져와 추천한다.</li>
<li>(포그라운드) DC인사이드 싱글벙글 지구촌 갤러리를 타겟으로 잡아 앱에 접속하면 페북에 팩트저장소나 배드마우스 사이트처럼 웃긴 컨텐츠를 볼 수있다.</li>
<li>각자 일이 있으니까 방대하게 기능을 설계하면 뇌절할 수 있음. 먼저 이정도까지만 구현해놓고 결과물을 보고 추가적인 개발 공수를 잡고 기획을 잡고, 추후 고도화를 해보는걸로.</li>
</ul>
<p><strong>주요 스토리보드</strong></p>
<ol>
<li><p>백앤드에서 DC 싱글벙글 갤러리 개념글  크롤링</p>
<ul>
<li>비공식 DC인사이드 API를 사용해. 힛갤추천글, 글조회, 작성, 수정, 삭제 기능을 가져온다.</li>
<li>긁어온  컨텐츠  내용을 DB저장해 사용한다.ㄱ</li>
<li>매  요청마다  크롤링 하면 비용적인 문제가 발생 Redis 캐싱이 아니면 문제가 발생</li>
<li>하루한번  크롤링  후  저장, 이후  저장한  컨텐츠들만  응답으로  던져주자.</li>
</ul>
</li>
<li><p>크롤링한  게시물  중  가장  인기가  있는  컨텐츠를  하루에  한번  푸시메시지로  날림</p>
</li>
<li><p>파이어베이스  클라우드  메시지  사용하면  무료로  구현  가능</p>
</li>
<li><p>프론트에서는  이런  푸시알림을  보고  접속해  크롤링해서  긁어온  다른  게시물들을  볼  수  있다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring_이해하기
(회원 관리 예제 - 백엔드 개발)]]></title>
            <link>https://velog.io/@wamt_2787/Spring-%EC%9E%85%EB%AC%B8-4%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@wamt_2787/Spring-%EC%9E%85%EB%AC%B8-4%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 03 Feb 2022 08:21:04 GMT</pubDate>
            <description><![CDATA[<h1 id="회원-관리-예제---백엔드-개발">회원 관리 예제 - 백엔드 개발</h1>
<blockquote>
<ul>
<li><strong>비즈니스 요구사항 정리</strong></li>
</ul>
</blockquote>
<ul>
<li><strong>회원 도메인과 리포지토리 만들기</strong></li>
<li><strong>회원 서비스 개발</strong></li>
<li><strong>회원 리포지토리 테스트 케이스 작성</strong></li>
<li><strong>회원 서비스 테스트</strong></li>
</ul>
<h3 id="비즈니스-요구사항-정리">비즈니스 요구사항 정리</h3>
<blockquote>
</blockquote>
<ul>
<li><p><strong>데이터</strong>: 회원ID, 이름</p>
</li>
<li><p><strong>기능</strong>: 회원 등록, 조회</p>
<ul>
<li>아직 데이터 저장소가 선정되지 않음(가상의 시나리오)</li>
</ul>
<h3 id="웹-애플리케이션-계층-구조">웹 애플리케이션 계층 구조</h3>
<blockquote>
<p><img src="https://images.velog.io/images/wamt_2787/post/8d57015b-2db4-4607-88bc-f4dbdab60ffd/image.png" alt=""></p>
</blockquote>
<ul>
<li><strong>컨트롤러</strong>: 웹 MVC의 컨트롤러 역할</li>
<li><strong>서비스</strong>: 핵심 비즈니스 로직 구현</li>
<li><strong>리포지토리</strong>: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리</li>
<li><strong>도메인</strong>: 비즈니스 도메인 객체, 예)회원,주문,쿠폰 등등 주로 데이터베이스에 저장하고 관리됨</li>
</ul>
</li>
</ul>
<hr>
<h1 id="code">Code</h1>
<h2 id="1-회원-도메인과-레포지토리-만들기">1. 회원 도메인과 레포지토리 만들기</h2>
<ul>
<li>요구사항인 Id(식별자), 이름 두가지가 들어간 Member클래스 생성</li>
<li>회원 레포지토리(정보 저장소)의 인터페이스를 만든다 4가지 기능 선언.</li>
<li>회원 레포지토리를 만들어 인터페이스를 가져온다. 각 메소드의 기능을 구현해준다.</li>
</ul>
<blockquote>
<h4 id="member">Member</h4>
</blockquote>
<pre><code class="language-java">    // domain(Pa)&gt;Member
   public class Member {

    private Long id;
    private String name;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}</code></pre>
<blockquote>
<h4 id="memberrepository-interface">MemberRepository (interface)</h4>
</blockquote>
<pre><code class="language-java">    // repository&gt;MemberRepository
   public interface MemberRepository {
   Member Save(Member member);
   Optional&lt;Member&gt; findById(Long id);
   Optional&lt;Member&gt; findByName(String name);
   List(Member&gt; findAll();
   //😳Optional 이란 findBy로 가져오는 값이 null일 경우의 반환방법😳
   //저장,아이디 찾기,이름 찾기,회원 전부 보여주기</code></pre>
<blockquote>
<h4 id="memorymemberrepository">MemoryMemberRepository</h4>
</blockquote>
<pre><code class="language-java">  //repository&gt;Memory&gt;MemberRepository
   private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;(); 
    private static long sequence = 0L; 
    // 😳store 라는 이름으로 HashMap 객체를 생성 sequence 는 키값을 생성해주는 존재😳

    @Override
    public Member save(Member member) {
        member.setId(++sequence);   
        store.put(member.getId(), member);
        return member;
    } // 😳저장할때 sequence 를 전위증가 해줘서 키값 다르게 생성😳


    @Override
    public Optional&lt;Member&gt; findById(Long id) {
        return Optional.ofNullable(store.get(id));
    } // 😳위에 말했듯이 Oprtional 를 사용해 id값이 null 일 경우를 대체할수있다.😳


    @Override
    public Optional&lt;Member&gt; findByName(String name) {
      return store.values().stream()
                .filter(member -&gt; member.getName().equals(name))
                .findAny();
    }// 😳store 에서 member.getName이 들어온 name과 같는지 확인하며 루프를 돈다 같은경우 필터링😳


    @Override
    public List&lt;Member&gt; findALl() {
        return new ArrayList&lt;&gt;(store.values());
    }
}    // 😳store    에 있는 멤버들을 반환😳

 public void clearStore() {
        store.clear();
        //😳AfterEach 로 사용할 ClearStore 작성😳</code></pre>
<h2 id="2-회원-레포지토리-테스트케이스-작성">2. 회원 레포지토리 테스트케이스 작성</h2>
<ul>
<li>개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 컨트롤러를 통해서 해당 기능을 실행한다. 이러한 방법은 준비와 실행 자체가 오래걸리고 여러번 반복실행을 테스트 할수없기에 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.</li>
</ul>
<blockquote>
<h4 id="memorymemberrepositorytest">MemoryMemberRepository.Test</h4>
</blockquote>
<pre><code class="language-java"> //test&gt;repository&gt;MemoryMemberRepository.Test
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
//😳Assertions 생략 할수있음 원래는 아래처럼 사용해야함
//Assertions.assertThat(member).isEqualTo(result);😳
public class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach() {
        repository.clearStore();
    }//😳call-back 할수있는 After Each를 사용해 메소드가 끝날떄 clearStore호출😳

    @Test
    public void save() {
        Member member = new Member();
        member.setName(&quot;spring&quot;);

        repository.save(member);

        Member result = repository.findById(member.getId()).get();
        assertThat(member).isEqualTo(result);
        //😳값이 일치하는 지 확인😳
    }

    @Test
    public void findByName() {
        Member member1 = new Member();
        member1.setName(&quot;Spring1&quot;);
        repository.save(member1);

        Member member2 = new Member();
        member2.setName(&quot;Spring2&quot;);
        repository.save(member2);

        Member result = repository.findByName(&quot;Spring1&quot;).get();
        assertThat(result).isEqualTo(member1);
    }//😳값이 일치하는 지 확인😳

    @Test
    public void findAll() {
        Member member1 = new Member();
        member1.setName(&quot;Spring1&quot;);
        repository.save(member1);

        Member member2 = new Member();
        member2.setName(&quot;Spring2&quot;);
        repository.save(member2);

        List&lt;Member&gt; result = repository.findALl();
        assertThat(result.size()).isEqualTo(2);
    }//😳값이 일치하는 지 확인 member 숫자 변경시 숫자변경😳
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA _CODE - Basic ]]></title>
            <link>https://velog.io/@wamt_2787/JAVA-CDE-Basic</link>
            <guid>https://velog.io/@wamt_2787/JAVA-CDE-Basic</guid>
            <pubDate>Sat, 22 Jan 2022 09:33:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="자바의-기초-code-형식">자바의 기초 Code 형식</h2>
</blockquote>
<pre><code class="language-java">public class javava {
   public static void main(String [] args) {
      // 구현코드 작성
             👇👇👇👇👇👇
접근제어자 클래스선언 클래스이름 {
   접근제어자 static 반환타입 메서드이름(파라미터)   {
      // 구현코드 작성</code></pre>
<blockquote>
<h2 id="class-😒">Class 😒</h2>
</blockquote>
<p><code>클래스(class)</code>란 객체를 정의하는 <strong>틀</strong> 또는 <strong>설계도</strong>와 같은 의미로 사용됩니다.자바에서는 설계도인 <code>class</code>를 가지고, 여러 객체를 생성하여 사용하게 된다.클래스는 객체의 상태를 나타내는 <code>변수(variable)</code> 와 객체의 행동을 나타내는 <code>메소드(method)</code>로 구성된다.</p>
<blockquote>
<h2 id="method-😳">Method 😳</h2>
</blockquote>
<p><code>메서드(method)</code>란 어떠한 특정 작업을 수행하기 위한 명령문의 집합이라 할 수 있다.클래스에서 메서드를 작성하여 사용하는 이유는 중복되는 코드의 반복적인 프로그래밍을 피할 수 있기 때문이다.또한,모듈화로 인해 코드의 가독성도 좋아진다.프로그램에 문제가 발생하거나 기능의 변경이 필요할 때도 손쉽게 유지보수를 할 수 있다.
메서드는 <code>접근 제어자, 리턴값, 메서드 이름 (인자, 인자2, ...)</code>의 형태로 선언한다. <code>중괄호 {}</code> 안에는 이 메서드가 수행할 동작을 선언한다
메서드를 만들려면 클래스가 있어야 한다. 클래스는 메서드와 변수라는 2가지 속성을 갖는다.</p>
<blockquote>
<h2 id="instance-👍🏻">instance 👍🏻</h2>
</blockquote>
<ol>
<li>자바에서 <code>class</code>를 사용하려면 해당 클래스 타입의 <code>객체</code>를 선언해야 한다.</li>
<li><code>class</code>로부터 <code>객체</code>를 선언하는 과정을 클래스의 인스턴스 화라고 한다. </li>
<li>인스턴스 화로 선언된 해당 클래스 타입의 객체를 <code>instance</code>라고 한다.        </li>
<li><code>instance</code>란 메모리에 할당된 <code>객체</code>를 의미한다.</li>
<li><strong>하나의 <code>Class</code>로부터 여러 개의 <code>Instance</code>를 생성</strong>할 수 있다.</li>
<li><code>instance</code>는 독립된 메모리 공간에 저장된 자신만의 필드 를 가질 수 있다.</li>
<li>해당 클래스의 모든 메소드(method)는 해당 클래스에서 생성된 모든 인스턴스가 공유하게 된다. </li>
</ol>
<blockquote>
<h2 id="접근-제어자-👿">접근 제어자 👿</h2>
</blockquote>
<ol>
<li><code>public</code> <code>private</code> <code>protected</code> <code>default</code>  가 있다.</li>
<li>클래스나 메서드에 접근할 수 있는 범위를 지정한다.</li>
<li><code>private</code> &gt; <code>protected</code> &gt; <code>public</code> 순서로 범위</li>
</ol>
<blockquote>
<h2 id="static-🤣"><strong>Static</strong> 🤣</h2>
</blockquote>
<ol>
<li>객체 생성없이 해당 함수(메서드)를 호출해서 사용할 수 있다.</li>
<li>프로그램을 실행하면 <code>static</code>으로 지정된 메서드를 찾아서 먼저 메모리에 할당</li>
<li><code>static</code>으로 지정된 메서드가 여러개인 경우에는 객체를 생성하는 것과 상관없이 모두 메모리에 할당시키고 <code>main</code>으로 이름이 만들어진 메서드가 있는지 찾아서 그 메서드를 가장 먼저 시작점의 메서드로써 호출을 하게 된다.</li>
</ol>
<blockquote>
<h2 id="변수🙈">변수🙈</h2>
<p>변수란 쉽게말해 <strong>바구니</strong> 다. 데이터를 <strong>저장</strong> 하는 메모리 공간 
변수를 사용하기 위해서는 먼저 변수의 타입에 맞는 선언을 해줘야 한다.</p>
</blockquote>
<pre><code class="language-java">    int a; int b; //-- 정수가 저장될 변수 이름을 a, b로 만들어라 --;;
        a = 3; b = 5;</code></pre>
<blockquote>
<h2 id="데이터-타입">데이터 타입</h2>
</blockquote>
<h3 id="기본형-타입primitive-data-type">기본형 타입(Primitive Data Type)</h3>
<ol>
<li>정수형 --&gt; <code>byte(1byte)</code> <code>short(2byte)</code> <code>int(4byte)</code> <code>long(8)</code></li>
<li>실수형 --&gt; <code>float(4byte)</code>  <code>double(8byte)</code></li>
<li>문자형 --&gt; <code>char(2byt)</code></li>
<li>논리형 --&gt; <code>boolean(1byte)</code> = true &amp; false<h3 id="참조형-타입reference-data-type">참조형 타입(Reference Data Type</h3>
</li>
</ol>
<ul>
<li>기본형에 속하지 않는 데이터형들을 말한다.</li>
<li>대표적으로 <code>class</code>  <code>array</code> <code>interface</code> <code>string</code> 이 있다</li>
<li>데이터가 저장된 메모리의 주소 값을 저장하는 변수이다.</li>
</ul>
<blockquote>
<h2 id="출력">출력</h2>
</blockquote>
<h3 id="printf">Printf</h3>
<p> <code>%d (정수)</code> <code>%f (소숫점 형식)</code> <code>%c(문자)</code> <code>%s(문자열)</code> 
 <code>%b(부울)</code> <code>%n(줄바꿈)</code> <code>%e(지수)</code> <code>%o(8진수)</code> <code>%x(16진수)</code>
 위 와같은 지시자를 사용해서 출력을 나타낼수있다.</p>
<pre><code class="language-java"> System.out.println(&quot;나는 %d살 입니다.%d,%d,%d&quot;,d,s,i,l);</code></pre>
   <img src="https://images.velog.io/images/wamt_2787/post/52b93f6c-4e6b-4b30-b757-14132b137f8f/image.png" height="50px" width="190px" align="center">

<p>해당 어노테이션이 적용된 클래스는 <code>Controller</code>임을 나타나고 <code>bean</code>으로 등록되며 해당 클래스가 <code>Controller</code>로 사용됨을 <strong>Spring Framework</strong>에 알립니다.</p>
<img  src="https://images.velog.io/images/wamt_2787/post/9bf1dd44-d119-4aec-a73c-22c061f79064/image.png" height="50px" width="300px" align="center"> 

<p>Spring 컨트롤러의 URI 메소드를 <code>GET</code> 방식으로 가져옵니다. 해당 어노테이션은 <code>@RequestMapping(method=RequestMethod.GET, value=..)</code> 의 축약형 표현입니다.</p>
<img src="https://images.velog.io/images/wamt_2787/post/c5e7746c-53ad-40fc-8c0b-06c0a30f6253/image.png" width="200px">

<p>해당 어노테이션이 되어 있다면 메소드에서 리턴되는 값은 <code>View</code> 를 통해서 
출력되지 않고 <code>HTTP Response Body</code> 에 직접 쓰여지게 됩니다.
이때 쓰여지기 전에 리턴되는 데이터 타입에 따라 MessageConverter 에서 변환이 이뤄진 후 쓰여지게 됩니다.</p>
   <img       src="https://images.velog.io/images/wamt_2787/post/39ded952-f097-4996-83d5-0c42f6d9c11f/image.png" align="center">

   <img src="https://images.velog.io/images/wamt_2787/post/0ddd5545-964c-4f68-aa5c-d634eb1f4aec/image.png" align="center">]]></description>
        </item>
        <item>
            <title><![CDATA[Spring_이해하기
(웹 개발 기초 3가지)]]></title>
            <link>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B03</link>
            <guid>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B03</guid>
            <pubDate>Fri, 21 Jan 2022 10:15:07 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-웹-개발-기초-3가지-방법">스프링 웹 개발 기초 3가지 방법</h1>
<ul>
<li>정적컨텐츠</li>
<li>MVC와 템플릿 엔진</li>
<li>API</li>
</ul>
<hr>
<h2 id="정적-컨텐츠">정적 컨텐츠</h2>
<p>** 파일을 그대로 웹 브라우저에게 전송 해주는 방식.** (static 폴더에 넣은 그대로 출력)</p>
<blockquote>
<h3 id="동작-원리">동작 원리</h3>
<p><img src="https://images.velog.io/images/wamt_2787/post/bbedfdf0-b216-4e24-801a-df51ed6c7b7c/image.png" alt=""></p>
</blockquote>
<pre><code> 1. 웹브라우저에서 hello-static.html 접근
 2. 내장 톰켓 서버 통과 -&gt; 스프링 컨테이너 에서 해당 관련 컨트롤러를 검색 
 3. 검색 없을시에 resources 내부에서 찾아서 반환</code></pre><hr>
<h2 id="mvc와-템플릿-엔진">MVC와 템플릿 엔진</h2>
<h3 id=""></h3>
<p>MVC 패턴은 Model,View,Conroller 3가지로 나뉘어 역할을 분할하여 처리하는 패턴이다 MVC 패턴이 나온 이유는 코드를 분리하지 않고 View 에서 모든 내용을 넣으면 가독성이 떨어지고 유지 보수가 힘들어지기 때문이다.</p>
<ul>
<li>Controller = 사용자가 접근한 URL에 요청을 파악해 맞는 Method를 호출하여 Service와 Business Logic 을처리한다 나온 결과는 Model에 저장하여 View 로 던져준다.</li>
<li>Model = Controller에서 받은 데이터를 저장하는 역할</li>
<li>View = Controller로 부터 받은 Model 데이터를 바탕으로 사용자에게 표현해준다.(HTML,JSP에 해당한다.)
<img src="https://images.velog.io/images/wamt_2787/post/133dc407-fee4-4801-a312-e1d67d1d3d09/image.png" alt=""></li>
</ul>
<h3 id="템플릿-엔진이란">템플릿 엔진이란?</h3>
<p>웹 개발에 있어 템플릿 엔진이란, 지정된 템플릿 양식과 데이터가 합쳐져 HTML 문서를 출력하는 소프트웨어를 이야기한다. 예전 스프링이나 서블릿을 사용했다면 
JSP, Freemaker 등이 떠오를테고 요즘은 리액트, 뷰 등이 떠오를 것이다.이것들 모두 결과적으로는 지정된 템플릿과 데이터 를 이용하여 HTML을 생성하는 템플릿 엔진이다.</p>
<blockquote>
<h2 id="code">Code</h2>
<p> <strong>Controller</strong></p>
</blockquote>
<pre><code class="language-java">@GetMapping(&quot;hello-mvc&quot;)
    public String helloMvc(@RequestParam(&quot;name&quot;)String name, Model model) {
        model.addAttribute(&quot;name&quot;, name);
        return &quot;hello-template&quot;;
    }</code></pre>
<blockquote>
<p><strong>Templeate</strong></p>
</blockquote>
<pre><code class="language-html">&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;/&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;hello &#39; + ${name}&quot;&gt;hello! empty&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>https:localhost: 8080/study/hello-mvc<strong>?name=spring!!</strong></p>
<blockquote>
<h3 id="동작원리">동작원리</h3>
<p><img src="https://images.velog.io/images/wamt_2787/post/db23af8d-bbaf-4d19-9f32-59651a1d5a2a/image.png" alt=""></p>
</blockquote>
<pre><code>1. 웹브라우저에서 hello-mvc 접근
2. 내장 톰켓 서버 통과 -&gt; 스프링 컨테이너 에서 해당 관련 컨트롤러를 검색 
3. helloController가 Model에 name을 담아 hello-template를 반환
4. 반환된 정보로 View Resolver를 통해 hello-template.html을 찾아 연결 html 변환후 실행
  // 정적 컨텐츠와 다르게 변환해주는 원리</code></pre><hr>
<h2 id="api">API</h2>
<p>ResponseBody 를 사용하여 Templeate 엔진과 다르게 View Resolver를 사용하지않고 HTTP의 BODY 문자내용을 직접 반환할수있다. 
과거에는 xml 방식으로 API가 많이 사용되었다 하지만 요즘에는 Json 방식
으로 통일되는 느낌으로 가고있다 Json 에대한 내용은 아래에 있다.</p>
<blockquote>
<h2 id="code-1">Code</h2>
</blockquote>
<pre><code class="language-java"> @GetMapping(&quot;hello-api&quot;)
    @ResponseBody
    public Hello helloApi(@RequestParam(&quot;name&quot;) String name) {
        Hello hello = new Hello();
        hello.setName(name);
        return hello;
    }
    static class Hello {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}</code></pre>
<p>문자가 아닌 객체를 넘겼을때 아래와 같은 출력이 나오는데
 <code>{&quot;name&quot;:&quot;spring!!!!!!&quot;}</code>
 이는 json 방식으로 key-value 로 이루어진 구조이다.</p>
<blockquote>
<h3 id="동작원리-1">동작원리</h3>
<p><img src="https://images.velog.io/images/wamt_2787/post/90cf57b2-71b1-45c4-ba23-36a7a10a54b5/image.png" alt=""></p>
</blockquote>
<pre><code>1. 웹브라우저에서 hello-api 접근
2. 내장 톰켓 서버 통과 -&gt; 스프링 컨테이너 에서 해당 관련 컨트롤러를 검색 
3. helloController가 @ResponseBody라는 에너테이션을 만남
4. ViewResolve 대신 HttpMessageConverter 가 동작
5. String 이면 Http동작에 그대로 Bdoy 를 넘기고 객체이면 Json방식으로 만들어서 반환

---
## &lt;span style=&quot;color: orange&quot;&gt;Reference&lt;/span&gt;
</code></pre><p>김영한 강사님의 Spring 입문 강의를 토대로 복습하기위한 글 이며 벨로그 글 
강의를 들은 다른분들의 학습내용이 포함되어 있습니다
해당하는 모든 출처는 링크와 강의에 있습니다.</p>
<pre><code>[블로그 링크](https://anaog.tistory.com/1)
[강의 링크](https://www.inflearn.com/course/스프링-핵심-원리-기본편)
[나무위키 링크](https://namu.wiki/w/Spring(프레임워크))</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Spring_이해하기 (환경설정)]]></title>
            <link>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B02</link>
            <guid>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B02</guid>
            <pubDate>Wed, 19 Jan 2022 08:07:42 GMT</pubDate>
            <description><![CDATA[<h2 id="springboot-설정">SpringBoot 설정</h2>
<blockquote>
<p>스프링 부트를 사용하기 위해선 아래의 링크를 접속해 설정을 먼저 해주어야한다.</p>
</blockquote>
<h2 id="spring-initializr"><strong><a href="https://start.spring.io/">Spring initializr</a></strong></h2>
<blockquote>
<h3 id="설정">설정</h3>
</blockquote>
<ol>
<li><strong>Maven 과 Gradle 의 선택?</strong>
<code>Maven</code>은 프로젝트의 전체적인 라이프사이클을 관리하기 위한 도구이며, 많은 편리함과 이점으로 인해 사용량이 많다 하지만 비교적 최근에 나온 <code>Gradle</code> 의
등장은 <code>Maven</code> 의 단점을 보완하였는데 속도와 관련된 내용도 있지만 <code>Groovy</code> 같은 스크립트로 플러그인의 관리를 할수있어 많이 사용하는 추세이다.<ol start="2">
<li><strong>Dependencies</strong>
어떤 라이브러리를 가져올지 정하는 항목이다.
강의 실습에서는 web 설계를 하기 때문에 <code>Spring Web</code> 라이브러리와
웹에서 <code>Html</code> 을 만들어주는 <code>templeate</code> 엔진이 필요하기 때문에
<code>Thymeleaf</code> 라는 라이브러리도 가져와 준다.</li>
</ol>
</li>
</ol>
<hr>
<p>설정이 끝났으면 Generate 버튼을 눌러주고 다운로드 받아준다.
이후 인텔리제이 에서 Open 해주면 기본 설정이 끝난 Spring Boot 가 준비된거다.</p>
<h2 id="만들어진-프로젝트-살펴보기">만들어진 프로젝트 살펴보기</h2>
<blockquote>
<p><strong>build.gradle</strong></p>
</blockquote>
<pre><code class="language-java"> dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-thymeleaf&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}</code></pre>
<p><strong>Spring Application</strong>
 기본으로 제공되는 SpringBoot 실행을 위한 동작 어플
 <image src="https://images.velog.io/images/wamt_2787/post/09f610e1-2c06-41ff-994a-8dfb2f3183db/image.png" width=60% ></p>
<pre><code class="language-java">@SpringBootApplication
public class LeehoSpringApplication {
    public static void main(String[] args) {
    SpringApplication.run(LeehoSpringApplication.class, args);
        }
    }</code></pre>
<p>** External Libraries
** 가져온 라이브러리들을 확인 할수있다.
 <code>bulid.gradle</code> 항목에선 분명히 2개의 라이브러리만 가져왔는데 많은 양의 라이브러리가 프로젝트에 땡겨져 온것을 확인할수 있다 이는 라이브러리들 간의 의존성 때문이다 <code>Spring web</code> 과 <code>Thymeleaf</code> 를 사용하기위해 추가 라이브러리 들을 가져오고 추가된 라이브러리를 사용하기위해 또 다른 라이브러리들을 가져오는 형식이기 때문에 라이브러리의 양이 많아진것이다.
<code>Springboot-starter_tomcat</code>을 확인해볼 수 있는데.이는 <code>tomcat</code> 이라는 웹서버 라이브러리 이다 프로젝트 라이브러리에 웹서버를 내장하고있는 현재의 모습과 달리 과거엔 웹서버와 개발 라이브러리를 분리해서 사용하였다 
 <br/></p>
<hr>
<h3 id="view-환경-설정">View 환경 설정</h3>
<p><code>static</code> 내부에 <code>Index.html</code> 이라는 welcome page 를 작성해보고
  web 어플리케이션 진입점인 <code>Controller</code>를 만들어보자.</p>
<blockquote>
<h3 id="indexhtml"><strong>index.html</strong></h3>
</blockquote>
<pre><code class="language-html">  &lt;head&gt;
    &lt;title&gt;Hi&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
hihihi
&lt;a href=&quot;/hello&quot;&gt;hi&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
<blockquote>
<h3 id="controller">Controller</h3>
</blockquote>
</blockquote>
<pre><code class="language-java">@Controller
public class HelloController {
    @GetMapping(&quot;hello&quot;)
    public String hello(Model model) {
        model.addAttribute(&quot;data&quot;, &quot;hello!!&quot;);
        return &quot;hello&quot;;
    }</code></pre>
<h3 id="hellohtml">hello.html</h3>
<pre><code class="language-html">&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
    -&gt; templeate 을 통한 html 양식을 thymeleaf 로 변환
&lt;head&gt;
    &lt;title&gt;Hello&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;안녕하세요. &#39; + ${data}&quot; &gt;안녕하세요. 손님&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="동작">동작</h3>
<p>컨트롤러가 실행되면 <strong>model</strong> 을 통해 데이터를 보내게 되는데 이때 addAttribute 로인해 <code>key = data</code>, <code>value = hello!!</code> 가 된다 이후 <code>return &quot;hello&quot;;</code> 를 통해 templeate 에 만들어둔 hello.html 로 값이 반환 <code>${data}</code> 에 value 값인 <code>hello!!</code> 가 대체 텍스트인 <code>손님</code> 대신에 들어가게된다.</p>
<h3 id="동작원리">동작원리</h3>
<p><img src="https://images.velog.io/images/wamt_2787/post/17b60dce-38f6-469a-bdda-d8033da1ba7a/image.png" alt=""></p>
<hr>
<h2 id="span-stylecolor-orangereferencespan"><span style="color: orange">Reference</span></h2>
<pre><code>김영한 강사님의 Spring 입문 강의를 토대로 복습하기위한 글 이며 벨로그 글 
강의를 들은 다른분들의 학습내용이 포함되어 있습니다
해당하는 모든 출처는 링크와 강의에 있습니다.
</code></pre><p><a href="https://anaog.tistory.com/1">블로그 링크</a>
<a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8">강의 링크</a>
<a href="https://namu.wiki/w/Spring(%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)">나무위키 링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring 이해하기
(사전지식)]]></title>
            <link>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wamt_2787/Spring-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 18 Jan 2022 11:08:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/wamt_2787/post/368d3817-3590-4e88-ab9b-d536922f4789/spring_logo.png" alt=""></p>
<hr>
<blockquote>
<h3 id="시작-하기에-앞서-spring-입문을-위한-사전지식">시작 하기에 앞서 Spring 입문을 위한 사전지식</h3>
</blockquote>
<ol>
<li>Java - 기본지식</li>
<li>Html,Css,Js - 웹 문서지식</li>
<li>Servlet,Jsp - 서버지식</li>
<li>DB - 기본 데이터베이스</li>
</ol>
<h1 id="spring-">Spring ?</h1>
<p>스프링 에 대한 사전적의미는 여러가지 있지만 한가지 명확하게 표현할수 있다면<span style="color: green">
<strong>&quot;스프링은 웹사이트 개발을 위한 백엔드 프레임워크이다.&quot;</strong></span>
기존에 있던 프레임워크 와의 호환성도 높아 혼용하여 사용이 가능하고 다른 프레임워크에 비해 경량 이지만 강력한 성능으로 기업들의 니즈를 충족시켰다.</p>
<blockquote>
<h3 id="백엔드">백엔드</h3>
<p>백엔드 (Back-end): 웹 개발 프로세스를 프론트(Front-end),와 백엔드(Back-end)로 구분하는 경우가많다. 일반적으로 서버측에서 동작하는 프로그램을 다루는 프로세스적 요소를 백엔드 라고 한다.</p>
</blockquote>
<h3 id="프레임워크">프레임워크</h3>
<p> 프레임워크 (Framework):프로그램 설계에 있어 미리 구축되어있는 뼈대 라고 이해할수있는 부분이다 백엔드 프로세스를 구축하는데 복잡한 초기 설정 구축을 위해 스프링이라는 기본 뼈대를 제공하여 상위 퀄리티의 프로그램을 비교적 빠른시간에 개발할 수 있게된다.</p>
<hr>
<h2 id="spring-특징">Spring 특징</h2>
<blockquote>
<h3 id="의존성-주입">의존성 주입</h3>
<p>(DI: Dependency Injection): 프로그램 구성요소간의 의존 구조를 외부 파일에서 정의해준다. 각 요소 또는 서비스 사이에 의존성이 존재하는 경우 스프링에서 의존 관계를 정의, 외부파일을 통해 설정해준다. 설정파일을 분리하기 때문에 유지보수가 쉬워지며 각 구성요소간 결합성이 낮아지게 된다.(각 개체가 독립적으로 존재, 연결 자체는 스프링에서 지원한다</p>
</blockquote>
<blockquote>
<h3 id="pojo-방식">POJO 방식</h3>
<p>Plain Old Java Object 의 약자, 말그대로 기존의 일반적인 java 코드를 이용해서 프로그래밍이 가능하다. 기존의 프레임워크(Java EE)의 방식에선 미리 설계되어있는 인터페이스나 클래스를 상속받아 무거운 객체들을 만들어야만 했는데, 그럴 필요가 없이 일반적인 java코드로만으로도 객체를 구성할 수 있게된다. 따라서 더 유연한 프로그래밍이 가능해지며 가볍고 생산성이 높아진다.</p>
</blockquote>
<blockquote>
<h3 id="관점지향-프로그래밍aop">관점지향 프로그래밍(AOP)</h3>
<p>관점에 따라 각각의 기능을 분리하여 프로그래밍 하는것이 가능해진다. 비즈니스 로직, {로깅, 트랜잭션, 보안} 등을 다른 모듈로 분리하여 따로 관리할 수 있다. 스프링에서는 서로다른 관점을 조합하는 기능또한 제공한다. 이러한 방식은 유지보수가 수월하며 각각의 관점에 최적화된 프로그래밍이 가능해진다는 장점이 있다.</p>
</blockquote>
<blockquote>
<h3 id="제어의-반전-ioc-inversion-of-control">제어의 반전 (IoC: Inversion of Control)</h3>
<p>기존의 프로그래밍 방식에선 라이브러리를 호출하여 코드에 포함시켜 프로그램을 실행시켰다. 하지만 스프링에서는 지원하는 형식에 맞게 프로그램을 작성하면 프레임워크에서 사용자가 작성한 코드를 호출해서 사용하는 방식으로 제어의 권한이 역전된 모습을 보여준다.</p>
</blockquote>
<hr>
<p><img src="https://images.velog.io/images/wamt_2787/post/3bf3e110-8b78-4db8-a331-6b69cee73688/spring_boot_logo.png" alt=""></p>
<h2 id="spring-boot">Spring Boot</h2>
<p><code>Spring</code> 을 더쉽게 이용하기 위한 도구 이다 스프링을 이용하여 개발할 때, 이것저것 세팅을 해야될요소가 많은데. 여러가지를 세팅해야하는 진입 장벽을 줄여주는 존재로 간단한 방법을 통해 초기 설정값을 잡아준다.</p>
<hr>
<h2 id="span-stylecolor-orangereferencespan"><span style="color: orange">Reference</span></h2>
<pre><code>김영한 강사님의 Spring 입문 강의를 토대로 복습하기위한 글 이며 벨로그 글 
강의를 들은 다른분들의 학습내용이 포함되어 있습니다
해당하는 모든 출처는 링크와 강의에 있습니다.
</code></pre><p><a href="https://anaog.tistory.com/1">블로그 링크</a>
<a href="https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8">강의 링크</a>
<a href="https://namu.wiki/w/Spring(%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)">나무위키 링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[벨로그 사용법]]></title>
            <link>https://velog.io/@wamt_2787/%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EC%82%AC%EB%9E%8C%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B2%A8%EB%A1%9C%EA%B7%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@wamt_2787/%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EC%82%AC%EB%9E%8C%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%B2%A8%EB%A1%9C%EA%B7%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Sun, 02 Jan 2022 10:18:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/wamt_2787/post/476424e5-34eb-4897-9706-05b463c16fc1/268ec5754d1433416bd11e2f0ca71201bcaac8767dc7bc4d9d7e94b322cc98bc7988415faac57d269e925dfcdcb2a8fe9eb4d6f31e67aa256990d94c2d16cea0d38cf6df010a54e7aebb26e19dc9f42a.gif" alt=""></p>
<h1 id="1제목">1.제목</h1>
<blockquote>
<p><code>h1</code> 부터 <code>h6</code> 까지 표현 할수있습니다
 상단의 아이콘을 이용해서 쓸수있으며 따로 <code>#</code> 을 써서 사용할수있습니다.
 <code>#</code> 의 개수로 조절 할수있습니다.</p>
</blockquote>
<h1 id="2br">2.br</h1>
<blockquote>
<p><code>&lt;br/&gt;</code> 을 통해 문단의 간격을 <code>줄 바꿈</code> 으로 나타낼수 있습니다.</p>
</blockquote>
<pre><code>  안녕하세요 &lt;br/&gt;
  응애 입니다.</code></pre><blockquote>
<blockquote>
</blockquote>
<p> 안녕하세요  <br/>
 응애 입니다.</p>
</blockquote>
<h1 id="3list">3.list</h1>
<blockquote>
<p><code>숫자</code> 와 <code>.</code>을 사용하여 작성하는 list 입니다.</p>
</blockquote>
<pre><code>  1. 하나
  2. 둘
  3. 셋</code></pre><p>  <code>,</code> , <code>*</code> ,<code>+</code> 을 사용한 List 를 사용할수 있습니다. 
  List 안에서 Tab을 두번하면 코드블럭 사용이 가능합니다.</p>
<pre><code>  -안녕하세요
      -들여쓰기도 가능해요
  -        코드블럭 이야</code></pre><blockquote>
<blockquote>
</blockquote>
<p>  -안녕하세요</p>
</blockquote>
<ul>
<li>들여쓰기도 가능해요</li>
<li>코드블럭 이야</li>
</ul>
<h1 id="4인용문">4.인용문</h1>
<blockquote>
<p>인용문을 작성할 때에는 <code>&gt;</code> 를 사용합니다.</p>
</blockquote>
<pre><code>&gt; 인용문
-    작성자
&gt; 내용입니다. `Enter` 키를 눌러 문단을 건너뛸수있네요 
&gt;&gt; `&gt;` 의 갯수에 따라 중첩문을 쓸수있네요</code></pre><blockquote>
<p>인용문</p>
</blockquote>
<ul>
<li>작성자</li>
</ul>
<blockquote>
<p>내용입니다.<code>Enter</code> 키를 눌러 문단을 건너뛸수있네요</p>
<blockquote>
<p><code>&gt;</code> 의 갯수에 따라 중첩문을 쓸수있네요</p>
</blockquote>
</blockquote>
<h1 id="5인라인코드--코드블럭인">5.인라인코드 &amp; 코드블럭인</h1>
<blockquote>
<p>백틱 을 사용해 인라인코드를 작성할수 있습니다.
<code>인라인 코드</code></p>
</blockquote>
<blockquote>
<p>백틱 3개를 사용한 코드블럭을 할수있으며 백틱 뒤에 언어 이름작성시
<code>code</code>로 코드 블럭이 가능합니다.</p>
</blockquote>
<pre><code class="language-javascript">let sumNumbers = (firstNum, lastNum) =&gt; {
  return firstNum + lastNum;
};
sumNumbers(100, 200)</code></pre>
<h1 id="6링크">6.링크</h1>
<blockquote>
<p>많이 쓰는 링크는 총 3가지가 있다.</p>
</blockquote>
<pre><code>인라인 링크
[인라인 링크](https://velog.io/)
url 링크
&lt;https://velog.io/&gt;
참조 링크
[velog]:https://velog.io/
[velog]</code></pre><p>인라인 링크
<a href="https://velog.io/">인라인 링크</a>
url 링크
<a href="https://velog.io/">https://velog.io/</a>
참조 링크
[velog]:<a href="https://velog.io/">https://velog.io/</a>
[velog]</p>
<h1 id="7테이블">7.테이블</h1>
<blockquote>
<p>수직선 <code>|</code> 과 수평선 <code>-</code> 을 사용해서 선을 구분할수있고
<code>space</code> 와 <code>:</code> 을 사용해서 정렬을 표시 할수있다</p>
<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th></th>
<th align="left">left</th>
<th align="center">center</th>
<th align="right">right</th>
</tr>
</thead>
<tbody><tr>
<td>row1</td>
<td align="left">l1</td>
<td align="center">c1</td>
<td align="right">r1</td>
</tr>
<tr>
<td>row2</td>
<td align="left">l2</td>
<td align="center">c2</td>
<td align="right">r2</td>
</tr>
<tr>
<td>row3</td>
<td align="left">l3</td>
<td align="center">c3</td>
<td align="right">r3</td>
</tr>
</tbody></table>
</blockquote>
<h1 id="8짜잘팁">8.짜잘팁</h1>
]]></description>
        </item>
    </channel>
</rss>