<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>신입개발자_장민기</title>
    <link>https://minkee95.tistory.com/</link>
    <description>제대로 된 개발자가 되기 위해 매일 코드를 짜는 장민기입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 26 May 2026 09:18:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>일일일코_장민기</managingEditor>
    <image>
      <title>신입개발자_장민기</title>
      <url>https://tistory1.daumcdn.net/tistory/6906539/attach/6ca71532aeff43358ff4b3e1753a1e78</url>
      <link>https://minkee95.tistory.com</link>
    </image>
    <item>
      <title>20240531_모놀리틱) 타입스크립트로 전환</title>
      <link>https://minkee95.tistory.com/345</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { NavLink, useNavigate } from 'react-router-dom';

interface MemberData {
    id: number;
    username: string;
    email: string;
    country: string;
    area: string;
}

const MyPage: React.FC = () =&amp;gt; {
    const [memberData, setMemberData] = useState&amp;lt;MemberData | null&amp;gt;(null);
    const navigate = useNavigate();

    useEffect(() =&amp;gt; {
        axios.get('http://localhost:8888/MyPage', { withCredentials: true })
            .then(response =&amp;gt; {
                if (response.status === 200) {
                    setMemberData(response.data);
                } else if (response.status === 401) {
                    navigate('/LoginPage');
                }
            })
            .catch(error =&amp;gt; {
                console.error('There was an error!', error);
                navigate('/LoginPage');
            });
    }, [navigate]);

    if (!memberData) {
        return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;My Page&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;ID: {memberData.id}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Username: {memberData.username}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Email: {memberData.email}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Country: {memberData.country}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Area: {memberData.area}&amp;lt;/p&amp;gt;
            &amp;lt;h2&amp;gt;Cloth&amp;lt;/h2&amp;gt;
            &amp;lt;h2&amp;gt;Weather&amp;lt;/h2&amp;gt;
            &amp;lt;h2&amp;gt;Dust&amp;lt;/h2&amp;gt;
            &amp;lt;h2&amp;gt;GPT&amp;lt;/h2&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/ClothEditPage&quot;&amp;gt;옷장&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/ChangePasswordPage&quot;&amp;gt;비밀번호 변경&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/ChangeEmailPage&quot;&amp;gt;이메일 변경&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;h2&amp;gt;로그아웃&amp;lt;/h2&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default MyPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import axios from 'axios';
import {NavLink, useNavigate} from 'react-router-dom';

function MyPage() {
    const [memberData, setMemberData] = useState(null);
    const navigate = useNavigate();

    useEffect(() =&amp;gt; {
        axios.get('http://localhost:8888/MyPage', { withCredentials: true })
            .then(response =&amp;gt; {
                if (response.status === 200) {
                    setMemberData(response.data);
                } else if (response.status === 401) {
                    navigate('/LoginPage');
                }
            })
            .catch(error =&amp;gt; {
                console.error('There was an error!', error);
                navigate('/LoginPage');
            });
    }, [navigate]);

    if (!memberData) {
        return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;My Page&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;{memberData.id}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Username: {memberData.username}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Email: {memberData.email}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Country: {memberData.country}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;Area: {memberData.area}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;h2&amp;gt;Cloth&amp;lt;/h2&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;h2&amp;gt;Weather&amp;lt;/h2&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;h2&amp;gt;Dust&amp;lt;/h2&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;h2&amp;gt;GPT&amp;lt;/h2&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/ClothEditPage&quot;&amp;gt;옷장&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/ChangePasswordPage&quot;&amp;gt;비밀번호 변경&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/ChangeEmailPage&quot;&amp;gt;이메일 변경&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;h2&amp;gt;로그아웃&amp;lt;/h2&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default MyPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;interface MemberData {
    id: number;
    username: string;
    email: string;
    country: string;
    area: string;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스를 생성해서 커스텀 타입 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const MyPage: React.FC = () =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리엑트에서 타입스크립트를 사용하기 위한 React.FC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다만 이 경우에는 &lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;children Props의 타입 체킹이 되지 않기 때문에 지양하라는 글이 많기도 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;- 다음 작업부터는 사용하지 않고 명시적으로 타입을 입력할 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const [memberData, setMemberData] = useState&amp;lt;MemberData | null&amp;gt;(null);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스를 useState에 타입 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기타 변경된 코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoginPage&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import {NavLink, useNavigate} from &quot;react-router-dom&quot;;
import React, {useState, FormEvent, ChangeEvent} from &quot;react&quot;;
import axios from &quot;axios&quot;;

const LoginPage: React.FC = () =&amp;gt; {

    const [username, setUsername] = useState&amp;lt;string&amp;gt;('');
    const [password, setPassword] = useState&amp;lt;string&amp;gt;('');
    const navigate = useNavigate();

    const handleSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post('/login', {
                username: username,
                password: password
            }, {
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            if (response.status === 200) {
                navigate('/MyPage');
            } else {
                console.log('로그인 실패');
                navigate('/LoginPage');
            }
        } catch (error) {
            console.error('Error:', error);
        }
    };

    const handleUsernameChange = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        setUsername(event.target.value);
    };

    const handlePasswordChange = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        setPassword(event.target.value);
    };

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;로그인 페이지&amp;lt;/h1&amp;gt;
            &amp;lt;form id=&quot;loginForm&quot; onSubmit={handleSubmit}&amp;gt;
                &amp;lt;label&amp;gt;username: &amp;lt;input type=&quot;text&quot; value={username} onChange={handleUsernameChange}/&amp;gt;&amp;lt;/label&amp;gt;
                &amp;lt;label&amp;gt;password: &amp;lt;input type=&quot;password&quot; value={password} onChange={handlePasswordChange}/&amp;gt;&amp;lt;/label&amp;gt;
                &amp;lt;span id=&quot;loginErrorField&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;div&amp;gt;
                    &amp;lt;input type=&quot;submit&quot; value=&quot;로그인&quot;/&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/RegisterPage&quot;&amp;gt;회원가입 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/FindUserInfo&quot;&amp;gt;회원 정보 찾기 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default LoginPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;RegisterPage&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from &quot;react&quot;;
import { NavLink, useNavigate } from &quot;react-router-dom&quot;;
import axios from &quot;axios&quot;;

const RegisterPage: React.FC = () =&amp;gt; {
    const [username, setUsername] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [password, setPassword] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [email, setEmail] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [country, setCountry] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [area, setArea] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [countryList, setCountryList] = useState&amp;lt;string[]&amp;gt;([]);
    const [areaList, setAreaList] = useState&amp;lt;string[]&amp;gt;([]);
    const [existUsername, setExistUsername] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [existEmail, setExistEmail] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [checkAuthorizationCodeField, setCheckAuthorizationCodeField] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [code, setCode] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const navigate = useNavigate();

    useEffect(() =&amp;gt; {
        fetch(&quot;/findCountries&quot;)
            .then((response: Response) =&amp;gt; response.json())
            .then(function(result: string[]){
                setCountryList(result);
                if(result.length &amp;gt; 0){
                    setCountry(result[0]);
                }
            })
    }, []);

    useEffect(() =&amp;gt; {
        fetch(`/findAreasByCountry?country=${country}`)
            .then((response: Response) =&amp;gt; response.json())
            .then(function(result: string[]){
                setAreaList(result);
                setArea(result[0]);
            })
    }, [country]);


    // 아이디 중복 체크
    const usernameAjax = async (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        const newUsername = event.target.value;
        setUsername(newUsername);
        try {
            const response = await axios.post&amp;lt;boolean&amp;gt;(`/usernameAjax?username=${newUsername}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                setExistUsername(response.data ? &quot;아이디 중복&quot; : &quot;&quot;);
            }
        } catch (error) {
            console.error('Error checking username:', error);
        }
    }

    // 이메일 중복 체크
    const emailAjax = async (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        const newEmail = event.target.value;
        setEmail(newEmail);
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post&amp;lt;boolean&amp;gt;(`/emailAjax?email=${newEmail}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                setExistEmail(response.data ? &quot;이메일 중복&quot; : &quot;&quot;);
            }
        } catch (error) {
            console.error(error);
        }
    }

    // 이메일 인증 코드 요청
    const emailAuthorizationCode = async () =&amp;gt; {
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post(`/emailAuthorizationCode?email=${email}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                alert(&quot;인증번호가 메일로 전송되었습니다.&quot;);
            }
        } catch (error) {
            console.error(error);
        }
    }

    // 인증 코드 확인
    const codeAjax = async (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        const newCode = event.target.value;
        setCode(newCode);
        const cookies = document.cookie.split(&quot;;&quot;).map(cookie =&amp;gt; cookie.split(&quot;=&quot;)).find(([name]) =&amp;gt; name.trim() === &quot;authorizationCode&quot;);
        if (cookies &amp;amp;&amp;amp; cookies[1] === newCode) {
            setCheckAuthorizationCodeField(&quot;확인되었습니다.&quot;);
        } else {
            setCheckAuthorizationCodeField(&quot;인증번호 오류&quot;);
        }
    }

    const handleSubmit = async (event: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        let errorMessage = '';
        switch (true) {
            case existUsername === &quot;아이디 중복&quot;:
                errorMessage = '아이디 중복 상태입니다.';
                break;
            case existEmail === &quot;이메일 중복&quot;:
                errorMessage = '이메일 중복 상태입니다.';
                break;
            case checkAuthorizationCodeField === &quot;인증번호 오류&quot;:
                errorMessage = '코드 불일치 상태입니다.';
                break;
            default:
                axios.defaults.withCredentials = true;
                try {
                    const response = await axios.post(&quot;/saveUser&quot;, {
                        username: username,
                        password: password,
                        email: email,
                        country: country,
                        area: area
                    }, {
                        headers: {
                            &quot;Content-Type&quot;: &quot;application/json&quot;
                        }
                    });
                    if (response.status === 200) {
                        navigate(&quot;/LoginPage&quot;);
                    } else {
                        navigate(&quot;/RegisterPage&quot;);
                    }
                } catch (error) {
                    console.error(&quot;Error: &quot;, error);
                }
        }
        if (errorMessage !== &quot;&quot;) {
            alert(errorMessage);
        }
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;form id=&quot;registerForm&quot; onSubmit={handleSubmit}&amp;gt;
                username:
                &amp;lt;input type=&quot;text&quot; id=&quot;username&quot; name=&quot;username&quot;
                       onChange={usernameAjax}
                       required minLength={3} /&amp;gt;&amp;lt;br /&amp;gt;
                &amp;lt;span id=&quot;existUsername&quot;&amp;gt;{existUsername}&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;

                password:
                &amp;lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot;
                       onChange={(event) =&amp;gt; setPassword(event.target.value)}
                       required minLength={3} /&amp;gt;&amp;lt;br /&amp;gt;

                email:
                &amp;lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;email&quot;
                       onChange={emailAjax}
                       required minLength={3} /&amp;gt;&amp;lt;br /&amp;gt;
                &amp;lt;span id=&quot;existEmail&quot;&amp;gt;{existEmail}&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;

                인증번호:
                &amp;lt;input type=&quot;text&quot; id=&quot;authorizationCode&quot; onChange={codeAjax} /&amp;gt;
                &amp;lt;input type=&quot;button&quot; id=&quot;sendEmail&quot; value=&quot;인증번호 보내기&quot; onClick={emailAuthorizationCode} /&amp;gt;&amp;lt;br /&amp;gt;
                &amp;lt;span id=&quot;checkAuthorizationCodeField&quot;&amp;gt;{checkAuthorizationCodeField}&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;

                country:
                &amp;lt;select id=&quot;country&quot; value={country} onChange={(event) =&amp;gt; setCountry(event.target.value)}&amp;gt;
                    {countryList &amp;amp;&amp;amp; countryList.map((country: string, index: number) =&amp;gt; (
                        &amp;lt;option key={index} value={country}&amp;gt;{country}&amp;lt;/option&amp;gt;
                    ))}
                &amp;lt;/select&amp;gt;&amp;lt;br /&amp;gt;

                area:
                &amp;lt;select id=&quot;area&quot; value={area} onChange={(event) =&amp;gt; setArea(event.target.value)}&amp;gt;
                    {areaList &amp;amp;&amp;amp; areaList.map((area: string, index: number) =&amp;gt; (
                        &amp;lt;option key={index} value={area}&amp;gt;{area}&amp;lt;/option&amp;gt;
                    ))}
                &amp;lt;/select&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;회원가입&quot; /&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/loginPage&quot;&amp;gt;로그인 페이지&amp;lt;/NavLink&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default RegisterPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;App&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import main from './images/main.jpg';
import LoginPage from './path/beforeLogin/LoginPage';
import ClothEditPage from './path/afterLogin/ClothEditPage'
import RegisterPage from &quot;./path/beforeLogin/RegisterPage&quot;
import './App.css';
import React, { useState, useEffect, FormEvent } from &quot;react&quot;;
import {HashRouter, Routes, Route, NavLink, useNavigate, useLocation} from &quot;react-router-dom&quot;;
import MyPage from &quot;./path/afterLogin/MyPage&quot;;
import axios from &quot;axios&quot;;

const App: React.FC = () =&amp;gt; {

    return (
        &amp;lt;HashRouter&amp;gt;
            &amp;lt;div className=&quot;App&quot;&amp;gt;
                &amp;lt;header className=&quot;mainImage&quot;&amp;gt;
                    &amp;lt;NavLink to='/loginPage'&amp;gt;&amp;lt;img src={main} className=&quot;App-logo&quot; alt=&quot;logo&quot; height={&quot;100px&quot;}/&amp;gt;&amp;lt;/NavLink&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;Routes&amp;gt;
                    &amp;lt;Route path='/' element={'MainPage'}/&amp;gt;
                    &amp;lt;Route path='/LoginPage' element={&amp;lt;LoginPage&amp;gt;&amp;lt;/LoginPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/RegisterPage' element={&amp;lt;RegisterPage&amp;gt;&amp;lt;/RegisterPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/FindUserInfo' element={&amp;lt;FindUserInfo&amp;gt;&amp;lt;/FindUserInfo&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/ViewUsername' element={&amp;lt;ViewUsername&amp;gt;&amp;lt;/ViewUsername&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/ViewChangedPassword' element={&amp;lt;ViewChangedPassword&amp;gt;&amp;lt;/ViewChangedPassword&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/MyPage' element={&amp;lt;MyPage&amp;gt;&amp;lt;/MyPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/ClothEditPage' element={&amp;lt;ClothEditPage&amp;gt;&amp;lt;/ClothEditPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/ChangePasswordPage' element={&amp;lt;ChangePasswordPage&amp;gt;&amp;lt;/ChangePasswordPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/ChangeEmailPage' element={&amp;lt;ChangeEmailPage&amp;gt;&amp;lt;/ChangeEmailPage&amp;gt;}/&amp;gt;
                    &amp;lt;Route path='/*' element={'Not Found'}/&amp;gt;
                &amp;lt;/Routes&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/HashRouter&amp;gt;
    );
}

function FindUserInfo() {
    const [email, setEmail] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [username, setUsername] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const [passwordEmail, setPasswordEmail] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const navigate = useNavigate();

    const handleUsernameSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true;
        try{
            const response = await axios.post(`/ViewUsername?email=${email}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                navigate(&quot;/ViewUsername&quot;, { state: { username: response.data } });
            } else {
                navigate(&quot;/FindUserInfo&quot;)
            }
        } catch (error: any){
            console.error(&quot;Error: &quot;, error.response ? error.response.data : error.message);
            navigate(&quot;/FindUserInfo&quot;);
        }
    }

    const handlePasswordSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true;
        const response = await axios.post(&quot;/SendPassword&quot;, {
            username: username,
            email: passwordEmail
        }, {
            headers: {
                &quot;Content-Type&quot;: &quot;application/json&quot;
            }
        });
        if(response.status === 200){
            console.log(response.data)
            navigate(&quot;/ViewChangedPassword&quot;)
        } else {
            navigate(&quot;/FindUserInfo&quot;)
        }
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;form method=&quot;post&quot; action=&quot;/FindUsernameByEmail&quot; onSubmit={handleUsernameSubmit}&amp;gt;
                email: &amp;lt;input type=&quot;email&quot; name=&quot;email&quot; onChange={(event) =&amp;gt; setEmail(event.target.value)}/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;아이디 찾기&quot;/&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;form method=&quot;post&quot; action=&quot;/UpdatePasswordByEmail&quot; onSubmit={handlePasswordSubmit}&amp;gt;
                id: &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; onChange={(event) =&amp;gt; setUsername(event.target.value)}/&amp;gt;
                email: &amp;lt;input type=&quot;email&quot; name=&quot;email&quot; onChange={(event) =&amp;gt; setPasswordEmail(event.target.value)}/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;비밀번호 찾기&quot;/&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/loginPage&quot;&amp;gt;로그인 페이지&amp;lt;/NavLink&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

function ViewUsername(){
    const [username, setUsername] = useState&amp;lt;string&amp;gt;('');
    const navigate = useNavigate();
    const location = useLocation();

    useEffect(() =&amp;gt; {
        const usernameFromState = location.state?.username;
        if (usernameFromState) {
            setUsername(usernameFromState);
        } else {
            navigate(&quot;/FindUserInfo&quot;);
        }
    }, [location.state, navigate]);

    return(
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;아이디 출력&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;username: {username}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/loginPage&quot;&amp;gt;로그인 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/RegisterPage&quot;&amp;gt;회원가입 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/FindUserInfo&quot;&amp;gt;회원 정보 찾기 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

function ViewChangedPassword(){
    return(
        &amp;lt;div&amp;gt;
            임시비밀번호로 변경되었습니다.
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/loginPage&quot;&amp;gt;로그인 페이지&amp;lt;/NavLink&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

function ChangePasswordPage() {
    const [password, setPassword] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const navigate = useNavigate();

    const handleSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post(&quot;/UpdatePasswordByUsername&quot;,
                {password: password},
                {headers: {'Content-Type':'application/json'}
                });
            if(response.status === 200){
                navigate('/=========================');
            } else {
                console.error(&quot;비밀번호 변경 실패&quot;)
                navigate('/=========================');
            }
        } catch (error) {
            console.error(&quot;Error: &quot;, error)
        }
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;form id=&quot;ChangePasswordForm&quot; onSubmit={handleSubmit}&amp;gt;
                New Password: &amp;lt;input type=&quot;text&quot; value={password}
                                     onChange={(event) =&amp;gt; setPassword(event.target.value)}/&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;변경&quot;/&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/MyPage&quot;&amp;gt;돌아가기&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

function ChangeEmailPage() {
    const [email, setEmail] = useState&amp;lt;string&amp;gt;(&quot;&quot;);
    const navigate = useNavigate();

    const handleSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true;
        try{
            const response = await axios.post(&quot;/UpdateEmailByUsername&quot;,
                {email: email},
                {
                    headers: {
                        'Content-Type':'application/json'
                    }
                });
            if(response.status === 200){
                navigate('/=========================');
            } else {
                console.error(&quot;이메일 변경 실패&quot;)
                navigate('/=========================');
            }
        } catch (error) {
            console.error(&quot;Error: &quot;, error)
        }
    }
    return(
        &amp;lt;div&amp;gt;
            &amp;lt;form id=&quot;ChangeEmailForm&quot; onSubmit={handleSubmit}&amp;gt;
                email: &amp;lt;input type=&quot;text&quot; value={email} onChange={(event) =&amp;gt; setEmail(event.target.value)}/&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;변경&quot;/&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/MyPage&quot;&amp;gt;돌아가기&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/345</guid>
      <comments>https://minkee95.tistory.com/345#entry345comment</comments>
      <pubDate>Fri, 31 May 2024 23:56:57 +0900</pubDate>
    </item>
    <item>
      <title>20240530 모놀리틱)_ajax, 이메일인증 및 submit 제약</title>
      <link>https://minkee95.tistory.com/344</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EFirz/btsHIHdkmnb/EToOLdv7mIB7RqkcnM7kkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EFirz/btsHIHdkmnb/EToOLdv7mIB7RqkcnM7kkk/img.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;466&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.5668%; margin-right: 10px;&quot; data-widthpercent=&quot;53.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EFirz/btsHIHdkmnb/EToOLdv7mIB7RqkcnM7kkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEFirz%2FbtsHIHdkmnb%2FEToOLdv7mIB7RqkcnM7kkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl8ZOO/btsHHJb3Shs/XpcmRKtbFFfJZfUxG2dKaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl8ZOO/btsHHJb3Shs/XpcmRKtbFFfJZfUxG2dKaK/img.png&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;414&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.2704%;&quot; data-widthpercent=&quot;46.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl8ZOO/btsHHJb3Shs/XpcmRKtbFFfJZfUxG2dKaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl8ZOO%2FbtsHHJb3Shs%2FXpcmRKtbFFfJZfUxG2dKaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;391&quot; height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6t7Xr/btsHIMFFUE1/NPv0QKjBhMQb5SOUyYM1Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6t7Xr/btsHIMFFUE1/NPv0QKjBhMQb5SOUyYM1Ik/img.png&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;391&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.1&quot; style=&quot;width: 50.5106%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6t7Xr/btsHIMFFUE1/NPv0QKjBhMQb5SOUyYM1Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6t7Xr%2FbtsHIMFFUE1%2FNPv0QKjBhMQb5SOUyYM1Ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d63oLW/btsHHRnp218/KVQcNH4qxkqIPFw3AKDFbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d63oLW/btsHHRnp218/KVQcNH4qxkqIPFw3AKDFbk/img.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;446&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.3266%;&quot; data-widthpercent=&quot;48.9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d63oLW/btsHHRnp218/KVQcNH4qxkqIPFw3AKDFbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd63oLW%2FbtsHHRnp218%2FKVQcNH4qxkqIPFw3AKDFbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;import React, {useEffect, useState} from &quot;react&quot;;
import {NavLink, useNavigate} from &quot;react-router-dom&quot;;
import axios from &quot;axios&quot;;

function RegisterPage() {
    const [username, setUsername] = useState(&quot;&quot;);
    const [password, setPassword] = useState(&quot;&quot;);
    const [email, setEmail] = useState(&quot;&quot;);
    const [country, setCountry] = useState(&quot;&quot;);
    const [area, setArea] = useState(&quot;&quot;);
    const navigate = useNavigate();
    const [countryList, setCountryList] = useState([]);
    const [areaList, setAreaList] = useState([]);
    const [existUsername, setExistUsername] = useState(&quot;&quot;);
    const [existEmail, setExistEmail] = useState(&quot;&quot;);
    const [checkAuthorizationCodeField, setCheckAuthorizationCodeField] = useState(&quot;&quot;);
    const [code, setCode] = useState(&quot;&quot;);

    useEffect(() =&amp;gt; {
        fetch(&quot;/findCountries&quot;).then((response) =&amp;gt; {
            return response.json();
        }).then(function (result) {
            console.log(result)
            setCountryList(result)
            if (result.length &amp;gt; 0) {
                setCountry(result[0]);
            }
        })
    }, []);
    useEffect(() =&amp;gt; {
        fetch(`/findAreasByCountry?country=${country}`).then((response) =&amp;gt; {
            return response.json();
        }).then(function (result) {
            console.log(result)
            setAreaList(result);
        })
    }, [country]);
    const usernameAjax = async (event) =&amp;gt; {
        const newUsername = event.target.value;
        setUsername(newUsername);
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post(`/usernameAjax?username=${newUsername}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                console.log(response.data);
                if (response.data === true) {
                    setExistUsername(&quot;아이디 중복&quot;);
                } else {
                    setExistUsername(&quot;&quot;);
                }
            }
        } catch (error) {
            console.error(error);
        }
    }
    const emailAjax = async (event) =&amp;gt; {
        const newEmail = event.target.value;
        setEmail(newEmail)
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post(`/emailAjax?email=${newEmail}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                console.log(response.data)
                if (response.data === true) {
                    setExistEmail(&quot;이메일 중복&quot;)
                } else {
                    setExistEmail(&quot;&quot;)
                }
            }
        } catch (error) {
            console.error(error)
        }
    }
    const emailAuthorizationCode = async (event) =&amp;gt; {
        axios.defaults.withCredentials = true;
        try {
            const response = await axios.post(`/emailAuthorizationCode?email=${email}`, {}, {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;
                }
            });
            if (response.status === 200) {
                console.log(response.data)
                alert(&quot;인증번호가 메일로 전송되었습니다.&quot;)
            }
        } catch (error) {
            console.error(error)
        }
    }
    const codeAjax = async (event) =&amp;gt; {
        const newCode = event.target.value;
        console.log(&quot;newCode: &quot;, newCode)
        setCode(newCode);
        const cookies = document.cookie;    //모든 쿠키 가져오기
        const cookieArray = cookies.split(&quot;;&quot;) //모든 쿠키를 배열로 만들기
        const authorizeCookie = cookieArray.map(cookie =&amp;gt; cookie.split(&quot;=&quot;)).find(([name]) =&amp;gt; name === &quot;authorizationCode&quot;);
        console.log(authorizeCookie)
        if (authorizeCookie[1] === newCode.toString()) {
            setCheckAuthorizationCodeField(&quot;확인되었습니다.&quot;)
        } else {
            setCheckAuthorizationCodeField(&quot;인증번호 오류&quot;)
        }
    }
    const handleSubmit = async (event) =&amp;gt; {
        event.preventDefault();
        let errorMessage = '';
        switch (true) {
            case    existUsername === &quot;아이디 중복&quot;:
                errorMessage = '아이디 중복 상태입니다.'
                break;
            case    existEmail === &quot;이메일 중복&quot;:
                errorMessage = '이메일 중복 상태입니다.'
                break;
            case    checkAuthorizationCodeField === &quot;인증번호 오류&quot;:
                errorMessage = '코드 불일치 상태입니다.'
                break;
            default:
                axios.defaults.withCredentials = true;
                try {
                    const response = await axios.post(&quot;/saveUser&quot;, {
                        username: username,
                        password: password,
                        email: email,
                        country: country,
                        area: area
                    }, {
                        headers: {
                            &quot;Content-Type&quot;: &quot;application/json&quot;
                        }
                    });
                    if (response.status === 200) {
                        navigate(&quot;/LoginPage&quot;)
                    } else {
                        navigate(&quot;/RegisterPage&quot;)
                    }
                } catch (error) {
                    console.error(&quot;Error: &quot;, error);
                }
        }
        if(errorMessage !== &quot;&quot;){
            alert(errorMessage);
        }
    }

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;form id=&quot;registerForm&quot; onSubmit={handleSubmit}&amp;gt;
                username:
                &amp;lt;input type=&quot;text&quot; id=&quot;username&quot; name=&quot;username&quot; value={username}
                       onChange={usernameAjax}
                       required minLength=&quot;3&quot;/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;span id=&quot;existUsername&quot;&amp;gt;{existUsername}&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;

                password:
                &amp;lt;input type=&quot;text&quot; id=&quot;password&quot; name=&quot;password&quot; value={password}
                       onChange={(event) =&amp;gt; setPassword(event.target.value)} required minLength=&quot;3&quot;/&amp;gt;&amp;lt;br/&amp;gt;

                email:
                &amp;lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;email&quot; value={email}
                       onChange={emailAjax}
                       required minLength=&quot;3&quot;/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;span id=&quot;existEmail&quot;&amp;gt;{existEmail}&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;

                인증번호:
                &amp;lt;input type=&quot;text&quot; id=&quot;authorizationCode&quot; onChange={codeAjax}/&amp;gt;
                &amp;lt;input type=&quot;button&quot; id=&quot;sendEmail&quot; value=&quot;인증번호 보내기&quot; onClick={emailAuthorizationCode}/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;span id=&quot;checkAuthorizationCodeField&quot;&amp;gt;{checkAuthorizationCodeField}&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;

                country:
                &amp;lt;select id=&quot;country&quot; value={country} onChange={(event) =&amp;gt; setCountry(event.target.value)}&amp;gt;
                    {countryList.map((country, index) =&amp;gt; (
                        &amp;lt;option key={index} value={country}&amp;gt;{country}&amp;lt;/option&amp;gt;
                    ))}
                &amp;lt;/select&amp;gt;&amp;lt;br/&amp;gt;

                area:
                &amp;lt;select id=&quot;area&quot; value={area} onChange={(event) =&amp;gt; setArea(event.target.value)}&amp;gt;
                    {areaList.map((area, index) =&amp;gt; (
                        &amp;lt;option key={index} value={area}&amp;gt;{area}&amp;lt;/option&amp;gt;
                    ))}
                &amp;lt;/select&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;회원가입&quot;/&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/loginPage&quot;&amp;gt;로그인 페이지&amp;lt;/NavLink&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default RegisterPage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RestController&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;package org.example.beforeLogin;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.mailController.MailController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
@RequiredArgsConstructor
@Slf4j
public class BeforeLoginRestController {

    private final BeforeLoginService beforeLoginService;
    private final MailController mailController;

    @PostMapping(&quot;/usernameAjax&quot;)
    public ResponseEntity&amp;lt;Boolean&amp;gt; usernameAjax(String username) {
        return ResponseEntity.ok(beforeLoginService.usernameAjax(username));
    }

    @PostMapping(&quot;/emailAjax&quot;)
    public ResponseEntity&amp;lt;Boolean&amp;gt; emailAjax(String email) {
        return ResponseEntity.ok(beforeLoginService.emailAjax(email));
    }

    @PostMapping(&quot;/emailAuthorizationCode&quot;)
    public ResponseEntity&amp;lt;Boolean&amp;gt; emailAuthorizationCode(String email, HttpServletResponse httpServletResponse) throws IOException {
        log.info(&quot;emailAuthorizationCode: {}&quot;, email);
        mailController.mailCookie(httpServletResponse);
        mailController.authorizationCode_Email(email);
        return ResponseEntity.ok(true);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;username ajax&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState 설정&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;const [username, setUsername] = useState(&quot;&quot;);
const [existUsername, setExistUsername] = useState(&quot;&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- username은 현재 입력된 아이디를 저장하고 출력하는 변수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- existUsername은 현재 입력된 아이디가 중복된 아이디인지를 출력하기 위해 경고를 저장하고 출력하는 변수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return 설정&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;text&quot; id=&quot;username&quot; name=&quot;username&quot; value={username}
       onChange={usernameAjax}
       required minLength=&quot;3&quot;/&amp;gt;&amp;lt;br/&amp;gt;
&amp;lt;span id=&quot;existUsername&quot;&amp;gt;{existUsername}&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- onChange를 통해 입력할 때마다 ajax를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ajax의 결과값은 {existUsername} 을 통해 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- value={username}은 없어도 상관없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios 설정&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const usernameAjax = async (event) =&amp;gt; {
    const newUsername = event.target.value;
    setUsername(newUsername);
    axios.defaults.withCredentials = true;
    try {
        const response = await axios.post(`/usernameAjax?username=${newUsername}`, {}, {
            headers: {
                &quot;Content-Type&quot;: &quot;application/json&quot;
            }
        });
        if (response.status === 200) {
            console.log(response.data);
            if (response.data === true) {
                setExistUsername(&quot;아이디 중복&quot;);
            } else {
                setExistUsername(&quot;&quot;);
            }
        }
    } catch (error) {
        console.error(error);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- event.target.value를 통해 현재 입력한 username을 부르고, newUsername이라는 변수에 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- newUsername에 저장한 변수를 useState를 사용하기 위해 setUsername&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- cors 처리를 위해 axios.defaults.withCredentials&amp;nbsp;=&amp;nbsp;true;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const&amp;nbsp;response&amp;nbsp;=&amp;nbsp;await&amp;nbsp;axios.post(`/usernameAjax?username=${newUsername}`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- post 타입으로 java에 username 전송하고 데이터를 response라는 변수로 받아옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키값과 비교하는 ajax&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const codeAjax = async (event) =&amp;gt; {
    const newCode = event.target.value;
    console.log(&quot;newCode: &quot;, newCode)
    setCode(newCode);
    const cookies = document.cookie;    //모든 쿠키 가져오기
    const cookieArray = cookies.split(&quot;;&quot;) //모든 쿠키를 배열로 만들기
    const authorizeCookie = cookieArray.map(cookie =&amp;gt; cookie.split(&quot;=&quot;)).find(([name]) =&amp;gt; name === &quot;authorizationCode&quot;);
    console.log(authorizeCookie)
    if (authorizeCookie[1] === newCode.toString()) {
        setCheckAuthorizationCodeField(&quot;확인되었습니다.&quot;)
    } else {
        setCheckAuthorizationCodeField(&quot;인증번호 오류&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개인프로젝트/기능프로그램_오늘뭐입지</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/344</guid>
      <comments>https://minkee95.tistory.com/344#entry344comment</comments>
      <pubDate>Fri, 31 May 2024 16:49:36 +0900</pubDate>
    </item>
    <item>
      <title>20240525_React와 Spring Boot 연결(SpringSecurity Login)</title>
      <link>https://minkee95.tistory.com/343</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 알아야 할 것은 기존에 사용하던 SpringSecurity와의 차이이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 팀프로젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모놀리틱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프론트엔드는 jsp, 백엔드는 java를 사용하며 port 번호가 동일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. MSA(1차)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MSA&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프론트엔드는 jsp, 백엔드는 java&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; SpringSecurity를 사용하는 모듈 자체가 동일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 프론트엔드와 백엔드의 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 프론트엔드는 React&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 백엔드는 자바&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; 포트번호가 다름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; SpringSecurity를 사용할 때 프론트엔드에서 인증요청이 백엔드로 넘어간 다음, 인증이 확인되면 프론트엔드로 다시 데이터를 브라우저에 뿌려줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 큰 차이가 발생하는 것은 1, 2번 vs 3번이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 SpringSecurity 사용은 동일 모듈에서 사용했기 때문에 쿠키가 저장되는 것은 같은 포트 번호로 저장되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) jsp도 java도 같은 모듈이기 때문에 동일한 포트번호를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 3번의 경우에는 프론트엔드와 백엔드가 분리되었기 때문에 쿠키가 다른 포트 번호로 저장되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) react는 3000번, java는 8090번을 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로인해 cors 등 다양한 문제가 발생했는데, 문제는 이걸 명시적으로 확인이 되지 않아서 문제 해결이 오래 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import { NavLink, useNavigate } from &quot;react-router-dom&quot;;
import React, { useState } from &quot;react&quot;;
import axios from &quot;axios&quot;;

function LoginPage() {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const navigate = useNavigate();

    const handleSubmit = async (event) =&amp;gt; {
        event.preventDefault();
        axios.defaults.withCredentials = true; //*******************************************************
        try {
            const response = await axios.post('http://localhost:8888/login', {
                username: username,
                password: password
            }, {
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            if (response.data.success) {
                navigate(response.data.frontendUrl);
            } else {
                console.log('로그인 실패');
                navigate(response.data.frontendUrl);
            }
        } catch (error) {
            console.error('Error:', error);
        }
    };

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;form id=&quot;loginForm&quot; onSubmit={handleSubmit}&amp;gt;
                &amp;lt;label&amp;gt;username: &amp;lt;input type=&quot;text&quot; value={username} onChange={(e) =&amp;gt; setUsername(e.target.value)} /&amp;gt;&amp;lt;/label&amp;gt;
                &amp;lt;label&amp;gt;password: &amp;lt;input type=&quot;password&quot; value={password} onChange={(e) =&amp;gt; setPassword(e.target.value)} /&amp;gt;&amp;lt;/label&amp;gt;
                &amp;lt;span id=&quot;loginErrorField&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;div&amp;gt;
                    &amp;lt;input type=&quot;submit&quot; value=&quot;로그인&quot; /&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;p&amp;gt;
                &amp;lt;NavLink to=&quot;/registerPage&quot;&amp;gt;회원가입 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;NavLink to=&quot;/findUserInfo&quot;&amp;gt;회원 정보 찾기 페이지&amp;lt;/NavLink&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default LoginPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 로그인 페이지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- axios를 사용하여 백엔드의 스프링시큐리티 로그인인증 주소로 아이디와 비밀번호를 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그인에 성공하면 백엔드에서 지정해준 마이페이지 주소로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그인에 실패하면 백엔드에서 지정해준 로그인 페이지 주소로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로 문제가 되었던 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) {

    String username = request.getParameter(&quot;username&quot;);
    String password = request.getParameter(&quot;password&quot;);
    log.info(&quot;Attempting to authenticate user: &quot; + username);
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    return authenticationManager.authenticate(authRequest);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에는 jsp의 파라미터를 가져왔기 때문에 request.getParameter를 사용하면 바로 username과 password를 가져올 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) {

    Map&amp;lt;String, String&amp;gt; requestBody = null;
    try {
        requestBody = new ObjectMapper().readValue(request.getInputStream(), Map.class);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    String username = requestBody.get(&quot;username&quot;);
    String password = requestBody.get(&quot;password&quot;);
    log.info(&quot;Attempting to authenticate username: &quot; + username);
    log.info(&quot;Attempting to authenticate password: &quot; + password);
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    return authenticationManager.authenticate(authRequest);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- React에서 보내준 데이터를 받고자 할 때는 이와 같이 변경해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- React에서 서버로 전송하는 데이터는 HTTP 요청의 본문으로 전달되며 JSON 형식이기 때문에 ObjectMapper를 사용하여 Body에서 username과 password를 가져와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 문제가 되었던 부분&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@GetMapping(&quot;/myPage&quot;)
public ResponseEntity&amp;lt;Object&amp;gt; myPage(Principal principal) {
    log.info(&quot;principal : {}&quot;, principal);
    MemberVO memberVO = clothService.checkLogined(principal.getName());
    if (memberVO != null) {
        UseMemberDataDTO useMemberDataDTO = new UseMemberDataDTO();
        BeanUtils.copyProperties(memberVO, useMemberDataDTO);
        return ResponseEntity.ok(useMemberDataDTO);
    }
    String frontendUrl = &quot;http://localhost:3000/loginPage&quot;; 
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(frontendUrl);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 principal이 null로 나오는 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 principal을 만들지 못한다고 생각하여 기존에 만들었던 LoginFilter와 JWTFilter를 계속 고쳐봤지만 문제가 해결되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 황당했던 부분은 쿠키를 만들었는데 안 만들어지는(?) 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoginFilter&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, Authentication authResult) throws IOException, ServletException {

    SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
    String username = securityUser.getUsername();

    Collection&amp;lt;? extends GrantedAuthority&amp;gt; authorities = securityUser.getAuthorities();
    Iterator&amp;lt;? extends GrantedAuthority&amp;gt; authoritiesIterator = authorities.iterator();
    GrantedAuthority grantedAuthority = authoritiesIterator.next();
    String role = grantedAuthority.getAuthority();

    String token = jjwtUtil.createJwt(username, role, expiredMs);

    Cookie authCookie = new Cookie(&quot;AuthToken&quot;, token);
    authCookie.setMaxAge(360000);
    authCookie.setPath(&quot;/&quot;);
    authCookie.setHttpOnly(true);
    if (request.isSecure()) {
        authCookie.setSecure(true);
    }
    response.addCookie(authCookie);
    response.sendRedirect(&quot;/myPage&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파트에서&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Cookie authCookie = new Cookie(&quot;AuthToken&quot;, token);
authCookie.setMaxAge(360000);
authCookie.setPath(&quot;/&quot;);
authCookie.setHttpOnly(true);
if (request.isSecure()) {
    authCookie.setSecure(true);
}
response.addCookie(authCookie);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 통해 쿠키를 만들었고, 이 과정 자체에는 아무런 문제가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;JWTFilter&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

    String token = null;
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if (&quot;AuthToken&quot;.equals(cookie.getName())) {
                token = cookie.getValue();
                break;
            }
        }
    }
    log.info(&quot;JJWT Token: {}&quot;, token);

    if (token != null &amp;amp;&amp;amp; jjwtUtil.validateToken(token)) {
        String username = jjwtUtil.getUsername(token);
        String country = jjwtUtil.getPassword(token);
        String area = jjwtUtil.getRole(token);

        MemberDTO memberDTO = new MemberDTO();
            memberDTO.setUsername(username);
            memberDTO.setCountry(country);
            memberDTO.setArea(area);

        SecurityUser springSecurityUser = new SecurityUser(memberDTO);
        Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser, null, springSecurityUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    filterChain.doFilter(request, response);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파트에서&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;String token = null;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
    for (Cookie cookie : cookies) {
        if (&quot;AuthToken&quot;.equals(cookie.getName())) {
            token = cookie.getValue();
            break;
        }
    }
}
log.info(&quot;JJWT Token: {}&quot;, token);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 통해 쿠키를 찾았고, 이 코드 자체에는 아무런 문제가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 기존 프로젝트에서 잘만 작동하던 코드였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 JWTFilter에서 token을 찾지 못하는 현상이 발생했고, Controller에서도 Principal이 null로 나오는 현상이 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TzPGT/btsHB7DpqF9/VfBYR27IhXZXFC5T10VBKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TzPGT/btsHB7DpqF9/VfBYR27IhXZXFC5T10VBKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TzPGT/btsHB7DpqF9/VfBYR27IhXZXFC5T10VBKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTzPGT%2FbtsHB7DpqF9%2FVfBYR27IhXZXFC5T10VBKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1794&quot; height=&quot;208&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 크롬 개발자 도구에서 애플리케이션 탭의 쿠키를 확인했을 때 JWT가 없는 것도 확인할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5J95c/btsHDF6GIZk/hBz726tkYN0tlSrkkffzF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5J95c/btsHDF6GIZk/hBz726tkYN0tlSrkkffzF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5J95c/btsHDF6GIZk/hBz726tkYN0tlSrkkffzF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5J95c%2FbtsHDF6GIZk%2FhBz726tkYN0tlSrkkffzF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;277&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 알고보니 이런 현상은 모두 CORS 때문에 발생한 현상이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS는 MSA로 프로젝트를 짜면서 발생한 적이 있었지만, CORS 문제라고 명시적으로 표시되었는데 이번에는 CORS 문제라고 어디에도 표시되지 않았다. 애초에 CORS 문제는 생각하고 있었기 때문에 백엔드의 Configuration 설정은 이미 해두어서 더 찾기 어려웠던 것 같다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;package org.example.util;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping(&quot;/**&quot;)
                .allowedOrigins(&quot;http://localhost:3000&quot;)
                .allowedMethods(&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;)
                .allowCredentials(true);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 CORS 문제라고 의심이 든 계기는 네트워크 응답헤더를 확인했을 때 Set-Cookie를 확인했을 때였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b34UOC/btsHBqDEORR/xmQVF91t062VzdXNcSrTy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b34UOC/btsHBqDEORR/xmQVF91t062VzdXNcSrTy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b34UOC/btsHBqDEORR/xmQVF91t062VzdXNcSrTy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb34UOC%2FbtsHBqDEORR%2FxmQVF91t062VzdXNcSrTy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;778&quot; height=&quot;234&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 어디에서도, 애플리케이션 탭에서도 확인이 안 되던 쿠키가 네트워크 응답헤더에는 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 거치며 CORS 문제를 재검토하게 되었고, 프론트에서도 백엔드처럼 credential 설정이 필요하다는 것을 알게 되었다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const handleSubmit = async (event) =&amp;gt; {
    event.preventDefault();
    axios.defaults.withCredentials = true; //*******************************************************&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;********** 의문인 부분**********&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 쿠키가 백엔드에서 생성되었고 응답헤더에 넣었다. 또한 쿠키는 클라이언트에서 관리한다. 그러면 이번 문제 상황에서는 어디에 저장이 되었던 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 백엔드 8888포트 / 프론트 3000포트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 백엔드 8888포트에 저장이 되진 않았을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 저장되었다면 백엔드에서 쿠키 조회를 했을 때 출력됐었겠지...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 프론트 3000번 포트에 저장이 되었는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 그러면 왜 localhost:3000 애플리케이션 탭의 쿠키에서 확인이 되지 않는거지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 그러면 8888번 포트 클라이언트에 저장이 된건가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 마찬가지로 localhost:8888 애플리케이션 탭의 쿠키에서 확인이 되지 않는다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론: 저장된 쿠키는 어디에 있었길래 확인이 어려웠던 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황도 난해하고, 찾아도 나오질 않는다...보충해야 할 부분...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@youjung/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%99%80-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%ED%96%88%EC%9D%84-%EB%95%8C-%EC%BF%A0%ED%82%A4-%EA%B3%B5%EC%9C%A0-%EC%95%88%EB%90%A0-%EB%95%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@youjung/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%99%80-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%ED%96%88%EC%9D%84-%EB%95%8C-%EC%BF%A0%ED%82%A4-%EA%B3%B5%EC%9C%A0-%EC%95%88%EB%90%A0-%EB%95%8C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716668577156&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;프론트와 백엔드 분리했을 때 쿠키 공유 안되는 이유 (feat. Credentials 옵션)&quot; data-og-description=&quot;문제 상황 [ 개발 환경 ] 프론트엔드: http://localhost:8081 백엔드: http://localhost:8080 혼자서 프론트엔드와 백엔드를 분리한 상황에서 로그인을 개발하고 있는데, 세션에서 유저 정보를 제대로 가져오&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@youjung/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%99%80-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%ED%96%88%EC%9D%84-%EB%95%8C-%EC%BF%A0%ED%82%A4-%EA%B3%B5%EC%9C%A0-%EC%95%88%EB%90%A0-%EB%95%8C&quot; data-og-url=&quot;https://velog.io/@youjung/프론트와-백엔드-분리했을-때-쿠키-공유-안될-때&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfB1H2/hyWdeGpGQA/kAWMaaMaFT6PgRnFZUYlWK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/lFdtC/hyWdovuEpN/zf5HkcPFVtRXXKyL8hLBY0/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/u2TRq/hyV91206OW/GHciOw5ongx5MMfKfNVnP0/img.jpg?width=2160&amp;amp;height=2160&amp;amp;face=0_0_2160_2160&quot;&gt;&lt;a href=&quot;https://velog.io/@youjung/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%99%80-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%ED%96%88%EC%9D%84-%EB%95%8C-%EC%BF%A0%ED%82%A4-%EA%B3%B5%EC%9C%A0-%EC%95%88%EB%90%A0-%EB%95%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@youjung/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%99%80-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%ED%96%88%EC%9D%84-%EB%95%8C-%EC%BF%A0%ED%82%A4-%EA%B3%B5%EC%9C%A0-%EC%95%88%EB%90%A0-%EB%95%8C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfB1H2/hyWdeGpGQA/kAWMaaMaFT6PgRnFZUYlWK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/lFdtC/hyWdovuEpN/zf5HkcPFVtRXXKyL8hLBY0/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/u2TRq/hyV91206OW/GHciOw5ongx5MMfKfNVnP0/img.jpg?width=2160&amp;amp;height=2160&amp;amp;face=0_0_2160_2160');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프론트와 백엔드 분리했을 때 쿠키 공유 안되는 이유 (feat. Credentials 옵션)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 상황 [ 개발 환경 ] 프론트엔드: http://localhost:8081 백엔드: http://localhost:8080 혼자서 프론트엔드와 백엔드를 분리한 상황에서 로그인을 개발하고 있는데, 세션에서 유저 정보를 제대로 가져오&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개인프로젝트/기능프로그램_오늘뭐입지</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/343</guid>
      <comments>https://minkee95.tistory.com/343#entry343comment</comments>
      <pubDate>Sat, 25 May 2024 17:06:04 +0900</pubDate>
    </item>
    <item>
      <title>20240525_옷 출력 필터링</title>
      <link>https://minkee95.tistory.com/342</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;813&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blitjE/btsHBM0tWfw/u9KOorLcNOEmsFOimzGXgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blitjE/btsHBM0tWfw/u9KOorLcNOEmsFOimzGXgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blitjE/btsHBM0tWfw/u9KOorLcNOEmsFOimzGXgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblitjE%2FbtsHBM0tWfw%2Fu9KOorLcNOEmsFOimzGXgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;813&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;813&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rM3bs/btsHBTE8HEb/FAEeKOvq4cUEEEMyeuMzT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rM3bs/btsHBTE8HEb/FAEeKOvq4cUEEEMyeuMzT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rM3bs/btsHBTE8HEb/FAEeKOvq4cUEEEMyeuMzT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrM3bs%2FbtsHBTE8HEb%2FFAEeKOvq4cUEEEMyeuMzT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;281&quot; height=&quot;198&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옷 출력 화면에 간단한 필터 기능을 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 카테고리가 기입된 필터를 변경하면 출력되는 옷은 해당 카테고리만 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 옷 수정 화면에서 필터링이 된 상태로 옷을 추가할 경우, 추가한 옷의 카테고리로 자동 필터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jsp&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;필터:&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;
            &amp;lt;select id=&quot;categoryFilter&quot;&amp;gt;
                &amp;lt;option value=&quot;기본&quot;&amp;gt;기본&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;모자&quot;&amp;gt;모자&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;겉옷&quot;&amp;gt;겉옷&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;상의&quot;&amp;gt;상의&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;하의&quot;&amp;gt;하의&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;양말&quot;&amp;gt;양말&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;신발&quot;&amp;gt;신발&amp;lt;/option&amp;gt;
            &amp;lt;/select&amp;gt;
        &amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Cloth&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;수정&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;삭제&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tbody id=&quot;allClothesField&quot;&amp;gt;&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;js&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$(&quot;#categoryFilter&quot;).on(&quot;change&quot;, function(){
    viewMyClothes()
})&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 옷 저장 Ajax
$(&quot;#clothSaveField&quot;).on(&quot;submit&quot;, function(event){
    event.preventDefault();
    var userid = $(&quot;#userid&quot;).val();
    var clothdata = $(&quot;#cloth&quot;).val();
    var category = $(&quot;#categoryForCloth&quot;).val();
    var categoryFilter = $(&quot;#categoryFilter&quot;);

    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/updateCloth&quot;,
        data: {
            userid: userid,
            clothdata : clothdata,
            category : category
        },
        datatype: &quot;text&quot;,
        success: function(){
            if(categoryFilter.val() !== &quot;기본&quot;){
                $(&quot;#categoryFilter&quot;).val(category)
            }
            viewMyClothes()
        },
        error: function(){
            console.log(&quot;옷 저장 ajax 에러&quot;)
        }
    })
})// 옷 저장 Ajax&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function viewMyClothes() {
    var userid = $(&quot;#userid&quot;).val()
    var categoryFilter = $(&quot;#categoryFilter&quot;).val()
    var allClothesField = $(&quot;#allClothesField&quot;)
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/selectAllClothes&quot;,
        data: {
            userid: userid,
            categoryFilter: categoryFilter
        },
        success: function (response) {
            var tableHtml = &quot;&quot;;
            response.forEach(function (cloth) {
                tableHtml += &quot;&amp;lt;tr&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='category'&amp;gt;&quot; + cloth.category + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='clothdata'&amp;gt;&quot; + cloth.clothdata + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='updateEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;수정&amp;lt;/td&amp;gt;&amp;gt;&quot;
                tableHtml += &quot;&amp;lt;td class='deleteEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;x&quot; + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;/tr&amp;gt;&quot;;
            });

            // 생성된 표를 화면에 출력
            allClothesField.html(tableHtml);
        },
        error: function () {
            console.log(&quot;저장된 옷 불러오기 실패&quot;)
        }
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Controller&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;//옷 불러오기 메서드
@PostMapping(&quot;/selectAllClothes&quot;)
public List&amp;lt;ClothDTO&amp;gt; selectAllClothes(Integer userid, String categoryFilter){
    List&amp;lt;ClothDTO&amp;gt; clothDTOList = clothService.findAllByUserid(userid);
    if(Objects.equals(categoryFilter, &quot;기본&quot;)){
        return clothDTOList;
    }
    List&amp;lt;ClothDTO&amp;gt; resultClothDTOList = new ArrayList&amp;lt;&amp;gt;();
    for (ClothDTO clothDTO : clothDTOList) {
        if(Objects.equals(clothDTO.category, categoryFilter)){
            resultClothDTOList.add(clothDTO);
        }
    }
    return resultClothDTOList;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jsp&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;필터:&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;
            &amp;lt;select id=&quot;categoryFilter&quot;&amp;gt;
                &amp;lt;option value=&quot;기본&quot;&amp;gt;기본&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;모자&quot;&amp;gt;모자&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;겉옷&quot;&amp;gt;겉옷&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;상의&quot;&amp;gt;상의&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;하의&quot;&amp;gt;하의&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;양말&quot;&amp;gt;양말&amp;lt;/option&amp;gt;
                &amp;lt;option value=&quot;신발&quot;&amp;gt;신발&amp;lt;/option&amp;gt;
            &amp;lt;/select&amp;gt;
        &amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Cloth&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;수정&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;삭제&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tbody id=&quot;allClothesField&quot;&amp;gt;&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에는 div 태그 하나만 두고, js에서 모든 태그를 다 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 바뀌는 부분은 테이블의 내용물 뿐인데 그래야 하나 싶은 생각이 들었고, 테이블 껍데기는 jsp에서 바로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그런데 저 select 내용물은 DB에 저장된 전체 카테고리만 출력해야 할지, 아니면 이대로 기입을 하는게 좋을지 고민이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 굳이 이런 것까지 DB에서 데이터를 불러와야 되나? 싶은 생각과 DB에 없는 카테고리가 출력되거나, DB에 추가된 카테고리가 출력되지 않을 수 있다는 생각이 교차 중.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다만, 해당 프로젝트에서는 카테고리가 더 추가될 만한 것이 애매하기 때문에 이대로도 괜찮다고 생각한다.(액세사리나 가방은 GPT가 패션을 잘 이해하기 못하고 날씨에 따라서 사용 구분을 잘하지 못한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;js&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;var userid = $(&quot;#userid&quot;).val()
var categoryFilter = $(&quot;#categoryFilter&quot;).val()
var allClothesField = $(&quot;#allClothesField&quot;)
$.ajax({
    type: &quot;post&quot;,
    url: &quot;/selectAllClothes&quot;,
    data: {
        userid: userid,
        categoryFilter: categoryFilter
    },
    success: function (response) {
        var tableHtml = &quot;&quot;;
        response.forEach(function (cloth) {
            tableHtml += &quot;&amp;lt;tr&amp;gt;&quot;;
            tableHtml += &quot;&amp;lt;td class='category'&amp;gt;&quot; + cloth.category + &quot;&amp;lt;/td&amp;gt;&quot;;
            tableHtml += &quot;&amp;lt;td class='clothdata'&amp;gt;&quot; + cloth.clothdata + &quot;&amp;lt;/td&amp;gt;&quot;;
            tableHtml += &quot;&amp;lt;td class='updateEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;수정&amp;lt;/td&amp;gt;&amp;gt;&quot;
            tableHtml += &quot;&amp;lt;td class='deleteEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;x&quot; + &quot;&amp;lt;/td&amp;gt;&quot;;
            tableHtml += &quot;&amp;lt;/tr&amp;gt;&quot;;
        });&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- jsp에서 만드는 테이블의 껍데기를 제거하고 새로 생긴 필터 데이터를 컨트롤러로 보낼 수 있도록 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$(&quot;#categoryFilter&quot;).on(&quot;change&quot;, function(){
    viewMyClothes()
})&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 필터를 변경할 때마다 재출력할 수 있도록 기능을 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 옷 저장 Ajax
$(&quot;#clothSaveField&quot;).on(&quot;submit&quot;, function(event){
    event.preventDefault();
    var userid = $(&quot;#userid&quot;).val();
    var clothdata = $(&quot;#cloth&quot;).val();
    var category = $(&quot;#categoryForCloth&quot;).val();
    var categoryFilter = $(&quot;#categoryFilter&quot;);

    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/updateCloth&quot;,
        data: {
            userid: userid,
            clothdata : clothdata,
            category : category
        },
        datatype: &quot;text&quot;,
        success: function(){
            if(categoryFilter.val() !== &quot;기본&quot;){
                $(&quot;#categoryFilter&quot;).val(category)
            }
            viewMyClothes()
        },
        error: function(){
            console.log(&quot;옷 저장 ajax 에러&quot;)
        }
    })
})// 옷 저장 Ajax&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전체 옷이 출력되지 않은 상태에서만 옷을 추가했을 때, 자동 필터링이 되도록 기능을 개선했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@PostMapping(&quot;/selectAllClothes&quot;)
public List&amp;lt;ClothDTO&amp;gt; selectAllClothes(Integer userid, String categoryFilter){
    List&amp;lt;ClothDTO&amp;gt; clothDTOList = clothService.findAllByUserid(userid);
    if(Objects.equals(categoryFilter, &quot;기본&quot;)){
        return clothDTOList;
    }
    List&amp;lt;ClothDTO&amp;gt; resultClothDTOList = new ArrayList&amp;lt;&amp;gt;();
    for (ClothDTO clothDTO : clothDTOList) {
        if(Objects.equals(clothDTO.category, categoryFilter)){
            resultClothDTOList.add(clothDTO);
        }
    }
    return resultClothDTOList;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에는 단순히 userid에 따라 옷 전체를 전부 출력하는 단순한 기능이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이제는 2가지 케이스로 나누어서 필터가 '기본' 상태일 때는 이전과 동일하게 옷 전체를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 필터가 '기본' 상태가 아닐 때는 해당 카테고리에 해당하는 옷들만 새로운 리스트에 넣어 필터링된 옷 목록을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선될 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 페이지네이션 추가&lt;/p&gt;</description>
      <category>개인프로젝트/기능프로그램_오늘뭐입지</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/342</guid>
      <comments>https://minkee95.tistory.com/342#entry342comment</comments>
      <pubDate>Sat, 25 May 2024 09:57:09 +0900</pubDate>
    </item>
    <item>
      <title>20240524_옷 수정하기</title>
      <link>https://minkee95.tistory.com/341</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 옷 출력 화면은 자바스크립트로 구현되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 옷 수정 버튼 및 수정 작업도 자바스크립트로 작동하는게 편하다...할 수 밖에 없다 인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;//옷 수정 상태로 변경
$(document).on(&quot;click&quot;, &quot;.updateEvent&quot;, function () {
    var currentRow = $(this).closest(&quot;tr&quot;)                                 //버튼이 소속된 행을 찾음
    var categoryCell = currentRow.find(&quot;.category&quot;);            //categoryCell 찾기
    var clothdataCell = currentRow.find(&quot;.clothdata&quot;);          //clothdataCell 찾기
    var categoryText = categoryCell.text();                            //categoryText 찾기
    var clothdataText = clothdataCell.text();                          //clothdataText 찾기
    var updateCell = currentRow.find(&quot;.updateEvent&quot;)            //수정 버튼 찾기

    var selectHtml ='&amp;lt;select id=&quot;categoryForCloth&quot; name=&quot;categoryForCloth&quot;&amp;gt;' +
                        '&amp;lt;option value=&quot;모자&quot;&amp;gt;모자&amp;lt;/option&amp;gt;' +
                        '&amp;lt;option value=&quot;겉옷&quot;&amp;gt;겉옷&amp;lt;/option&amp;gt;' +
                        '&amp;lt;option value=&quot;상의&quot;&amp;gt;상의&amp;lt;/option&amp;gt;' +
                        '&amp;lt;option value=&quot;하의&quot;&amp;gt;하의&amp;lt;/option&amp;gt;' +
                        '&amp;lt;option value=&quot;양말&quot;&amp;gt;양말&amp;lt;/option&amp;gt;' +
                        '&amp;lt;option value=&quot;신발&quot;&amp;gt;신발&amp;lt;/option&amp;gt;' +
                    '&amp;lt;/select&amp;gt;';
    categoryCell.html(selectHtml);
    categoryCell.find('select').val(categoryText);

    clothdataCell.html(&quot;&amp;lt;input type='text' id='cloth' name='cloth' value='&quot;+clothdataText+&quot;'/&amp;gt;&quot;)

    updateCell.text(&quot;저장&quot;);
    updateCell.removeClass(&quot;updateEvent&quot;).addClass(&quot;saveEvent&quot;);
})

// 옷 업데이트 ajax
$(document).on(&quot;click&quot;, &quot;.saveEvent&quot;, function () {
    var userid = $(&quot;#userid&quot;).val();
    var currentRow = $(this).closest(&quot;tr&quot;);                              // 버튼이 소속된 행을 찾음
    var categoryCell = currentRow.find(&quot;.category select&quot;);   // &amp;lt;select&amp;gt; 요소 찾기
    var clothdataCell = currentRow.find(&quot;.clothdata input&quot;);  // &amp;lt;input&amp;gt; 요소 찾기
    var updateCell = currentRow.find(&quot;.saveEvent&quot;);           // 저장 버튼 찾기

    var categoryText = categoryCell.val(); // 선택된 카테고리 값
    var clothdataText = clothdataCell.val(); // 입력된 옷 데이터 값

    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/updateCloth&quot;,
        data: {
            id: updateCell.data(&quot;id&quot;),
            userid: userid,
            category: categoryText,
            clothdata: clothdataText
        },
        success: function() {
            console.log(&quot;데이터 저장 성공&quot;);
        },
        error: function() {
            console.log(&quot;데이터 저장 실패&quot;);
        }
    });

    // 선택된 값과 입력된 값으로 셀을 업데이트
    categoryCell.closest(&quot;.category&quot;).text(categoryText);
    clothdataCell.closest(&quot;.clothdata&quot;).text(clothdataText);

    // 저장 버튼을 수정 버튼으로 변경
    updateCell.text(&quot;수정&quot;);
    updateCell.removeClass(&quot;saveEvent&quot;).addClass(&quot;updateEvent&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function viewMyClothes() {
    var userid = $(&quot;#userid&quot;).val()
    var allClothesField = $(&quot;#allClothesField&quot;)
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/selectAllClothes&quot;,
        data: {userid: userid},
        success: function (response) {
            var tableHtml = &quot;&amp;lt;table&amp;gt;&quot;;
            tableHtml += &quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Cloth&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;수정&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;삭제&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&quot;;
            response.forEach(function (cloth) {
                tableHtml += &quot;&amp;lt;tr&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='category'&amp;gt;&quot; + cloth.category + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='clothdata'&amp;gt;&quot; + cloth.clothdata + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;td class='updateEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;수정&quot; + &quot;&amp;lt;/td&amp;gt;&amp;gt;&quot;
                tableHtml += &quot;&amp;lt;td class='deleteEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;x&quot; + &quot;&amp;lt;/td&amp;gt;&quot;;
                tableHtml += &quot;&amp;lt;/tr&amp;gt;&quot;;
            });
            tableHtml += &quot;&amp;lt;/table&amp;gt;&quot;;

            // 생성된 표를 화면에 출력
            allClothesField.html(tableHtml);
        },
        error: function () {
            console.log(&quot;저장된 옷 불러오기 실패&quot;)
        }
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;tableHtml += &quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Cloth&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;수정&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;삭제&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&quot;;
response.forEach(function (cloth) {
    tableHtml += &quot;&amp;lt;tr&amp;gt;&quot;;
    tableHtml += &quot;&amp;lt;td class='category'&amp;gt;&quot; + cloth.category + &quot;&amp;lt;/td&amp;gt;&quot;;
    tableHtml += &quot;&amp;lt;td class='clothdata'&amp;gt;&quot; + cloth.clothdata + &quot;&amp;lt;/td&amp;gt;&quot;;
    tableHtml += &quot;&amp;lt;td class='updateEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;수정&quot; + &quot;&amp;lt;/td&amp;gt;&amp;gt;&quot;
    tableHtml += &quot;&amp;lt;td class='deleteEvent' data-id='&quot; + cloth.id + &quot;'&amp;gt;&quot; + &quot;x&quot; + &quot;&amp;lt;/td&amp;gt;&quot;;
    tableHtml += &quot;&amp;lt;/tr&amp;gt;&quot;;
});
tableHtml += &quot;&amp;lt;/table&amp;gt;&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tableHtml 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 헤더: 수정 파트 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 바디: 각 컬럼 클래스 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;var currentRow = $(this).closest(&quot;tr&quot;)                      //버튼이 소속된 행을 찾음
var categoryCell = currentRow.find(&quot;.category&quot;);            //categoryCell 찾기
var clothdataCell = currentRow.find(&quot;.clothdata&quot;);          //clothdataCell 찾기
var categoryText = categoryCell.text();                     //categoryText 찾기
var clothdataText = clothdataCell.text();                   //clothdataText 찾기
var updateCell = currentRow.find(&quot;.updateEvent&quot;)            //수정 버튼 찾기&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- this와 closest를 사용하여 수정 버튼을 누른 행을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 행으로부터 각 컬럼의 셀과 데이터를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;var selectHtml ='&amp;lt;select id=&quot;categoryForCloth&quot; name=&quot;categoryForCloth&quot;&amp;gt;' +
                    '&amp;lt;option value=&quot;모자&quot;&amp;gt;모자&amp;lt;/option&amp;gt;' +
                    '&amp;lt;option value=&quot;겉옷&quot;&amp;gt;겉옷&amp;lt;/option&amp;gt;' +
                    '&amp;lt;option value=&quot;상의&quot;&amp;gt;상의&amp;lt;/option&amp;gt;' +
                    '&amp;lt;option value=&quot;하의&quot;&amp;gt;하의&amp;lt;/option&amp;gt;' +
                    '&amp;lt;option value=&quot;양말&quot;&amp;gt;양말&amp;lt;/option&amp;gt;' +
                    '&amp;lt;option value=&quot;신발&quot;&amp;gt;신발&amp;lt;/option&amp;gt;' +
                '&amp;lt;/select&amp;gt;';
categoryCell.html(selectHtml);
categoryCell.find('select').val(categoryText);

clothdataCell.html(&quot;&amp;lt;input type='text' id='cloth' name='cloth' value='&quot;+clothdataText+&quot;'/&amp;gt;&quot;)

updateCell.text(&quot;저장&quot;);
updateCell.removeClass(&quot;updateEvent&quot;).addClass(&quot;saveEvent&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- html와 text메서드를 통해 각 컬럼의 셀에 데이터를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 수정은 저장으로 바꾸고 class도 변경해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;var userid = $(&quot;#userid&quot;).val();
var currentRow = $(this).closest(&quot;tr&quot;);                   // 버튼이 소속된 행을 찾음
var categoryCell = currentRow.find(&quot;.category select&quot;);   // &amp;lt;select&amp;gt; 요소 찾기
var clothdataCell = currentRow.find(&quot;.clothdata input&quot;);  // &amp;lt;input&amp;gt; 요소 찾기
var updateCell = currentRow.find(&quot;.saveEvent&quot;);           // 저장 버튼 찾기

var categoryText = categoryCell.val(); // 선택된 카테고리 값
var clothdataText = clothdataCell.val(); // 입력된 옷 데이터 값&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 저장 버튼을 누르면 작동되는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 마찬가지로 행의 정보를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 여기에서 초기화하는 변수들은 DTO로 만들어 백엔드로 넘어갈 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;//옷 추가 및 수정하기 메서드
@PostMapping(&quot;/updateCloth&quot;)
public void updateCloth(ClothDTO updatedCloth){
    clothService.updateClothes(updatedCloth);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 백엔드의 url은 옷 추가 메서드와 동일하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- save를 사용하는 jpa이기 때문에 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// 선택된 값과 입력된 값으로 셀을 업데이트
categoryCell.closest(&quot;.category&quot;).text(categoryText);
clothdataCell.closest(&quot;.clothdata&quot;).text(clothdataText);

// 저장 버튼을 수정 버튼으로 변경
updateCell.text(&quot;수정&quot;);
updateCell.removeClass(&quot;saveEvent&quot;).addClass(&quot;updateEvent&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ajax가 종료되면 현재 input과 select로 바뀌어 있는 셀을 text로 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저장 버튼도 원래대로 수정 버튼으로 변경하고, 클래스도 바꾸어준다.&lt;/p&gt;</description>
      <category>개인프로젝트/기능프로그램_오늘뭐입지</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/341</guid>
      <comments>https://minkee95.tistory.com/341#entry341comment</comments>
      <pubDate>Fri, 24 May 2024 11:44:23 +0900</pubDate>
    </item>
    <item>
      <title>20240523_메일 시스템(임시비밀번호 전송, 이메일 인증)</title>
      <link>https://minkee95.tistory.com/340</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React 공부 및 모놀리틱 프로젝트 구현에 시간을 쓰다보니 오랜만에 기능을 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.properties&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=이메일
spring.mail.password=이메일 코드
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.auth=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MailController&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;package org.member.mailController;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequiredArgsConstructor
@Slf4j
public class MailController {

    @Value(&quot;${spring.mail.username}&quot;)
    private static String from;
    private final MailService mailService;

    private static final String from_email = from;
    private String expiredTime;
    private String authorizationCode;

    @PostMapping(&quot;/authorizationCode_Email&quot;)
    public void authorizationCode_Email(String to_email) throws IOException {

        Map&amp;lt;String,String&amp;gt; changeData = new HashMap&amp;lt;&amp;gt;();

        //******************복사 후 수정하는 부분******************
        String emailCode = &quot;authorizationCode_Email&quot;;
        String email_title = &quot;[회원가입인증] 회원가입 인증번호 전송&quot;;
        changeData.put(&quot;##인증번호##&quot;, authorizationCode);               
        changeData.put(&quot;##인증번호_유효기간##&quot;, expiredTime);        
        //******************복사 후 수정하는 부분******************

        String email_Form = &quot;static/mailForm/&quot;+emailCode+&quot;_Form.html&quot;;
        String innerImageName = emailCode+&quot;_Image&quot;;
        String innerImagePath = &quot;/static/mailForm/image/&quot;+innerImageName+&quot;.png&quot;;

        String email_body = mailService.emailBody(email_Form, changeData);
        mailService.sendEmail(from_email, to_email, email_title, email_body, innerImageName, innerImagePath);
    }


    public void temporaryPassword_Email(String to_email, String temporaryPassword, String username) throws IOException {

        Map&amp;lt;String,String&amp;gt; changeData = new HashMap&amp;lt;&amp;gt;();

        //******************복사 후 수정하는 부분******************
        String emailCode = &quot;temporaryPassword_Email&quot;;
        String email_title = &quot;[회원정보변경] 임시 비밀번호 발급&quot;;
        changeData.put(&quot;##유저_링크##&quot;, &quot;www.naver.com&quot;);           //임시 링크
        changeData.put(&quot;##유저_아이디##&quot;, username);
        changeData.put(&quot;##유저_비밀번호##&quot;, temporaryPassword);
        //******************복사 후 수정하는 부분******************

        String email_Form = &quot;static/mailForm/&quot;+emailCode+&quot;_Form.html&quot;;
        String innerImageName = emailCode+&quot;_Image&quot;;
        String innerImagePath = &quot;/static/mailForm/image/&quot;+innerImageName+&quot;.png&quot;;

        String email_body = mailService.emailBody(email_Form, changeData);
        mailService.sendEmail(from_email, to_email, email_title, email_body, innerImageName, innerImagePath);

    }

    @PostMapping(&quot;/mailCookie&quot;)
    public void mailCookie(HttpServletResponse httpServletResponse){

        authorizationCode = &quot;111111&quot;; //임시 인증번호
        int authorialTime = 180;

        LocalDateTime now = LocalDateTime.now();
        expiredTime = now.plus(Duration.ofSeconds(authorialTime)).toString();

        Cookie cookie = new Cookie(&quot;authorizationCode&quot;, authorizationCode);
            cookie.setMaxAge(authorialTime);
            httpServletResponse.addCookie(cookie);
    }

    @PostMapping(&quot;/checkAuthorizationCode&quot;)
    public boolean checkAuthorizationCode(String enteredCode){
        return authorizationCode.equals(enteredCode);
    }

    @PostMapping(&quot;/deleteAuthorizataionCodeCookiee&quot;)
    public void deleteAuthorizataionCodeCookiee(HttpServletResponse httpServletResponse){
        Cookie cookie = new Cookie(&quot;authorizationCode&quot;, &quot;&quot;);
        cookie.setMaxAge(0);
        httpServletResponse.addCookie(cookie);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;MailService&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;package org.member.mailController;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class MailService {

    private final JavaMailSender mailSender;

    public void sendEmail(String fromEmail, String toEmail, String emailTitle, String emailBody, String innerImageName, String innerImagePath) {
        mailSender.send(
                mimeMessage -&amp;gt; {
                    MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);                // true: 멀티파트 메세지를 사용
                    messageHelper.setFrom(fromEmail);
                    messageHelper.setTo(toEmail);
                    messageHelper.setSubject(emailTitle);
                    messageHelper.setText(emailBody, true);                                                        // true: html을 사용
                    messageHelper.addInline(innerImageName, new ClassPathResource(innerImagePath));
                });
    }

    public String emailBody(String emailForm, Map&amp;lt;String, String&amp;gt; changeData) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource(emailForm);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream()));

        String line;
        StringBuilder email_body = new StringBuilder();
        while ((line = bufferedReader.readLine()) != null) {
            for (Map.Entry&amp;lt;String, String&amp;gt; entry : changeData.entrySet()) {
                line = line.replace(entry.getKey(), entry.getValue());
            }
            email_body.append(line);
            email_body.append(&quot;\n&quot;);
        }
        return email_body.toString();
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;jsp&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;인증번호: &amp;lt;input type=&quot;text&quot; id=&quot;authorizationCode&quot;&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;input type=&quot;button&quot; id=&quot;sendEmail&quot; value=&quot;인증번호 보내기&quot;&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;span id=&quot;checkAuthorizationCodeField&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;js&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$(function() {

    //인증번호 쿠키 삭제
    deleteAuthorizataionCodeCookiee()
     $(&quot;#email&quot;).on(&quot;input&quot;, function () {
        $(&quot;#existEmail&quot;).html('')
    })
    //인증번호 칸 입력 시 경고창 삭제
    $(&quot;#authorizationCode&quot;).on(&quot;input&quot;, function () {
        $(&quot;#checkAuthorizationCodeField&quot;).html('')
    })
    //제출 시 재확인 및 경고창 확인
    $(&quot;#registerForm&quot;).on(&quot;submit&quot;, function (event) {
        checkUsername()
        checkEmail()
        checkAuthorizationCode()
        if ($(&quot;#existUsername&quot;).html().trim() !== &quot;&quot;
            || $(&quot;#existEmail&quot;).html().trim() !== &quot;&quot;
            || $(&quot;#checkAuthorizationCodeField&quot;).text().trim() !== &quot;확인되었습니다.&quot;) {
            event.preventDefault();
        } else {
            $(&quot;#registerForm&quot;)[0].submit();
        }
    })
})

function send_authorizationCode() {
    const to_email = $(&quot;#email&quot;).val();
    let authorizationCode = &quot;&quot;;
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/mailCookie&quot;,
        dataType: &quot;text&quot;,
        error: function () {
            console.log(&quot;인증번호 발급 에러&quot;)
        }
    });
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/authorizationCode_Email&quot;,
        data: {
            to_email: to_email
        }
    });
}

function checkAuthorizationCode() {
    const authorizationCode = $(&quot;#authorizationCode&quot;).val()
    const checkAuthorizationCodeField = $(&quot;#checkAuthorizationCodeField&quot;)
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/checkAuthorizationCode&quot;,
        data: {
            enteredCode: authorizationCode
        },
        success: function (response) {
            if (response === true) {
                checkAuthorizationCodeField.html(&quot;확인되었습니다.&quot;).css(&quot;color&quot;, &quot;black&quot;);
            } else {
                checkAuthorizationCodeField.html(&quot;인증번호가 다릅니다.&quot;).css(&quot;color&quot;, &quot;red&quot;);
            }
        },
        error: function () {
            console.log(&quot;인증번호 미발송&quot;)
        }
    })
}

function deleteAuthorizataionCodeCookiee() {
    $.ajax({
        type: &quot;post&quot;,
        url: &quot;/deleteAuthorizataionCodeCookiee&quot;,
        success: function () {
            console.log(&quot;쿠키 삭제 성공&quot;)
        },
        error: function () {
            console.log(&quot;쿠키 삭제 실패&quot;)
        }
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MemberService&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;mailController.temporaryPassword_Email(memberVO.getEmail(), newPassword, memberVO.getUsername());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 팀프로젝트 때 쓰던 것과 별 다른게 없다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/140&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://minkee95.tistory.com/140&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716460820266&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 팀플) 20240310_이메일 시스템 구축&quot; data-og-description=&quot;자고 일어나자 마자 암호화/복호화 시스템 한번 보고 이메일 시스템 구축에 들어갔다. 암호화 자체는 잘 되지만, 어제랑 달리 암호가 어째서인지 바뀌진 않는다. 안 되는 건 아니지만, 뭔가 짜게&quot; data-og-host=&quot;minkee95.tistory.com&quot; data-og-source-url=&quot;https://minkee95.tistory.com/140&quot; data-og-url=&quot;https://minkee95.tistory.com/140&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Xu7cv/hyV9UIVJ7N/UVCvQPlkv4T8lUmnFx6cfk/img.png?width=295&amp;amp;height=242&amp;amp;face=0_0_295_242,https://scrap.kakaocdn.net/dn/bxogQU/hyV9UPGgny/MVDSgKsDkKzR7W5jm1OiCK/img.png?width=295&amp;amp;height=242&amp;amp;face=0_0_295_242,https://scrap.kakaocdn.net/dn/fgk2H/hyV9MYp9jd/xazlVmmTZieTFbpdAgVg5K/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/140&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://minkee95.tistory.com/140&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Xu7cv/hyV9UIVJ7N/UVCvQPlkv4T8lUmnFx6cfk/img.png?width=295&amp;amp;height=242&amp;amp;face=0_0_295_242,https://scrap.kakaocdn.net/dn/bxogQU/hyV9UPGgny/MVDSgKsDkKzR7W5jm1OiCK/img.png?width=295&amp;amp;height=242&amp;amp;face=0_0_295_242,https://scrap.kakaocdn.net/dn/fgk2H/hyV9MYp9jd/xazlVmmTZieTFbpdAgVg5K/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 팀플) 20240310_이메일 시스템 구축&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자고 일어나자 마자 암호화/복호화 시스템 한번 보고 이메일 시스템 구축에 들어갔다. 암호화 자체는 잘 되지만, 어제랑 달리 암호가 어째서인지 바뀌진 않는다. 안 되는 건 아니지만, 뭔가 짜게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;minkee95.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/141&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://minkee95.tistory.com/141&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716460826183&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 팀플) 20240310_이메일 인증 구현&quot; data-og-description=&quot;우선 컨트롤러에 다 때려박았던 코드를 서비스로 분산했다. 이메일 인증 시스템을 위해서는 크게 6가지 작업이 필요하다. 1. 6자리 랜덤 정수를 만드는 함수 2. 그 6자리 정수를 3분만 유효한 쿠키&quot; data-og-host=&quot;minkee95.tistory.com&quot; data-og-source-url=&quot;https://minkee95.tistory.com/141&quot; data-og-url=&quot;https://minkee95.tistory.com/141&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bDGgNl/hyV9SEkbB9/JKaoEvhVKyIk3iIrbfMVcK/img.png?width=800&amp;amp;height=412&amp;amp;face=0_0_800_412,https://scrap.kakaocdn.net/dn/g0d21/hyV9WzXrIR/qm5BvAXPt6y1r09KoXKHt1/img.png?width=800&amp;amp;height=412&amp;amp;face=0_0_800_412,https://scrap.kakaocdn.net/dn/XGiNH/hyV9PHB9u5/S5eEyOz7f32OGZyVIhYjy0/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/141&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://minkee95.tistory.com/141&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bDGgNl/hyV9SEkbB9/JKaoEvhVKyIk3iIrbfMVcK/img.png?width=800&amp;amp;height=412&amp;amp;face=0_0_800_412,https://scrap.kakaocdn.net/dn/g0d21/hyV9WzXrIR/qm5BvAXPt6y1r09KoXKHt1/img.png?width=800&amp;amp;height=412&amp;amp;face=0_0_800_412,https://scrap.kakaocdn.net/dn/XGiNH/hyV9PHB9u5/S5eEyOz7f32OGZyVIhYjy0/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 팀플) 20240310_이메일 인증 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;우선 컨트롤러에 다 때려박았던 코드를 서비스로 분산했다. 이메일 인증 시스템을 위해서는 크게 6가지 작업이 필요하다. 1. 6자리 랜덤 정수를 만드는 함수 2. 그 6자리 정수를 3분만 유효한 쿠키&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;minkee95.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/163&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://minkee95.tistory.com/163&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716460831252&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 팀플) 20240312_Resource에 있는 이메일.html 사용하기&quot; data-og-description=&quot;https://komas.tistory.com/142 [Spring] 스프링 Resources 파일 읽기, 복사 resoruces에 위치한 data.json을 읽어야 한다. { &amp;quot;hello&amp;quot;: &amp;quot;123&amp;quot; } data.json의 내용은 다음과 같다. @SpringBootApplication public class RestApIsApplication { pub&quot; data-og-host=&quot;minkee95.tistory.com&quot; data-og-source-url=&quot;https://minkee95.tistory.com/163&quot; data-og-url=&quot;https://minkee95.tistory.com/163&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/34sDN/hyV92tshmt/ShMog7BkfZPoCsKl2wiYak/img.png?width=411&amp;amp;height=518&amp;amp;face=0_0_411_518,https://scrap.kakaocdn.net/dn/5gJof/hyV9U3cKAP/vALJkKkG1SSpiVeEzTDBHk/img.png?width=411&amp;amp;height=518&amp;amp;face=0_0_411_518,https://scrap.kakaocdn.net/dn/b4DS4i/hyV9Rk8b2o/66B898pIKWSS4CiASOK1E1/img.png?width=1094&amp;amp;height=707&amp;amp;face=0_0_1094_707&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/163&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://minkee95.tistory.com/163&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/34sDN/hyV92tshmt/ShMog7BkfZPoCsKl2wiYak/img.png?width=411&amp;amp;height=518&amp;amp;face=0_0_411_518,https://scrap.kakaocdn.net/dn/5gJof/hyV9U3cKAP/vALJkKkG1SSpiVeEzTDBHk/img.png?width=411&amp;amp;height=518&amp;amp;face=0_0_411_518,https://scrap.kakaocdn.net/dn/b4DS4i/hyV9Rk8b2o/66B898pIKWSS4CiASOK1E1/img.png?width=1094&amp;amp;height=707&amp;amp;face=0_0_1094_707');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 팀플) 20240312_Resource에 있는 이메일.html 사용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://komas.tistory.com/142 [Spring] 스프링 Resources 파일 읽기, 복사 resoruces에 위치한 data.json을 읽어야 한다. { &quot;hello&quot;: &quot;123&quot; } data.json의 내용은 다음과 같다. @SpringBootApplication public class RestApIsApplication { pub&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;minkee95.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://minkee95.tistory.com/175&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716460838652&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스프링 부트 팀플) 20240313_이메일 시스템 완성&quot; data-og-description=&quot;MailController 일부 //단순 메일 전송 @PostMapping(&amp;quot;/joinEmail&amp;quot;) public String joinEmail(String userEmail, String userName) throws Exception { String authNumber = makeRandomNumber(); Map changeData = new HashMap(); System.out.println(userEmail); Sys&quot; data-og-host=&quot;minkee95.tistory.com&quot; data-og-source-url=&quot;https://minkee95.tistory.com/175&quot; data-og-url=&quot;https://minkee95.tistory.com/175&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bMwu8y/hyV9N36QnL/CHs065p2XNv307OHygf44k/img.png?width=558&amp;amp;height=725&amp;amp;face=0_0_558_725,https://scrap.kakaocdn.net/dn/3RjF9/hyV9Q7z1Bl/DS1z6Bo70S3PKP6pta1mk0/img.png?width=558&amp;amp;height=725&amp;amp;face=0_0_558_725,https://scrap.kakaocdn.net/dn/dDCIgJ/hyV92mDTFe/UFGBZgeELemeBxovSfwsvk/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174&quot;&gt;&lt;a href=&quot;https://minkee95.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://minkee95.tistory.com/175&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bMwu8y/hyV9N36QnL/CHs065p2XNv307OHygf44k/img.png?width=558&amp;amp;height=725&amp;amp;face=0_0_558_725,https://scrap.kakaocdn.net/dn/3RjF9/hyV9Q7z1Bl/DS1z6Bo70S3PKP6pta1mk0/img.png?width=558&amp;amp;height=725&amp;amp;face=0_0_558_725,https://scrap.kakaocdn.net/dn/dDCIgJ/hyV92mDTFe/UFGBZgeELemeBxovSfwsvk/img.jpg?width=1092&amp;amp;height=1440&amp;amp;face=283_652_762_1174');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 부트 팀플) 20240313_이메일 시스템 완성&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MailController 일부 //단순 메일 전송 @PostMapping(&quot;/joinEmail&quot;) public String joinEmail(String userEmail, String userName) throws Exception { String authNumber = makeRandomNumber(); Map changeData = new HashMap(); System.out.println(userEmail); Sys&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;minkee95.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개인프로젝트/기능프로그램_오늘뭐입지</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/340</guid>
      <comments>https://minkee95.tistory.com/340#entry340comment</comments>
      <pubDate>Thu, 23 May 2024 19:40:56 +0900</pubDate>
    </item>
    <item>
      <title>redux toolkit 기초</title>
      <link>https://minkee95.tistory.com/339</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;App.js&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div style=&quot;background-color: #282a36; color: #f6f6f4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; React &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; { Provider, useDispatch, useSelector } &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;react-redux&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; counterSlice &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;./counterSlice.js&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; store &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;./store.js&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// const initialState = {value:0};&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// const reducer = (state, action) =&amp;gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; if(action.type===&quot;up&quot;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return{...state, value:state.value + action.step}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; return state;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// const store = createStore(reducer, initialState);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// createStore(reducer);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// export default function App(){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; return(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Provider store={store}&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Counter&amp;gt;&amp;lt;/Counter&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/Provider&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; );&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// const Counter = () =&amp;gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; const dispatch = useDispatch();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; const count = useSelector((state) =&amp;gt; state.value)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; return(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;button onClick={() =&amp;gt; {dispatch({type:'up', step:2})}}&amp;gt;+&amp;lt;/button&amp;gt;{count}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; );&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Provider&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Counter&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Counter&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Provider&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Counter&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;useDispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;useSelector&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;state: &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;); &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.counter.value}); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//state 중 counter reducer의 state(store 기준)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//counter는 counterSlice.reducer이기 떄문에 counterSlice의 value 출력&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// dispatch({type: 'counterSlice/up', step: 2}) //type은 counterSlice라고 name이 붙은 slice&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//해당 slice의 reducers 중 up이라고 이름 붙은 함수&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;counterSlice&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;actions&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;up&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)) &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//type은 counterSlice/up&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//--&amp;gt; 자동으로 counterSlice라고 name이 붙은 slice의 up 함수 사용&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//2는 payload라는 이름으로 전달&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;+&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;store.js&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div style=&quot;background-color: #282a36; color: #f6f6f4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; { configureStore } &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; counterSlice &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;./counterSlice&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;configureStore&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;({&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;reducer&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;counter&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;counterSlice&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;reducer&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;})&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counterSlice.js&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div style=&quot;background-color: #282a36; color: #f6f6f4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; { createSlice } &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;counterSlice&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;createSlice&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;({&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;counterSlice&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;initialState&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}, &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//초기값은 0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;reducers&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;up&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;action: &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;//state.value += action.step;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;payload&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;});&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;counterSlice&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;redux&amp;nbsp;toolkit &lt;br /&gt;-&amp;nbsp;설정할&amp;nbsp;것을&amp;nbsp;줄여줌 &lt;br /&gt;-&amp;nbsp;반복적으로&amp;nbsp;작성하는&amp;nbsp;코드를&amp;nbsp;줄여줌 &lt;br /&gt;-&amp;nbsp;불변성&amp;nbsp;유지&amp;nbsp;용이 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;기존&amp;nbsp;redux &lt;br /&gt;import&amp;nbsp;React&amp;nbsp;from&amp;nbsp;&quot;react&quot;; &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;Provider,&amp;nbsp;useDispatch,&amp;nbsp;useSelector&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;react-redux&quot;; &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;createStore&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;redux&quot;; &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;initialState&amp;nbsp;=&amp;nbsp;{value:0}; &lt;br /&gt;const&amp;nbsp;reducer&amp;nbsp;=&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(action.type===&quot;up&quot;){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return{...state,&amp;nbsp;value:state.value&amp;nbsp;+&amp;nbsp;action.step} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;state; &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;store&amp;nbsp;=&amp;nbsp;createStore(reducer,&amp;nbsp;initialState); &lt;br /&gt;createStore(reducer); &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;export&amp;nbsp;default&amp;nbsp;function&amp;nbsp;App(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Provider&amp;nbsp;store={store}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Counter&amp;gt;&amp;lt;/Counter&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Provider&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;); &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;Counter&amp;nbsp;=&amp;nbsp;()&amp;nbsp;=&amp;gt;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;dispatch&amp;nbsp;=&amp;nbsp;useDispatch(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;count&amp;nbsp;=&amp;nbsp;useSelector((state)&amp;nbsp;=&amp;gt;&amp;nbsp;state.value) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{dispatch({type:'up',&amp;nbsp;step:2})}}&amp;gt;+&amp;lt;/button&amp;gt;{count} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;); &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#################################### &lt;br /&gt;const&amp;nbsp;initialState&amp;nbsp;=&amp;nbsp;{value:0}; &lt;br /&gt;//&amp;nbsp;초기값&amp;nbsp;지정 &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;reducer&amp;nbsp;=&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(action.type===&quot;up&quot;){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return{...state,&amp;nbsp;value:state.value&amp;nbsp;+&amp;nbsp;action.step} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;state; &lt;br /&gt;} &lt;br /&gt;//&amp;nbsp;useSelect와&amp;nbsp;useDispatch&amp;nbsp;사용&amp;nbsp;시&amp;nbsp;사용할&amp;nbsp;함수&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;store&amp;nbsp;=&amp;nbsp;createStore(reducer,&amp;nbsp;initialState); &lt;br /&gt;createStore(reducer); &lt;br /&gt;//&amp;nbsp;useSelect와&amp;nbsp;useDispatch&amp;nbsp;사용&amp;nbsp;시&amp;nbsp;사용할&amp;nbsp;함수와&amp;nbsp;초기값&amp;nbsp;지정 &lt;br /&gt;&lt;br /&gt;&amp;lt;Provider&amp;nbsp;store={store}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Counter&amp;gt;&amp;lt;/Counter&amp;gt; &lt;br /&gt;&amp;lt;/Provider&amp;gt; &lt;br /&gt;//Provider&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;#################################### &lt;br /&gt;const&amp;nbsp;dispatch&amp;nbsp;=&amp;nbsp;useDispatch(); &lt;br /&gt;//useDispatch&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;count&amp;nbsp;=&amp;nbsp;useSelector((state)&amp;nbsp;=&amp;gt;&amp;nbsp;state.value) &lt;br /&gt;//useSelector&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;&amp;lt;button&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{dispatch({type:'up',&amp;nbsp;step:2})}}&amp;gt;+&amp;lt;/button&amp;gt;{count} &lt;br /&gt;//dispatch:&amp;nbsp;액션&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;리덕스&amp;nbsp;툴킷&amp;nbsp;도입 &lt;br /&gt;npx&amp;nbsp;create-react-app&amp;nbsp;my-app&amp;nbsp;--template&amp;nbsp;redux &lt;br /&gt;npm&amp;nbsp;install&amp;nbsp;@reduxjs/toolkit &lt;br /&gt;slice:&amp;nbsp;store&amp;nbsp;안의&amp;nbsp;store &lt;br /&gt;&lt;br /&gt;import&amp;nbsp;{&amp;nbsp;createSlice&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;@reduxjs/toolkit&quot;; &lt;br /&gt;const&amp;nbsp;counterSlice&amp;nbsp;=&amp;nbsp;createSlice({ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name:'counterSlice', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initialState:{value:0}, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reducers:{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;up:&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;state.value&amp;nbsp;+=&amp;nbsp;action.step; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}); &lt;br /&gt;-&amp;nbsp;counterSlice라는&amp;nbsp;이름의&amp;nbsp;small&amp;nbsp;store&amp;nbsp;작성 &lt;br /&gt;-&amp;nbsp;initialState로&amp;nbsp;초기값&amp;nbsp;지정 &lt;br /&gt;-&amp;nbsp;reducers(복수형)을&amp;nbsp;통해&amp;nbsp;액션들&amp;nbsp;설정 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;리덕스&amp;nbsp;툴킷&amp;nbsp;효과 &lt;br /&gt;&lt;br /&gt;up:&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;&amp;nbsp;{} &lt;br /&gt;==&amp;gt;&amp;nbsp;const&amp;nbsp;reducer&amp;nbsp;=&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(action.type===&quot;up&quot;){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;state; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;-&amp;nbsp;툴킷을&amp;nbsp;통해&amp;nbsp;같은&amp;nbsp;역할 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;state.value&amp;nbsp;+=&amp;nbsp;action.step; &lt;br /&gt;==&amp;gt;&amp;nbsp;return{...state,&amp;nbsp;value:state.value&amp;nbsp;+&amp;nbsp;action.step} &lt;br /&gt;-&amp;nbsp;툴킷을&amp;nbsp;통해&amp;nbsp;같은&amp;nbsp;역할 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;store&amp;nbsp;작성 &lt;br /&gt;-&amp;nbsp;slice를&amp;nbsp;모아서&amp;nbsp;store로&amp;nbsp;작성 &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;createSlice,&amp;nbsp;configureStore&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;@reduxjs/toolkit&quot;; &lt;br /&gt;const&amp;nbsp;store&amp;nbsp;=&amp;nbsp;configureStore({ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reducer:{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;counter:counterSlice.reducer, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}) &lt;br /&gt;&lt;br /&gt;&amp;lt;Provider&amp;nbsp;store={store}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Counter&amp;gt;&amp;lt;/Counter&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;lt;/Provider&amp;gt; &lt;br /&gt;&lt;br /&gt;reducer:&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;슬라이스&amp;nbsp;이름:&amp;nbsp;&amp;nbsp;슬라이스&amp;nbsp;상수.reducer &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;5.&amp;nbsp;Counter&amp;nbsp;작성 &lt;br /&gt;function&amp;nbsp;Counter(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;dispatch&amp;nbsp;=&amp;nbsp;useDispatch(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;count&amp;nbsp;&amp;nbsp;=&amp;nbsp;useSelector((state)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&quot;state:&amp;nbsp;&quot;,&amp;nbsp;state);&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;state.counter.value});&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//state&amp;nbsp;중&amp;nbsp;counter&amp;nbsp;reducer의&amp;nbsp;state(store&amp;nbsp;기준) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//counter는&amp;nbsp;counterSlice.reducer이기&amp;nbsp;떄문에&amp;nbsp;counterSlice의&amp;nbsp;value&amp;nbsp;출력 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;dispatch({type:&amp;nbsp;'counterSlice/up',&amp;nbsp;step:&amp;nbsp;2})&amp;nbsp;//type은&amp;nbsp;counterSlice라고&amp;nbsp;name이&amp;nbsp;붙은&amp;nbsp;slice &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//해당&amp;nbsp;slice의&amp;nbsp;reducers&amp;nbsp;중&amp;nbsp;up이라고&amp;nbsp;이름&amp;nbsp;붙은&amp;nbsp;함수 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dispatch(counterSlice.actions.up(2))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//type은&amp;nbsp;counterSlice/up &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//--&amp;gt;&amp;nbsp;자동으로&amp;nbsp;counterSlice라고&amp;nbsp;name이&amp;nbsp;붙은&amp;nbsp;slice의&amp;nbsp;up&amp;nbsp;함수&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//2는&amp;nbsp;payload라는&amp;nbsp;이름으로&amp;nbsp;전달 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}&amp;gt;+&amp;lt;/button&amp;gt;{count} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;6. js 분할 &lt;br /&gt;&lt;br /&gt;//App.js&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;===================================== &lt;br /&gt;import&amp;nbsp;React&amp;nbsp;from&amp;nbsp;&quot;react&quot;; &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;Provider,&amp;nbsp;useDispatch,&amp;nbsp;useSelector&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;react-redux&quot;; &lt;br /&gt;import&amp;nbsp;counterSlice&amp;nbsp;from&amp;nbsp;&quot;./counterSlice.js&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//분할 &lt;br /&gt;import&amp;nbsp;store&amp;nbsp;from&amp;nbsp;&quot;./store.js&quot;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//분할 &lt;br /&gt;&lt;br /&gt;export&amp;nbsp;default&amp;nbsp;function&amp;nbsp;App(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Provider&amp;nbsp;store={store}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Counter&amp;gt;&amp;lt;/Counter&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Provider&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;function&amp;nbsp;Counter(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;dispatch&amp;nbsp;=&amp;nbsp;useDispatch(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;count&amp;nbsp;&amp;nbsp;=&amp;nbsp;useSelector((state)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&quot;state:&amp;nbsp;&quot;,&amp;nbsp;state);&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;state.counter.value});&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//state&amp;nbsp;중&amp;nbsp;counter&amp;nbsp;reducer의&amp;nbsp;state(store&amp;nbsp;기준) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//counter는&amp;nbsp;counterSlice.reducer이기&amp;nbsp;떄문에&amp;nbsp;counterSlice의&amp;nbsp;value&amp;nbsp;출력 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;button&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;dispatch({type:&amp;nbsp;'counterSlice/up',&amp;nbsp;step:&amp;nbsp;2})&amp;nbsp;//type은&amp;nbsp;counterSlice라고&amp;nbsp;name이&amp;nbsp;붙은&amp;nbsp;slice &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//해당&amp;nbsp;slice의&amp;nbsp;reducers&amp;nbsp;중&amp;nbsp;up이라고&amp;nbsp;이름&amp;nbsp;붙은&amp;nbsp;함수 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dispatch(counterSlice.actions.up(2))&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//type은&amp;nbsp;counterSlice/up &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//--&amp;gt;&amp;nbsp;자동으로&amp;nbsp;counterSlice라고&amp;nbsp;name이&amp;nbsp;붙은&amp;nbsp;slice의&amp;nbsp;up&amp;nbsp;함수&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//2는&amp;nbsp;payload라는&amp;nbsp;이름으로&amp;nbsp;전달 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}&amp;gt;+&amp;lt;/button&amp;gt;{count} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;//Store.js&amp;nbsp;&amp;nbsp;===================================== &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;configureStore&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;@reduxjs/toolkit&quot;; &lt;br /&gt;import&amp;nbsp;counterSlice&amp;nbsp;from&amp;nbsp;&quot;./counterSlice&quot;; &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;store&amp;nbsp;=&amp;nbsp;configureStore({ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reducer:{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;counter:counterSlice.reducer, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}) &lt;br /&gt;&lt;br /&gt;export&amp;nbsp;default&amp;nbsp;store; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//CounterSlice.js&amp;nbsp;===================================== &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;createSlice&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;@reduxjs/toolkit&quot;; &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;counterSlice&amp;nbsp;=&amp;nbsp;createSlice({ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name:'counterSlice', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initialState:{value:0},&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//초기값은&amp;nbsp;0 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;reducers:{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;up:&amp;nbsp;(state,&amp;nbsp;action)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&quot;action:&amp;nbsp;&quot;,&amp;nbsp;action) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//state.value&amp;nbsp;+=&amp;nbsp;action.step; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;state.value&amp;nbsp;+=&amp;nbsp;action.payload; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}); &lt;br /&gt;&lt;br /&gt;export&amp;nbsp;default&amp;nbsp;counterSlice;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>단순 코드 기록/RE: React</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/339</guid>
      <comments>https://minkee95.tistory.com/339#entry339comment</comments>
      <pubDate>Thu, 23 May 2024 08:35:03 +0900</pubDate>
    </item>
    <item>
      <title>redux 기초</title>
      <link>https://minkee95.tistory.com/338</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div style=&quot;background-color: #282a36; color: #f6f6f4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; React, {useState} &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;./style.css&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {createStore} &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;redux&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; { Provider, useSelector, useDispatch, connect } &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;react-redux&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;reducer&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;currentState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;currentState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;undefined&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #7b7f8b;&quot;&gt;// number의 기본값을 1로 지정&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; };&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;newState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;currentState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.type &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;PLUS&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;newState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.number&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;newState&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;createStore&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;reducer&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(){&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;container&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Root&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;grid&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Provider&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Provider&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Right1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Right1&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Right2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Right2&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Right3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Right3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;useDispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Right3&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;PLUS&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;})}&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Left1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Left1&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Left2&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Left2&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;Left3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;Left3&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;useSelector&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.number);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Left3: &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;Redux &lt;br /&gt;-&amp;nbsp;원거리&amp;nbsp;state를&amp;nbsp;연결 &lt;br /&gt;ex)&amp;nbsp;말단&amp;nbsp;state와&amp;nbsp;root의&amp;nbsp;이벤트를&amp;nbsp;연결 &lt;br /&gt;-&amp;nbsp;이때&amp;nbsp;reducer를&amp;nbsp;통해&amp;nbsp;store에&amp;nbsp;있는&amp;nbsp;state를&amp;nbsp;어떻게&amp;nbsp;바꿀&amp;nbsp;것인지&amp;nbsp;결정하는&amp;nbsp;역할 &lt;br /&gt;&lt;br /&gt;redux를&amp;nbsp;쓰지&amp;nbsp;않고&amp;nbsp;연결할&amp;nbsp;경우 &lt;br /&gt;-&amp;gt;&amp;nbsp;컴포넌트마다&amp;nbsp;연결해주어야&amp;nbsp;함 &lt;br /&gt;import&amp;nbsp;React,&amp;nbsp;{useState}&amp;nbsp;from&amp;nbsp;&quot;react&quot;; &lt;br /&gt;import&amp;nbsp;&quot;./style.css&quot;; &lt;br /&gt;&lt;br /&gt;export&amp;nbsp;default&amp;nbsp;function&amp;nbsp;App(){ &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;[number,&amp;nbsp;setNubmer]&amp;nbsp;=&amp;nbsp;useState(1); &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;id=&quot;container&quot;&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Root:&amp;nbsp;{number}&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;id=&quot;grid&quot;&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Left1&amp;nbsp;number={number}&amp;gt;&amp;lt;/Left1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Right1&amp;nbsp;onIncrease&amp;nbsp;=&amp;nbsp;{()&amp;nbsp;=&amp;gt;&amp;nbsp;{setNubmer(number+1)}}&amp;gt;&amp;lt;/Right1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;Right1&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Right1&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Right2&amp;nbsp;onIncrease={()=&amp;gt;{props.onIncrease()}}&amp;gt;&amp;lt;/Right2&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;Right2&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Right2&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Right3&amp;nbsp;onIncrease={()=&amp;gt;{props.onIncrease()}}&amp;gt;&amp;lt;/Right3&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;Right3&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Right3&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;type=&quot;button&quot;&amp;nbsp;value={&quot;+&quot;}&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{props.onIncrease()}}&amp;gt;&amp;lt;/input&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;Left1&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Left1:&amp;nbsp;{props.number}&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Left2&amp;nbsp;number={props.number}&amp;gt;&amp;lt;/Left2&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;Left2&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Left2:&amp;nbsp;{props.number}&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Left3&amp;nbsp;number={props.number}&amp;gt;&amp;lt;/Left3&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;Left3&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Left3:&amp;nbsp;{props.number}&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Redux&amp;nbsp;설치 &lt;br /&gt;npm&amp;nbsp;install&amp;nbsp;redux&amp;nbsp;react-redux &lt;br /&gt;&lt;br /&gt;import&amp;nbsp;{createStore}&amp;nbsp;from&amp;nbsp;&quot;redux&quot;; &lt;br /&gt;import&amp;nbsp;{&amp;nbsp;Provider,&amp;nbsp;useSelector,&amp;nbsp;useDispatch,&amp;nbsp;connect&amp;nbsp;}&amp;nbsp;from&amp;nbsp;&quot;react-redux&quot;; &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;각각의&amp;nbsp;state의&amp;nbsp;변화를&amp;nbsp;불변하게&amp;nbsp;유지해야&amp;nbsp;함 &lt;br /&gt;--&amp;gt;&amp;nbsp;새로운&amp;nbsp;state를&amp;nbsp;만들&amp;nbsp;때&amp;nbsp;과거의&amp;nbsp;state를&amp;nbsp;복제하여&amp;nbsp;사용 &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Provider:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;state를&amp;nbsp;어떤&amp;nbsp;컴포넌트들에&amp;nbsp;제공할&amp;nbsp;지&amp;nbsp;가장&amp;nbsp;바깥쪽에&amp;nbsp;있는&amp;nbsp;울타리를&amp;nbsp;정하는&amp;nbsp;역할 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;props&amp;nbsp;중에&amp;nbsp;store을&amp;nbsp;반드시&amp;nbsp;정의해야&amp;nbsp;함 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--&amp;gt;&amp;nbsp;Redux와&amp;nbsp;React를&amp;nbsp;연결하여&amp;nbsp;React&amp;nbsp;컴포넌트&amp;nbsp;트리에서&amp;nbsp;Redux&amp;nbsp;스토어를&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;하기&amp;nbsp;위함 &lt;br /&gt;-&amp;nbsp;useSelector:&amp;nbsp;&amp;nbsp;어떤&amp;nbsp;state&amp;nbsp;값을&amp;nbsp;쓰고&amp;nbsp;싶은지&amp;nbsp;선택 &lt;br /&gt;-&amp;nbsp;useDispatch:&amp;nbsp;&amp;nbsp;state&amp;nbsp;값을&amp;nbsp;변경할&amp;nbsp;때&amp;nbsp;사용 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Redux&amp;nbsp;사용 &lt;br /&gt;&lt;br /&gt;const&amp;nbsp;reducer&amp;nbsp;=&amp;nbsp;(currentState,&amp;nbsp;action)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(currentState&amp;nbsp;===&amp;nbsp;undefined){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;number:&amp;nbsp;1,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;number의&amp;nbsp;기본값을&amp;nbsp;1로&amp;nbsp;지정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;newState&amp;nbsp;=&amp;nbsp;{...currentState}; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(action.type&amp;nbsp;===&amp;nbsp;&quot;PLUS&quot;){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;newState.number++; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;newState; &lt;br /&gt;} &lt;br /&gt;const&amp;nbsp;store&amp;nbsp;=&amp;nbsp;createStore(reducer); &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;App() &lt;br /&gt;&amp;lt;Provider&amp;nbsp;store={store}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Left1&amp;gt;&amp;lt;/Left1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Right1&amp;gt;&amp;lt;/Right1&amp;gt; &lt;br /&gt;&amp;lt;/Provider&amp;gt; &lt;br /&gt;&lt;br /&gt;Right3() &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;dispatch&amp;nbsp;=&amp;nbsp;useDispatch(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;input&amp;nbsp;type=&quot;button&quot;&amp;nbsp;value={&quot;+&quot;}&amp;nbsp;onClick={()&amp;nbsp;=&amp;gt;&amp;nbsp;{dispatch({type&amp;nbsp;:&amp;nbsp;&quot;PLUS&quot;})}}&amp;gt;&amp;lt;/input&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;&lt;br /&gt;Left3() &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;number&amp;nbsp;=&amp;nbsp;useSelector((state)&amp;nbsp;=&amp;gt;&amp;nbsp;state.number); &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h1&amp;gt;Left3:&amp;nbsp;{number}&amp;lt;/h1&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;const&amp;nbsp;reducer&amp;nbsp;및&amp;nbsp;const&amp;nbsp;store&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;const&amp;nbsp;reducer:&amp;nbsp;현재&amp;nbsp;state&amp;nbsp;상태에&amp;nbsp;대한&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;const&amp;nbsp;store:&amp;nbsp;&amp;nbsp;수정되면&amp;nbsp;안되는&amp;nbsp;store를&amp;nbsp;상수로&amp;nbsp;선언하고&amp;nbsp;reducer&amp;nbsp;주입 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;state&amp;nbsp;갱신&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;리덕스는&amp;nbsp;각각의&amp;nbsp;state&amp;nbsp;변화를&amp;nbsp;불변하게&amp;nbsp;유지해야하기&amp;nbsp;때문에&amp;nbsp;기존&amp;nbsp;state를&amp;nbsp;복제해서&amp;nbsp;사용 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;Provider&amp;nbsp;store={store}&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4.&amp;nbsp;Left3의&amp;nbsp;useSelector&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;reducer&amp;nbsp;설정할&amp;nbsp;때,&amp;nbsp;state가&amp;nbsp;없는&amp;nbsp;상태일&amp;nbsp;때는&amp;nbsp;1이&amp;nbsp;부여되도록&amp;nbsp;기본값&amp;nbsp;설정됨 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--&amp;gt;&amp;nbsp;state.number을&amp;nbsp;출력하는&amp;nbsp;함수를&amp;nbsp;통해서&amp;nbsp;상수&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;==&amp;gt;&amp;nbsp;상수&amp;nbsp;출력 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5.&amp;nbsp;action&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;action.type이&amp;nbsp;plue면&amp;nbsp;신규&amp;nbsp;state&amp;nbsp;값&amp;nbsp;+1이&amp;nbsp;되도록&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.&amp;nbsp;Right3의&amp;nbsp;useDispatch&amp;nbsp;설정 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;useDispatch()를&amp;nbsp;사용하는&amp;nbsp;상수&amp;nbsp;선언 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;해당&amp;nbsp;상수에&amp;nbsp;값을&amp;nbsp;넣는&amp;nbsp;이벤트&amp;nbsp;선언&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>단순 코드 기록/RE: React</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/338</guid>
      <comments>https://minkee95.tistory.com/338#entry338comment</comments>
      <pubDate>Wed, 22 May 2024 23:16:51 +0900</pubDate>
    </item>
    <item>
      <title>Context Api 기초</title>
      <link>https://minkee95.tistory.com/337</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체코드&lt;/p&gt;
&lt;pre id=&quot;code_1716370664053&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, {createContext, useContext} from &quot;react&quot;;
import './App.css';


const themeDefault = {border: '10px solid green'}
const themeContext = createContext(themeDefault)

export default function App(){
    const theme = useContext(themeContext)
    console.log(&quot;theme: &quot;, theme)
    return(
        &amp;lt;themeContext.Provider value={theme}&amp;gt;
        &amp;lt;div className=&quot;root&quot; style={{border: '10px solid purple'}}&amp;gt;
            &amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;
            &amp;lt;Sub1&amp;gt;&amp;lt;/Sub1&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;/themeContext.Provider&amp;gt;
    )
}

function Sub1(){
    const theme = useContext(themeContext)
    return(
        &amp;lt;themeContext.Provider value={{border: '10px solid red'}}&amp;gt;
        &amp;lt;div style={theme}&amp;gt;
            &amp;lt;h2&amp;gt;Sub1&amp;lt;/h2&amp;gt;
            &amp;lt;Sub2&amp;gt;&amp;lt;/Sub2&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;/themeContext.Provider&amp;gt;
    )
}

function Sub2() {
    const theme = useContext(themeContext);
    return (
      &amp;lt;div style={theme}&amp;gt;
        &amp;lt;h3&amp;gt;Sub2&amp;lt;/h3&amp;gt;
        &amp;lt;Sub3&amp;gt;&amp;lt;/Sub3&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
  
  function Sub3() {
    const theme = useContext(themeContext);
    return (
      &amp;lt;div style={{border: '10px solid blue'}}&amp;gt;
        &amp;lt;h3&amp;gt;Sub3&amp;lt;/h3&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;Context&amp;nbsp;Api(createContext) &lt;br /&gt;-&amp;nbsp;애플리케이션에서&amp;nbsp;전역적으로&amp;nbsp;디자인을&amp;nbsp;공유하는&amp;nbsp;테마&amp;nbsp;기능&amp;nbsp;구현 &lt;br /&gt;import&amp;nbsp;React,&amp;nbsp;{createContext}&amp;nbsp;from&amp;nbsp;&quot;react&quot;; &lt;br /&gt;const&amp;nbsp;themeContext&amp;nbsp;=&amp;nbsp;createContext(themeDefault) &lt;br /&gt;--&amp;gt;&amp;nbsp;themeDefault가&amp;nbsp;새로&amp;nbsp;생성한&amp;nbsp;컨텍스트의&amp;nbsp;기본값이&amp;nbsp;됨 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;기본값&amp;nbsp;읽어오기(useContext) &lt;br /&gt;import&amp;nbsp;React,&amp;nbsp;{createContext,&amp;nbsp;useContext}&amp;nbsp;from&amp;nbsp;&quot;react&quot;; &lt;br /&gt;export&amp;nbsp;default&amp;nbsp;function&amp;nbsp;App(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;theme&amp;nbsp;=&amp;nbsp;useContext(themeContext) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;출력&amp;nbsp;시:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;theme&amp;nbsp;=&amp;nbsp;Object{border:&amp;nbsp;&quot;10px&amp;nbsp;solid&amp;nbsp;green} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;하나에만&amp;nbsp;사용:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;className=&quot;root&quot;&amp;nbsp;style={theme}&amp;gt; &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;테마&amp;nbsp;전체&amp;nbsp;적용 &lt;br /&gt;function&amp;nbsp;Sub1(){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;theme&amp;nbsp;=&amp;nbsp;useContext(themeContext) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return( &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;themeContext.Provider&amp;nbsp;value={{border:&amp;nbsp;'10px&amp;nbsp;solid&amp;nbsp;green'}}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;style={theme}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;h2&amp;gt;Sub1&amp;lt;/h2&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Sub2&amp;gt;&amp;lt;/Sub2&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/themeContext.Provider&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;) &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;function&amp;nbsp;Sub2()&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;theme&amp;nbsp;=&amp;nbsp;useContext(themeContext); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;style={theme}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;function&amp;nbsp;Sub3()&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;theme&amp;nbsp;=&amp;nbsp;useContext(themeContext); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;style={theme}&amp;gt; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;&amp;lt;themeContext.Provider&amp;gt;를&amp;nbsp;return&amp;nbsp;최상단에&amp;nbsp;배치(전체를&amp;nbsp;감싸는&amp;nbsp;형태) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;&amp;lt;themeContext.Provider&amp;gt;에&amp;nbsp;value&amp;nbsp;속성&amp;nbsp;부여 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;하위&amp;nbsp;컴포넌트&amp;nbsp;각각&amp;nbsp;useContext와&amp;nbsp;style&amp;nbsp;부여 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;테마&amp;nbsp;적용&amp;nbsp;순서 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;스타일&amp;nbsp;속성으로&amp;nbsp;직접&amp;nbsp;부여 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;className=&quot;root&quot;&amp;nbsp;style={{border:&amp;nbsp;'10px&amp;nbsp;solid&amp;nbsp;purple'}}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;style={{border:&amp;nbsp;'10px&amp;nbsp;solid&amp;nbsp;blue'}}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;하위&amp;nbsp;컴포넌트의&amp;nbsp;경우,&amp;nbsp;상위&amp;nbsp;컴포넌트의&amp;nbsp;themeContext.Provider에서&amp;nbsp;부여한&amp;nbsp;value &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;themeContext.Provider&amp;nbsp;value={{border:&amp;nbsp;'10px&amp;nbsp;solid&amp;nbsp;red'}}&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;하위&amp;nbsp;컴포넌트의&amp;nbsp;경우,&amp;nbsp;최상위&amp;nbsp;컴포넌트의&amp;nbsp;themeContext.Provider에서&amp;nbsp;부여한&amp;nbsp;value &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;themeContext.Provider&amp;nbsp;value={theme}&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AaoI5/btsHxCDQ5Np/72EPMWrklzuKspMeMkXQWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AaoI5/btsHxCDQ5Np/72EPMWrklzuKspMeMkXQWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AaoI5/btsHxCDQ5Np/72EPMWrklzuKspMeMkXQWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAaoI5%2FbtsHxCDQ5Np%2F72EPMWrklzuKspMeMkXQWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;486&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>단순 코드 기록/RE: React</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/337</guid>
      <comments>https://minkee95.tistory.com/337#entry337comment</comments>
      <pubDate>Wed, 22 May 2024 18:38:36 +0900</pubDate>
    </item>
    <item>
      <title>Styled Components 기초</title>
      <link>https://minkee95.tistory.com/336</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체코드&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div style=&quot;background-color: #282a36; color: #f6f6f4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; React &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;./style.css&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; styled &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;styled-components&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;SimpleButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;styled&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; color: white;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; background-color: green;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;LargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;styled&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;SimpleButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; font-size: 50px;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;ReactButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;props: &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;className&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.className&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.children&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;};&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;ReactLargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;styled&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;ReactButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; font-size: 50px;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;PrimaryButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;styled&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; color: &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;${function&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #bf9eee;&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;props: &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.primary)&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;white&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;blue&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;&amp;nbsp; &amp;nbsp; background-color: &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ffb86c;&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;.primary&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;blue&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;gray&lt;/span&gt;&lt;span style=&quot;color: #dee492;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #e7ee98;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;(&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;SimpleButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Simple&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;SimpleButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;LargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Large&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;LargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;ReactButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;React&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;ReactButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;ReactLargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;ReactLarge&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;ReactLargeButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;PrimaryButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;Normal&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;PrimaryButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;PrimaryButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #62e884;&quot;&gt;primary&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;PrimaryButton&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #97e1f1;&quot;&gt;PrimaryButton&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #f286c4;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;&amp;nbsp; &amp;nbsp; )&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #f6f6f4;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;Styled&amp;nbsp;Component &lt;br /&gt;-&amp;nbsp;css&amp;nbsp;코드를&amp;nbsp;그대로&amp;nbsp;작성할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;컴포넌트를&amp;nbsp;만드는&amp;nbsp;기술 &lt;br /&gt;-&amp;nbsp;기존에는&amp;nbsp;style&amp;nbsp;속성에&amp;nbsp;{style}&amp;nbsp;객체를&amp;nbsp;주입함 &lt;br /&gt;--&amp;gt;&amp;nbsp;css를&amp;nbsp;자바스크립트화&amp;nbsp;해서&amp;nbsp;사용하다&amp;nbsp;보니&amp;nbsp;기존의&amp;nbsp;css와&amp;nbsp;다르게&amp;nbsp;사용해야&amp;nbsp;한다는&amp;nbsp;불편함이&amp;nbsp;발생 &lt;br /&gt;--&amp;gt;&amp;nbsp;stytled.태그명``을&amp;nbsp;사용하여&amp;nbsp;``안에&amp;nbsp;css&amp;nbsp;코드를&amp;nbsp;그대로&amp;nbsp;사용하면서&amp;nbsp;객체화한&amp;nbsp;css를&amp;nbsp;주입하는&amp;nbsp;것과&amp;nbsp;같은&amp;nbsp;효과를&amp;nbsp;냄 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Styled&amp;nbsp;Components&amp;nbsp;설치 &lt;br /&gt;npm&amp;nbsp;install&amp;nbsp;styled-components &lt;br /&gt;import&amp;nbsp;styled&amp;nbsp;from&amp;nbsp;'styled-components'; &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Styled&amp;nbsp;Components&amp;nbsp;사용 &lt;br /&gt;3.1&amp;nbsp;기본적인&amp;nbsp;사용 &lt;br /&gt;const&amp;nbsp;SimpleButton&amp;nbsp;=&amp;nbsp;styled.button` &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;white; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;green; &lt;br /&gt;`; &lt;br /&gt;&amp;lt;SimpleButton&amp;gt;Simple&amp;lt;/SimpleButton&amp;gt; &lt;br /&gt;-&amp;nbsp;녹색&amp;nbsp;배경의&amp;nbsp;흰&amp;nbsp;글씨를&amp;nbsp;가진&amp;nbsp;버튼&amp;nbsp;생성 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.2&amp;nbsp;Styled&amp;nbsp;Components&amp;nbsp;중첩&amp;nbsp;사용 &lt;br /&gt;const&amp;nbsp;LargeButton&amp;nbsp;=&amp;nbsp;styled(SimpleButton)` &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;50px; &lt;br /&gt;`; &lt;br /&gt;&amp;lt;LargeButton&amp;gt;Large&amp;lt;/LargeButton&amp;gt; &lt;br /&gt;-&amp;nbsp;&amp;lt;SimpleButton&amp;gt;에&amp;nbsp;추가로&amp;nbsp;폰트&amp;nbsp;사이즈&amp;nbsp;50px &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.3&amp;nbsp;기존&amp;nbsp;컴포넌트에&amp;nbsp;Styled&amp;nbsp;Components&amp;nbsp;추가 &lt;br /&gt;const&amp;nbsp;ReactButton&amp;nbsp;=&amp;nbsp;(props)&amp;nbsp;=&amp;gt;&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&quot;props:&amp;nbsp;&quot;,&amp;nbsp;props) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;lt;button&amp;nbsp;className={props.className}&amp;gt;{props.children}&amp;lt;/button&amp;gt; &lt;br /&gt;}; &lt;br /&gt;const&amp;nbsp;ReactLargeButton&amp;nbsp;=&amp;nbsp;styled(ReactButton)` &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;50px; &lt;br /&gt;`; &lt;br /&gt;&amp;lt;ReactButton&amp;gt;React&amp;lt;/ReactButton&amp;gt; &lt;br /&gt;&amp;lt;ReactLargeButton&amp;gt;ReactLarge&amp;lt;/ReactLargeButton&amp;gt; &lt;br /&gt;-&amp;nbsp;className={props.className}를&amp;nbsp;부여하여,&amp;nbsp;className이&amp;nbsp;있으면&amp;nbsp;적용되도록&amp;nbsp;함 &lt;br /&gt;-&amp;nbsp;Styled&amp;nbsp;Components를&amp;nbsp;사용하면&amp;nbsp;임의의&amp;nbsp;클래스가&amp;nbsp;부여됨 &lt;br /&gt;--&amp;gt;&amp;nbsp;그냥&amp;nbsp;컴포넌트인&amp;nbsp;&amp;lt;ReactButton&amp;gt;에는&amp;nbsp;css가&amp;nbsp;적용되지&amp;nbsp;않음 &lt;br /&gt;--&amp;gt;&amp;nbsp;Styled&amp;nbsp;Components가&amp;nbsp;추가로&amp;nbsp;부여된&amp;nbsp;&amp;lt;ReactLargeButton&amp;gt;에는&amp;nbsp;css가&amp;nbsp;적용됨 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;3.4&amp;nbsp;props에&amp;nbsp;따른&amp;nbsp;Styled&amp;nbsp;Components&amp;nbsp;별개&amp;nbsp;적용 &lt;br /&gt;const&amp;nbsp;PrimaryButton&amp;nbsp;=&amp;nbsp;styled.button` &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;${function(props){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log('props:&amp;nbsp;',&amp;nbsp;props) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(props.primary){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;'white' &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;'blue'; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;${(props)&amp;nbsp;=&amp;gt;&amp;nbsp;props.primary&amp;nbsp;?&amp;nbsp;'blue'&amp;nbsp;:&amp;nbsp;'gray'} &lt;br /&gt;`; &lt;br /&gt;&amp;lt;PrimaryButton&amp;gt;Normal&amp;lt;/PrimaryButton&amp;gt; &lt;br /&gt;&amp;lt;PrimaryButton&amp;nbsp;primary&amp;gt;PrimaryButton&amp;lt;/PrimaryButton&amp;gt; &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Styled&amp;nbsp;Component인&amp;nbsp;&amp;lt;PrimaryButton&amp;gt;에&amp;nbsp;기본적으로&amp;nbsp;글씨색은&amp;nbsp;파랑,&amp;nbsp;배경색은&amp;nbsp;회색을&amp;nbsp;부여 &lt;br /&gt;-&amp;nbsp;prop으로&amp;nbsp;primary를&amp;nbsp;갖고&amp;nbsp;있을&amp;nbsp;경우에는&amp;nbsp;글씨색이&amp;nbsp;흰색,&amp;nbsp;배경색은&amp;nbsp;파랑을&amp;nbsp;부여 &lt;br /&gt;&lt;br /&gt;================================================================ &lt;br /&gt;&lt;br /&gt;+++&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt; &amp;nbsp;index.js,&amp;nbsp;app.js&amp;nbsp;역할,&amp;nbsp;분리&amp;nbsp;이유 &lt;br /&gt;index.js와&amp;nbsp;app.js&amp;nbsp;분리하는&amp;nbsp;주&amp;nbsp;이유는&amp;nbsp;프로젝트의&amp;nbsp;구조를&amp;nbsp;명확하게&amp;nbsp;하고&amp;nbsp;코드의&amp;nbsp;관리를&amp;nbsp;용이하게&amp;nbsp;하기&amp;nbsp;위해서다. &lt;br /&gt;&lt;br /&gt;index.js &lt;br /&gt;index.js는 애플리케이션의 진입점 역할을 하며 애플리케이션을 시작하는 곳&lt;br /&gt;--&amp;gt; 애플리케이션의 전체적인 중요한 설정들을 포함하고 있다. 이는 해당 애플리케이션의 로직이 아니라 로직에 필요한 설정들을 담고 있다. &lt;br /&gt;&lt;br /&gt;app.js &lt;br /&gt;app.js는 애플리케이션의 주요 로직을 담고 있는 핵심 컴포넌트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&amp;gt; 애플리케이션의 라우팅, 상태 관리 등의 주요 기능을 구현한다. &lt;br /&gt;&lt;br /&gt;분리하는 이유 &lt;br /&gt;index.js와 app.js를 분리함으로써, 애플리케이션의 설정과 주요 로직을 명확하게 구분할 수 있다.&lt;br /&gt;만약&amp;nbsp;app.js에서&amp;nbsp;주요&amp;nbsp;설정과&amp;nbsp;로직을&amp;nbsp;모두&amp;nbsp;담당하면&amp;nbsp;app.js의&amp;nbsp;역할이&amp;nbsp;너무&amp;nbsp;커지면서&amp;nbsp;유지보수에도&amp;nbsp;좋지&amp;nbsp;않다. &lt;br /&gt;&lt;br /&gt;이렇게&amp;nbsp;분리하게&amp;nbsp;되면&amp;nbsp;각&amp;nbsp;파일의&amp;nbsp;역할이&amp;nbsp;명확해져&amp;nbsp;코드를&amp;nbsp;이해하고&amp;nbsp;유지보수하는데&amp;nbsp;효율적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&amp;nbsp;나중에&amp;nbsp;app.js의&amp;nbsp;로직을&amp;nbsp;다른&amp;nbsp;프로젝트에서&amp;nbsp;재사용하거나,&amp;nbsp;교체하는&amp;nbsp;작업이&amp;nbsp;필요할&amp;nbsp;때&amp;nbsp;분리된&amp;nbsp;app.js의&amp;nbsp;로직을&amp;nbsp;다른&amp;nbsp;프로젝트의&amp;nbsp;index.js&amp;nbsp;설정에&amp;nbsp;붙여&amp;nbsp;넣기만&amp;nbsp;하면&amp;nbsp;되기에&amp;nbsp;매우&amp;nbsp;편리하다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>단순 코드 기록/RE: React</category>
      <author>일일일코_장민기</author>
      <guid isPermaLink="true">https://minkee95.tistory.com/336</guid>
      <comments>https://minkee95.tistory.com/336#entry336comment</comments>
      <pubDate>Wed, 22 May 2024 04:54:30 +0900</pubDate>
    </item>
  </channel>
</rss>