[React] ๋ฆฌ์กํธ Context API ๋ ๋๋ง ์ต์ ํํ๊ธฐ
TL;DR
๋ฆฌ์กํธ์์ ์ ๊ณตํ๋ ContextAPI๋ฅผ ์ฌ์ฉํ๋ฉด ์ํ๋ ์ํ(๊ฐ)๋ฅผ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ปดํฌ๋ํธ๋ผ๋ฆฌ ํน์ ์ํ๋ฅผ ๊ณต์ ํ ๋๋ ์ ์ฉํ๋ค. ํ์ง๋ง Context.Provider
ํ์์์ ์ปจํ
์คํธ๋ฅผ ๊ตฌ๋
ํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ ๊ฐ์ ๋ณ๊ฒฝํ ๋๋ง๋ค ๋ฆฌ๋ ๋๋ง ๋๋ค. Context.Provider
๊ฐ์ด ์
๋ฐ์ดํธ๋๋ฉด useContext
ํ
์ด ์ต์ ์ปจํ
์คํธ ๊ฐ์ ์ฌ์ฉํด ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ฅผ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ด์ฒ๋ผ ContextAPI๋ ๋ ๋๋ง ์ต์ ํ์ ์ผ์ผ์ด ์ ๊ฒฝ์จ์ผํ๋ ๋จ์ ์ด ์๋ค. ์ฌ๋ฌ ์ต์ ํ ๋ฐฉ๋ฒ์ค์์ ์ํ/๋์คํจ์น ๋จ์๋ก ์ปจํ ์คํธ๋ฅผ ์ชผ๊ฐ๋ ๋ฐฉ์์ด ๊ฐ์ฅ ๊ฐ๋จํ๋ค. ๋์ Provider๊ฐ ๋๋ฌด ๋ง์์ง๋ ๋จ์ (Wrapper Hell)์ด ์๋ค.
- Context ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํด๋น Context.Provider ์์ชฝ์์ ์ํ๋ฅผ ๊ตฌ๋ (์๋น)ํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋๋ค
- ์ํ(state)์ ๋์คํจ์น(dispatch) ํจ์๋ฅผ ๋์ผํ Context์ ๋ฃ์ผ๋ฉด, ๋์คํจ์น ํจ์๋ง ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ ์ํ ์ ๋ฐ์ดํธ์ ๋ฆฌ๋ ๋๋ง ๋๋ค โก๏ธ
- ์ํ/๋์คํจ์น ํจ์ ์ ์ฉ Context๋ฅผ ๊ฐ๊ฐ ๋ง๋ค๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค
- ์ํ/๋์คํจ์น ํจ์๋ฅผ ๊ฐ์ Context์ ๋ฃ์ด์ผ ํ๋ค๋ฉด
useMemo
๋ฅผ ์ฌ์ฉํด์ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค โก๏ธ - ์ด์ธ์๋
React.memo
๋ฅผ ์ฌ์ฉํด์ ์ปจํ ์คํธ ๊ฐ์ prop์ผ๋ก ๋ฐ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ฑฐ๋, use-context-selector ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ์ ์ฌ์ฉํด์ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค - Provider ์์ชฝ์ ์์ง๋ง ์ปจํ ์คํธ ์ํ๋ฅผ ๊ตฌ๋ ํ์ง ์๋ ์ปดํฌ๋ํธ๋ ์ํ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง ๋์ง ์๋๋ค
๋ฆฌ๋ ๋๋ง ๋ฐ์ ์ํฉ
FamilyName, FirstName 2๊ฐ์ input์ด ์๊ณ , ์ปจํ ์คํธ ์ญ์ 2๊ฐ๋ฅผ ๋ง๋ค์ด์ ๊ฐ input์ ์ํ๋ก ์ฌ์ฉํ๊ณ ์๋ค. ์ด๋ ๊ฒ form ๋ฐ์ดํฐ๋ฅผ ์ปดํฌ๋ํธ ์ํ๋ก ๋๋ ๋ฐฉ์์ controlled form๋ผ๊ณ ํ๋ค(๋ฐ๋๋ก value ๋ณ๊ฒฝ์ ๊ตฌ๋ ํ์ง ์๊ณ ํ์ํ ๋๋ง pullํด์ ์ฌ์ฉํ๋ ๋ฐฉ์์ uncontrolled๋ผ๊ณ ํ๋ค).
FamilyName, FirstName 2๊ฐ์ ์ปจํ ์คํธ๋ฅผ ๋ง๋ค์์ง๋ง, ๋ ์ค 1๊ฐ ์ปจํ ์คํธ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด Provider ์์ชฝ์์ ์ํ๋ฅผ ๊ตฌ๋ ํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋๋ค.
`Context.Provider` ์์์ `useContext` ์ฌ์ฉ์ ๊ฐ์ ํ๊ธฐ ์ํ ํฉํ ๋ฆฌ ํจ์ โผ
import { useContext } from 'react';
// ์ปจํ
์คํธ Provider ๋ด๋ถ์์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ factory ํจ์
export const factoryUseContext =
(context, name = '') =>
() => {
const ctx = useContext(context);
switch (ctx) {
case undefined:
case null:
const ctxName = name === '' ? '' : `${name} `;
const msg = `${ctxName}Context must be used withing a ${ctxName}ContextProvider`;
throw new Error(msg);
default:
return ctx;
}
};
// context/NameContext1
import { factoryUseContext } from './factoryUseContext';
const FamilyNameContext = createContext(null);
const FirstNameContext = createContext(null);
const useFamilyNameCtx = factoryUseContext(FamilyNameContext);
const useFirstNameCtx = factoryUseContext(FirstNameContext);
const Provider = ({ children }) => {
const [familyName, setFamilyName] = useState(''); // familyName input์ ์ฌ์ฉ
const [firstName, setFirstName] = useState(''); // firstName input์ ์ฌ์ฉ
return (
<FamilyNameContext.Provider value={[familyName, setFamilyName]}>
<FirstNameContext.Provider value={[firstName, setFirstName]}>
{children}
</FirstNameContext.Provider>
</FamilyNameContext.Provider>
);
};
// context/useNameContext.js
// ...
export const useNameContext = (selected) => {
const { Context } = contextMap[selected];
return useCallback(
() => (
<Context.Provider>
<NameInput useContext={Context.useFamilyNameCtx} name="FamilyName" />
<NameInput useContext={Context.useFirstNameCtx} name="FirstName" />
<Dummy />{' '}
{/* Provider ์์ชฝ์ ์์ง๋ง ์ปจํ
์คํธ ๊ฐ์ ๊ตฌ๋
ํ์ง ์์์ ๋ฆฌ๋ ๋๋ง ์ํจ */}
</Context.Provider>
),
[Context],
);
};
import {
NameContext1,
NameContext2,
NameContext3,
NameContext4,
NameContext5,
} from './';
import { Dummy, NameInput } from '../components';
import { useCallback } from 'react';
const contextMap = {
context1: {
Context: NameContext1,
label: 'CONTEXT_1 (only state ctx)',
},
context2: {
Context: NameContext2,
label: 'CONTEXT_2 (state/dispatch ctx)',
},
context3: {
Context: NameContext3,
label: 'CONTEXT_3 (only state ctx with memo)',
},
context4: {
Context: NameContext4,
label: 'CONTEXT_4 (state/dispatch ctx with reducer)',
},
context5: {
Context: NameContext5,
label: 'CONTEXT_5 (only state ctx with reducer/memo)',
},
};
export const options = Object.entries(contextMap).map(([k, v]) => ({
value: k,
label: v.label,
}));
export const useNameContext = (selected) => {
const { Context } = contextMap[selected];
return useCallback(
() => (
<Context.Provider>
<NameInput useContext={Context.useFamilyNameCtx} name="FamilyName" />
<NameInput useContext={Context.useFirstNameCtx} name="FirstName" />
<Dummy />
</Context.Provider>
),
[Context],
);
};
// index.js
import { options, useNameContext } from './context';
const [, secondOption] = options;
function App() {
// ...
const NameContextProvider = useNameContext(selectedCtx);
// ๋ค๋ฅธ ์ปดํฌ๋ํธ ์๋ต
return <NameContextProvider />;
}
import React, { useState, useCallback } from 'react';
import ReactDOM from 'react-dom/client';
import style from './style.css';
import { SelectBox } from './components';
import { options, useNameContext } from './context';
const [, secondOption] = options;
function App() {
const [selectedCtx, setSelectedCtx] = useState(secondOption.value);
const onChange = useCallback((value) => setSelectedCtx(value), []);
const NameContextProvider = useNameContext(selectedCtx);
return (
<>
<h1 className="title">Context API Rendering Test</h1>
<div className="mb-7">
<span className="span-label">Selected Context</span>
<SelectBox
options={options}
onChange={onChange}
deaultValue={selectedCtx}
/>
</div>
<NameContextProvider />
</>
);
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
// components/NameInput.js
const NameInput = ({ useContext, name }) => {
const [state, setState] = useContext();
// ...
return (
<div className="mb-10">
{/* ... */}
<input
name={name}
type="text"
value={state}
onChange={({ target }) => setState(target.value)}
placeholder="input value then check number of rendering times"
/>
</div>
);
};
import React, { useRef } from 'react';
const NameInput = ({ useContext, name }) => {
const [state, setState] = useContext();
const count = useRef(0);
return (
<div className="mb-10">
<span className="span-label">
<span>{`${name} Rendered`}</span>
<strong className="counter">{`${++count.current}`}</strong>
</span>
<input
name={name}
type="text"
value={state}
onChange={({ target }) => setState(target.value)}
placeholder="input value then check number of rendering times"
/>
</div>
);
};
export default NameInput;
๋ ๋๋ง ์ต์ ํ
์ํ / ๋์คํจ์น ์ปจํ ์คํธ ๋ถ๋ฆฌํด์ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
๐ก Provider๊ฐ ๋๋ฌด ๋ง์์ง๋ ๋จ์ ์ด ์์ง๋ง(Wrapper Hell) ์ ์ผ ๋ฌด๋ํ ๋ฐฉ๋ฒ
์ํ(state) ์ ์ฉ Context์ ๋์คํจ์น(dispatch) ์ ์ฉ Context๋ฅผ ๊ฐ๊ฐ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค. FamilyName, FirstName 2๊ฐ์ ์ํ๋ก ๊ด๋ฆฌํ๋ฏ๋ก ์ด 4๊ฐ์ ์ปจํ ์คํธ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค.
- FamilyName
- FamilyNameStateContext
- FamilyNameDispatchContext
- FirstName
- FirstNameStateContext
- FirstNameDispatchContext
// context/NameContext2
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);
// use...Context ํ
์๋ต
const Provider = ({ children }) => {
const [familyName, setFamilyName] = useState('');
const [firstName, setFirstName] = useState('');
return (
<FamilyNameStateContext.Provider value={familyName}>
<FamilyNameDispatchContext.Provider value={setFamilyName}>
<FirstNameStateContext.Provider value={firstName}>
<FirstNameDispatchContext.Provider value={setFirstName}>
{children}
</FirstNameDispatchContext.Provider>
</FirstNameStateContext.Provider>
</FamilyNameDispatchContext.Provider>
</FamilyNameStateContext.Provider>
);
};
import React, { useState, createContext } from 'react';
import { factoryUseContext } from './factoryUseContext';
// state, dispatch ์ปจํ
์คํธ๋ฅผ ๋ถ๋ฆฌํด์ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);
const useFamilyNameCtx = () => [
factoryUseContext(FamilyNameStateContext)(),
factoryUseContext(FamilyNameDispatchContext)(),
];
const useFirstNameCtx = () => [
factoryUseContext(FirstNameStateContext)(),
factoryUseContext(FirstNameDispatchContext)(),
];
const Provider = ({ children }) => {
const [familyName, setFamilyName] = useState('');
const [firstName, setFirstName] = useState('');
return (
<FamilyNameStateContext.Provider value={familyName}>
<FamilyNameDispatchContext.Provider value={setFamilyName}>
<FirstNameStateContext.Provider value={firstName}>
<FirstNameDispatchContext.Provider value={setFirstName}>
{children}
</FirstNameDispatchContext.Provider>
</FirstNameStateContext.Provider>
</FamilyNameDispatchContext.Provider>
</FamilyNameStateContext.Provider>
);
};
export { useFamilyNameCtx, useFirstNameCtx, Provider };
useMemo ํ์ฉ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
์ํ(state)์ ๋์คํจ์น(disatch) ํจ์๋ฅผ ๋์ผํ Context์ ๋ฃ์ด์ผ ํ๋ค๋ฉด useMemo
๋ฅผ ์ฌ์ฉํด์ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค. dispatch ํจ์๋ ํ๋ฒ ์ ์ํ๋ฉด ๋ณ๊ฒฝ์ด ์์ผ๋ฏ๋ก useMemo
์์กด์ฑ ๋ฐฐ์ด์ ์ํ(state)๋ง ์ถ๊ฐํ๋ฉด ๋๋ค.
// context/NameContext3
// createContext ์๋ต
// use...Context ํ
์๋ต
const Provider = ({ children }) => {
const [familyName, setFamilyName] = useState('');
const [firstName, setFirstName] = useState('');
const familyNameValue = useMemo(
() => [familyName, setFamilyName],
[familyName],
);
const firstNameValue = useMemo(() => [firstName, setFirstName], [firstName]);
return (
<FamilyNameContext.Provider value={familyNameValue}>
<FirstNameContext.Provider value={firstNameValue}>
{children}
</FirstNameContext.Provider>
</FamilyNameContext.Provider>
);
};
import React, { useState, createContext, useMemo } from 'react';
import { factoryUseContext } from './factoryUseContext';
// useMemo๋ฅผ ์ฌ์ฉํด์ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ
const FamilyNameContext = createContext(null);
const FirstNameContext = createContext(null);
const useFamilyNameCtx = factoryUseContext(FamilyNameContext);
const useFirstNameCtx = factoryUseContext(FirstNameContext);
const Provider = ({ children }) => {
const [familyName, setFamilyName] = useState('');
const [firstName, setFirstName] = useState('');
const familyNameValue = useMemo(
() => [familyName, setFamilyName],
[familyName],
);
const firstNameValue = useMemo(() => [firstName, setFirstName], [firstName]);
return (
<FamilyNameContext.Provider value={familyNameValue}>
<FirstNameContext.Provider value={firstNameValue}>
{children}
</FirstNameContext.Provider>
</FamilyNameContext.Provider>
);
};
export { useFamilyNameCtx, useFirstNameCtx, Provider };
useReducer๋ฅผ ์ฌ์ฉํ ๋ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
useReducer
๋ฅผ ์ฌ์ฉํ ๋์ ๋ ๋๋ง ์ต์ ํ ์ญ์ โ์ํ/๋์คํจ์น Context๋ฅผ ์ชผ๊ฐ๊ฑฐ๋, โuseMemo
(๋์คํจ์น Context๋ฅผ ๋ง๋ค์ง ์์๋)๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค. ์๋๋ 1๊ฐ reducer๋ง ์ฌ์ฉํด์ FamilyName, FirstName์ ํด๋นํ๋ ์ํ ๊ฐ์ ์ปจํ
์คํธ value
๋ก ๋ฃ์ด์ ์ฌ์ฉํ๋ ์์(์ํ/๋์คํจ์น Context ๋ถ๋ฆฌ).
๐ก 1๊ฐ reducer์ ๋์คํจ์น ํจ์๋ฅผ ์ฌ๋ฌ ์ปจํ ์คํธ์์ ์ฌ์ฉํ ๋ ๋์คํจ์น ํจ์๋ฅผ useCallback์ผ๋ก ํ ๋ฒ ๊ฐ์ธ์ค์ผ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค.
// context/NameContext4
// createContext ์๋ต
const SET_FAMILY_NAME = 'SET_FAMILY_NAME';
const SET_FIRST_NAME = 'SET_FIRST_NAME';
const familyNameAction = (payload) => ({ type: SET_FAMILY_NAME, payload });
const firstNameAction = (payload) => ({ type: SET_FIRST_NAME, payload });
const reducer = (state, action) => {
switch (action.type) {
case SET_FAMILY_NAME:
return { ...state, familyName: action.payload };
case SET_FIRST_NAME:
return { ...state, firstName: action.payload };
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
};
// use...Context ํ
์๋ต
const initialState = { familyName: '', firstName: '' };
const Provider = ({ children }) => {
const [{ familyName, firstName }, dispatch] = useReducer(
reducer,
initialState,
);
// 1๊ฐ reducer์ ๋์คํจ์น ํจ์๋ฅผ ์ฌ๋ฌ ์ปจํ
์คํธ์์ ์ฌ์ฉํ ๋ useCallback์ผ๋ก ๊ฐ์ธ์ค์ผ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค
const setFamilyName = useCallback((v) => dispatch(familyNameAction(v)), []);
const setFirstName = useCallback((v) => dispatch(firstNameAction(v)), []);
return (
<FamilyNameStateContext.Provider value={familyName}>
<FamilyNameDispatchContext.Provider value={setFamilyName}>
<FirstNameStateContext.Provider value={firstName}>
<FirstNameDispatchContext.Provider value={setFirstName}>
{children}
</FirstNameDispatchContext.Provider>
</FirstNameStateContext.Provider>
</FamilyNameDispatchContext.Provider>
</FamilyNameStateContext.Provider>
);
};
import React, { createContext, useReducer, useCallback } from 'react';
import { factoryUseContext } from './factoryUseContext';
// useReducer ์ฌ์ฉ, state/dispatch ์ปจํ
์คํธ ๋ถ๋ฆฌํด์ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
const FamilyNameStateContext = createContext(null);
const FamilyNameDispatchContext = createContext(null);
const FirstNameStateContext = createContext(null);
const FirstNameDispatchContext = createContext(null);
const SET_FAMILY_NAME = 'SET_FAMILY_NAME';
const SET_FIRST_NAME = 'SET_FIRST_NAME';
const familyNameAction = (payload) => ({ type: SET_FAMILY_NAME, payload });
const firstNameAction = (payload) => ({ type: SET_FIRST_NAME, payload });
const reducer = (state, action) => {
switch (action.type) {
case SET_FAMILY_NAME:
return { ...state, familyName: action.payload };
case SET_FIRST_NAME:
return { ...state, firstName: action.payload };
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
};
const useFamilyNameCtx = () => [
factoryUseContext(FamilyNameStateContext)(),
factoryUseContext(FamilyNameDispatchContext)(),
];
const useFirstNameCtx = () => [
factoryUseContext(FirstNameStateContext)(),
factoryUseContext(FirstNameDispatchContext)(),
];
const initialState = { familyName: '', firstName: '' };
const Provider = ({ children }) => {
const [{ familyName, firstName }, dispatch] = useReducer(
reducer,
initialState,
);
// 1๊ฐ reducer๋ฅผ ์ฌ์ฉํ ๋ useCallback์ผ๋ก dispatch๋ฅผ ๊ฐ์ธ์ค์ผ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค
// State, Dispatch ์ปจํ
์คํธ๋ฅผ ๋ถ๋ฆฌํ์ง ์์ผ๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค
const setFamilyName = useCallback((v) => dispatch(familyNameAction(v)), []);
const setFirstName = useCallback((v) => dispatch(firstNameAction(v)), []);
return (
<FamilyNameStateContext.Provider value={familyName}>
<FamilyNameDispatchContext.Provider value={setFamilyName}>
<FirstNameStateContext.Provider value={firstName}>
<FirstNameDispatchContext.Provider value={setFirstName}>
{children}
</FirstNameDispatchContext.Provider>
</FirstNameStateContext.Provider>
</FamilyNameDispatchContext.Provider>
</FamilyNameStateContext.Provider>
);
};
export { useFamilyNameCtx, useFirstNameCtx, Provider };
CodeSandbox
์ฐธ๊ณ ๊ธ
- Preventing rerenders with React.memo and useContext hook. · Issue #15156 · facebook/react
- How to use React Context effectively
- ๋ค๋ฅธ ์ฌ๋๋ค์ด ์ ์๋ ค์ฃผ๋ ๋ฆฌ์กํธ์์ Context API ์ ์ฐ๋ ๋ฐฉ๋ฒ
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[Git] ๋ณํฉ(Merge) ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํ ๋ฆฌ๋ฒ ์ด์ค Rebase
[Git] ๋ณํฉ(Merge) ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํ ๋ฆฌ๋ฒ ์ด์ค Rebase
2024.05.12 -
[React] svg ํ์ผ → ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ๋ณํํ๊ธฐ - React SVGR
[React] svg ํ์ผ → ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ๋ณํํ๊ธฐ - React SVGR
2024.05.12 -
[HTML/CSS] form ์ธ๋ถ์์ form ์ฐ๊ฒฐ / document ๊ฐ์ฒด๋ก form ์ ๊ทผํ๊ธฐ
[HTML/CSS] form ์ธ๋ถ์์ form ์ฐ๊ฒฐ / document ๊ฐ์ฒด๋ก form ์ ๊ทผํ๊ธฐ
2024.05.11 -
[HTML/CSS] Tailwind CSS์์ ํ์ ์ ๋ ํฐ(~) ์ฌ์ฉํ๊ธฐ
[HTML/CSS] Tailwind CSS์์ ํ์ ์ ๋ ํฐ(~) ์ฌ์ฉํ๊ธฐ
2024.05.11