Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
999 changes: 999 additions & 0 deletions keyword/chapter10/keyword_10.md

Large diffs are not rendered by default.

Empty file.
49 changes: 49 additions & 0 deletions practice/chapter10/useCallback + memo/vite-project/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback, useState } from 'react';
import Child from './components/Child';

function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');

console.log('✅ Parent 렌더링');

const handleChildClick = useCallback(() => {
console.log('🎉 자식 버튼 클릭');
}, []);

return (
<div
style={{
maxWidth: '700px',
margin: '50px auto',
padding: '20px',
textAlign: 'center',
}}
>
<h1>useCallback + memo 실습</h1>

<hr />

<h2>부모 컴포넌트</h2>

<h3>Count : {count}</h3>

<button onClick={() => setCount((prev) => prev + 1)}>Count 증가</button>

<br />
<br />

<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="입력해보세요"
/>

<p>입력값 : {text}</p>

<Child onClick={handleChildClick} />
</div>
);
}

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { memo } from 'react';

interface ChildProps {
onClick: () => void;
}

const Child = memo(({ onClick }: ChildProps) => {
console.log('🔥 Child 렌더링');

return (
<div
style={{
border: '2px solid #4CAF50',
padding: '20px',
marginTop: '20px',
borderRadius: '10px',
}}
>
<h2>자식 컴포넌트</h2>

<p>부모가 리렌더링되어도 props가 같다면 다시 렌더링되지 않습니다.</p>

<button onClick={onClick}>자식 버튼 클릭</button>
</div>
);
});

export default Child;
111 changes: 111 additions & 0 deletions practice/chapter10/useCallback + memo/vite-project/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;

--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;

font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

@media (max-width: 1024px) {
font-size: 16px;
}
}

@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}

#social .button-icon {
filter: invert(1) brightness(2);
}
}

#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}

body {
margin: 0;
}

h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}

h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}

code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}

code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
10 changes: 10 additions & 0 deletions practice/chapter10/useCallback + memo/vite-project/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
184 changes: 184 additions & 0 deletions practice/chapter10/useMemo/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;

&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}

.hero {
position: relative;

.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}

.base {
width: 170px;
position: relative;
z-index: 0;
}

.framework,
.vite {
position: absolute;
}

.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}

.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}

#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;

@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}

#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;

& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}

.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}

@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}

#docs {
border-right: 1px solid var(--border);

@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}

#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;

.logo {
height: 18px;
}

a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;

&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}

@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;

li {
flex: 1 1 calc(50% - 8px);
}

a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}

#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}

.ticks {
position: relative;
width: 100%;

&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}

&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}
Loading