<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>I made it 💻</title>
        <link>https://velog.io/</link>
        <description>🚧개발중🚧</description>
        <lastBuildDate>Sun, 13 Sep 2020 16:06:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. I made it 💻. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ji-silver" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Git] Git 명령어 (2)]]></title>
            <link>https://velog.io/@ji-silver/Git-Git-%EB%AA%85%EB%A0%B9%EC%96%B4-2</link>
            <guid>https://velog.io/@ji-silver/Git-Git-%EB%AA%85%EB%A0%B9%EC%96%B4-2</guid>
            <pubDate>Sun, 13 Sep 2020 16:06:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1-한글-처리와-git-reset">1. 한글 처리와 git reset</h2>
<h3 id="1-한글-처리">1) 한글 처리</h3>
<ul>
<li>터미널에서 한글이 깨질 때: set LC_ALL=ko_KR.UTF-8 명령</li>
<li>git status에서 한글이 깨질 때: <code>git config --global --edit</code> 명령 후 [core]에 <code>quotepath = false</code> 추가해주기</li>
</ul>
<h3 id="2-git-reset">2) git reset</h3>
<p>실수로 커밋된 걸 되돌리기</p>
<ul>
<li><strong>git reset</strong> HEAD~1: HEAD는 현재 위치. 현재 위치에서 한 단계 전으로 내려가기 (커밋 자체가 사라져버림)<blockquote>
<p>뒤에 --soft, --mixed(기본), --hard 옵션을 붙여 어떤 단계로 돌아갈지 직접 설정 가능</p>
</blockquote>
</li>
</ul>
<h2 id="2-git-revert-git-branch">2. git revert, git branch</h2>
<ul>
<li><strong>git revert</strong>: reset은 기록없이 전 단계로 돌아가지만 revert는 되돌아가되 commit 기록이 남음 (협업 시 사용)</li>
<li><strong>git branch</strong>: 같은 코드에 각자 서로 다른 작업을 할 수 있게 복사본을 만들고 나중에 하나로 합칠 수 있음. 브랜치 생성 후 <code>git checkout</code>으로 원하는 브랜치로 이동</li>
</ul>
<h2 id="3-git-merge-git-rebase">3. git merge, git rebase</h2>
<ul>
<li><strong>git merge</strong>: master 브랜치와 다른 브랜치를 합치는 것. (같은줄의 내용을 동시에 수정했다면 충돌이 일어나기 때문에 그 부분을 하나로 합쳐주기)</li>
<li><strong>git rebase</strong>: merge로 병합시키면 commit된 줄기가 생기고, rebase는 필요없는 commit은 생략하여 하나의 줄기만 있기 때문에 깔끔하다는 장점이 있음</li>
</ul>
<h2 id="4-git-cherry-pick과-기타-명령어">4. git cherry-pick과 기타 명령어</h2>
<ul>
<li><strong>git cherry-pick</strong>: 여러 commit을 선택적으로 가져오기</li>
<li><strong>git tag</strong>: 커밋을 참조하기 위해 아이디 대신 이름을 붙이는 것</li>
<li><strong>git stash</strong>: commit 하지 않고 하던 작업 임시저장 (다시 가져올 때 <code>git stash apply</code>)</li>
<li><strong>git fetch</strong>: pull처럼 원격저장소에서 파일을 가져오지만 병합은 안 되어있기 때문에 merge 해줘야 함</li>
</ul>
<h2 id="5-git-flow-전략">5. git flow 전략</h2>
<p>⭐<strong>TIP</strong>
initial commit 하나 만들고 바로 여러 기능들의 branch들을 만들어 commit이 쌓이면 하나로 합쳐주기 (이때 충돌이 나면 거기서 해결해주기) 올바른 commit이 되었을 때 master로 옮기기</p>
<blockquote>
<p>branch는 충돌이 많이 나기 때문에 전략을 잘 짜기!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] Git 명령어 (1)]]></title>
            <link>https://velog.io/@ji-silver/Git-Git-%EB%AA%85%EB%A0%B9%EC%96%B4-1</link>
            <guid>https://velog.io/@ji-silver/Git-Git-%EB%AA%85%EB%A0%B9%EC%96%B4-1</guid>
            <pubDate>Fri, 11 Sep 2020 15:24:45 GMT</pubDate>
            <description><![CDATA[<h2 id="1-git을-쓰는-이유">1. Git을 쓰는 이유</h2>
<h3 id="1-버전-관리">1) 버전 관리</h3>
<p>코딩하고 있을 때 중간에 꺼져서 날아가거나, 배포 후 버전을 되돌리고 싶을 때 한 파일로 버전을 관리할 수 있음</p>
<h3 id="2-⭐협업">2) ⭐협업</h3>
<p>예를 들어 팀원이 두 명 이상일 때 보고서 작성 시 한 파일을 동시에 수정하는 일이 발생할 수 있으므로 효율적으로 합쳐서 관리</p>
<h2 id="2-git-status-git-add">2. git status, git add</h2>
<p>터미널에 <code>git init</code> 명령어를 사용하여 git에서 관리하게 끔 만들어주기 (.git 폴더 생성)</p>
<ul>
<li><strong>git status</strong>: 상태 확인</li>
<li><strong>git add</strong>:  git에서 모든 파일을 관리 할 필요는 없기 때문에 <code>git add 파일</code> 설정 후 다시 <code>git status</code> 명령 시 관리하는 파일을 확인할 수 있음 (모든 파일 관리는 git add .)</li>
</ul>
<h2 id="3-git-commit">3. git commit</h2>
<ul>
<li><strong>git commit</strong>: 어떤 상태인지 기억해달라고 메시지를 남기는 것.<ul>
<li><code>git commit</code> 명령 후 메시지 남긴 후 (ex. Initial commit...) <code>:wq</code> 입력** (w: write, q: quit)**</li>
<li>메시지가 짧다면 <code>git commit -m &quot;Initial commit&quot;</code></li>
</ul>
</li>
<li><strong>git log</strong>: 커밋 목록 보기</li>
<li><strong>git checkout</strong>: 수정한 파일을 다시 되돌리고 싶을 때 사용</li>
<li><strong>git commit -am &quot;커밋 메시지&quot;</strong>: a는 all(add .), m은 commit message 모든 파일을 한꺼번에 올림 👉 단, 한 번도 add되지 않은 파일은 add로 먼저 올려주기</li>
</ul>
<blockquote>
<p>❗ 오류가 난다면 <strong>git config --global user.email &quot;이메일 주소&quot;</strong>, <strong>git config --global user.name &quot;이름&quot;</strong> 설정 후 커밋 작성</p>
</blockquote>
<h2 id="4-github과-git-remote">4. GitHub과 git remote</h2>
<p>github 가입 후 new repository 생성</p>
<ul>
<li><strong>git remote</strong> add origin <a href="https://github.com/ji-silver/%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%A6%84">https://github.com/ji-silver/파일이름</a>
: origin으로 주소 등록하여 원격 저장소 관리</li>
</ul>
<h2 id="5-git-push-git-pull">5. git push, git pull</h2>
<ul>
<li><strong>git push</strong> origin <em>(<del>master</del>) *</em>main**: 원격 저장소에 업로드. origin은 원격 저장소 이름, master는 컴퓨터 브랜치 이름</li>
<li><strong>git pull</strong> origin (<del>master</del>) <strong>main</strong>: 나 이외에 다른 사람이 같은 파일의 코드를 변경 시 변경 내용을 컴퓨터로 가져옴</li>
</ul>
<blockquote>
<p>🔥🔥🔥
*10월 1일부터 기본 브랜치 이름이 &#39;master&#39; 에서 &#39;main&#39; 으로 변경되었음!!
<code>git branch -M main</code>으로 master 브랜치를 main으로 바꿔준 후 remote 저장소에 push하기</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-8 지뢰찾기]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-8-%EC%A7%80%EB%A2%B0%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-8-%EC%A7%80%EB%A2%B0%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Thu, 03 Sep 2020 17:11:34 GMT</pubDate>
            <description><![CDATA[<h2 id="1-context-api">1. Context API</h2>
<p>전역적으로 데이터가 사용되야 할 때 (TicTacToe-&gt; Table-&gt; Tr-&gt; Td) 사용</p>
<h2 id="2-createcontext와-provider">2. createContext와 Provider</h2>
<ul>
<li><code>createContext()</code> 함수를 사용하여 Context 만들기</li>
<li>Context API의 데이터에 접근해야 하는 컴포넌트를 Provider로 묶어주기</li>
<li>전달할 데이터는 <code>value={}</code> 안에 넣어주기</li>
</ul>
<h4 id="minesearchjsx">MineSearch.jsx</h4>
<pre><code class="language-jsx">import React, { useReducer, createContext, useMemo } from &#39;react&#39;;

export const TableContext = createContext({
  // 초깃값 설정
});

const MineSearch = () =&gt; {
    const [state, dispatch] = useReducer(reducer, initialState);

    const value = useMemo(() =&gt; ({ tableData: state.tableData, dispatch }), [state.tableData]); // tableDate값이 바뀔 때 갱신

    return (
        &lt;TableContext.Provider value={value}&gt;
            &lt;Form /&gt;
            &lt;div&gt;{state.timer}&lt;/div&gt;
            &lt;Table /&gt;
            &lt;div&gt;{state.result}&lt;/div&gt;
        &lt;/TableContext.Provider&gt;
    );
};</code></pre>
<p>❗ Context API는 성능 최적화하기 힘들기 때문에 <code>useMemo()</code>로 객체값을 기억하게 하여 캐싱 작업하기</p>
<h2 id="3-usecontext-사용해-지뢰-칸-렌더링">3. useContext 사용해 지뢰 칸 렌더링</h2>
<h3 id="1-배열에-지뢰-심기">1) 배열에 지뢰 심기</h3>
<h4 id="minesearchjsx-1">MineSearch.jsx</h4>
<pre><code class="language-jsx">export const CODE = {
    MINE: -7, // 지뢰
    NORMAL: -1, // 일반 칸
    QUESTION: -2, // 물음표
    FLAG :-3, // 깃발
    QUESTION_MINE: -4, // 물음표 칸이 지뢰인 경우
    FLAG_MINE: -5, // 깃발 칸이 지뢰인 경우
    CLICKED_MINE: -6, // 지뢰 클릭
    OPENED: 0, // 칸 열었을 때 (0이상이면 다 opened)
};

...

const plantMine = (row, cell, mine) =&gt; {
    console.log(row, cell, mine);
    const candidate = Array(row * cell).fill().map((arr, i) =&gt; { //0 ~ 99까지
        return i;
    });
    const shuffle = [];
    while(candidate.length &gt; row * cell - mine) {
        const chosen = candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0];
        shuffle.push(chosen); // 랜덤으로 뽑은 20개 숫자들을 shuffle 배열에 넣기
    }
    //2차원 배열 만들기
    const data = [];
    for (let i = 0; i &lt; row; i++) {
        const rowData = [];
        data.push(rowData);
        for (let j = 0; j &lt; cell; j++) {
            rowData.push(CODE.NORMAL); // 일반칸을 기본으로 설정
        }
    }
    //2차원 배열에 지뢰 심기
    for (let k = 0; k &lt; shuffle.length; k++) {
        const ver = Math.floor(shuffle[k] / cell);
        const hor = shuffle[k] % cell;
        data[ver][hor] = CODE.MINE; // 지뢰 심기
    }
    console.log(data);
    return data;
};

export const START_GAME = &#39;START_GAME&#39;;

const reducer = (state, action) =&gt; {
    switch (action.type) {
        case START_GAME: 
        return {
            ...state,
            tableData: plantMine(action.row, action.cell, action.mine) // tableData에 지뢰가 심어짐
        };
        default:
            return state;
    }
};</code></pre>
<h3 id="2-화면에-나타내기">2) 화면에 나타내기</h3>
<h4 id="tablejsx">Table.jsx</h4>
<pre><code class="language-jsx">import React, { useContext } from &#39;react&#39;;
import Tr from &#39;./Tr&#39;;
import { TableContext } from &#39;./MineSearch&#39;;

const Table = () =&gt; {
    const { tableData } = useContext(TableContext);
    return (
        &lt;table&gt;
            {Array(tableData.length).fill().map((tr, i) =&gt; &lt;Tr rowIndex={i} /&gt;)}
        &lt;/table&gt;
    )
};

export default Table;</code></pre>
<h4 id="trjsx">Tr.jsx</h4>
<pre><code class="language-jsx">import React, { useContext } from &#39;react&#39;;
import Td from &#39;./Td&#39;;
import { TableContext } from &#39;./MineSearch&#39;;

const Tr = ({ rowIndex }) =&gt; {
    const { tableData } = useContext(TableContext);
    return (
        &lt;tr&gt;
            {tableData[0] &amp;&amp; Array(tableData[0].length).fill().map((td, i) =&gt;
                &lt;Td rowIndex={rowIndex} cellIndex={i} /&gt;
            )}
        &lt;/tr&gt;
    )
};

export default Tr;</code></pre>
<h4 id="tdjsx">Td.jsx</h4>
<pre><code class="language-jsx">import React, { useContext } from &#39;react&#39;;
import { CODE, TableContext } from &#39;./MineSearch&#39;;

const getTdStyle = (code) =&gt; {
    switch (code) {
        case CODE.NORMAL:
        case CODE.MINE:
            return {
                background: &#39;#444&#39;,
            };
        case CODE.OPENED:
            return {
                background: &#39;white&#39;
            };
        default:
            return {
                background: &#39;white&#39;
            };
    }
};

const getTdText = (code) =&gt; {
    switch (code) {
        case CODE.NORMAL:
            return &#39;&#39;;
        case CODE.MINE:
            return &#39;X&#39;;
        default:
            return code || &#39;&#39;;
    }
};

const Td = ({ rowIndex, cellIndex }) =&gt; {
    const { tableData } = useContext(TableContext);
    return (
        &lt;td
            style={getTdStyle(tableData[rowIndex][cellIndex])}
        &gt;{getTdText(tableData[rowIndex][cellIndex])}&lt;/td&gt;
    )
};

export default Td;</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/0dbc7f52-cc83-40e9-82c7-15f125a32e2b/%EC%BA%A1%EC%B2%98.PNG" alt=""></p>
<h2 id="4-왼쪽-오른쪽-클릭-로직-작성하기">4. 왼쪽 오른쪽 클릭 로직 작성하기</h2>
<h4 id="tdjsx-1">Td.jsx</h4>
<pre><code class="language-jsx">const Td = ({ rowIndex, cellIndex }) =&gt; {
    const { tableData, dispatch, halted } = useContext(TableContext);

    const onClickTd = useCallback(() =&gt; {
        if (halted) {
            return; // 게임이 멈추면 클릭x
        }
        switch (tableData[rowIndex][cellIndex]) {
            //이미 연 칸, 깃발, ?칸은 그냥 return
            case CODE.OPENED:
            case CODE.FLAG_MINE:
            case CODE.FLAG:
            case CODE.QUESTION_MINE:
            case CODE.QUESTION:
                return;
            case CODE.NORMAL:
                dispatch({ type: OPEN_CELL, row: rowIndex, cell: cellIndex });
                return;
            case CODE.MINE:
                dispatch({ type: CLICK_MINE, row: rowIndex, cell: cellIndex });
                return;
            default:
                return;
        }
    }, [tableData[rowIndex][cellIndex], halted]);

    const onRightClickTd = useCallback((e) =&gt; {
        e.preventDefault(); // 우클릭 시 나오는 메뉴 안 뜨게 하기
        if (halted) {
            return;
        }
        switch (tableData[rowIndex][cellIndex]) {
            case CODE.NORMAL:
            case CODE.MINE:
                dispatch({ type: FLAG_CELL, row: rowIndex, cell: cellIndex });
                return;
            case CODE.FLAG_MINE:
            case CODE.FLAG:
                dispatch({ type: QUESTION_CELL, row: rowIndex, cell: cellIndex });
                return;
            case CODE.QUESTION_MINE:
            case CODE.QUESTION:
                dispatch({ type: NORMALIZE_CELL, row: rowIndex, cell: cellIndex });
                return;
            default:
                return;
        }
    }, [tableData[rowIndex][cellIndex], halted]);

    return (
        &lt;td
            style={getTdStyle(tableData[rowIndex][cellIndex])}
            onClick={onClickTd}
            onContextMenu={onRightClickTd} // 우클릭 이벤트
        &gt;{getTdText(tableData[rowIndex][cellIndex])}&lt;/td&gt;
    )
};</code></pre>
<h4 id="minesearchjsx-2">MineSearch.jsx</h4>
<pre><code class="language-jsx">export const TableContext = createContext({ // 초깃값 설정
    tableData: [],
    halted: true, // 게임 중단 코드
    dispatch: () =&gt; { },
});

const initialState = {
    tableData: [],
    timer: 0,
    result: &#39;&#39;,
    halted: false,
};

...

export const START_GAME = &#39;START_GAME&#39;;
export const OPEN_CELL = &#39;OPEN_CELL&#39;;
export const CLICK_MINE = &#39;CLICK_MINE&#39;;
export const FLAG_CELL = &#39;FLAG_CELL&#39;;
export const QUESTION_CELL = &#39;QUESTION_CELL&#39;;
export const NORMALIZE_CELL = &#39;NORMALIZE_CELL&#39;;

const reducer = (state, action) =&gt; {
    switch (action.type) {
        case START_GAME:
            return {
                ...state,
                tableData: plantMine(action.row, action.cell, action.mine),
                halted: false,
            };
        case OPEN_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            tableData[action.row][action.cell] = CODE.OPENED; // 클릭한 칸을 OPENED으로 바꾸기
            return {
                ...state,
                tableData,
            };
        }
        case CLICK_MINE: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            tableData[action.row][action.cell] = CODE.CLICKED_MINE;
            return {
                ...state,
                tableData,
                halted: true,
            };
        }
        case FLAG_CELL:
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            if (tableData[action.row][action.cell] === CODE.MINE) {
                tableData[action.row][action.cell] = CODE.FLAG_MINE;
            } else {
                tableData[action.row][action.cell] = CODE.FLAG;
            }
            return {
                ...state,
                tableData,
            };
        case QUESTION_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            if (tableData[action.row][action.cell] === CODE.FLAG_MINE) {
                tableData[action.row][action.cell] = CODE.QUESTION_MINE;
            } else {
                tableData[action.row][action.cell] = CODE.QUESTION;
            }
            return {
                ...state,
                tableData,
            };
        }
        case NORMALIZE_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            if (tableData[action.row][action.cell] === CODE.FLAG_MINE) {
                tableData[action.row][action.cell] = CODE.MINE;
            } else {
                tableData[action.row][action.cell] = CODE.NORMAL;
            }
            return {
                ...state,
                tableData,
            };
        }
        default:
            return state;
    }
};</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/464e4946-1adb-4cba-a12f-de5f921863c9/%EC%BA%A1%EC%B2%981.PNG" alt=""></p>
<h2 id="5-지뢰-개수-표시하기">5. 지뢰 개수 표시하기</h2>
<h4 id="minesearchjsx-3">MineSearch.jsx</h4>
<pre><code class="language-jsx">const reducer = (state, action) =&gt; {
    switch (action.type) {
        case OPEN_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...state.tableData[action.row]];
            let around = [];
            if (tableData[action.row - 1]) { // 클릭한 칸 기준으로 윗줄이 있으면
                around = around.concat(
                    tableData[action.row - 1][action.cell - 1],
                    tableData[action.row - 1][action.cell],
                    tableData[action.row - 1][action.cell + 1]
                );
            }
            around = around.concat(
                tableData[action.row][action.cell - 1],
                tableData[action.row][action.cell + 1]
            );
            if (tableData[action.row + 1]) { // 클릭한 칸 기준으로 밑에줄이 있으면
                around = around.concat(
                    tableData[action.row + 1][action.cell - 1],
                    tableData[action.row + 1][action.cell],
                    tableData[action.row + 1][action.cell + 1]
                );
            }
            // 주변 지뢰 갯수 찾기
            const count = around.filter((v) =&gt; [CODE.MINE, CODE.FLAG_MINE, CODE.QUESTION_MINE].includes(v)).length;
            console.log(around, count);
            tableData[action.row][action.cell] = count;
            return {
                ...state,
                tableData,
            };
        }
    }
};</code></pre>
<h2 id="6-빈-칸들-한-번에-열기">6. 빈 칸들 한 번에 열기</h2>
<h4 id="minesearchjsx-4">MineSearch.jsx</h4>
<pre><code class="language-jsx">case OPEN_CELL: {
    const tableData = [...state.tableData];
    // tableData[action.row] = [...state.tableData[action.row]];
    tableData.forEach((row, i) =&gt; { // 클릭한 칸 뿐 아니라 모든 칸을 새로운 객체로 만들기
        tableData[i] = [...state.tableData[i]];
    });
    const checked = [];
    const checkAround = (row, cell) =&gt; {
        // 닫힌 칸이 아니면 리턴
        if ([CODE.OPENED, CODE.FLAG_MINE, CODE.FLAG, CODE.QUESTION_MINE, CODE.QUESTION].includes(tableData[row][cell])) {
            return;
        }
        // 상하좌우 없는 칸은 안 열기
        if (row &lt; 0 || row &gt;= tableData.length || cell &lt; 0 || cell &gt;= tableData[0].length) {
            return;
        }
        // 옆에칸 서로 검사하는 거 막아주기
        if (checked.includes(row + &#39;,&#39; + cell)) { // 이미 검사한 칸이면 
            return;
        } else { // 아니면 checked 배열에 넣어주기 
            checked.push(row + &#39;,&#39; + cell);
        }
        let around = [];
        if (tableData[row - 1]) {
            around = around.concat(
                tableData[row - 1][cell - 1],
                tableData[row - 1][cell],
                tableData[row - 1][cell + 1]
            );
        }
        around = around.concat(
            tableData[row][cell - 1],
            tableData[row][cell + 1]
        );
        if (tableData[row + 1]) {
            around = around.concat(
                tableData[row + 1][cell - 1],
                tableData[row + 1][cell],
                tableData[row + 1][cell + 1]
            );
        }
        const count = around.filter((v) =&gt; [CODE.MINE, CODE.FLAG_MINE, CODE.QUESTION_MINE].includes(v)).length;
        tableData[row][cell] = count;

        // 지뢰가 없으면 주변 8칸 열기
        if (count === 0) {
            const near = [];
            if (row - 1 &gt; -1) {
                near.push([row - 1, cell - 1]);
                near.push([row - 1, cell]);
                near.push([row - 1, cell + 1]);
            }
            near.push([row, cell - 1]);
            near.push([row, cell + 1]);
            if (row + 1 &lt; tableData.length) {
                near.push([row + 1, cell - 1]);
                near.push([row + 1, cell]);
                near.push([row + 1, cell + 1]);
            }
            near.forEach((n) =&gt; {
                if (tableData[n[0]][n[1]] !== CODE.OPENE) {
                    checkAround(n[0], n[1]);
                }
            })
        }
    };
    checkAround(action.row, action.cell);
    return {
        ...state,
        tableData,
    };
}</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/037a1930-246e-43bd-9c94-cbb4609a36de/%EC%BA%A1%EC%B2%98.PNG" alt=""></p>
<h2 id="7-승리-조건-체크와-타이머">7. 승리 조건 체크와 타이머</h2>
<p>승리조건: 클릭한 칸이 가로 * 세로 - 지뢰개수 한 값과 같으면 승리</p>
<h3 id="1-승리조건-체크">1) 승리조건 체크</h3>
<pre><code class="language-jsx">const initialState = {
    tableData: [],
    date: {
        row: 0,
        cell: 0,
        mine: 0,
    },
    result: &#39;&#39;,
    halted: true,
    openedCount: 0,
};

const reducer = (state, action) =&gt; {
    switch (action.type) {
        case START_GAME:
            return {
                ...state,
                data: { // 게임 시작 시 data 기록해두기
                    row: action.row,
                    cell: action.cell,
                    mine: action.mine,
                },
                openedCount: 0,
                tableData: plantMine(action.row, action.cell, action.mine),
                halted: false,
                timer: 0,
            };
        case OPEN_CELL: {
            let openedCount = 0;
            const checkAround = (row, cell) =&gt; {
                openedCount += 1; // 칸을 열 때마다 1씩 올려주기
              ...
            };
        ...
        }; 
        checkAround(action.row, action.cell);
            let halted = false;
            let result = &#39;&#39;;
            console.log(state.data.row * state.data.cell - state.data.mine, state.openedCount, openedCount);
            if (state.data.row * state.data.cell - state.data.mine === state.openedCount + openedCount) { // 승리
                halted = true; // 승리 시 게임 멈추기
                result = &#39;승리하셨습니다!&#39;;
            }
            return {
                ...
                halted,
                result
            };
        }      </code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/b5634fc9-ce24-4bb7-9c6d-c5441edccb83/%EC%BA%A1%EC%B2%98.PNG" alt=""></p>
<h3 id="2-타이머">2) 타이머</h3>
<pre><code class="language-jsx">
const initialState = {
    ...
    timer: 0,
};

export const INCREMENT_TIMER = &#39;INCREMENT_TIMER&#39;;

const MineSearch = () =&gt; {
    useEffect(() =&gt; {
        let timer;
        if (halted === false) { // 게임 시작 시 타이머 실행
            timer = setInterval(() =&gt; {
                dispatch({ type: INCREMENT_TIMER });
            }, 1000);
        }
        return () =&gt; {
            clearInterval(timer);
        }
    }, [halted]);
};</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/3db78e00-77c7-41b0-b2c8-149004a1d2c9/%EC%BA%A1%EC%B2%981.PNG" alt=""></p>
<p>마지막으로 useMemo 사용하여 최적화 하기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-7 틱택토]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-7-%ED%8B%B1%ED%83%9D%ED%86%A0</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-7-%ED%8B%B1%ED%83%9D%ED%86%A0</guid>
            <pubDate>Fri, 14 Aug 2020 15:06:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-usereducer-소개">1. useReducer 소개</h2>
<p>state가 많아지면 관리가 힘들어지기 때문에 <code>useReducer</code>를 사용하여 하나의 state, setState 로 통일할 수 있음</p>
<pre><code class="language-jsx">const [state, dispatch] = useReducer(reducer, initialState);</code></pre>
<h2 id="2-reducer-action-dispatch의-관계">2. reducer, action, dispatch의 관계</h2>
<ul>
<li><code>reducer</code>: state와 action객체를 파라미터로 받아와 새로운 상태로 반환해주는 함수</li>
<li><code>action</code>: 업데이트를 위한 정보를 가지고 있음. 이름은 대문자로 보통 사용</li>
<li><code>dispatch</code>: 액션을 실행시키는 함수</li>
</ul>
<h4 id="tictactoejsx">TicTacToe.jsx</h4>
<pre><code class="language-jsx">import React, { useState, useReducer, useCallback } from &#39;react&#39;;
import Table from &#39;./Table&#39;;

const initialState = { // state들 여기에 묶어주기
    winner: &#39;&#39;,
    turn: &#39;O&#39;,
    tableData: [[&#39;&#39;, &#39;&#39;, &#39;&#39;], [&#39;&#39;, &#39;&#39;, &#39;&#39;], [&#39;&#39;, &#39;&#39;, &#39;&#39;]],
};

const SET_WINNER = &#39;SET_WINNER&#39;;

const reducer = (state, action) =&gt; { // action을 해석 후 state를 바꿔주는 역할
    switch (action.type) {
        case &#39;SET_WINNER&#39;:
            return { // return으로 새로운 상태 반환
                ...state, // 기존 state를 직접 바꾸지 않고 얕은 복사 후 바뀌는 부분만 넣기
                winner: action.winner,
            }
    };
};

const TicTacToe = () =&gt; {
    const [state, dispatch] = useReducer(reducer, initialState);
    const onClickTable = useCallback( // 컴포넌트에 들어가는 이벤트 함수는 useCallback 써주기 (함수 재사용)
        () =&gt; {
            dispatch({ type: SET_WINNER, winner: &#39;O&#39; }); // dispatch안에 들어가는 객체를 action이라고 함
        }, []);

    return (
        &lt;&gt;
            &lt;Table onClick={onClickTable} tableData={state.tableData} /&gt;
            {state.winner &amp;&amp; &lt;div&gt;{state.winner}님의 승리!&lt;/div&gt;}
        &lt;/&gt;
    );
};

export default TicTacToe;</code></pre>
<h2 id="3-action-만들어-dispatch-하기">3. action 만들어 dispatch 하기</h2>
<p>클릭한 곳이 몇 번째 줄, 몇 번째 칸인지 알아내고 클릭할 때마다 턴 바꾸기</p>
<h4 id="tictactoejsx-1">TicTacToe.jsx</h4>
<pre><code class="language-jsx">import React, { useState, useReducer, useCallback } from &#39;react&#39;;
import Table from &#39;./Table&#39;;

const initialState = {...};

export const SET_WINNER = &#39;SET_WINNER&#39;;
export const CLICK_CELL = &#39;CLICK_CELL&#39;;
export const CHANGE_TURN = &#39;CHANGE_TURN&#39;;

const reducer = (state, action) =&gt; {
    switch (action.type) {
        case SET_WINNER:
            return {...};

        case CLICK_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...tableData[action.row]];
            tableData[action.row][action.cell] = state.turn;
            return {
                ...state,
                tableData,
            };
        }
        case CHANGE_TURN: {
            return {
                ...state,
                turn: state.turn === &#39;O&#39; ? &#39;X&#39; : &#39;O&#39;,
                  // 기존 턴이 &#39;O&#39;면 &#39;X&#39;로 &#39;X&#39;면 &#39;O&#39;로
            };
        }
    }
};

const TicTacToe = () =&gt; {
    const [state, dispatch] = useReducer(reducer, initialState);
    const onClickTable = useCallback(
        () =&gt; {
            dispatch({ type: SET_WINNER, winner: &#39;O&#39; });
        }, []);

    return (
        &lt;&gt;
            &lt;Table onClick={onClickTable} tableData={state.tableData} dispatch={dispatch} /&gt;
            {state.winner &amp;&amp; &lt;div&gt;{state.winner}님의 승리!&lt;/div&gt;}
        &lt;/&gt;
    );
};

export default TicTacToe;</code></pre>
<h4 id="tablejsx">Table.jsx</h4>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import Tr from &#39;./Tr&#39;;

const Table = ({ onClick, tableData, dispatch }) =&gt; {
    return (
        &lt;table&gt;
            {Array(tableData.length).fill().map((tr, i) =&gt; (&lt;Tr dispatch={dispatch} rowIndex={i} rowData={tableData[i]} /&gt;))}
        &lt;/table&gt;
    );
};

export default Table;</code></pre>
<h4 id="trjsx">Tr.jsx</h4>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import Td from &#39;./Td&#39;;

const Tr = ({ rowData, rowIndex, dispatch }) =&gt; {
    return (
        &lt;tr&gt;
            {Array(rowData.length).fill().map((td, i) =&gt; (
                &lt;Td dispatch={dispatch} rowIndex={rowIndex} cellIndex={i} cellData={rowData[i]}&gt;{&#39;&#39;}&lt;/Td&gt;
            ))}
        &lt;/tr&gt;
    );
};

export default Tr;</code></pre>
<h4 id="tdjsx">Td.jsx</h4>
<pre><code class="language-jsx">import React, { useCallback } from &#39;react&#39;;
import { CLICK_CELL, CHANGE_TURN } from &#39;./TicTacToe&#39;;

const Td = ({ rowIndex, cellIndex, dispatch, cellData }) =&gt; {
    const onClickTd = useCallback(() =&gt; {
        console.log(rowIndex, cellIndex);
        dispatch({ type: CLICK_CELL, row: rowIndex, cell: cellIndex }); // 칸 클릭 후
        dispatch({ type: CHANGE_TURN }); // 턴 바꾸기
    }, []);

    return (
        &lt;td onClick={onClickTd}&gt;{cellData}&lt;/td&gt;
    );
};

export default Td;</code></pre>
<h2 id="4-틱택토-구현하기">4. 틱택토 구현하기</h2>
<p>한 번 눌렀던 칸은 못 누르게 하고, 승자 &amp; 무승부 가리기</p>
<h4 id="tdjsx-1">Td.jsx</h4>
<pre><code class="language-jsx">import React, { useCallback } from &#39;react&#39;;
import { CLICK_CELL } from &#39;./TicTacToe&#39;;

const Td = ({ rowIndex, cellIndex, dispatch, cellData }) =&gt; {
    const onClickTd = useCallback(() =&gt; {
        console.log(rowIndex, cellIndex);
        if (cellData) { // 기존 데이터가 있으면 리턴
            return;
        }
        dispatch({ type: CLICK_CELL, row: rowIndex, cell: cellIndex });
    }, [cellData]);

    return (
        &lt;td onClick={onClickTd}&gt;{cellData}&lt;/td&gt;
    );
};

export default Td;</code></pre>
<h4 id="tictactoejsx-2">TicTacToe.jsx</h4>
<pre><code class="language-jsx">import React, { useEffect, useReducer, useCallback } from &#39;react&#39;;
import Table from &#39;./Table&#39;;

const initialState = {
    winner: &#39;&#39;,
    turn: &#39;O&#39;,
    tableData: [
        [&#39;&#39;, &#39;&#39;, &#39;&#39;],
        [&#39;&#39;, &#39;&#39;, &#39;&#39;],
        [&#39;&#39;, &#39;&#39;, &#39;&#39;],
    ],
    recentCell: [-1, -1], // 없는 칸 임의로 설정
};

export const SET_WINNER = &#39;SET_WINNER&#39;;
export const CLICK_CELL = &#39;CLICK_CELL&#39;;
export const CHANGE_TURN = &#39;CHANGE_TURN&#39;;
export const RESET_GAME = &#39;RESET_GAME&#39;;

const reducer = (state, action) =&gt; {
    switch (action.type) {
        case SET_WINNER:
            return {
                ...state,
                winner: action.winner,
            };
        case CLICK_CELL: {
            const tableData = [...state.tableData];
            tableData[action.row] = [...tableData[action.row]];
            tableData[action.row][action.cell] = state.turn;
            return {
                ...state,
                tableData,
                recentCell: [action.row, action.cell], // 최근 클릭한 칸 기억해두기
            };
        }
        case CHANGE_TURN: {
            return {
                ...state,
                turn: state.turn === &#39;O&#39; ? &#39;X&#39; : &#39;O&#39;,
            };
        }
        case RESET_GAME: {
            return {
                ...state,
                turn: &#39;O&#39;,
                tableData: [
                    [&#39;&#39;, &#39;&#39;, &#39;&#39;],
                    [&#39;&#39;, &#39;&#39;, &#39;&#39;],
                    [&#39;&#39;, &#39;&#39;, &#39;&#39;],
                ],
                recentCell: [-1, -1],
            };
        }
        default:
            return state;
    }
};

const TicTacToe = () =&gt; {
    const [state, dispatch] = useReducer(reducer, initialState);
    const { tableData, turn, winner, recentCell } = state;

    const onClickTable = useCallback(
        () =&gt; {
            dispatch({ type: SET_WINNER, winner: &#39;O&#39; });
        }, []);

    useEffect(() =&gt; {
        const [row, cell] = recentCell;
        if (row &lt; 0) { // useEffect는 첫 랜더링때도 실행되기 때문에 걸러주기
            return;
        }
        let win = false;
        if (tableData[row][0] === turn &amp;&amp; tableData[row][1] === turn &amp;&amp; tableData[row][2] === turn) { // 가로 검사
            win = true;
        }
        if (tableData[0][cell] === turn &amp;&amp; tableData[1][cell] === turn &amp;&amp; tableData[2][cell] === turn) { // 세로 검사
            win = true;
        }
        if (tableData[0][0] === turn &amp;&amp; tableData[1][1] === turn &amp;&amp; tableData[2][2] === turn) { // 왼쪽 대각선 검사
            win = true;
        }
        if (tableData[0][2] === turn &amp;&amp; tableData[1][1] === turn &amp;&amp; tableData[2][0] === turn) { // 오른쪽 대각선
            win = true;
        }
        console.log(win, row, cell, tableData, turn);
        if (win) { // 승리시
            dispatch({ type: SET_WINNER, winner: turn });
            dispatch({ type: RESET_GAME });
        } else {
            let all = true; // 무승부
            tableData.forEach((row) =&gt; {  //무승부 검사
                row.forEach((cell) =&gt; {
                    if (!cell) {
                        all = false;
                    }
                });
            });
            if (all) {
                dispatch({ type: RESET_GAME });
            } else {
                dispatch({ type: CHANGE_TURN });
            }
        }
    }, [recentCell]);

    return (
        &lt;&gt;
            &lt;Table onClick={onClickTable} tableData={tableData} dispatch={dispatch} /&gt;
            {winner &amp;&amp; &lt;div&gt;{winner}님의 승리&lt;/div&gt;}
        &lt;/&gt;
    )
};

export default TicTacToe;</code></pre>
<p>❗ <code>useReducer</code>에선 state가 비동기적이기 때문에 <code>useEffect</code> 사용</p>
<h2 id="5-테이블-최적화하기">5. 테이블 최적화하기</h2>
<p><code>React.memo()</code>, <code>useMemo()</code> 사용하여 최적화하기</p>
<h4 id="tdjsx-2">Td.jsx</h4>
<pre><code class="language-jsx">import React, { useCallback, memo } from &#39;react&#39;;
import { CLICK_CELL } from &#39;./TicTacToe&#39;;

const Td = memo(({ rowIndex, cellIndex, dispatch, cellData }) =&gt; {
    const onClickTd = useCallback(() =&gt; {
        console.log(rowIndex, cellIndex);
        if (cellData) {
            return;
        }
        dispatch({ type: CLICK_CELL, row: rowIndex, cell: cellIndex });
    }, [cellData]);

    return (
        &lt;td onClick={onClickTd}&gt;{cellData}&lt;/td&gt;
    );
});

export default Td;</code></pre>
<h4 id="trjsx-1">Tr.jsx</h4>
<pre><code class="language-jsx">import React, { memo, useMemo } from &#39;react&#39;;
import Td from &#39;./Td&#39;;

const Tr = memo(({ rowData, rowIndex, dispatch }) =&gt; {
    return (
        &lt;tr&gt;
            {Array(rowData.length).fill().map((td, i) =&gt; (
                useMemo(() =&gt; &lt;Td key={i} dispatch={dispatch} rowIndex={rowIndex} cellIndex={i} cellData={rowData[i]}&gt;{&#39;&#39;}&lt;/Td&gt;,
                [rowData[i]])
            ))}
        &lt;/tr&gt;
    );
});

export default Tr;</code></pre>
<h4 id="tablejsx-1">Table.jsx</h4>
<pre><code class="language-jsx">import React, { useMemo } from &#39;react&#39;;
import Tr from &#39;./Tr&#39;;

const Table = ({ onClick, tableData, dispatch }) =&gt; {
    return (
        &lt;table&gt;
            {Array(tableData.length).fill().map((tr, i) =&gt; (
                useMemo(
                    () =&gt; &lt;Tr key={i} dispatch={dispatch} rowIndex={i} rowData={tableData[i]} /&gt;,
                    [tableData[i]]
                )
            ))}
        &lt;/table&gt;
    );
};

export default Table;</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/e2aee749-de02-457c-8017-f7a6c2b2291f/ezgif-4-ba8d9dc323f5.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-6 로또 추첨기]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-6-%EB%A1%9C%EB%98%90-%EC%B6%94%EC%B2%A8%EA%B8%B0</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-6-%EB%A1%9C%EB%98%90-%EC%B6%94%EC%B2%A8%EA%B8%B0</guid>
            <pubDate>Mon, 10 Aug 2020 11:30:19 GMT</pubDate>
            <description><![CDATA[<h2 id="1-로또-추첨기-컴포넌트">1. 로또 추첨기 컴포넌트</h2>
<h4 id="lottojsx">Lotto.jsx</h4>
<pre><code class="language-jsx">import React, { Component } from &#39;react&#39;
import Ball from &#39;./Ball&#39;; 

function getWinNumbers() {
    console.log(&#39;getWinNumbers&#39;);
    const candidate = Array(45).fill().map((v, i) =&gt; i + 1); // 45개의 숫자를 candidate 배열에 넣기
    const shuffle = [];
    while (candidate.length &gt; 0) {
        shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]); //배열에서 랜덤으로 숫자를 뽑아 셔플배열에 넣기
    }
    const bonusNumber = shuffle[shuffle.length - 1]; // 섞여진 숫자중에 마지막 숫자를 보너스로 뽑기
    const winNumbers = shuffle.slice(0, 6).sort((p, c) =&gt; p - c); // 셔플 정렬
    return [...winNumbers, bonusNumber];
};

class Lotto extends Component {
    state = {
        winNumbers: getWinNumbers(), // 당첨 숫자들
        winBalls: [],
        bonus: null, // 보너스 공
        redo: false, //재실행
    };

    render() {
        const { winBalls, bonus, redo } = this.state;
        return (
            &lt;&gt;
                &lt;div&gt;당첨 숫자&lt;/div&gt;
                &lt;div id=&quot;결과창&quot;&gt;
                    {winBalls.map((v) =&gt; &lt;Ball key={v} number={v} /&gt;)}
                &lt;/div&gt;
                &lt;div&gt;보너스!&lt;/div&gt;
                {bonus &amp;&amp; &lt;Ball number={bonus} /&gt;}
                {redo &amp;&amp; &lt;button onClick={this.onClickRedo}&gt;한 번 더!&lt;/button&gt;}
            &lt;/&gt;
        )
    }
}

export default Lotto;
</code></pre>
<h4 id="balljsx">Ball.jsx</h4>
<pre><code class="language-jsx">import React, { memo } from &#39;react&#39;

const Ball = memo(({number}) =&gt;  {
    let background;
    if(number &lt;= 10) {
        background = &#39;red&#39;;
    } else if (number &lt;= 20) {
        background = &#39;orange&#39;;
    } else if (number &lt;= 30) {
        background = &#39;yellow&#39;;
    } else if (number &lt;= 40) {
        background = &#39;blue&#39;;
    } else {
        background = &#39;green&#39;;
    }
    return (
        &lt;div className=&quot;ball&quot; style={{ background }}&gt;{number}&lt;/div&gt;
    );
});

export default Ball;</code></pre>
<h2 id="2-settimeout-여러-번-사용하기">2. setTimeout 여러 번 사용하기</h2>
<ul>
<li>for문에 <code>let</code> 사용 시 클로저 문제 발생 x</li>
<li><code>setTimemout()</code> 사용 시 끝나고 항상 clear 해줘야 함 (메모리 누수 문제 발생)<pre><code class="language-jsx">...
timeouts = [];
</code></pre>
</li>
</ul>
<p>componentDidMount() {
    const { winNumbers } = this.state;
    for (let i = 0; i &lt; winNumbers.length - 1; i++) { // 보너스 공 때문에 1 빼줌
        this.timeouts[i] = setTimeout(() =&gt; {
            this.setState((prevState) =&gt; {
                return {
                    winBalls: [...prevState.winBalls, winNumbers[i]],
                };
            });
        }, (i + 1) * 1000); // 순서대로 1초, 2초, 3초 ...
    }
    this.timeouts[6] = setTimeout(() =&gt; {
        this.setState({
            bonus: winNumbers[6], // 마지막공이 보너스 공
            redo: true,
        });
    }, 7000);
}</p>
<p>componentWillUnmount() {
    this.timeouts.forEach((v) =&gt; {
        clearTimeout(v);
    });
}</p>
<pre><code>
## 3. componentDidUpdate
`componentDidUpdate()`를 이용하여 버튼 클릭 시 재실행하게 하기
```jsx
...
runTimeouts = () =&gt; {
    const { winNumbers } = this.state;
    for (let i = 0; i &lt; winNumbers.length - 1; i++) {
        this.timeouts[i] = setTimeout(() =&gt; {
            this.setState((prevState) =&gt; {
                return {
                    winBalls: [...prevState.winBalls, winNumbers[i]],
                };
            });
        }, (i + 1) * 1000);
    }
    this.timeouts[6] = setTimeout(() =&gt; {
        this.setState({
            bonus: winNumbers[6],
            redo: true,
        });
    }, 7000);
};

componentDidMount() {
    this.runTimeouts();
}

componentDidUpdate(prevProps, prevState) { // 전 props, 전 state값이 바뀔 때 실행
    if (this.state.winBalls.length === 0) { // redo 클릭 시 winBalls는 빈 배열
        this.runTimeouts();
    }
}

onClickRedo = () =&gt; {
    this.setState({
        winNumbers: getWinNumbers(),
        winBalls: [],
        bonus: null,
        redo: false,
    });
    this.timeouts = [];
};</code></pre><h2 id="4-useeffect로-업데이트-감지하기">4. useEffect로 업데이트 감지하기</h2>
<p>Class를 Hooks로 바꾸기</p>
<pre><code class="language-jsx">import React, { useState, useRef, useEffect } from &#39;react&#39;
import Ball from &#39;./Ball&#39;;

function getWinNumbers() {
    console.log(&#39;getWinNumbers&#39;);
    const candidate = Array(45).fill().map((v, i) =&gt; i + 1); // 45개의 숫자를 candidate 배열에 넣기
    const shuffle = [];
    while (candidate.length &gt; 0) {
        shuffle.push(candidate.splice(Math.floor(Math.random() * candidate.length), 1)[0]); //배열에서 랜덤으로 숫자를 뽑아 셔플배열에 넣기
    }
    const bonusNumber = shuffle[shuffle.length - 1]; // 섞여진 숫자중에 마지막 숫자를 보너스로 뽑기
    const winNumbers = shuffle.slice(0, 6).sort((p, c) =&gt; p - c); // 셔플 정렬
    return [...winNumbers, bonusNumber];
};

const Lotto = () =&gt; {
    const [winNumbers, setWinNumbers] = useState(getWinNumbers());
    const [winBalls, setWinBalls] = useState([]);
    const [bonus, setBonus] = useState(null);
    const [redo, setRedo] = useState(false);
    const timeouts = useRef([]);

    useEffect(() =&gt; {
        console.log(&#39;useEffect&#39;);
        for (let i = 0; i &lt; winNumbers.length - 1; i++) {
            timeouts.current[i] = setTimeout(() =&gt; {
                setWinBalls((prevBalls) =&gt; [...prevBalls, winNumbers[i]]);
            }, (i + 1) * 1000);
        }
        timeouts.current[6] = setTimeout(() =&gt; {
            setBonus(winNumbers[6]);
            setRedo(true);
        }, 7000);
        return () =&gt; { // componentWillUnMount는 return
            timeouts.current.forEach((v) =&gt; {
                clearTimeout(v);
            });
        };
    }, [timeouts.current]); // 빈 배열이면 componentDidMount와 동일
    //배열에 요소가 있으면 componentDidMount랑 componentDidUpdate 둘 다 수행

    const onClickRedo = () =&gt; {
        setWinNumbers(getWinNumbers());
        setWinBalls([]);
        setBonus(null);
        setRedo(false);
        timeouts.current = [];
    };

    return (
        &lt;&gt;
            &lt;div&gt;당첨 숫자&lt;/div&gt;
            &lt;div id=&quot;결과창&quot;&gt;
                {winBalls.map((v) =&gt; &lt;Ball key={v} number={v} /&gt;)}
            &lt;/div&gt;
            &lt;div&gt;보너스!&lt;/div&gt;
            {bonus &amp;&amp; &lt;Ball number={bonus} /&gt;}
            {redo &amp;&amp; &lt;button onClick={onClickRedo}&gt;한 번 더!&lt;/button&gt;}
        &lt;/&gt;
    );
}

export default Lotto;</code></pre>
<ul>
<li><code>timeouts.current[i] = ...</code> 는 current 배열에 요소를 넣어준거라 바뀌지 않고, <code>timeouts.current = []</code> 는 current에 직접 넣어 값이 바뀜<h2 id="5-usememo와-usecallback">5. useMemo와 useCallback</h2>
<h3 id="1-usememo">1) useMemo</h3>
</li>
<li>복잡한 함수 결과값을 기억하는 함수</li>
<li>state값이 바뀔 때마다 랜더링 되면서 호출되기 때문에 결과값을 기억한 후 재사용가능<pre><code class="language-jsx">const Lotto = () =&gt; {
  const lottoNumbers = useMemo(() =&gt; getWinNumbers(), []);
  const [winNumbers, setWinNumbers] = useState(lottoNumbers);
  ...</code></pre>
<h3 id="2-usecallback">2) useCallback</h3>
</li>
<li><code>useMemo</code>는 결과값을 재사용 할 때 사용하지만, <code>useCallback</code>은 함수 자체를 기억해둬서 재실행되도 새로 생성되지 않고 재사용</li>
<li>단, useCallback안에서 state 사용 시 배열 안에 넣어줘야 함. 배열 안에 값이 바뀌면 새로 실행</li>
<li>자식 컴포넌트에게 props로 함수를 넘길 때 꼭 사용</li>
</ul>
<pre><code class="language-jsx">const onClickRedo = useCallback(() =&gt; {
    setWinNumbers(getWinNumbers());
    setWinBalls([]);
    setBonus(null);
    setRedo(false);
    timeouts.current = [];
}, [winNumbers]);</code></pre>
<blockquote>
</blockquote>
<h3 id="hooks에-대한-자잘한-팁들">Hooks에 대한 자잘한 팁들</h3>
<ul>
<li>Hooks는 순서가 매우 중요!</li>
<li>조건문 안에 절대 넣으면 안됨, 함수나 반복문 안에도 웬만하면 넣지 말기</li>
</ul>
<p><img src="https://images.velog.io/images/ji-silver/post/80e6e391-14e0-4ebb-ae51-e824e3517e1c/ezgif-7-d5f8c04806af.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-5 가위바위보]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-5-%EA%B0%80%EC%9C%84%EB%B0%94%EC%9C%84%EB%B3%B4</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-5-%EA%B0%80%EC%9C%84%EB%B0%94%EC%9C%84%EB%B3%B4</guid>
            <pubDate>Thu, 30 Jul 2020 17:19:44 GMT</pubDate>
            <description><![CDATA[<h2 id="1-리액트-라이프사이클">1. 리액트 라이프사이클</h2>
<ul>
<li><code>componentDidMount()</code>: 처음 render가 성공적으로 됐다면 실행, 리랜더링이 되면 실행x (비동기 요청)</li>
<li><code>componentDidUpdate()</code>: 리랜더링 후 실행</li>
<li><code>componentWillUnmount()</code>: 컴포넌트가 제거되기 직전 실행 (비동기 요청 정리)</li>
<li>클래스의 경우
constructor -&gt; render -&gt; ref -&gt; <strong>componentDidMount</strong> -&gt;
(setState / props 바뀔 때) -&gt; shouldComponentUpdate(true) -&gt; render -&gt;  <strong>componentDidUpdate</strong> -&gt;
부모가 나를 없앴을 때 -&gt; <strong>componentWillUnmount</strong> -&gt; 소멸</li>
</ul>
<h2 id="2-setinterval과-라이프사이클-연동하기">2. setInterval과 라이프사이클 연동하기</h2>
<pre><code class="language-jsx">interval;

componentDidMount() {
    this.interval = setInterval(() =&gt; {
      ...
    }, 1000);
}

componentWillUnmount() {
    clearInterval(this.interval);
}</code></pre>
<p><code>setInterval()</code>, <code>setTimeout()</code>은 없애주지 않으면 메모리 누수가 생기므로 완료되지 않은 비동기 요청은 <strong>componentWillUnmount()</strong>에서 정리해주기</p>
<blockquote>
<p>❗ 비동기 안에서 바깥에 있는 함수를 참조하면 <strong>클로저</strong> 문제 발생 항상 주의</p>
</blockquote>
<h2 id="3-가위바위보-게임-만들기">3. 가위바위보 게임 만들기</h2>
<h4 id="rspjsx">RSP.jsx</h4>
<pre><code class="language-jsx">import React, { Component } from &#39;react&#39;;

const rspCoords = {
    바위: &#39;0&#39;,
    가위: &#39;-260px&#39;,
    보: &#39;-538px&#39;
};

const scores = {
    가위: 1,
    바위: 0,
    보: -1,
};

const computerChoice = (imgCoord) =&gt; {
    return Object.entries(rspCoords).find(function (v) {
        return v[1] === imgCoord;
    })[0];
};

class RSP extends Component {
    state = {
        result: &#39;&#39;,
        imgCoord: &#39;0&#39;,
        score: 0,
    };

    interval;

    componentDidMount() {
        this.interval = setInterval(this.changeHand, 100);
    }

    componentWillUnmount() {
        clearInterval(this.interval);
    }

    changeHand = () =&gt; {
        const { imgCoord } = this.state;
        if (imgCoord === rspCoords.바위) {
            this.setState({
                imgCoord: rspCoords.가위,
            });
        } else if (imgCoord === rspCoords.가위) {
            this.setState({
                imgCoord: rspCoords.보,
            });
        } else if (imgCoord === rspCoords.보) {
            this.setState({
                imgCoord: rspCoords.바위,
            });
        }
    }

    // 버튼 클릭 시 인터벌 잠시 멈추고 점수 계산 후 다시 실행
    onClickBtn = (choice) =&gt; {
        const { imgCoord } = this.state;
        clearInterval(this.interval);
        const myScore = scores[choice];
        const cpuScore = scores[computerChoice(imgCoord)];
        const diff = myScore - cpuScore; // 내 스코어와 컴퓨터 스코어 빼기
        if (diff === 0) { // 차이가 없으면
            this.setState({
                result: &#39;비겼습니다!&#39;,
            });
        } else if ([-1, 2].includes(diff)) {
            this.setState((prevState) =&gt; {
                return {
                    result: &#39;이겼습니다!&#39;,
                    score: prevState.score + 1,
                };
            });
        } else {
            this.setState((prevState) =&gt; {
                return {
                    result: &#39;졌습니다!&#39;,
                    score: prevState.score - 1,
                };
            });
        }
        setTimeout(() =&gt; {
            this.interval = setInterval(this.changeHand, 100);
        }, 2000); // 결과 나온 후 2초 기다렸다가 다시 돌리기
    };

    render() {
        const { result, score, imgCoord } = this.state;
        return (
            &lt;&gt;
                &lt;div id=&quot;computer&quot; style={{ background: `url(https://data.ac-illust.com/data/thumbnails/4f/4f63b32d7d43ea2cb231c0724200cf8e_t.jpeg) ${imgCoord} 0` }}&gt;&lt;/div&gt;
                &lt;div&gt;
                    &lt;button id=&quot;rock&quot; className=&quot;btn&quot; onClick={() =&gt; this.onClickBtn(&#39;바위&#39;)}&gt;바위&lt;/button&gt;
                    &lt;button id=&quot;scissor&quot; className=&quot;btn&quot; onClick={() =&gt; this.onClickBtn(&#39;가위&#39;)}&gt;가위&lt;/button&gt;
                    &lt;button id=&quot;paper&quot; className=&quot;btn&quot; onClick={() =&gt; this.onClickBtn(&#39;보&#39;)}&gt;보&lt;/button&gt;
                &lt;/div&gt;
                &lt;div&gt;{result}&lt;/div&gt;
                &lt;div&gt;현재 {score}점&lt;/div&gt;
            &lt;/&gt;
        );
    }
}

export default RSP;</code></pre>
<h2 id="4-고차-함수">4. 고차 함수</h2>
<p>메서드 안에 함수를 호출하는 부분이 있으면 화살표 함수를 빼서 함수를 연달아 쓸 수 있음</p>
<pre><code class="language-jsx">onClickBtn = (choice) =&gt; () =&gt; {
  ...
};

...

render() {
  return (
    &lt;button onClick={this.onClickBtn(&#39;바위&#39;)}&gt;바위&lt;/button&gt;
    &lt;button onClick={this.onClickBtn(&#39;가위&#39;)}&gt;가위&lt;/button&gt;
    &lt;button onClick={this.onClickBtn(&#39;보&#39;)}&gt;보&lt;/button&gt;
  );
}</code></pre>
<h2 id="5-hooks와-useeffect">5. Hooks와 useEffect</h2>
<p>Hooks엔 LifeCycle이 없지만 <strong>useEffect</strong>로 동일하게 동작하게 할 수 있음</p>
<pre><code class="language-jsx">useEffect(() =&gt; { // componentDidMount, componentDidUpadate 역할 (1대1 대응은 아님)
    interval.current = setInterval(changeHand, 100);
    return () =&gt; { //componentWillUnmount 역할
        clearInterval(interval.current);
    }
}, [imgCoord]);
//두번째 인수 배열에 넣은 값(imgCoord)이 바뀔 때 useEffect 실행 (useEffect를 다시 실행할 값만 넣어주기)</code></pre>
<p>매번 <code>clearInterval()</code>을 하기 때문에 <code>setTimeout()</code> 하는 것과 동일</p>
<blockquote>
<p>👉 state가 업데이트 될 때 마다 useEffect가 실행 후 종료가 되므로 <code>setTimeout()</code>을 사용해도 됨!</p>
</blockquote>
<h2 id="6-클래스와-hooks-라이프사이클-비교">6. 클래스와 Hooks 라이프사이클 비교</h2>
<ul>
<li><strong>클래스</strong>: componentDidMount, componentDidUpadate, componentWillUnmount로 state를 한 번에 조작할 수 있음</li>
<li><strong>Hooks</strong>: useEffect로 state를 개별로 담당. useEffect는 여러 개 작성 가능</li>
</ul>
<p><img src="https://images.velog.io/images/ji-silver/post/5bb79c0e-25ae-4028-b52a-18ce3fbb4f3b/ezgif-7-e87a4a0ccfc61.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-4 반응속도체크]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-4-%EB%B0%98%EC%9D%91%EC%86%8D%EB%8F%84%EC%B2%B4%ED%81%AC</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-4-%EB%B0%98%EC%9D%91%EC%86%8D%EB%8F%84%EC%B2%B4%ED%81%AC</guid>
            <pubDate>Sat, 25 Jul 2020 16:12:56 GMT</pubDate>
            <description><![CDATA[<h2 id="1-react-조건문">1. React 조건문</h2>
<ul>
<li>jsx에서는 for, if문 사용 x -&gt; <strong>삼항연산자</strong> 또는 <strong>AND(&amp;&amp;) 연산자</strong> 사용<h4 id="responsecheckjsx">ResponseCheck.jsx</h4>
<pre><code class="language-jsx">renderAverage = () =&gt; {
  const { result } = this.state;
  return result.length === 0
      ? null // jsx에서 false, undefined, null은 태그 없음을 의미. 처음엔 null, result에 값이 있으면 랜더링
      : &lt;div&gt;평균 시간: {result.reduce((a, c) =&gt; a + c) / result.length}ms&lt;/div&gt;
}</code></pre>
<blockquote>
<p>코드가 지저분할 때 이렇게 함수로 랜더링 부분을 분리해도 되지만 새로운 컴포넌트로 만들어 주는 게 더 좋음!</p>
</blockquote>
</li>
</ul>
<h2 id="2-settimeout-넣어-반응속도체크">2. setTimeout 넣어 반응속도체크</h2>
<pre><code class="language-jsx">timeout;
startTime;
endTime;

onClickScreen = () =&gt; {
    const { state, message, result } = this.state;
    if (state === &#39;waiting&#39;) {
        this.setState({
            state: &#39;ready&#39;,
            message: &#39;초록색이 되면 클릭하세요.&#39;,
        });
        this.timeout = setTimeout(() =&gt; {
            this.setState({
                state: &#39;now&#39;,
                message: &#39;지금 클릭&#39;,
            });
            this.startTime = new Date(); // state에 넣으면 바뀔 때 마다 랜더링 되기 때문에 this.startTime
        }, Math.floor(Math.random() * 1000) + 2000); // 2초 ~ 3초 중 랜덤
    } else if (state === &#39;ready&#39;) {
        clearTimeout(this.timeout); // 초기화 되지 않고 setTimeout이 실행되기 때문에 삭제하는 코드
        this.setState({
            state: &#39;waiting&#39;,
            message: &#39;너무 성급하시군요! 초록색이 된 후에 클릭하세요.&#39;,
        });
    } else if (state === &#39;now&#39;) { // 반응 속도 체크
        this.endTime = new Date();
        this.setState((prevState) =&gt; {
            return {
                state: &#39;waiting&#39;,
                message: &#39;클릭해서 시작하세요.&#39;,
                result: [...prevState.result, this.endTime - this.startTime],
            };
        });
    }
};</code></pre>
<h2 id="3-반응속도체크-hooks로-전환하기">3. 반응속도체크 Hooks로 전환하기</h2>
<ul>
<li>this의 속성들을 ref로 표현, ref는 안에 <strong>current</strong>가 들어 있어서 값을 가져올 때도 뒤에 current를 넣어야함</li>
<li>useRef 는 값이 바뀌기는 하지만 화면에 영향을 미치고 싶지 않을 때 사용 (리랜더링 되지 않음)<pre><code class="language-jsx">import React, { useState, useRef } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>const ResponseCheck = () =&gt; {
    const [state, setState] = useState(&#39;waiting&#39;);
    const [message, setMessage] = useState(&#39;클릭해서 시작하세요.&#39;);
    const [result, setResult] = useState([]);
    const timeout = useRef(null);
    const startTime = useRef();
    const endTime = useRef();</p>
<pre><code>const onClickScreen = () =&gt; {
    if (state === &#39;waiting&#39;) {
        setState(&#39;ready&#39;);
        setMessage(&#39;초록색이 되면 클릭하세요.&#39;);
        timeout.current = setTimeout(() =&gt; {
            setState(&#39;now&#39;);
            setMessage(&#39;지금 클릭&#39;);
            startTime.current = new Date();
        }, Math.floor(Math.random() * 1000) + 2000);
    } else if (state === &#39;ready&#39;) {
        clearTimeout(timeout.current);
        setState(&#39;waiting&#39;);
        setMessage(&#39;너무 성급하시군요! 초록색이 된 후에 클릭하세요.&#39;);
    } else if (state === &#39;now&#39;) {
        endTime.current = new Date();
        setState(&#39;waiting&#39;);
        setMessage(&#39;클릭해서 시작하세요.&#39;);
        setResult(prevResult =&gt; {
            return [...prevResult, endTime.current - startTime.current];
        });
    }
};

const onReset = () =&gt; {
    setResult([]);
};

const renderAverage = () =&gt; {
    return result.length === 0
        ? null
        : &lt;&gt;
            &lt;div&gt;평균 시간: {result.reduce((a, c) =&gt; a + c) / result.length}ms&lt;/div&gt;
            &lt;button onClick={onReset}&gt;리셋&lt;/button&gt;
        &lt;/&gt;
};

return (
    &lt;&gt;
        &lt;div id=&quot;screen&quot; className={state} onClick={onClickScreen}&gt;
            {message}
        &lt;/div&gt;
        {renderAverage()}
    &lt;/&gt;
);</code></pre><p>};</p>
<p>export default ResponseCheck;</p>
<p>```</p>
<h2 id="4-return-내부에-for과-if-쓰기">4. return 내부에 for과 if 쓰기</h2>
<ul>
<li>jsx에선 <code>{}</code> 중괄호 사용 시 자바스크립트 코드 사용 가능</li>
<li>jsx에선 못 쓰지만 함수 안에서는 for, if문 사용 가능</li>
</ul>
<p>함수를 선언 하자마자 즉시 <strong>실행 하는 함수</strong>로 만들어주기
<img src="https://images.velog.io/images/ji-silver/post/c9d5fa29-715f-4d51-9f1a-3a8ebb15d1d8/ezgif-4-010ff441d0cc.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-3 숫자야구]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-3-%EC%88%AB%EC%9E%90%EC%95%BC%EA%B5%AC</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-3-%EC%88%AB%EC%9E%90%EC%95%BC%EA%B5%AC</guid>
            <pubDate>Thu, 23 Jul 2020 14:41:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-import와-require-비교">1. import와 require 비교</h2>
<p>1) <strong>require</strong>: node의 모듈 시스템</p>
<pre><code class="language-jsx">//NumberBaseball.jsx
module.exports = NumberBaseball; 

//client.jsx
const NumberBaseball = require(&#39;./NumberBaseball&#39;);</code></pre>
<p>NumberBaseball class가 module.exports 담겨서 다른파일에서 require로 불러오기</p>
<p>2) <strong>import</strong>: ES2015(ES6) 모듈 시스템</p>
<pre><code class="language-jsx">export const hello = &#39;hello&#39;; // 가져올 때 import { hello }
export default NumberBaseball; // 가져올 때 import NumberBaseball

import React, { Component } from &#39;react&#39;;
//따라서 React는 export default, Component는 export const Component</code></pre>
<ul>
<li>exports 되는 게 객체나 배열이면 구조 분해 가능</li>
<li>default는 한 번만 사용 가능</li>
<li>엄밀히 따지면 export default와 module.exports 다르지만 호환은 가능</li>
</ul>
<blockquote>
<p>node로 webpack을 돌리면 import 사용 시 에러가 나지만 babel이 import를 require로 바꿔줌
(node에서는 require, react에서는 import와 export 사용)</p>
</blockquote>
<h2 id="2-리액트-반복문map">2. 리액트 반복문(map)</h2>
<ul>
<li>리액트에서는 반복문을 map 메서드로 사용</li>
<li>달라지는 부분을 배열로 만들고 받아오기. 공통되는 부분은 return<pre><code class="language-jsx">&lt;ul&gt;
  {[&#39;사과&#39;, &#39;바나나&#39;, &#39;포도&#39;, &#39;귤&#39;, &#39;감&#39;, &#39;배&#39;, &#39;밤&#39;,].map((v) =&gt; {
      return (
          &lt;li&gt;{v}&lt;/li&gt;
      );
  })}
&lt;/ul&gt;</code></pre>
</li>
</ul>
<p><img src="https://images.velog.io/images/ji-silver/post/cddc46d7-d41f-482e-9d7b-9679691de561/%EC%BA%A1%EC%B2%98.PNG" alt=""></p>
<h2 id="3-리액트-반복문key">3. 리액트 반복문(key)</h2>
<ul>
<li>달라지는 부분을 2차원 배열 또는 객체로 만들기</li>
<li>리액트가 key를 보고 같은 컴포넌트인지 아닌지 판단.
key는 고유한 값이 들어가야함<pre><code class="language-jsx">&lt;ul&gt;
  {[
      { fruit: &#39;사과&#39;, taste: &#39;달다&#39; },
      { fruit: &#39;바나나&#39;, taste: &#39;맛없다&#39; },
      { fruit: &#39;포도&#39;, taste: &#39;새콤하다&#39; },
      { fruit: &#39;귤&#39;, taste: &#39;시다&#39; },
      { fruit: &#39;감&#39;, taste: &#39;떫다&#39; },
      { fruit: &#39;배&#39;, taste: &#39;맛있다&#39; },
      { fruit: &#39;밤&#39;, taste: &#39;맛있다&#39; },
  ].map((v, i) =&gt; { //두번째는 인덱스
      return (
          &lt;li key={v.fruit + v.taste}&gt;&lt;b&gt;{v.fruit}&lt;/b&gt; - {v.taste}&lt;/li&gt;
      );
  })}
&lt;/ul&gt;</code></pre>
</li>
</ul>
<p>화살표 함수 사용 시 return 생략 가능. 중괄호가 없으면 return을 뜻함</p>
<pre><code class="language-jsx">.map((v, i) =&gt;
    &lt;li key={v.fruit + v.taste}&gt;&lt;b&gt;{v.fruit}&lt;/b&gt; - {v.taste}&lt;/li&gt;
)</code></pre>
<h2 id="4-컴포넌트-분리와-props">4. 컴포넌트 분리와 props</h2>
<ul>
<li>컴포넌트를 분리하는 이유: 재사용성, 가독성, 성능 최적화(반복문 사용 시 성능 문제 발생)</li>
<li>컴포넌트 분리 시 매개변수 <code>v</code>, <code>i</code>를 사용하기 위해 연결해주는 <strong>props</strong> 필요
NumberBaseball class가 Try의 부모!<h4 id="tryjsx">Try.jsx</h4>
<pre><code class="language-jsx">import React, { Component } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>class Try extends Component {
    render() {
        return(
            <li>
                <b>{this.props.value.fruit}</b> - {this.props.index}
                <div>컨텐츠</div>
                <div>컨텐츠1</div>
                <div>컨텐츠2</div>
                <div>컨텐츠3</div>
            </li>
        );
    }
}</p>
<p>export default Try;</p>
<pre><code>#### NumberBaseball.jsx
```jsx
import Try from &#39;./Try&#39;;
...
fruits = [
    { fruit: &#39;사과&#39;, taste: &#39;달다&#39; },
    { fruit: &#39;바나나&#39;, taste: &#39;맛없다&#39; },
    { fruit: &#39;포도&#39;, taste: &#39;새콤하다&#39; },
    { fruit: &#39;귤&#39;, taste: &#39;시다&#39; },
    { fruit: &#39;감&#39;, taste: &#39;떫다&#39; },
    { fruit: &#39;배&#39;, taste: &#39;맛있다&#39; },
    { fruit: &#39;밤&#39;, taste: &#39;맛있다&#39; },
];

render() {
    return (
        &lt;&gt;
            ...
            &lt;ul&gt;
                {this.fruits.map((v, i) =&gt; {
                    return (
                        &lt;Try key={v.fruit + v.taste} value={v} index={i} /&gt;
                    );
                })}
            &lt;/ul&gt;
        &lt;/&gt;
    );
}
</code></pre><h2 id="5-주석과-메서드-바인딩">5. 주석과 메서드 바인딩</h2>
<h3 id="1-주석">1) 주석</h3>
<pre><code class="language-jsx">{/* jsx 주석 */}</code></pre>
<h3 id="2-바인딩">2) 바인딩</h3>
<p><code>render()</code> 밖에서 화살표 함수를 쓰지 않으면 this가 undefined가 되어 연결해주는 <code>bind()</code>함수를 써야함.
화살표 함수를 사용하면 자동으로 바인딩</p>
<h2 id="6-숫자야구-만들기">6. 숫자야구 만들기</h2>
<ul>
<li><code>push()</code> 사용 시 뭐가 바뀌었는지 감지 x, 예전 state와 현재 state가 다르면 render<h4 id="numberbaseballjsx">NumberBaseball.jsx</h4>
<pre><code class="language-jsx">import React, { Component } from &#39;react&#39;;
import Try from &#39;./Try&#39;;
</code></pre>
</li>
</ul>
<p>function getNumbers() { //숫자 4개를 겹치지 않고 랜덤하게 뽑는 함수
    const candidate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    const array = [];
    for (let i = 0; i &lt; 4; i += 1) {
        const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
        array.push(chosen);
    }
    return array;
}</p>
<p>class NumberBaseball extends Component {
    state = {
        result: &#39;&#39;,
        value: &#39;&#39;,
        answer: getNumbers(),
        tries: [],
    };</p>
<pre><code>onSubmitForm = (e) =&gt; {
    const { result, value, tries, answer } = this.state;
    e.preventDefault();
    if (value === answer.join(&#39;&#39;)) { //답이 맞는 지 비교
        this.setState((prevState) =&gt; {
            return {
                result: &#39;홈런!&#39;,
                tries: [...prevState.tries, { try: value, result: &#39;홈런!&#39; }],
            }
        });
        alert(&#39;게임을 다시 시작합니다!&#39;);
        this.setState({
            value: &#39;&#39;,
            answer: getNumbers(),
            tries: [],
        });
    } else { // 답 틀렸으면
        const answerArray = value.split(&#39;&#39;).map((v) =&gt; parseInt(v));
        let strike = 0;
        let ball = 0;
        if (tries.length &gt;= 9) { // 10번 이상 틀렸을 때
            this.setState({
                result: `10번 넘게 틀려서 실패! 답은 ${answer.join(&#39;,&#39;)}였습니다!`,
            });
            alert(&#39;게임을 다시 시작합니다!&#39;);
            this.setState({
                value: &#39;&#39;,
                answer: getNumbers(),
                tries: [],
            });
        } else {
            for (let i = 0; i &lt; 4; i += 1) {
                if (answerArray[i] === answer[i]) {
                    strike += 1;
                } else if (answer.includes(answerArray[i])) {
                    ball += 1;
                }
            }
            this.setState((prevState) =&gt; {
                return {
                    tries: [...prevState.tries, { try: value, result: `${strike} 스트라이크, ${ball} 볼입니다` }],
                    value: &#39;&#39;,
                };
            });
        }
    }
};

onChangeInput = (e) =&gt; {
    console.log(this.state.answer);
    this.setState({
        value: e.target.value,
    });
};

render() {
    const { result, value, tries } = this.state;
    return (
        &lt;&gt;
            &lt;h1&gt;{result}&lt;/h1&gt;
            &lt;form onSubmit={this.onSubmitForm}&gt;
                &lt;input maxLength={4} value={value} onChange={this.onChangeInput} /&gt;
            &lt;/form&gt;
            &lt;div&gt;시도: {tries.length}&lt;/div&gt;
            &lt;ul&gt;
                {tries.map((v, i) =&gt; {
                    return (
                        &lt;Try key={`${i + 1}차 시도: `} tryInfo={v} /&gt;
                    );
                })}
            &lt;/ul&gt;
        &lt;/&gt;
    );
}</code></pre><p>}</p>
<p>export default NumberBaseball;</p>
<pre><code>#### Try.jsx
```jsx
import React, { Component } from &#39;react&#39;;

class Try extends Component {
    render() {
        const { tryInfo } = this.props; // 구조 분해 문법
        return (
            &lt;li&gt;
                &lt;div&gt;{tryInfo.try}&lt;/div&gt;
                &lt;div&gt;{tryInfo.result}&lt;/div&gt;
            &lt;/li&gt;
        );
    }
}

export default Try;</code></pre><h2 id="7-숫자야구-hooks로-전환하기">7. 숫자야구 Hooks로 전환하기</h2>
<h4 id="numberbaseballjsx-1">NumberBaseball.jsx</h4>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;
import Try from &#39;./Try&#39;;

function getNumbers() {
    const candidate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    const array = [];
    for (let i = 0; i &lt; 4; i += 1) {
        const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
        array.push(chosen);
    }
    return array;
}

const NumberBaseball = () =&gt; {
    const [result, setResult] = useState(&#39;&#39;);
    const [value, setValue] = useState(&#39;&#39;);
    const [answer, setAnswer] = useState(getNumbers());
    const [tries, setTries] = useState([]);

    const onSubmitForm = (e) =&gt; {
        e.preventDefault();
        if (value === answer.join(&#39;&#39;)) {
            setResult(&#39;홈런!&#39;);
            setTries((prevTries) =&gt; { // 이전 tries 배열로 새 tries 배열을 만들기 때문에 함수형으로 만들기
                return [...prevTries, { try: value, result: &#39;홈런!&#39; }]
            });
            alert(&#39;게임을 다시 시작합니다!&#39;);
            setValue(&#39;&#39;);
            setAnswer(getNumbers());
            setTries([]);
        } else {
            const answerArray = value.split(&#39;&#39;).map((v) =&gt; parseInt(v));
            let strike = 0;
            let ball = 0;
            if (tries.length &gt;= 9) {
                setResult(`10번 넘게 틀려서 실패! 답은 ${answer.join(&#39;,&#39;)}였습니다!`);
                alert(&#39;게임을 다시 시작합니다!&#39;);
                setValue(&#39;&#39;);
                setAnswer(getNumbers());
                setTries([]);
            } else {
                console.log(answer.join(&#39;&#39;));
                for (let i = 0; i &lt; 4; i += 1) {
                    if (answerArray[i] === answer[i]) {
                        strike += 1;
                    } else if (answer.includes(answerArray[i])) {
                        ball += 1;
                    }
                }
                setTries((prevTries) =&gt; [...prevTries, { try: value, result: `${strike} 스트라이크, ${ball} 볼입니다` }]);
                setValue(&#39;&#39;);
            }
        }
    };

    const onChangeInput = (e) =&gt; {
        setValue(e.target.value);
    };

    return (
        &lt;&gt;
            &lt;h1&gt;{result}&lt;/h1&gt;
            &lt;form onSubmit={onSubmitForm}&gt;
                &lt;input maxLength={4} value={value} onChange={onChangeInput} /&gt;
            &lt;/form&gt;
            &lt;div&gt;시도: {tries.length}&lt;/div&gt;
            &lt;ul&gt;
                {tries.map((v, i) =&gt; {
                    return (
                        &lt;Try key={`${i + 1}차 시도: `} tryInfo={v} /&gt;
                    );
                })}
            &lt;/ul&gt;
        &lt;/&gt;
    );
};

export default NumberBaseball;</code></pre>
<h4 id="tryjsx-1">Try.jsx</h4>
<pre><code class="language-jsx">import React from &#39;react&#39;;

const Try = ({tryInfo}) =&gt; {
  return (
    &lt;li&gt;
      &lt;div&gt;{tryInfo.try}&lt;/div&gt;
      &lt;div&gt;{tryInfo.result}&lt;/div&gt;
    &lt;/li&gt;
  );
};

export default Try;</code></pre>
<h2 id="8-shouldcomponentupdate">8. shouldComponentUpdate</h2>
<ul>
<li>state, props가 바뀌거나 setState 호출 시 랜더링 됨.
건들이지 않은 컴포넌트도 다시 랜더링 👉 <em>성능 문제 발생</em></li>
<li><code>shouldComponentUpdate()</code>의 return 값이 true면 랜더링, false 랜더링 하지 않게 구현</li>
</ul>
<h2 id="9-purecomponent와-reactmemo">9. PureComponent와 React.memo</h2>
<h3 id="1-purecomponent">1) PureComponent</h3>
<p><code>shouldComponentUpdate()</code>에서 true, false 할 것인지를 state가 바뀌었는지 안 바뀌었는지 판단 후 자동으로 구현한 컴포넌트</p>
<pre><code class="language-jsx">import React, { PureComponent } from &#39;react&#39;;

class Test extends PureComponent { ...</code></pre>
<p>❗ 단 state엔 복잡한 객체구조는 안 쓰는 게 좋음</p>
<h3 id="2-reactmemo">2) React.memo</h3>
<p>Hooks에서 사용하는 리랜더링 방지 함수</p>
<pre><code class="language-jsx">const React = require(&#39;react&#39;);
const { memo } = React; 

const Try = memo(({ tryInfo }) =&gt; {...</code></pre>
<h2 id="10-reactcreateref">10. React.createRef</h2>
<ul>
<li>class에서 ref를 Hooks와 비슷하게 만들 수 있음</li>
<li><code>this.inputRef.current.focus();</code> 로 접근 가능<pre><code class="language-jsx">import React, { Component, createRef } from &#39;react&#39;;
...
InputRef = createRef();
</code></pre>
</li>
</ul>
<pre><code>함수형 방식은 세밀한 작업을 하고싶을 때 사용

## 11. props와 state 연결하기
- `render()` 안에 `this.setState()` 사용 시 무한반복이 되므로 사용 X
- props는 부모가 바꿔야하며 자식이 바꿀 수 없음
만약 props를 바꿔야한다면 state로 만들어서 바꾸기
```jsx
const Try = memo(({ tryInfo }) =&gt; {
    const [result, setResult] = useState(tryInfo.result);

    const onClick = () =&gt; {
        setResult(&#39;1&#39;);
    }

    return (
        &lt;li&gt;
            &lt;div&gt;{tryInfo.try}&lt;/div&gt;
            &lt;div onClick={onClick}&gt;{tryInfo.result}&lt;/div&gt;
        &lt;/li&gt;
    );
});</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-2 끝말잇기]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-2-%EB%81%9D%EB%A7%90%EC%9E%87%EA%B8%B0</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-2-%EB%81%9D%EB%A7%90%EC%9E%87%EA%B8%B0</guid>
            <pubDate>Fri, 17 Jul 2020 15:35:17 GMT</pubDate>
            <description><![CDATA[<h2 id="1-웹팩-설치하기">1. 웹팩 설치하기</h2>
<ul>
<li><strong>create-react-app</strong>으로 자동 설치 가능</li>
<li>터미널 실행 후 <code>npm init</code>: package.json 파일 생성</li>
<li><code>npm i react react-dom</code>: npm이 react, react-dom 설치</li>
<li><code>npm i -D webpack webpack-cli</code>: webpack, webpack-cli 설치
<code>-D</code>: 개발할 때만 사용한다는 의미. 실제 서비스 할 땐 webpack 필요 x </li>
<li>👉실제 서비스에서는 <strong>dependencies</strong> 기록
개발에만 쓰이는 것들은 <strong>devDependencies</strong> 에 기록</li>
</ul>
<h4 id="clientjsx">client.jsx</h4>
<pre><code class="language-jsx">//React, React-dom 불러오기
const React = require(&#39;react&#39;);
const ReactDom = require(&#39;react-dom&#39;);</code></pre>
<p>jsx문법 사용 시 웬만하면 파일 확장자를 jsx로 쓰기</p>
<h4 id="indexhtml">index.html</h4>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;끝말잇기&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;script src=&quot;./dist/app.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="2-모듈-시스템과-웹팩-설정">2. 모듈 시스템과 웹팩 설정</h2>
<h4 id="wordrelayjsx">WordRelay.jsx</h4>
<pre><code class="language-jsx">//npm에서 react를 불러오기
const React = require(&#39;react&#39;);
const { Component } = require(&#39;react&#39;);

class WordRelay extends Component {
    state = {
        text: &#39;Hello, webpack&#39;
    };
    render() {
        return &lt;h1&gt;{this.state.text}&lt;/h1&gt;;
    }
}
//컴포넌트를 바깥에서도 사용할 수 있는 코드
module.exports = WordRelay;</code></pre>
<h4 id="clientjsx-1">client.jsx</h4>
<pre><code class="language-jsx">const React = require(&#39;react&#39;);
const ReactDom = require(&#39;react-dom&#39;);

// 필요한 모듈 불러오기
const WordRelay = require(&#39;./WordRelay&#39;);

ReactDom.render(&lt;WordRelay /&gt;, document.querySelector(&#39;#root&#39;));</code></pre>
<h4 id="webpackconfigjs">webpack.config.js</h4>
<pre><code class="language-jsx">//webpack은 webpack.config.js 모든게 돌아감
const path = require(&#39;path&#39;);

module.exports = {
    name: &#39;wordrelay-setting&#39;,
    mode: &#39;development&#39;, // 실서비스: production
    devtool: &#39;eval&#39;, // 빠르게
    resolve: {
        extensions: [&#39;.js&#39;, &#39;.jsx&#39;]
    },

    entry: {
        app: [&#39;./client&#39;]
    }, // 입력
    output: {
        path: path.join(__dirname, &#39;dist&#39;),
        filename: &#39;app.js&#39;
    }, // 출력
};</code></pre>
<ul>
<li><code>path.join</code>: 경로를 함쳐줌</li>
<li><code>__dirname</code>: 현재 폴더 (현재 폴더가 dist라는 것)
👉 client.jsx 와 WordRelay.jsx를 합쳐 app.js 파일 생성</li>
</ul>
<h2 id="3-웹팩으로-빌드하기">3. 웹팩으로 빌드하기</h2>
<blockquote>
<ul>
<li>webpack은 명령어로 등록이 안 되어있어서 터미널에서 오류 발생</li>
</ul>
</blockquote>
<p>1) webpack 명령어 등록을 해주기
2) package.json에 scripts로 등록 후 <code>npm run dev</code></p>
<pre><code>&quot;scripts&quot;: {
    &quot;dev&quot;: &quot;webpack&quot;
},</code></pre><p>3) 터미널에 <code>npx webpack</code> 입력
👉 dist 디렉터리에 app.js 파일 생성됨</p>
<h3 id="1-babel-설치">1) babel 설치</h3>
<ul>
<li>npm i -D @babel/core @babel/preset-env @babel/preset-react babel-loader @babel/plugin-proposal-class-properties<ul>
<li><code>@babel/core</code>: babel 기본</li>
<li><code>@babel/preset-env</code>: 브라우저에 맞게 이전 문법지원</li>
<li><code>@babel/preset-react</code>: jsx 지원</li>
<li><code>babel-loader</code>: babel과 webpack 연결</li>
</ul>
</li>
</ul>
<h3 id="2-babel-webpack-연결">2) babel, webpack 연결</h3>
<h4 id="webpackconfigjs-1">webpack.config.js</h4>
<pre><code class="language-jsx">//entry파일 읽고 module 적용 후 output으로 출력

entry: {
    app: [&#39;./client&#39;]
}, // 입력

module: {
    rules: [{ // 여러개의 규칙 적용할 수 있기 때문에 배열
        test: /\.jsx?/, // 정규표현식. js, jsx파일에 rule 적용(babel)
        loader: &#39;babel-loader&#39;, 
        options: {
            presets: [&#39;@babel/preset-env&#39;, &#39;@babel/preset-react&#39;],
            plugins: [&#39;@babel/plugin-proposal-class-properties&#39;],
        },
    }],
},

output: {
    path: path.join(__dirname, &#39;dist&#39;),
    filename: &#39;app.js&#39;
}, // 출력</code></pre>
<h2 id="4-babelpreset-env와-plugins">4. @babel/preset-env와 plugins</h2>
<h3 id="1-babelpreset-env">1) @babel/preset-env</h3>
<ul>
<li>presets: plugin들의 모음</li>
<li>@babel/preset-env: 이전 브라우저 자동 지원</li>
<li><a href="https://github.com/browserslist/browserslist#full-list">browserslist</a> 옵션들 참고<h4 id="webpackconfigjs-2">webpack.config.js</h4>
<pre><code class="language-jsx">options: {
  presets: [
      [&#39;@babel/preset-env&#39;, {
          target: {
              browsers: [&#39;&gt; 5% in KR&#39;, &#39;last 2 chrome versions&#39;],
              // 한국에서 브라우저 점유율이 5% 이상인 브라우저는 지원
              // chrome의 최신버전 2가지만 호환되게, 모든 브라우저를 호환하면 babel의 작업량이 늘어나서 느려짐 
          },
          debug: true,
      }],
      &#39;@babel/preset-react&#39;
  ],
  plugins: [&#39;@babel/plugin-proposal-class-properties&#39;],
},</code></pre>
<h3 id="2-plugins">2) plugins</h3>
<pre><code class="language-js">module.exports = {
  mode: &#39;development&#39;,
  entry: {
    ...
  },
  module: {
    ...
  },
  plugins: [
      new webpack.LoaderOptionsPlugin({ debug: true }),
    // options에 debug: true 넣어주기
  ],
  output: {
    ...
  },
};</code></pre>
mode, entry, module, plugins, output이 제일 중요!<h2 id="5-끝말잇기-class-만들기">5. 끝말잇기 class 만들기</h2>
<h4 id="wordrelayjsx-1">WordRelay.jsx</h4>
<pre><code class="language-jsx">const React = require(&#39;react&#39;);
const { Component } = require(&#39;react&#39;);
</code></pre>
</li>
</ul>
<p>class WordRelay extends Component {
    state = {
        word: &#39;제로초&#39;,
        value: &#39;&#39;,
        result: &#39;&#39;,
    };</p>
<pre><code>onSubmitForm = (e) =&gt; {
    e.preventDefault();
    if (this.state.word[this.state.word.length - 1] === this.state.value[0]) {
        this.setState({
            result: &#39;딩동댕&#39;,
            word: this.state.value,
            value: &#39;&#39;,
        });
        this.input.focus();
    } else {
        this.setState({
            result: &#39;땡&#39;,
            value: &#39;&#39;,
        });
        this.input.focus();
    }
};

onChangeInput = (e) =&gt; {
    this.setState({
        value: e.target.value
    });
};

input; // this.input 생성

onRefInput = (c) =&gt; {
    this.input = c;
};

render() {
    return (
        &lt;&gt;
            &lt;div&gt;{this.state.word}&lt;/div&gt;
            &lt;form onSubmit={this.onSubmitForm}&gt;
                &lt;input ref={this.onRefInput} value={this.state.value} onChange={this.onChangeInput} /&gt;
                &lt;button&gt;입력!&lt;/button&gt;
            &lt;/form&gt;
            &lt;div&gt;{this.state.result}&lt;/div&gt;
        &lt;/&gt;
    );
}</code></pre><p>}</p>
<p>module.exports = WordRelay;</p>
<pre><code>input에 value, onChange는 세트. 아니면 `defaultValue` 넣기

## 6. webpack-dev-server와 hot-loader
- `npm i -D react-hot-loader`
- `npm i -D webpack-dev-server`: webpack.config.js 읽어 build 후 유지
```js
&quot;scripts&quot;: {
    &quot;dev&quot;: &quot;webpack-dev-server --hot&quot;
 },</code></pre><h4 id="clientjsx-2">client.jsx</h4>
<pre><code class="language-jsx">const React = require(&#39;react&#39;);
const ReactDom = require(&#39;react-dom&#39;);
const { hot } = require(&#39;react-hot-loader/root&#39;);

const WordRelay = require(&#39;./WordRelay&#39;);

const Hot = hot(WordRelay);

ReactDom.render(&lt;Hot /&gt;, document.querySelector(&#39;#root&#39;));</code></pre>
<h4 id="webpackconfigjs-3">webpack.config.js</h4>
<pre><code class="language-jsx">...
module: {
    rules: [{
        test: /\.jsx?/,
        loader: &#39;babel-loader&#39;,
        options: {
            presets: [&#39;@babel/preset-env&#39;, &#39;@babel/preset-react&#39;],
            plugins: [
                &#39;@babel/plugin-proposal-class-properties&#39;,
              ⭐&#39;react-hot-loader/babel&#39;,
            ],
        },
    }],
},</code></pre>
<p>로컬호스트 서버를 이용해서 프론트엔트 프로그램을 돌릴 수 있음. 저장하는 순간 변경된게 자동으로 빌드 됨</p>
<h2 id="7-끝말잇기-hooks로-전환하기">7. 끝말잇기 Hooks로 전환하기</h2>
<h4 id="wordrelayjsx-2">WordRelay.jsx</h4>
<pre><code class="language-jsx">const React = require(&#39;react&#39;);
const { useState, useRef } = require(&#39;react&#39;);

const WordRelay = () =&gt; {
    const [word, setWord] = useState(&#39;지은&#39;);
    const [value, setValue] = useState(&#39;&#39;);
    const [result, setResult] = useState(&#39;&#39;);
    const inputRef = useRef(null);

    const onSubmitForm = (e) =&gt; {
        e.preventDefault();
        if (word[word.length - 1] === value[0]) {
            setResult(&#39;딩동댕&#39;);
            setWord(value);
            setValue(&#39;&#39;);
            inputRef.current.focus();
        } else {
            setResult(&#39;땡&#39;);
            setValue(&#39;&#39;);
            inputRef.current.focus();
        }
    };

    const onChangeInput = (e) =&gt; {
        setValue(e.target.value);
    };

    return (
        &lt;&gt;
            &lt;div&gt;{word}&lt;/div&gt;
            &lt;form onSubmit={onSubmitForm}&gt;
                &lt;input ref={inputRef} value={value} onChange={onChangeInput} /&gt;
                &lt;button&gt;입력!&lt;/button&gt;
            &lt;/form&gt;
            &lt;div&gt;{result}&lt;/div&gt;
        &lt;/&gt;
    );

};

module.exports = WordRelay;</code></pre>
<p><img src="https://images.velog.io/images/ji-silver/post/00a46221-9377-4ab0-8154-a5880611ea59/ezgif-2-4c1eeadfe479.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 웹 게임-1 구구단]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-1-%EA%B5%AC%EA%B5%AC%EB%8B%A8</link>
            <guid>https://velog.io/@ji-silver/React-%EC%9B%B9-%EA%B2%8C%EC%9E%84-1-%EA%B5%AC%EA%B5%AC%EB%8B%A8</guid>
            <pubDate>Sat, 11 Jul 2020 17:53:24 GMT</pubDate>
            <description><![CDATA[<h2 id="1-첫-리액트-컴포넌트">1. 첫 리액트 컴포넌트</h2>
<ul>
<li><p>create-react-app없이 html로 컴포넌트 만들기</p>
</li>
<li><p>lecture 디렉터리 만들고 그 안에 index.html 생성</p>
<h4 id="indexhtml">index.html</h4>
<pre><code class="language-javascript">// react, reactDom
&lt;script src=&quot;https://unpkg.com/react@16/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/react-dom@16/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;</code></pre>
<pre><code class="language-javascript">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
  // root에 리액트 넣기
  &lt;script&gt;
      const e = React.createElement; //html 태그를 만들어주는 함수

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

          render() {
              return e(&#39;button&#39;, null, &#39;Like&#39;); //&lt;button&gt;Like&lt;/button&gt; 
          }
      }
  &lt;/script&gt;
  &lt;script&gt;
      ReactDOM.render(e(LikeButton), document.querySelector(&#39;#root&#39;)); // ReactDOM은 웹에 실제로 반영시켜줌
  &lt;/script&gt;</code></pre>
</li>
</ul>
<h2 id="2-html-속성과-상태state">2. HTML 속성과 상태(state)</h2>
<ul>
<li><p><strong>상태(state)</strong>는 바뀌는 부분, 혹은 바뀔 수 있는 부분</p>
<pre><code class="language-javascript">class LikeButton extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          liked: false
      };
  }

  render() {
      return e(&#39;button&#39;,
          { onClick: () =&gt; { this.setState({ liked: true }) }, type: &#39;submit&#39; },
          this.state.liked === true ? &#39;Liked&#39; : &#39;Like&#39;
      );
      // 두번째 자리에 html 속성 넣기
      //&lt;button onclick&quot;() =&gt; {console.log(&#39;click&#39;)}&quot; type=&quot;submit&quot;&gt;Like&lt;/button&gt; 
  }
}</code></pre>
</li>
</ul>
<h2 id="3-jsx와-바벨babel">3. JSX와 바벨(babel)</h2>
<ul>
<li><p>자바스크립트에서 HTML문법을 쓸 수 없지만 <strong>babel</strong> 사용시 쓸 수 있음</p>
</li>
<li><p>script태그에 <code>&lt;script type=&quot;text/babel&quot;&gt;</code> 넣어주기</p>
</li>
<li><p>JSX (JS + XML) =&gt; 닫는 태그를 꼭 해줘야 함(문법이 엄격함)</p>
<pre><code class="language-javascript">&lt;script src=&quot;https://unpkg.com/@babel/standalone/babel.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/babel&quot;&gt;
...
&lt;/script&gt;</code></pre>
<pre><code class="language-javascript">&lt;script type=&quot;text/babel&quot;&gt;
  class LikeButton extends React.Component {
      constructor(props) {
          super(props);
          this.state = {
              liked: false
          };
      }

      render() {
          return &lt;button type=&quot;submit&quot; onClick={() =&gt; this.setState({ liked: true })}&gt;
              {this.state.liked === true ? &#39;Liked&#39; : &#39;Like&#39;}
          &lt;/button&gt;;
          // e(&#39;button&#39;,
          //     { onClick: () =&gt; { this.setState({ liked: true }) }, type: &#39;submit&#39; },
          //     this.state.liked === true ? &#39;Liked&#39; : &#39;Like&#39;
          // );
      }
  }
&lt;/script&gt;
&lt;script type=&quot;text/babel&quot;&gt;
  ReactDOM.render(&lt;LikeButton /&gt;, document.querySelector(&#39;#root&#39;));
&lt;/script&gt;</code></pre>
<p>❗ 컴포넌트 장점: 원하는 개수만큼 쉽게 늘릴 수 있음</p>
<h2 id="4-구구단-만들기">4. 구구단 만들기</h2>
<h4 id="indexhtml-1">index.html</h4>
<pre><code class="language-html">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;script type=&quot;text/babel&quot;&gt;
  class GuGuDan extends React.Component {
      constructor(props) {
          super(props);
          this.state = {
              first: Math.ceil(Math.random() * 9),
              second: Math.ceil(Math.random() * 9),
              value: &#39;&#39;,
              result: &#39;&#39;,
          };
      }
      render() {
          return (
              &lt;div&gt;
                  &lt;div&gt;{this.state.first} 곱하기 {this.state.second}는?&lt;/div&gt;
                  &lt;form&gt;
                      &lt;input type=&quot;number&quot; value={this.state.value} onChange={(e) =&gt; this.setState({ value: e.target.value })} /&gt;
                      &lt;button&gt;입력!&lt;/button&gt;
                  &lt;/form&gt;
                  &lt;div&gt;{this.state.result}&lt;/div&gt;
              &lt;/div&gt;
          );
      }
  }
&lt;/script&gt;
&lt;script type=&quot;text/babel&quot;&gt;
  ReactDOM.render(&lt;GuGuDan /&gt;, document.querySelector(&#39;#root&#39;));
&lt;/script&gt;</code></pre>
<h3 id="1-클래스-메서드">1) 클래스 메서드</h3>
<pre><code class="language-javascript">onSubmit = (e) =&gt; {
  e.preventDefault();
  if (parseInt(this.state.value) === this.state.first * this.state.second) {
      this.setState({
          result: this.state.value + &#39; 정답&#39;,
          first: Math.ceil(Math.random() * 9),
          second: Math.ceil(Math.random() * 9),
          value: &#39;&#39;
      });
  } else {
      this.setState({
          result: &#39;땡&#39;,
          value: &#39;&#39;,
      });
  }
};
</code></pre>
</li>
</ul>
<p>onChange = (e) =&gt; {
    this.setState({ value: e.target.value }) // input태그는 onChange로 value를 직접 바꿔야함
};</p>
<pre><code>```javascript
render() {
    return (
        &lt;div&gt;
            &lt;div&gt;{this.state.first} 곱하기 {this.state.second}는?&lt;/div&gt;
            &lt;form onSubmit={this.onSubmit}&gt;
                &lt;input type=&quot;number&quot; value={this.state.value} onChange={this.onChange} /&gt;

                &lt;button&gt;입력!&lt;/button&gt;
            &lt;/form&gt;
            &lt;div&gt;{this.state.result}&lt;/div&gt;
        &lt;/div&gt;
    );
}</code></pre><blockquote>
<p><code>render()</code> 안에서 쓴 함수는 function 사용 가능.
<strong>but!</strong> 밖에서 썼다면 무조건 화살표 함수 쓰기 (this가 현재 class를 가리키지 않기 때문)
❗ 밖에서 쓰려면 <code>bind()</code> 함수 써야함</p>
</blockquote>
<p>🔥<strong>JSX</strong>와 <strong>javascript</strong>코드는 웬만하면 섞어쓰지 않기🔥</p>
<h3 id="2-fragment와-기타-팁들">2) Fragment와 기타 팁들</h3>
<ul>
<li>쓸떼없는 <strong>div</strong>태그가 감싸져있어서 방해가 됨 -&gt; <code>&lt;&gt;</code>빈태그 또는 <code>&lt;React.Fragment&gt;</code> 로 대신 감싸면 <code>&lt;div id=&quot;root&quot;&gt;</code> 다음 바로 태그가 나옴<pre><code class="language-javascript">render() {
  return (
      &lt;div&gt;
          &lt;div&gt;
            ...
            &lt;/div&gt;
      &lt;/div&gt;
  );
}
</code></pre>
</li>
</ul>
<pre><code>```javascript
render() {
    return (
        &lt;React.Fragment&gt;
            &lt;div&gt;
              ...
            &lt;/div&gt;
        &lt;/React.Fragment&gt;
    );
}</code></pre><pre><code class="language-javascript">//이 부분 생략 가능 (실무에선 이 방식을 더 많이 사용)
constructor(props) {
    super(props);
}</code></pre>
<h3 id="3-함수형-setstate">3) 함수형 setState</h3>
<ul>
<li>setState는 비동기이기 때문에 예전 state값으로 
새로운 state를 만들 때 리턴해주는 함수 사용하기</li>
<li><strong>prevState</strong>는 바꾸기 전 상태값이 들어있음</li>
</ul>
<pre><code class="language-javascript">this.setState((prevState) =&gt; {
    return {
          // 현재 값
        result: prevState.value + &#39; 정답&#39;,
          // 미래에 바꾸겠다는 state 값
        first: Math.ceil(Math.random() * 9),
        second: Math.ceil(Math.random() * 9),
        value: &#39;&#39;
    };
});</code></pre>
<h3 id="4-ref">4) ref</h3>
<ul>
<li>React로 input에 포커스 주기</li>
</ul>
<pre><code class="language-javascript">onSubmit = (e) =&gt; {
    e.preventDefault();
    if (parseInt(this.state.value) === this.state.first * this.state.second) {
        ...
      ⭐this.input.focus();
    }
};

input;

render() {
    return (
        &lt;React.Fragment&gt;
            &lt;input ⭐ref={(c) =&gt; {this.input = c;}}
             type=&quot;number&quot;
           value={this.state.value}
           onChange={this.onChange} /&gt;
        &lt;/React.Fragment&gt;
    );
}</code></pre>
<p>state 값이 바뀔 때마다 <code>render()</code>함수가 계속 실행되기 때문에 밖으로 빼줌</p>
<pre><code class="language-javascript">
input;

onRefInput = (c) =&gt; {this.input = c;};

render() {
    return (
        &lt;React.Fragment&gt;
            &lt;input ref={this.onRefInput} type=&quot;number&quot; value={this.state.value} onChange={this.onChange} /&gt;
        &lt;/React.Fragment&gt;
    );
}</code></pre>
<h2 id="5-react-hooks">5. React Hooks</h2>
<ul>
<li><p>React도 Hooks 쓰기를 권장함</p>
</li>
<li><p>setState, ref를 안 쓸 때 함수형 컴포넌트를 사용. 하지만 사용할 수 있도록 한 것이 Hooks</p>
</li>
<li><p>Hooks는 state가 바뀌면 함수 자체가 재실행 되어 조금 더 느릴 수 있음</p>
<pre><code class="language-jsx">&lt;script type=&quot;text/babel&quot;&gt;
  const GuGuDan = () =&gt; {
      //state를 선언하는 방법, state를 분리하기
      const [first, setFirst] = React.useState(Math.ceil(Math.random() * 9));
      const [second, setSecond] = React.useState(Math.ceil(Math.random() * 9));
      const [value, setValue] = React.useState(&#39;&#39;);
      const [result, setResult] = React.useState(&#39;&#39;);
      //ref
      const inputRef = React.useRef(); //useRef로 DOM에 접근

      const onSubmitForm = (e) =&gt; {
          e.preventDefault();
          if (parseInt(value) === first * second) {
              setResult(&#39;정답: &#39; + value);
              setFirst(Math.ceil(Math.random() * 9));
              setSecond(Math.ceil(Math.random() * 9));
              setValue(&#39;&#39;);
              inputRef.current.focus();
          } else {
              setResult(&#39;땡&#39;);
              setValue(&#39;&#39;);
              inputRef.current.focus();
          }
      };

      const onChangeInput = (e) =&gt; {
          setValue(e.target.value);
      };

      return (
          &lt;React.Fragment&gt;
              &lt;div&gt;{first}곱하기{second}는?&lt;/div&gt;
              &lt;form onSubmit={onSubmitForm}&gt;
                  &lt;input ref={inputRef} onChange={onChangeInput} value={value} /&gt;
                  &lt;button&gt;입력!&lt;/button&gt;
              &lt;/form&gt;
              &lt;div id=&quot;result&quot;&gt;{result}&lt;/div&gt;
          &lt;/React.Fragment&gt;
      );
  }
&lt;/script&gt;</code></pre>
</li>
</ul>
<p><img src="https://images.velog.io/images/ji-silver/post/982de0b7-f5b4-4a49-b39a-d5282ec4f742/ezgif-2-6dd2f2ed4360.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-6 Update, Delete구현]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-6-Update-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-6-Update-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 06 Jul 2020 13:48:30 GMT</pubDate>
            <description><![CDATA[<h2 id="1update">1.Update</h2>
<ul>
<li>Read기능, Create 기능 결합</li>
<li>components 디렉터리에 <strong>UpdateContents.js</strong> 파일 생성<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">import UpdateContent from &#39;./components/UpdateContent&#39;;
</code></pre>
</li>
</ul>
<p>...</p>
<p>else if (this.state.mode === &#39;update&#39;) {
  _article = &lt;UpdateContent onSubmit={function (_title, _desc) {
    this.max_content_id = this.max_content_id + 1;
    var _contents = this.state.contents.concat(
      { id: this.max_content_id, title: _title, desc: _desc }
    )
    this.setState({
      contents: _contents
    });
    console.log(_title, _desc);
  }.bind(this)}&gt;</UpdateContent>
}</p>
<pre><code>
- render()함수 안 코드가 복잡하기 때문에 새로운 함수로 쪼개서 분리시켜보기
```javascript
getContent() {
  var _title, _desc, _article = null;
  if (this.state.mode === &#39;welcome&#39;) {
    _title = this.state.welcome.title;
    _desc = this.state.welcome.desc;
    _article = &lt;ReadContent title={_title} desc={_desc}&gt;&lt;/ReadContent&gt;
  } else if (this.state.mode === &#39;read&#39;) {
    var i = 0;
    while (i &lt; this.state.contents.length) {
      var data = this.state.contents[i];
      if (data.id === this.state.selected_content_id) {
        _title = data.title;
        _desc = data.desc;
        break;
      }
      i = i + 1;
    }
    _article = &lt;ReadContent title={_title} desc={_desc}&gt;&lt;/ReadContent&gt;
  } else if (this.state.mode === &#39;create&#39;) {
    _article = &lt;CreateContent onSubmit={function (_title, _desc) {
      // add content to this.state.contents
      this.max_content_id = this.max_content_id + 1;
      // this.state.contents.push(
      // {id:this.max_content_id, title:_title, desc:_desc}
      // );
      var _contents = this.state.contents.concat(
        { id: this.max_content_id, title: _title, desc: _desc }
      )
      this.setState({
        contents: _contents
      });
      console.log(_title, _desc);
    }.bind(this)}&gt;&lt;/CreateContent&gt;
  } else if (this.state.mode === &#39;update&#39;) {
    _article = &lt;UpdateContent onSubmit={function (_title, _desc) {
      this.max_content_id = this.max_content_id + 1;
      var _contents = this.state.contents.concat(
        { id: this.max_content_id, title: _title, desc: _desc }
      )
      this.setState({
        contents: _contents
      });
      console.log(_title, _desc);
    }.bind(this)}&gt;&lt;/UpdateContent&gt;
  }
  return _article;
}
render() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Subject
        title={this.state.subject.title}
        sub={this.state.subject.sub}
        onChangePage={function () {
          this.setState({ mode: &#39;welcome&#39; });
        }.bind(this)}
      &gt;
      &lt;/Subject&gt;
      &lt;TOC onChangePage={function (id) {
        this.setState({
          mode: &#39;read&#39;,
          selected_content_id: Number(id)
        });
      }.bind(this)} data={this.state.contents}&gt;&lt;/TOC&gt;
      &lt;Control onChangeMode={function (_mode) {
        this.setState({
          mode: _mode
        });
      }.bind(this)}&gt;&lt;/Control&gt;
      //getContent()를 쓰는 건 _article이기 때문에 
      {this.getContent()}
    &lt;/div&gt;
  );
}</code></pre><ul>
<li>UpdateContent 컴포넌트 실행 시 입력값으로 현재 선택된 id에 해당되는 contents를 주입시키기</li>
<li>mode가 read일 때 하는 방법과 같아 코드가 중복되기 때문에 <strong>getReadContent()</strong> 함수 생성하여 코드 넣어 리팩토링 하기<pre><code class="language-javascript">getReadContent() {
var i = 0;
while (i &lt; this.state.contents.length) {
  var data = this.state.contents[i];
  if (data.id === this.state.selected_content_id) {
    return data;
  }
  i = i + 1;
}
}
...
</code></pre>
</li>
</ul>
<p>else if (this.state.mode === &#39;read&#39;) {
  var _content = this.getReadContent();
  _article = <ReadContent title={_content.title} desc={_content.desc}></ReadContent>
} else if (this.state.mode === &#39;update&#39;) {
  _content = this.getReadContent();
  _article = &lt;UpdateContent data={_content} onSubmit={function (_title, _desc) {
    this.max_content_id = this.max_content_id + 1;
    var _contents = this.state.contents.concat(
      { id: this.max_content_id, title: _title, desc: _desc }
    )
    this.setState({
      contents: _contents
    });
    console.log(_title, _desc);
  }.bind(this)}&gt;</UpdateContent>
}</p>
<pre><code>UpdateContent 컴포넌트의 data의 값을 _content를 주며 현재 id값을 찾는다.
(현재 id 기본값은 2번이기때문에 2번 contents 출력 )
## 2. form
#### UpdateContent.js
```javascript
import React, { Component } from &#39;react&#39;;

class UpdateContent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            id: this.props.id,
            title: this.props.data.title,
            desc: this.props.data.desc
        }
        this.inputFormHandler = this.inputFormHandler.bind(this);
    }
    inputFormHandler(e) {
        this.setState({ [e.target.name]: e.target.value });
        // state값을 동적으로 바꾸기 위해 setState 사용
        // e.target.value를 사용하면 현재 텍스트 값을 읽을 수 있음
    }
    render() {
        console.log(this.props.data);
        return (
            &lt;article&gt;
                &lt;h2&gt;Update&lt;/h2&gt;
                &lt;form action=&quot;/create_process&quot; method=&quot;post&quot; onSubmit={function (e) {
                    e.preventDefault();
                    this.props.onSubmit(
                        this.state.id,
                        this.state.title,
                        this.state.desc
                    );
                }.bind(this)}
                &gt;
                    &lt;input type=&quot;hidden&quot; name=&quot;id&quot; value={this.state.id}&gt;&lt;/input&gt;
                    &lt;p&gt;
                        &lt;input
                            type=&quot;text&quot;
                            name=&quot;title&quot;
                            placeholder=&quot;title&quot;
                            value={this.state.title} //props는 읽기전용이기 때문에 수정이 되지 않음 -&gt; 가변적인 데이터 state화 시키기
                            onChange={this.inputFormHandler} // onChange함수는 변화가 일어났는지 탐지
                        &gt;&lt;/input&gt;
                    &lt;/p&gt;
                    &lt;p&gt;&lt;textarea
                        onChange={this.inputFormHandler}
                        name=&quot;desc&quot;
                        placeholder=&quot;description&quot;
                        value={this.state.desc} // textarea의 내용은 value 사용
                    &gt;
                    &lt;/textarea&gt;
                    &lt;/p&gt;
                    &lt;p&gt;&lt;input type=&quot;submit&quot;&gt;&lt;/input&gt;&lt;/p&gt;
                &lt;/form&gt;
            &lt;/article&gt;
        );
    }
}

export default UpdateContent;

</code></pre><h2 id="3-state-변경">3. state 변경</h2>
<ul>
<li>update, create로 새로운 값 입력 시 mode를 &#39;read&#39;로 바꾸기<h4 id="appjs-1">App.js</h4>
<pre><code class="language-javascript">...
else if (this.state.mode === &#39;create&#39;) {
_article = &lt;CreateContent onSubmit={function (_title, _desc) {
  this.max_content_id = this.max_content_id + 1;
  var _contents = Array.from(this.state.contents);
  _contents.push({ id: this.max_content_id, title: _title, desc: _desc });
  // var _contents = this.state.contents.concat(
  //   { id: this.max_content_id, title: _title, desc: _desc }
  // )
  this.setState({
    contents: _contents,
    mode: &#39;read&#39;,
    selected_content_id: this.max_content_id
  });
  console.log(_title, _desc);
}.bind(this)}&gt;&lt;/CreateContent&gt;
} else if (this.state.mode === &#39;update&#39;) {
_content = this.getReadContent();
_article = &lt;UpdateContent data={_content} onSubmit={function (_id, _title, _desc) {
  var _contents = Array.from(this.state.contents); //state의 contents 배열 복제하여 새로운 배열 만들어짐
  var i = 0;
  while (i &lt; _contents.length) {
    if (_contents[i].id === _id) { // _contents의 id 값과 입력받은 id값이 같으면 실행
      _contents[i] = { id: _id, title: _title, desc: _desc }; // 교체하기
      break;
    }
    i = i + 1;
  }
  this.setState({
    contents: _contents,
    mode: &#39;read&#39;
  });
  console.log(_title, _desc);
}.bind(this)}&gt;&lt;/UpdateContent&gt;
}</code></pre>
<h2 id="4-delete">4. Delete</h2>
<h4 id="appjs-2">App.js</h4>
<pre><code class="language-javascript">...
&lt;Control onChangeMode={function (_mode) {
if (_mode === &#39;delete&#39;) {
  if (window.confirm(&#39;really?&#39;)) { // confirm() 실행 시 확인누르면 true, cancel 누르면 false
    var _contents = Array.from(this.state.contents);
    var i = 0;
    while (i &lt; _contents.length) {
      if (_contents[i].id === this.state.selected_content_id) {
        _contents.splice(i, 1) // splice()는 (어디서부터, 어디까지)를 지울 것인지 구하는 함수
        break;
      }
      i = i + 1;
    }
    this.setState({
      mode: &#39;welcome&#39;,
      contents: _contents
    });
    alert(&#39;deleted!&#39;);
  }
} else {
  this.setState({
    mode: _mode
  });
}
}.bind(this)}&gt;&lt;/Control&gt;
</code></pre>
</li>
</ul>
<p>```</p>
<p><img src="https://images.velog.io/images/ji-silver/post/ae423a15-5aa1-410a-a84f-3d13a3a50b6e/ezgif-2-4a57972688c4.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-5 Create 구현]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-5-Create-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-5-Create-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sat, 04 Jul 2020 07:42:42 GMT</pubDate>
            <description><![CDATA[<h2 id="1-mode-변경">1. mode 변경</h2>
<ul>
<li><code>&lt;TOC&gt;</code>와 <code>&lt;Content&gt;</code> 컴포넌트 사이에 create, update, delete 버튼 만들기</li>
<li>components 디렉터리 안에 Control.js 파일 생성</li>
</ul>
<h4 id="controljs">Control.js</h4>
<pre><code class="language-javascript">class Control extends Component {
    render() {
        return (
            &lt;ul&gt;
                &lt;li&gt;&lt;a href=&quot;/create&quot; onClick={function(e){  // 클릭 시 onChangeMode 핸들러 실행되게 하는 코드
                    e.preventDefault();
                    this.props.onChangeMode(&#39;create&#39;); // 호출 시 mode값 전달하기
                }.bind(this)}&gt;create&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;/update&quot; onClick={function(e){
                    e.preventDefault();
                    this.props.onChangeMode(&#39;update&#39;);
                }.bind(this)}&gt;update&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;input onClick={function(e){
                    e.preventDefault();
                    this.props.onChangeMode(&#39;delete&#39;);
                }.bind(this)} type=&quot;button&quot; value=&quot;delete&quot;&gt;&lt;/input&gt;&lt;/li&gt;
            &lt;/ul&gt;
        );
    }
}</code></pre>
<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">import Control from &quot;./components/Control&quot;;

&lt;TOC onChangePage={function (id) {
    this.setState({
        mode: &#39;read&#39;,
        selected_content_id: Number(id)
    });
}.bind(this)} data={this.state.contents}&gt;&lt;/TOC&gt;
⭐&lt;Control onChangeMode={function (_mode) {
    this.setState({ // 함수 호출 시 mode state값 바꾸기
        mode: _mode
    });
}.bind(this)}&gt;&lt;/Control&gt;
&lt;Content title={_title} desc={_desc}&gt;&lt;/Content&gt;</code></pre>
<h2 id="2-mode-전환">2. mode 전환</h2>
<ul>
<li>create 클릭 시 <code>&lt;CreateContent&gt;</code> 컴포넌트로 바꿔보기</li>
<li>원래 있던 create 파일을 ReadContent로 모두 바꾸고, CreateContent 파일 생성</li>
</ul>
<h4 id="appjs-1">App.js</h4>
<pre><code class="language-javascript">import CreateContent from &#39;./components/CreateContent&#39;;
import UpdateContent from &#39;./components/UpdateContent&#39;;

render() {
    var _title, _desc, _article = null;
    if (this.state.mode === &#39;welcome&#39;) {
        _title = this.state.welcome.title;
        _desc = this.state.welcome.desc;
        _article = &lt;ReadContent title={_title} desc={_desc}&gt;&lt;/ReadContent&gt;
    } else if (this.state.mode === &#39;read&#39;) {
        var i = 0;
        while (i &lt; this.state.contents.length) {
            var data = this.state.contents[i];
            if (data.id === this.state.selected_content_id) {
                _title = data.title;
                _desc = data.desc;
                break;
            }
            i = i + 1;
        }
        _article = &lt;ReadContent title={_title} desc={_desc}&gt;&lt;/ReadContent&gt;
   ⭐} else if (this.state.mode === &#39;create&#39;) { // mode가 &#39;create&#39;일 때 CreateContent 나오게 하기
        _article = &lt;CreateContent&gt;&lt;/CreateContent&gt;
    }
    return (
        &lt;div className=&quot;App&quot;&gt;
            &lt;Subject
                title={this.state.subject.title}
                sub={this.state.subject.sub}
                onChangePage={function () {
                    this.setState({ mode: &#39;welcome&#39; });
                }.bind(this)}
            &gt;
            &lt;/Subject&gt;
            &lt;TOC onChangePage={function (id) {
                this.setState({
                    mode: &#39;read&#39;,
                    selected_content_id: Number(id)
                });
            }.bind(this)} data={this.state.contents}&gt;&lt;/TOC&gt;
            &lt;Control onChangeMode={function (_mode) {
                this.setState({
                    mode: _mode
                });
            }.bind(this)}&gt;&lt;/Control&gt;
           ⭐{_article}
        //가변적으로 바꾸기 위해 _article변수 사용
        &lt;/div&gt;
    );
}</code></pre>
<h2 id="3-form">3. form</h2>
<h4 id="createcontentjs">CreateContent.js</h4>
<pre><code class="language-javascript">import React, { Component } from &#39;react&#39;;

class CreateContent extends Component {
    render() {
        return (
            &lt;article&gt;
                &lt;h2&gt;Create&lt;/h2&gt;
                &lt;form action=&quot;/create_process&quot; method=&quot;post&quot;
                    // 사용자가 데이터 추가, 수정 시 method를 post 방식을 써야 url 노출x 
                    onSubmit={function (e) { // Submit 버튼 클릭 시 폼 태그에 포함되어 있는 onSubmit 이벤트 실행
                        e.preventDefault();
                    }.bind(this)}
                &gt;
                    &lt;p&gt;&lt;input type=&quot;text&quot; name=&quot;title&quot; placeholder=&quot;title&quot;&gt;&lt;/input&gt;&lt;/p&gt;
                    &lt;p&gt;&lt;textarea name=&quot;desc&quot; placeholder=&quot;description&quot;&gt;&lt;/textarea&gt;&lt;/p&gt;
                    &lt;p&gt;&lt;input type=&quot;submit&quot;&gt;&lt;/input&gt;&lt;/p&gt;
                &lt;/form&gt;
            &lt;/article&gt;
        );
    }
}

export default CreateContent;</code></pre>
<h3 id="1-onsubmit-이벤트">1) onSubmit 이벤트</h3>
<h4 id="appjs-2">App.js</h4>
<pre><code class="language-javascript">else if (this.state.mode === &#39;create&#39;) {
  _article = &lt;CreateContent onSubmit={function (_title, _desc) {
  // add content to this.state.contents
  }.bind(this)}&gt;&lt;/CreateContent&gt;
}</code></pre>
<h4 id="createcontentjs-1">CreateContent.js</h4>
<pre><code class="language-javascript">&lt;form action=&quot;/create_process&quot; method=&quot;post&quot;
    onSubmit={function (e) {
        e.preventDefault();
        this.props.onSubmit(
            e.target.title.value,
            e.target.desc.value
        );
    }.bind(this)}
&gt;
    &lt;p&gt;&lt;input type=&quot;text&quot; name=&quot;title&quot; placeholder=&quot;title&quot;&gt;&lt;/input&gt;&lt;/p&gt;
    &lt;p&gt;&lt;textarea name=&quot;desc&quot; placeholder=&quot;description&quot;&gt;&lt;/textarea&gt;&lt;/p&gt;
    &lt;p&gt;&lt;input type=&quot;submit&quot;&gt;&lt;/input&gt;&lt;/p&gt;
&lt;/form&gt;</code></pre>
<p>e.target은 form 자체를 가리킴
value는 입력한 값을 가져올 수 있기 때문에 _title, _desc 인자로 값을 넘겨줌</p>
<h2 id="4-contents-변경">4. contents 변경</h2>
<ul>
<li>onSubmit 이벤트가 실행됐을 때 App컴포넌트의 contents 배열 끝에 데이터 추가하기<h4 id="appjs-3">App.js</h4>
<pre><code class="language-javascript">class App extends Component {
constructor(props) {
  super(props);
⭐this.max_content_id = 3; // state값이 아닌 이유는 UI에 아무런 영향을 주지 않기 때문
  this.state = {
    mode: &#39;create&#39;,
    selected_content_id: 2,
    subject: { title: &#39;WEB&#39;, sub: &#39;World Wide Web!&#39; },
    welcome: { title: &#39;welcome&#39;, desc: &#39;Hello, React!!!&#39; },
    contents: [
      { id: 1, title: &#39;HTML&#39;, desc: &#39;HTML is for information&#39; },
      { id: 2, title: &#39;CSS&#39;, desc: &#39;CSS is for design&#39; },
      { id: 3, title: &#39;JavaScript&#39;, desc: &#39;JavaScript is for interactive&#39; }
    ]
  }
}
</code></pre>
</li>
</ul>
<p>render() {
...
  else if (this.state.mode === &#39;create&#39;) {
    _article = &lt;CreateContent onSubmit={function (_title, _desc) {
      this.max_content_id = this.max_content_id + 1; // 원래 있던 id값을 1 증가
    ⭐var _contents = this.state.contents.concat(
        { id: this.max_content_id, title: _title, desc: _desc }
      )
      this.setState({
        contents: _contents
      });
    }.bind(this)}&gt;</CreateContent>
  }
}  </p>
<pre><code>
&gt; 
- 왜 **push**가 아닌 **concat**으로 배열 추가를 했을까?
    - push는 원본배열 값이 바뀜, concat은 원본을 바꾸지 않고 원본을 변경한 새로운 배열 리턴
```javascript
var a = [1, 2];
a.push(3);
console.log(a);
[1, 2, 3]</code></pre><pre><code class="language-javascript">var a = [1, 2];
var b = a.concat(3);
console.log(a, b);
[1, 2] [1, 2, 3]
=&gt; 원본이 바뀌지 않음</code></pre>
<h3 id="1-shouldcomponentupdate">1) shouldComponentUpdate</h3>
<ul>
<li>App.js에 contents 배열의 값이 바뀐다면 TOC 컴포넌트의 render()를 통해 TOC가 다시 그려짐
<em>But!</em> contents가 바뀌지 않아도 TOC가 또 랜더링 됨</li>
<li>shouldComponentUpdate() 함수는 컴포넌트의 render함수가 실행될지 실행되지 않을지 결정할 수 있음</li>
<li><strong>shouldComponentUpdate(newProps, newState)</strong>로 사용할 수 있다.</li>
</ul>
<h4 id="tocjs">TOC.js</h4>
<pre><code class="language-javascript">class TOC extends Component {
  shouldComponentUpdate(newProps, newState){ //- TOC 컴포넌트의 Props와 State값이 바뀐 값을 매개변수로 준다
    if(this.props.data === newProps.data){
      return false; // 현재 props와 바뀐 props가 같다면 함수 호출 x --&gt;
    }
    return true; //그렇지 않다면 render() 호출
  }
}  </code></pre>
<ul>
<li>return false 면 render 함수 호출x, true면 render가 호출됨</li>
<li>TOC로 들어오는 data의 props가 바뀌었을 때 render호출, 바뀌지 않았을 때 render 호출x</li>
</ul>
<blockquote>
<p>🔥그렇기 때문에 concat을 쓰지 않고 push를 썼다면?
원본이 바뀌어 이전값, 바뀐값이 같아져서 shouldComponentUpdate()를 쓰지 못함.</p>
</blockquote>
<h3 id="2-immutable">2) immutable</h3>
<ul>
<li>원본을 바꾸지 않음<h4 id="배열">&lt;배열&gt;</h4>
<pre><code class="language-javascript">var a = [1, 2];
var b = Array.from(a);
console.log(a, b, a===b);
=&gt; [1, 2] [1, 2] false
</code></pre>
</li>
</ul>
<p>b.push(3);
console.log(a, b, a===b);
=&gt; [1, 2] [1, 2, 3] false</p>
<p>a,b는 내용이 같을 뿐 완전히 다름</p>
<pre><code>
- 만약 concat이 아닌 push를 쓰고 싶다면 **Array.from()** 사용
```javascript
var newContents = Array.from(this.state.contenst);
newContents.push({id:this.max_content_id, title:_title, desc:_desc});
this.setState({
  contents: newContents
});</code></pre><h4 id="객체">&lt;객체&gt;</h4>
<ul>
<li><strong>Object.assign()</strong> 첫 번째 인자로 빈객체 또는 새로운 객체 넣을 수 있음<pre><code class="language-javascript">var a = { name: &#39;egoing&#39; };
var b = Object.assign({}, a);
console.log(a, b, a === b);
=&gt; {name: &quot;egoing&quot;} {name: &quot;egoing&quot;} false
</code></pre>
</li>
</ul>
<p>b.name = &#39;leezche&#39;;
console.log(a, b, a === b);
=&gt; {name: &quot;egoing&quot;} {name: &quot;leezche&quot;} false</p>
<p>a, b는 내용이 같지만 같은 객체가 아님</p>
<p>```</p>
<ul>
<li>immutable.js를 사용하면 모든 명령어들이 원본에 불변하기 때문에 일관된 코드를 사용할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-4 이벤트]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-4-%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-4-%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Tue, 30 Jun 2020 17:43:05 GMT</pubDate>
            <description><![CDATA[<h2 id="1-이벤트-state-props-그리고-redner-함수">1. 이벤트 state props 그리고 redner 함수</h2>
<ul>
<li>props, state, event 이 3개가 서로 상호작용하여 애플리케이션의 역동성을 만듦</li>
</ul>
<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            ⭐mode: &#39;read&#39;,
            ⭐welcome: { title: &#39;Welcome&#39;, desc: &#39;Hello, React!!&#39; },
            subject: { title: &#39;WEB&#39;, sub: &#39;World Wide Web!&#39; },
            contents: [
                { id: 1, title: &#39;HTML&#39;, desc: &#39;HTML is for information&#39; },
                { id: 2, title: &#39;CSS&#39;, desc: &#39;CSS is for design&#39; },
                { id: 3, title: &#39;JavaScript&#39;, desc: &#39;JavaScript is for interactive&#39; }
            ]
        }
    }

    render() { //어떤 HTML을 그릴것인가를 결정하는 함수
        var _title, _desc = null;
        if (this.state.mode === &#39;welcome&#39;) { //state의 mode값이 &#39;weclome&#39; 일 때 실행
            _title = this.state.welcome.title;
            _desc = this.state.welcome.desc;
        } else if (this.state.mode === &#39;read&#39;) { //state의 mode값이 &#39;read&#39; 일 때 실행
            _title = this.state.contents[0].title;
            _desc = this.state.contents[0].desc;
        }
        return (
            &lt;div className=&quot;App&quot;&gt;
                &lt;Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}&gt;
                &lt;/Subject&gt;
                &lt;TOC data={this.state.contents}&gt;&lt;/TOC&gt;
                  &lt;Content title={_title} desc={_desc}&gt;&lt;/Content&gt;
            &lt;/div&gt;
        );
    }
}</code></pre>
<p>mode값이 welcome, read에 따라 실행되는 코드가 바뀜</p>
<p><strong>props, state</strong>값이 바뀌면 그 state를 가지고 있는 컴포넌트의 render함수가 다시 호출.
그리고 render함수 하위에 있는 컴포넌트들도 다시 호출** (화면 다시 그려짐)**</p>
<blockquote>
<p>React에서는 링크 클릭시 event를 발생시켜
state, props를 변경함으로써 페이지 전체가 re-rendering </p>
</blockquote>
<h2 id="2-이벤트-설치">2. 이벤트 설치</h2>
<pre><code class="language-javascript">...
return (
    &lt;div className=&quot;App&quot;&gt;

        {/* &lt;Subject
            title={this.state.subject.title}
            sub={this.state.subject.sub}&gt;
        &lt;/Subject&gt; */}

        &lt;header&gt;
            &lt;h1&gt;&lt;a href=&quot;#&quot; onClick={function (e) {
                e.preventDefault();
                //이벤트가 발생한 태그의 기본 동작방법을 못하게 막는 코드
            }}&gt;{this.state.subject.title}&lt;/a&gt;&lt;/h1&gt;
            {this.state.subject.sub}
        &lt;/header&gt;
        &lt;TOC data={this.state.contents}&gt;&lt;/TOC&gt;
        &lt;Content title={_title} desc={_desc}&gt;&lt;/Content&gt;
    &lt;/div&gt;
);</code></pre>
<h2 id="3-이벤트에서-state-변경하기">3. 이벤트에서 state 변경하기</h2>
<ul>
<li>링크 클릭 시 App 컴포넌트의 mode 값을 welcome으로 바꿔보기</li>
<li><code>this.state.mode = &#39;welcome&#39;;</code> =&gt; ❌<strong>오류</strong>❌</li>
</ul>
<pre><code class="language-javascript">&lt;header&gt;
    &lt;h1&gt;&lt;a href=&quot;#&quot; onClick={function (e) {
        e.preventDefault();
        this.state.mode = &#39;welcome&#39;; 
    }}
&lt;/header&gt;</code></pre>
<ul>
<li><strong>위 코드로 작성시 2가지 문제점 발생</strong>
1) 이벤트가 호출됐을 때 실행되는 함수 안에서는 <strong>this</strong>의 값이 컴포넌트 자기 자신을 가리키지 않고 아무 값도 세팅되어 있지 않음.
=&gt; 함수가 끝난 직후 <code>bind()</code> 함수 추가
2) 동적으로 state값을 바꿀때는 <code>setState ()</code>사용</li>
</ul>
<pre><code class="language-javascript">&lt;header&gt;
    &lt;h1&gt;&lt;a href=&quot;#&quot; onClick={function (e) {
        e.preventDefault();
        this.setState({
            mode: &#39;welcome&#39;
        });
    }.bind(this)}&gt;{this.state.subject.title}&lt;/a&gt;&lt;/h1&gt;
    {this.state.subject.sub}
&lt;/header&gt;</code></pre>
<blockquote>
<p>❗ 하지만 <strong>render()</strong> 함수 밖에서 onClick 이벤트 함수를 화살표함수로(ES6) 만들면 <strong>bind()</strong> 없이 this 사용 가능</p>
</blockquote>
<h2 id="4-컴포넌트-이벤트-만들기">4. 컴포넌트 이벤트 만들기</h2>
<ul>
<li>App.js에서 이전에 작업한 주석처리 했던 Subject 컴포넌트 다시 살리고 Subject.js에서 갖고 온 코드는 지우기<h3 id="1-subject-컴포넌트에-이벤트-설치해보기">1) Subject 컴포넌트에 이벤트 설치해보기</h3>
</li>
</ul>
<h4 id="appjs-1">App.js</h4>
<pre><code class="language-javascript">...
&lt;Subject
    title={this.state.subject.title}
    sub={this.state.subject.sub}
    //onChagePage 이벤트 만들고, 링크 클릭시 함수가 실행되게 하기
    onChangPage={function () {
        this.setState({
            mode: &#39;welcome&#39; // 클릭시 mode값을 welcome으로 바꾸기
        });
    }.bind(this)}&gt;
&lt;/Subject&gt;</code></pre>
<h4 id="subjectjs">Subject.js</h4>
<pre><code class="language-javascript">&lt;header&gt;
    &lt;h1&gt;&lt;a href=&quot;#&quot; onClick={function (e) {
        e.preventDefault();
        this.props.onChangePage();
        //Subject 컴포넌트에 onChangePage로 함수가 전달되는데 그 함수를 호출하는 코드
    }.bind(this)}&gt;{this.props.title}&lt;/a&gt;&lt;/h1&gt;
    {this.props.sub}
&lt;/header&gt;</code></pre>
<h3 id="2-1-toc-컴포넌트-클릭-시-state의-mode를-read로-바꾸기">2-1) TOC 컴포넌트 클릭 시 state의 mode를 read로 바꾸기</h3>
<h4 id="appjs-2">App.js</h4>
<pre><code class="language-javascript">...
//onChangePage이벤트 만들기
&lt;TOC onChangePage={function (id) { 
    this.setState({
        mode: &#39;read&#39;
    });
}.bind(this)} data={this.state.contents}&gt;&lt;/TOC&gt;</code></pre>
<h4 id="tocjs">TOC.js</h4>
<pre><code class="language-javascript">while (i &lt; data.length) {
    lists.push(
        &lt;li key={data[i].id}&gt;
            &lt;a href={&quot;/content/&quot; + data[i].id}
                onClick={function (e) {
                    e.preventDefault();
                    this.props.onChangPage();
                }.bind(this)}
            &gt;{data[i].title}&lt;/a&gt;
        &lt;/li&gt;);
    i = i + 1;
}</code></pre>
<h3 id="2-2-내가-선택한-내용을-본문에-나타내기">2-2) 내가 선택한 내용을 본문에 나타내기</h3>
<h4 id="appjs-3">App.js</h4>
<pre><code class="language-javascript">this.state = {
    mode: &#39;read&#39;,
 ⭐selected_content_id = 2, // 기본적으로 2번 contents가 선택되게 함
    welcome: { title: &#39;Welcome&#39;, desc: &#39;Hello, React!!&#39; },
    subject: { title: &#39;WEB&#39;, sub: &#39;World Wide Web!&#39; },
    contents: [
        { id: 1, title: &#39;HTML&#39;, desc: &#39;HTML is for information&#39; },
        { id: 2, title: &#39;CSS&#39;, desc: &#39;CSS is for design&#39; },
        { id: 3, title: &#39;JavaScript&#39;, desc: &#39;JavaScript is for interactive&#39; }
    ]
}

render() {
    var _title, _desc, _article = null;
    if (this.state.mode === &#39;welcome&#39;) {
        _title = this.state.welcome.title;
        _desc = this.state.welcome.desc;
    }⭐ else if (this.state.mode === &#39;read&#39;) {
        var i = 0;
        while (i &lt; this.state.contents.length) {
            var data = this.state.contents[i]; //현재 순번에 해당되는 contents를 data변수에 지정
            if (data.id === this.state.selected_content_id) {
                _title = data.title;
                _desc = data.desc;
                break; //조건문을 끝내는 코드
            }
            i = i + 1;
        }
    }
    return (
        &lt;div className=&quot;App&quot;&gt;
            &lt;TOC onChangePage={function (id) {
                this.setState({
                    mode: &#39;read&#39;,
                    selected_content_id: Number(id)
                    // 인자가 문자로 넘어가기 때문에 숫자로 바꿔주는 Number 명령어를 사용
                });
            }.bind(this)} data={this.state.contents}&gt;
            &lt;/TOC&gt;
        &lt;/div&gt;
    );
}</code></pre>
<h4 id="tocjs-1">TOC.js</h4>
<pre><code class="language-javascript">while (i &lt; data.length) {
    lists.push(
        &lt;li key={data[i].id}&gt;
            &lt;a href={&quot;/content/&quot; + data[i].id}
                data-id={data[i].id} // 
                onClick={function (e) {
                    e.preventDefault();
                    this.props.onChangPage(e.target.dataset.id);
                    //onChangePage 함수를 호출하는 코드의 인자로 e.target에서 얻어진 id 값을 넘겨줌
                }.bind(this)}
            &gt;{data[i].title}&lt;/a&gt;
        &lt;/li&gt;);
    i = i + 1;
}</code></pre>
<ul>
<li>저기서 <code>e.target</code>은 a태그를 가리키며, dataset을 통하여 data-id값을 알아낼 수 있음</li>
</ul>
<ul>
<li>🔥하위가 상위에게 명령할 땐 이벤트를 실행해 state 값 변경 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-3 State]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-3-State</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-3-State</guid>
            <pubDate>Mon, 29 Jun 2020 16:41:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-state-소개">1. State 소개</h2>
<ul>
<li><strong>Props</strong>: 사용자가 컴포넌트를 사용하는 입장에서 중요</li>
<li><strong>State</strong>: Props의 값에 따라 내부의 구현에 필요한 데이터, 외부에서 알 필요 없는 정보 은닉</li>
</ul>
<h2 id="2-state-사용">2. State 사용</h2>
<pre><code>&lt;Subject title=&quot;WEB&quot; sub=&quot;world wide web!&quot;&gt;&lt;/Subject&gt;</code></pre><p>위 props의 값들이 하드코딩되어 있는 걸 state로 만들고, 
state값을 Subject의 props로 전달하는 걸로 코드 개선</p>
<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">class App extends Component {
//state값을 초기화하는 코드, render 함수보다 먼저 실행
    constructor(props) {
        super(props);
        this.state = {
            subject: { title: &#39;WEB&#39;, sub: &#39;World Wide Web!&#39; }
        }
    }

    render() {
        return (
            &lt;div className=&quot;App&quot;&gt;
                &lt;Subject title={this.state.subject.title}
                sub={this.state.subject.sub}&gt;
                &lt;/Subject&gt;
            &lt;/div&gt;
        );
    }
}</code></pre>
<p>이렇게 코드를 개선하면 외부에서는 state값에 subject가 있는지 없는지 알 수 없음. </p>
<h2 id="3-key">3. key</h2>
<ul>
<li>여러 개의 element를 자동으로 생성시 <strong>key</strong>라고 하는 props를 가져야 함</li>
<li>각각의 목록을 다른 것들과 구분할 수 있는 식별자를 <code>key={}</code> 안에 넣어주면 됨</li>
</ul>
<h4 id="appjs-1">App.js</h4>
<pre><code class="language-javascript">class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            subject: { title: &#39;WEB&#39;, sub: &#39;World Wide Web!&#39; },
            contents: [
                { id: 1, title: &#39;HTML&#39;, desc: &#39;HTML is for information&#39; },
                { id: 2, title: &#39;CSS&#39;, desc: &#39;CSS is for design&#39; },
                { id: 3, title: &#39;JavaScript&#39;, desc: &#39;JavaScript is for interactive&#39; }
            ]
        }
    }

    render() {
        return (
            &lt;div className=&quot;App&quot;&gt;
                &lt;Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}&gt;
                &lt;/Subject&gt;
                &lt;TOC data={this.state.contents}&gt;&lt;/TOC&gt;
            &lt;/div&gt;
        );
    }
}</code></pre>
<h4 id="tocjs">TOC.js</h4>
<pre><code class="language-javascript">class TOC extends Component {
    render() {
        //글 목록 생성하기
        var lists = [];
        var data = this.props.data
        var i = 0;
        while (i &lt; data.length) {
            lists.push(&lt;li key={data[i].id}&gt;&lt;a href={&quot;/content/&quot; + data[i].id}&gt;{data[i].title}&lt;/a&gt;&lt;/li&gt;)
            //&lt;li&gt;태그가 생성되어 lists 배열에 담김
            i = i + 1;
        }
        return (
            &lt;nav&gt;
                &lt;ul&gt;
                    {lists}
                &lt;/ul&gt;
            &lt;/nav&gt;
        );
    }
}</code></pre>
<blockquote>
<ul>
<li><strong>state</strong> 와 <strong>props</strong>의 관계<ul>
<li>부모인 App은 state를 사용, 자식에겐 props로 전달<ul>
<li>자식 컴포넌트는 props를 읽기만 하고 직접 수정할 수 없음</li>
<li>state는 컴포넌트 내부에 선언, 값 변경 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-2 컴포넌트 만들기]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-2-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 29 Jun 2020 08:54:13 GMT</pubDate>
            <description><![CDATA[<h2 id="1-컴포넌트-만들기">1. 컴포넌트 만들기</h2>
<h4 id="purehtml">pure.html</h4>
<pre><code>&lt;html&gt;
    &lt;body&gt;
        &lt;header&gt;
            &lt;h1&gt;WEB&lt;/h1&gt;
            world wide web!
        &lt;/header&gt;

        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li&gt;&lt;a href=&quot;1.html&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;2.html&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;3.html&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;

        &lt;article&gt;
            &lt;h2&gt;HTML&lt;/h2&gt;
            HTML is HyperText Markup Language.
        &lt;/article&gt;

    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>** HTML로 짠 코드를 React로 정리해보기**</p>
<h4 id="appjs">App.js</h4>
<pre><code class="language-javascript">class Subject extends Component {
    render() {    // 일반적인 함수는 앞에 function이 붙지만 class에 소속된 함수는 생략
        return (
            &lt;header&gt;    //컴포넌트를 만들 때는 반드시 하나의 최상위 태그로 시작해야 함
                &lt;h1&gt;WEB&lt;/h1&gt;
                world wide web!
            &lt;/header&gt;
        );
    }
}
</code></pre>
<pre><code class="language-javascript">class TOC extends Component {
    render() {
        return (
            &lt;nav&gt;
                &lt;ul&gt;
                    &lt;li&gt;&lt;a href=&quot;1.html&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;2.html&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;3.html&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/nav&gt;
        );
    }
}</code></pre>
<pre><code class="language-javascript">class Content extends Component {
    render() {
        return (
            &lt;article&gt;
                &lt;h2&gt;HTML&lt;/h2&gt;
                HTML is HyperText Markup Language.
            &lt;/article&gt;
        );
    }
}</code></pre>
<pre><code class="language-javascript">/* 컴포넌트 이름에만 집중하게 함으로써 복잡도를 획기적으로 낮춤
(pure.html 코드보다 훨씬 코드가 간편해짐) */

class APP extends Coponent {
    render() {
        return (
            &lt;div className=&quot;App&quot;&gt;
                &lt;Subject&gt;&lt;/Subject&gt;
                &lt;TOC&gt;&lt;/TOC&gt;
                &lt;Content&gt;&lt;/Content&gt;
            &lt;/div&gt;
        );
    }
}</code></pre>
<p>실행결과 <code>&lt;div class=&quot;App&quot;&gt;</code> 안에 <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;article&gt;</code> 태그가 다 들어가 있음</p>
<blockquote>
<h4 id="❗-위에-코드는-html-같지만-자바스크립트-jsx">❗ 위에 코드는 HTML 같지만 자바스크립트 (JSX)</h4>
</blockquote>
<ul>
<li>JSX로 코드 작성시 Creat React App이 알아서 자바스크립트 코드로 변환<ul>
<li>태그를 열었으면 꼭 닫아야 함</li>
<li>두 개 이상의 태그는 무조건 하나의 태그로 감싸야 함
간단하게는 <code>&lt;div&gt;</code> 태그로, 복잡하다면 <code>&lt;Fragment&gt;</code>태그로 감싸기</li>
</ul>
</li>
</ul>
<h2 id="2-props">2. props</h2>
<ul>
<li>어떠한 값을 컴포넌트에게 전달할 때 사용<h3 id="1-props-사용법">1) props 사용법</h3>
</li>
<li>React 매뉴얼 <strong><a href="https://reactjs.org/docs/components-and-props.html">Components and Props</a></strong><pre><code class="language-javascript">class Welcome extends React.Component {
render() {
  return &lt;h1&gt;Hello, {this.props.name}&lt;/h1&gt;;
}
}</code></pre>
자신이 받아온 props값은 this로 조회 가능</li>
</ul>
<h3 id="2-props-사용하기">2) props 사용하기</h3>
<h4 id="appjs-1">App.js</h4>
<pre><code class="language-javascript">class Sample extends Component {
    render() {
        return (
            &lt;header&gt;
                //title, sub값을 받고 싶으면 props라는 파라미터를 통해 조회 가능
                &lt;h1&gt;{this.props.title}&lt;/h1&gt;
                {this.props.sub}

            &lt;/header&gt;
        );
    }
}

class APP extends Coponent {
    render() {
        return (
            &lt;div className=&quot;App&quot;&gt;
                //Subject 컴포넌트를 사용할 때 title, sub 같은 속성을 추가
                &lt;Subject title=&quot;WEB&quot; sub=&quot;world wide web!&quot;&gt;&lt;/Subject&gt;
            &lt;/div&gt;
        );
    }
}</code></pre>
<p>Subject 컴포넌트는 언제나 똑같은 값을 갖고 있었지만 title, sub값에 따라 달라짐
<strong>(재사용성 높아짐)</strong></p>
<h2 id="3-컴포넌트-파일로-분리하기">3. 컴포넌트 파일로 분리하기</h2>
<ol>
<li>src 디렉터리에 components 디렉터리 만들기</li>
<li>components 디렉터리 안에 각각의 컴포넌트 별로  파일 만들기</li>
</ol>
<h4 id="tocjs">TOC.js</h4>
<pre><code class="language-javascript">// Component를 TOC.js 안에 쓰기 위해 리액트를 불러오는 코드
import React, { Component } from &#39;react&#39;;

class TOC extends Component {
    render() {
        return (
            &lt;nav&gt;
                &lt;ul&gt;
                    &lt;li&gt;&lt;a href=&quot;1.html&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;2.html&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;3.html&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/nav&gt;
        );
    }
}

export default TOC;  // TOC 컴포넌트를 가져다 사용할 수 있는 코드</code></pre>
<h4 id="appjs-2">App.js</h4>
<pre><code class="language-javascript">//컴포넌트를 App.js에서 불러오는 코드, 상단에 코드 추가 후 각각의 클래스들 지우기
import TOC from &quot;./components/TOC&quot;;
import Content from &quot;./components/Content&quot;;
import Subject from &quot;./components/Subject&quot;;
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 생활코딩-1 리액트 입문]]></title>
            <link>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-1-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9E%85%EB%AC%B8</link>
            <guid>https://velog.io/@ji-silver/React-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-1-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9E%85%EB%AC%B8</guid>
            <pubDate>Sun, 28 Jun 2020 15:38:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-리액트-왜-쓸까">1. 리액트 왜 쓸까?</h2>
<h3 id="리액트가-없다면">리액트가 없다면?</h3>
<ul>
<li>작성한 태그가 많아지면 코드가 한눈에 들어오지 않음<h3 id="사용자-정의-태그-component로-작성">사용자 정의 태그 Component로 작성</h3>
</li>
<li>가독성</li>
<li>재사용성 높아짐</li>
<li>유지보수를 쉽게 할 수 있음</li>
</ul>
<h2 id="2-개발환경-종류">2. 개발환경 종류</h2>
<p><a href="https://reactjs.org/">https://reactjs.org/</a></p>
<h3 id="1-online-playgrounds">1) Online Playgrounds</h3>
<p>컴퓨터에 세팅할 수 없는 환경이라면  **<a href="https://codesandbox.io">CodeSandbox</a> **이용</p>
<h3 id="2-add-react-to-a-website">2) Add React to a Website</h3>
<p>부분적으로 React 기능을 추가하고 싶을 때 서비스 추가
(🚨 초급사용자가 사용하기에 까다로울 수 있음)</p>
<h3 id="3-create-a-new-react-app">3) Create a New React App</h3>
<p>Toolchain: 여러가지 개발환경, 도구를 모아놓은 것
<strong><a href="https://github.com/facebook/create-react-app">create-react-app</a></strong></p>
<h2 id="3-개발환경-준비">3. 개발환경 준비</h2>
<h3 id="1-nodejs-설치">1) node.js 설치</h3>
<ul>
<li><strong><a href="https://nodejs.org/en/">Node.js 공식 홈페이지</a></strong></li>
<li><code>npm -v</code> : 설치 확인</li>
</ul>
<h3 id="2-create-react-app-설치">2) Create React App 설치</h3>
<h4 id="--npm을-이용한-방법">- npm을 이용한 방법</h4>
<ul>
<li><code>npm install -g create-react-app</code></li>
<li><code>create-react-app -V</code> : 설치 확인. 설치한 프로그램의 버전이 나오면 성공</li>
</ul>
<h4 id="--npx을-이용한-방법-공식적으로-권장하는-방법">- npx을 이용한 방법 (공식적으로 권장하는 방법)</h4>
<ul>
<li><code>npx create-react-app</code></li>
</ul>
<blockquote>
<p>👉 <strong>npm</strong>과 <strong>npx</strong>의 차이:
<strong>npm</strong>은 설치하는 프로그램, <strong>npx</strong>는 임시로 설치해서 실행 후 지워지는 프로그램</p>
</blockquote>
<h2 id="4-개발환경-구축">4. 개발환경 구축</h2>
<p>리액트 개발환경을 만들기 위해 <strong>react-app</strong> 폴더 만들기 </p>
<ul>
<li><code>cd 경로</code></li>
<li><code>create-react-app .</code> : 현재 디렉토리가 create react app에 의해 개발환경이 됨</li>
</ul>
<h2 id="5-샘플-웹앱-실행">5. 샘플 웹앱 실행</h2>
<p><strong><a href="https://code.visualstudio.com/">Visual Studio Code</a></strong> 에디터 이용</p>
<ul>
<li>터미널 띄우기 : View  - Terminal </li>
<li><code>npm (run) start</code> : 실행</li>
<li><code>ctrl + c</code> : 실행 종료</li>
</ul>
<h2 id="6-디렉터리-구조-파악">6. 디렉터리 구조 파악</h2>
<h3 id="1-js">1) JS</h3>
<ul>
<li>public - index.html
react 실행 시 나오는 첫 화면.
컴포넌트가 <code>&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code> 태그 안에 들어감</li>
<li>src - index.js
<code>document.getElementById(&#39;root&#39;)</code> : id 를 이용해 index.html 에 진입.
<code>&lt;App /&gt;</code> : 리액트를 통해 만든 사용자 정의 태그 (컴포넌트)
<code>import App from &#39;./App&#39;</code> : App 컴포넌트를 구현한 걸 불러옴 (App.js)</li>
<li>src - App.js
App 컴포넌트로 실제 구현한 파일</li>
</ul>
<h3 id="2-css">2) CSS</h3>
<ul>
<li><code>import &#39;./App.css&#39;</code> css 파일 불러오기</li>
</ul>
<h2 id="7-배포">7. 배포</h2>
<ul>
<li>터미널에 <code>npm run build</code> 명령 실행 시 디렉터리에 build 파일 생성</li>
<li>공백과 같은 불필요한 내용 없어짐 = 용량이 줄어듦 </li>
</ul>
]]></description>
        </item>
    </channel>
</rss>