소개
21기 교육앱만들기 수업 후 강의 교안을 웹으로 만들어 줬더니 반응이 좋아서 수업 때마다 만들어서 보여주고 있습니다.
어려운 코딩 수업이 조금은 친근해지길 기대해 봅니다.
진행 방법
어떤 도구를 사용했고, 어떻게 활용하셨나요?
cluade로 만들었습니다.
Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.
한국어로 된 문자 메시지의 스크린샷
Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.
한국어로 된 문자 메시지의 스크린샷
설명을 해주더니 이제는 제 패턴을 인식했는지 이러네요.
"이제 HTML 웹 페이지로도 만들어드릴까요? 😊"
한국어 웹사이트 스크린샷
Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>C# 함수 & 오버라이딩</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"/>
<style>
:root {
--bg: #f6f4ef;
--surface: #ffffff;
--surface2: #eeeae1;
--border: #e0dbd0;
--text: #1c1a14;
--muted: #9a9285;
--fn-c: #0369a1;
--fn-bg: rgba(3,105,161,0.07);
--fn-bd: rgba(3,105,161,0.25);
--ov-c: #7c3aed;
--ov-bg: rgba(124,58,237,0.07);
--ov-bd: rgba(124,58,237,0.25);
--vt-c: #d97706;
--vt-bg: rgba(217,119,6,0.07);
--vt-bd: rgba(217,119,6,0.25);
--gr-c: #16a34a;
--gr-bg: rgba(22,163,74,0.07);
--gr-bd: rgba(22,163,74,0.25);
--code-bg:#1a1d2e;
--shadow: 0 2px 14px rgba(0,0,0,0.07);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Noto Sans KR', sans-serif;
line-height: 1.7;
}
/* ── 히어로 ── */
.hero {
background: var(--code-bg);
padding: 72px 24px 60px;
text-align: center;
position: relative;
overflow: hidden;
border-bottom: 1px solid #252840;
}
.hero::after {
content: 'override';
position: absolute; bottom: -24px; right: -10px;
font-family: 'JetBrains Mono', monospace;
font-size: 8rem; font-weight: 700;
color: rgba(255,255,255,0.025);
pointer-events: none;
}
.hero-tag {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: .22em;
text-transform: uppercase; color: #585b7a;
margin-bottom: 18px;
}
.hero h1 {
font-size: clamp(2rem,5vw,3.4rem);
font-weight: 900; letter-spacing: -.03em;
line-height: 1.1; color: #cdd6f4;
margin-bottom: 16px;
}
.hero h1 .hl-fn { color: #89b4fa; }
.hero h1 .hl-ov { color: #cba6f7; }
.hero-sub {
font-size: 15px; color: #585b7a;
max-width: 460px; margin: 0 auto 30px;
line-height: 1.85;
}
.hero-chips {
display: flex; justify-content: center;
gap: 8px; flex-wrap: wrap;
}
.hero-chip {
font-family: 'JetBrains Mono', monospace;
font-size: 12px; padding: 6px 16px;
border-radius: 20px; border: 1px solid;
text-decoration: none; transition: all .2s;
}
.chip-fn { border-color: rgba(137,180,250,0.4); color: #89b4fa; }
.chip-ov { border-color: rgba(203,166,247,0.4); color: #cba6f7; }
.chip-vt { border-color: rgba(249,226,175,0.4); color: #f9e2af; }
.hero-chip:hover { filter: brightness(1.4); transform: translateY(-2px); }
/* ── 래퍼 ── */
.wrapper {
max-width: 880px; margin: 0 auto;
padding: 0 24px 100px;
}
/* ── 섹션 ── */
.section { margin-top: 68px; animation: fadeUp .5s ease both; }
.section:nth-child(1) { animation-delay: .05s; }
.section:nth-child(2) { animation-delay: .12s; }
.section:nth-child(3) { animation-delay: .19s; }
.section:nth-child(4) { animation-delay: .26s; }
.section:nth-child(5) { animation-delay: .33s; }
.sec-header {
display: flex; align-items: center;
gap: 14px; margin-bottom: 22px;
padding-bottom: 18px;
border-bottom: 1px solid var(--border);
}
.badge {
font-family: 'JetBrains Mono', monospace;
font-size: 12px; font-weight: 700;
padding: 5px 16px; border-radius: 8px;
border: 1.5px solid; white-space: nowrap;
}
.badge-fn { background: var(--fn-bg); border-color: var(--fn-bd); color: var(--fn-c); }
.badge-ov { background: var(--ov-bg); border-color: var(--ov-bd); color: var(--ov-c); }
.badge-vt { background: var(--vt-bg); border-color: var(--vt-bd); color: var(--vt-c); }
.badge-gr { background: var(--gr-bg); border-color: var(--gr-bd); color: var(--gr-c); }
.sh-title h2 { font-size: 1.4rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: 2px; }
.sh-title p { font-size: 13px; color: var(--muted); }
/* ── 설명 카드 ── */
.desc-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
padding: 22px 28px;
margin-bottom: 16px;
font-size: 15px; line-height: 2;
color: #3a3930;
box-shadow: var(--shadow);
}
.desc-card strong { color: var(--text); }
.key-point {
display: inline-flex; align-items: center;
gap: 8px; margin-top: 14px;
padding: 10px 16px; border-radius: 8px;
font-size: 13.5px; font-weight: 600;
}
.kp-fn { background: var(--fn-bg); border: 1px solid var(--fn-bd); color: var(--fn-c); }
.kp-ov { background: var(--ov-bg); border: 1px solid var(--ov-bd); color: var(--ov-c); }
.kp-vt { background: var(--vt-bg); border: 1px solid var(--vt-bd); color: var(--vt-c); }
.kp-gr { background: var(--gr-bg); border: 1px solid var(--gr-bd); color: var(--gr-c); }
/* ── 비유 박스 ── */
.analogy-box {
background: #fffbeb;
border: 1px solid #fde68a;
border-radius: 12px;
padding: 18px 22px;
margin-bottom: 16px;
font-size: 14px; line-height: 1.85;
color: #78350f;
}
.analogy-box .ab-title {
font-weight: 700; font-size: 13px;
letter-spacing: .06em; margin-bottom: 8px;
color: #92400e;
}
/* ── 코드 블록 ── */
.code-block {
background: var(--code-bg);
border: 1px solid #252840;
border-radius: 12px; overflow: hidden;
margin-bottom: 16px;
}
.cb-bar {
display: flex; align-items: center; gap: 7px;
padding: 10px 16px;
background: rgba(255,255,255,0.03);
border-bottom: 1px solid #252840;
}
.dot { width: 10px; height: 10px; border-radius: 50%; }
.dr { background: #f38ba8; }
.dy { background: #f9e2af; }
.dg { background: #a6e3a1; }
.cb-fname { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #44475a; margin-left: 4px; }
.cb-lang { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #44475a; margin-left: auto; }
pre {
padding: 20px 22px;
font-family: 'JetBrains Mono', monospace;
font-size: 13.5px; line-height: 2.1;
overflow-x: auto; color: #cdd6f4;
}
.kw { color: #cba6f7; }
.cl { color: #89b4fa; }
.fn { color: #89dceb; }
.str { color: #a6e3a1; }
.num { color: #fab387; }
.cm { color: #44475a; font-style: italic; }
.ret { color: #f38ba8; }
.virt{ color: #f9e2af; font-weight: 700; }
.over{ color: #cba6f7; font-weight: 700; }
.out { color: #a6e3a1; }
/* ── 함수 구조 시각화 ── */
.fn-structure {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px; padding: 24px 28px;
margin-bottom: 16px; box-shadow: var(--shadow);
}
.fn-diagram {
display: flex; align-items: stretch;
gap: 0; font-family: 'JetBrains Mono', monospace;
font-size: 14px; flex-wrap: wrap;
margin-bottom: 20px;
}
.fn-part {
display: flex; flex-direction: column;
align-items: center; gap: 8px;
}
.fn-token {
padding: 10px 14px; border-radius: 8px;
border: 2px solid; font-weight: 700;
font-size: 15px; white-space: nowrap;
}
.ft-ret { background: rgba(243,139,168,0.1); border-color: rgba(243,139,168,0.3); color: #f38ba8; }
.ft-name { background: var(--fn-bg); border-color: var(--fn-bd); color: var(--fn-c); }
.ft-paren{ background: rgba(255,255,255,0.05); border-color: #30344a; color: #6c7086; padding: 10px 8px; }
.ft-param{ background: var(--vt-bg); border-color: var(--vt-bd); color: var(--vt-c); }
.ft-body { background: var(--gr-bg); border-color: var(--gr-bd); color: var(--gr-c); }
.fn-label {
font-size: 9px; letter-spacing: .1em;
text-transform: uppercase; padding: 2px 8px;
border-radius: 10px; white-space: nowrap;
}
.lbl-ret { background: rgba(243,139,168,0.1); color: #f38ba8; }
.lbl-name { background: var(--fn-bg); color: var(--fn-c); }
.lbl-param{ background: var(--vt-bg); color: var(--vt-c); }
.lbl-body { background: var(--gr-bg); color: var(--gr-c); }
.fn-rules {
display: grid; grid-template-columns: repeat(auto-fit, minmax(170px,1fr));
gap: 10px;
}
.fn-rule {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 10px; padding: 14px 16px;
}
.fn-rule .fr-num {
font-family: 'JetBrains Mono', monospace;
font-size: 1.2rem; font-weight: 700;
color: var(--fn-c); margin-bottom: 4px;
}
.fn-rule .fr-title { font-size: 13px; font-weight: 700; margin-bottom: 3px; }
.fn-rule .fr-desc { font-size: 12px; color: var(--muted); line-height: 1.6; }
/* ── 오버라이딩 흐름도 ── */
.inherit-flow {
display: flex; flex-direction: column;
gap: 0; margin-bottom: 16px;
}
.flow-parent {
background: var(--surface);
border: 2px solid var(--fn-bd);
border-radius: 14px 14px 0 0;
padding: 20px 24px;
box-shadow: var(--shadow);
}
.flow-arrow {
display: flex; justify-content: center;
align-items: center; height: 40px;
background: linear-gradient(to bottom, var(--fn-bg), var(--ov-bg));
position: relative;
}
.flow-arrow::after {
content: '▼ 상속 (inheritance)';
font-family: 'JetBrains Mono', monospace;
font-size: 11px; color: var(--muted);
letter-spacing: .08em;
}
.flow-children {
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
}
@media (max-width: 540px) { .flow-children { grid-template-columns: 1fr; } }
.flow-child {
border: 2px solid var(--ov-bd);
padding: 20px 24px;
background: var(--surface);
}
.flow-child:first-child { border-radius: 0 0 0 14px; border-right-width: 1px; }
.flow-child:last-child { border-radius: 0 0 14px 0; border-left-width: 1px; }
.flow-label {
font-family: 'JetBrains Mono', monospace;
font-size: 10px; letter-spacing: .12em;
text-transform: uppercase; margin-bottom: 8px;
}
.fl-parent { color: var(--fn-c); }
.fl-child { color: var(--ov-c); }
.flow-class-name {
font-family: 'JetBrains Mono', monospace;
font-size: 1.1rem; font-weight: 700;
margin-bottom: 10px;
}
.fcn-parent { color: var(--fn-c); }
.fcn-child { color: var(--ov-c); }
.flow-method {
font-family: 'JetBrains Mono', monospace;
font-size: 12.5px; padding: 8px 12px;
border-radius: 8px;
}
.fm-parent { background: var(--fn-bg); border: 1px solid var(--fn-bd); color: var(--fn-c); }
.fm-child { background: var(--ov-bg); border: 1px solid var(--ov-bd); color: var(--ov-c); }
/* ── virtual / override 비교 ── */
.vo-grid {
display: grid; grid-template-columns: 1fr 1fr;
gap: 14px; margin-bottom: 16px;
}
@media (max-width: 520px) { .vo-grid { grid-template-columns: 1fr; } }
.vo-card {
border-radius: 12px; overflow: hidden;
border: 1.5px solid; box-shadow: var(--shadow);
}
.vo-card.vt { border-color: var(--vt-bd); }
.vo-card.ov { border-color: var(--ov-bd); }
.vo-head {
padding: 14px 18px;
display: flex; align-items: center; gap: 10px;
}
.vo-card.vt .vo-head { background: var(--vt-bg); }
.vo-card.ov .vo-head { background: var(--ov-bg); }
.vo-kw {
font-family: 'JetBrains Mono', monospace;
font-size: 1.4rem; font-weight: 700;
}
.vo-card.vt .vo-kw { color: var(--vt-c); }
.vo-card.ov .vo-kw { color: var(--ov-c); }
.vo-head-text .vht-title { font-size: 14px; font-weight: 700; margin-bottom: 2px; }
.vo-head-text .vht-sub { font-size: 12px; color: var(--muted); }
.vo-body {
padding: 16px 18px;
background: var(--surface);
}
.vo-body ul {
list-style: none;
display: flex; flex-direction: column; gap: 8px;
}
.vo-body ul li {
font-size: 13px; color: #555;
padding-left: 18px; position: relative;
line-height: 1.65;
}
.vo-body ul li::before { content: '·'; position: absolute; left: 4px; font-size: 18px; line-height: 1.1; }
.vo-card.vt .vo-body ul li::before { color: var(--vt-c); }
.vo-card.ov .vo-body ul li::before { color: var(--ov-c); }
/* ── 인터랙티브 데모 ── */
.demo-wrap {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px; overflow: hidden;
box-shadow: var(--shadow);
}
.demo-top {
padding: 13px 20px;
background: var(--surface2);
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
gap: 12px; flex-wrap: wrap;
}
.demo-top span {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; color: var(--muted);
letter-spacing: .12em; text-transform: uppercase;
}
.animal-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.animal-btn {
padding: 7px 16px; border-radius: 8px;
border: 1.5px solid var(--border);
background: transparent; cursor: pointer;
font-family: 'Noto Sans KR', sans-serif;
font-size: 13px; color: var(--muted);
transition: all .2s;
}
.animal-btn:hover { border-color: #aaa; color: var(--text); }
.animal-btn.active {
background: var(--ov-bg);
border-color: var(--ov-bd);
color: var(--ov-c); font-weight: 700;
}
.demo-body { padding: 24px; }
.demo-console {
background: #0e1018;
border: 1px solid #252840;
border-radius: 10px; overflow: hidden;
margin-bottom: 14px;
}
.dc-bar {
display: flex; align-items: center; gap: 6px;
padding: 9px 14px;
background: #13161f;
border-bottom: 1px solid #252840;
}
.dc-dot { width: 10px; height: 10px; border-radius: 50%; }
.dd-r{background:#f38ba8;}.dd-y{background:#f9e2af;}.dd-g{background:#a6e3a1;}
.dc-body {
padding: 16px 20px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px; line-height: 2.2;
min-height: 60px;
}
#demo-output {
transition: all .3s;
}
.demo-code-box {
background: var(--code-bg);
border: 1px solid #252840;
border-radius: 8px;
padding: 14px 18px;
font-family: 'JetBrains Mono', monospace;
font-size: 12.5px; line-height: 1.9;
color: #6c7086;
}
/* ── 비교표 ── */
.cmp-table {
width: 100%; border-collapse: collapse;
font-size: 13.5px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px; overflow: hidden;
box-shadow: var(--shadow);
}
.cmp-table th {
padding: 13px 18px; text-align: left;
font-family: 'JetBrains Mono', monospace;
font-size: 10px; letter-spacing: .12em;
text-transform: uppercase; color: var(--muted);
background: var(--surface2);
border-bottom: 1px solid var(--border);
}
.cmp-table td {
padding: 14px 18px;
border-bottom: 1px solid rgba(0,0,0,0.04);
vertical-align: middle; color: #555;
}
.cmp-table tr:last-child td { border-bottom: none; }
.cmp-table tr:hover td { background: var(--surface2); }
.cmp-table td:first-child { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; }
.cmp-table .row-vt td:first-child { color: var(--vt-c); }
.cmp-table .row-ov td:first-child { color: var(--ov-c); }
/* ── 핵심 정리 ── */
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px,1fr));
gap: 14px; margin-top: 60px;
}
.sum-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px; padding: 22px;
box-shadow: var(--shadow); transition: transform .2s;
}
.sum-card:hover { transform: translateY(-4px); }
.sum-dot { width: 10px; height: 10px; border-radius: 50%; margin-bottom: 12px; }
.sum-name { font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 700; margin-bottom: 8px; }
.sum-desc { font-size: 13px; color: var(--muted); line-height: 1.7; }
.sum-when { margin-top: 10px; font-size: 12px; color: var(--muted); border-top: 1px solid var(--border); padding-top: 10px; }
@keyframes fadeUp { from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:translateY(0);} }
</style>
</head>
<body>
<!-- 히어로 -->
<div class="hero">
<div class="hero-tag">C# · OOP · Method</div>
<h1>
<span class="hl-fn">함수</span> &
<span class="hl-ov">오버라이딩</span>
</h1>
<p class="hero-sub">
코드를 묶는 함수부터 부모 기능을 자식이 바꾸는 오버라이딩까지<br/>
아주 쉽게 이해해봐요!
</p>
<div class="hero-chips">
<a href="#sec-fn" class="hero-chip chip-fn">함수(Method)</a>
<a href="#sec-ov" class="hero-chip chip-ov">오버라이딩</a>
<a href="#sec-vt" class="hero-chip chip-vt">virtual / override</a>
</div>
</div>
<div class="wrapper">
<!-- ── 01 함수 ── -->
<div class="section" id="sec-fn">
<div class="sec-header">
<div class="badge badge-fn">01 · 함수 (Method)</div>
<div class="sh-title">
<h2>함수란 무엇인가요?</h2>
<p>이름 붙은 코드 묶음 — 한 번 만들고 여러 번 사용!</p>
</div>
</div>
<div class="analogy-box">
<div class="ab-title">🍕 쉬운 비유</div>
함수는 <strong>요리 레시피</strong>와 같아요!<br/>
레시피(함수)를 한 번 만들어두면, 피자가 먹고 싶을 때마다 꺼내서 쓸 수 있어요.<br/>
매번 처음부터 요리법을 생각할 필요가 없죠. → <strong>코드 재사용!</strong>
</div>
<div class="desc-card">
<strong>함수(Method)</strong>는 특정 동작을 하는 코드를 <strong>묶어서 이름을 붙인 것</strong>이에요.<br/>
필요할 때마다 이름을 불러서 <strong>반복 사용</strong>할 수 있어요.<br/>
값을 받아서(<strong>매개변수</strong>) 처리하고, 결과를 돌려줄(<strong>반환</strong>) 수 있어요.
<br/>
<div class="key-point kp-fn">📌 한 번 만들고 → 여러 번 재사용!</div>
</div>
<!-- 함수 구조 시각화 -->
<div class="fn-structure">
<p style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted);letter-spacing:.12em;text-transform:uppercase;margin-bottom:20px;">함수 구조 분해</p>
<div class="fn-diagram" style="gap:6px;margin-bottom:32px;flex-wrap:wrap;align-items:flex-end;justify-content:center;">
<div class="fn-part">
<div class="fn-token ft-ret">int</div>
<div class="fn-label lbl-ret">반환 타입</div>
</div>
<div style="padding-bottom:18px;color:#6c7086;font-family:'JetBrains Mono',monospace;font-size:18px;"> </div>
<div class="fn-part">
<div class="fn-token ft-name">Add</div>
<div class="fn-label lbl-name">함수 이름</div>
</div>
<div style="padding-bottom:18px;color:#6c7086;font-family:'JetBrains Mono',monospace;font-size:18px;">(</div>
<div class="fn-part">
<div class="fn-token ft-param">int a, int b</div>
<div class="fn-label lbl-param">매개변수</div>
</div>
<div style="padding-bottom:18px;color:#6c7086;font-family:'JetBrains Mono',monospace;font-size:18px;">)</div>
<div style="padding-bottom:18px;color:#6c7086;font-family:'JetBrains Mono',monospace;"> </div>
<div class="fn-part">
<div class="fn-token ft-body">{ return a+b; }</div>
<div class="fn-label lbl-body">본문</div>
</div>
</div>
<div class="fn-rules">
<div class="fn-rule">
<div class="fr-num">01</div>
<div class="fr-title">반환 타입</div>
<div class="fr-desc">돌려줄 값의 타입. 없으면 void</div>
</div>
<div class="fn-rule">
<div class="fr-num">02</div>
<div class="fr-title">함수 이름</div>
<div class="fr-desc">호출할 때 쓰는 이름. 동사로!</div>
</div>
<div class="fn-rule">
<div class="fr-num">03</div>
<div class="fr-title">매개변수</div>
<div class="fr-desc">외부에서 받아올 값. 없어도 OK</div>
</div>
<div class="fn-rule">
<div class="fr-num">04</div>
<div class="fr-title">본문 & return</div>
<div class="fr-desc">실제 동작 코드 + 결과 반환</div>
</div>
</div>
</div>
<div class="code-block">
<div class="cb-bar">
<div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div>
<div class="cb-lang">C#</div>
</div>
<pre><span class="cm">// ① 함수 만들기 (정의)</span>
<span class="kw">int</span> <span class="fn">Add</span>(<span class="kw">int</span> a, <span class="kw">int</span> b)
{
<span class="ret">return</span> a + b;
}
<span class="cm">// ② 반환값 없는 함수 (void)</span>
<span class="kw">void</span> <span class="fn">SayHello</span>(<span class="kw">string</span> name)
{
Console.<span class="fn">WriteLine</span>(<span class="str">$"안녕하세요, {name}님!"</span>);
}
<span class="cm">// ③ 함수 사용하기 (호출)</span>
<span class="kw">int</span> result = <span class="fn">Add</span>(<span class="num">3</span>, <span class="num">5</span>); <span class="cm">// result = 8</span>
Console.<span class="fn">WriteLine</span>(result); <span class="out">// 8</span>
<span class="fn">SayHello</span>(<span class="str">"홍길동"</span>); <span class="out">// 안녕하세요, 홍길동님!</span>
<span class="fn">SayHello</span>(<span class="str">"김철수"</span>); <span class="out">// 안녕하세요, 김철수님!</span></pre>
</div>
</div>
<!-- ── 02 오버라이딩 ── -->
<div class="section" id="sec-ov">
<div class="sec-header">
<div class="badge badge-ov">02 · 오버라이딩 (Overriding)</div>
<div class="sh-title">
<h2>부모 함수를 자식이 덮어쓰기</h2>
<p>상속받은 메서드를 자식 클래스에서 새로 정의</p>
</div>
</div>
<div class="analogy-box">
<div class="ab-title">🍕 쉬운 비유</div>
<strong>피자 본점(부모)</strong>에 "피자 굽기" 레시피가 있어요.<br/>
<strong>분점(자식)</strong>이 그 레시피를 그대로 쓰지 않고 <strong>자기만의 방식으로 바 꾸는 것</strong>이 오버라이딩이에요!<br/><br/>
🐶 강아지는 "소리내기"를 <strong>멍멍</strong>으로,<br/>
🐱 고양이는 "소리내기"를 <strong>야옹</strong>으로 — 같은 함수를 다르게 구현!
</div>
<div class="desc-card">
<strong>오버라이딩</strong>은 부모 클래스의 함수를 <strong>자식 클래스에서 새로 정의</strong>하는 것이에요.<br/>
부모의 함수를 그대로 쓰지 않고, <strong>자식만의 방식으로 바꿀 수 있어요.</strong><br/>
부모에서 <strong>virtual</strong>로 열어 주고, 자식에서 <strong>override</strong>로 덮어써요.
<br/>
<div class="key-point kp-ov">📌 부모 virtual → 자식 override → 자식만의 동작!</div>
</div>
<!-- 상속 흐름도 -->
<div class="inherit-flow">
<div class="flow-parent">
<div class="flow-label fl-parent">부모 클래스 (Parent)</div>
<div class="flow-class-name fcn-parent">Animal</div>
<div class="flow-method fm-parent">
virtual void Speak() → "..." 출력
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-children">
<div class="flow-child">
<div class="flow-label fl-child">자식 클래스 (Child)</div>
<div class="flow-class-name fcn-child">Dog 🐶</div>
<div class="flow-method fm-child">
override void Speak() → "멍멍!" 출력
</div>
</div>
<div class="flow-child">
<div class="flow-label fl-child">자식 클래스 (Child)</div>
<div class="flow-class-name fcn-child">Cat 🐱</div>
<div class="flow-method fm-child">
override void Speak() → "야옹!" 출력
</div>
</div>
</div>
</div>
<div class="code-block">
<div class="cb-bar">
<div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div>
<div class="cb-lang">C#</div>
</div>
<pre><span class="cm">// 부모 클래스</span>
<span class="kw">class</span> <span class="cl">Animal</span>
{
<span class="kw">public</span> <span class="virt">virtual</span> <span class="kw">void</span> <span class="fn">Speak</span>() <span class="cm">// virtual = "바꿔도 돼!"</span>
{
Console.<span class="fn">WriteLine</span>(<span class="str">"..."</span>);
}
}
<span class="cm">// 자식 클래스 — Dog</span>
<span class="kw">class</span> <span class="cl">Dog</span> : <span class="cl">Animal</span>
{
<span class="kw">public</span> <span class="over">override</span> <span class="kw">void</span> <span class="fn">Speak</span>() <span class="cm">// override = "내가 새로 만들게!"</span>
{
Console.<span class="fn">WriteLine</span>(<span class="str">"멍멍! 🐶"</span>);
}
}
<span class="cm">// 자식 클래스 — Cat</span>
<span class="kw">class</span> <span class="cl">Cat</span> : <span class="cl">Animal</span>
{
<span class="kw">public</span> <span class="over">override</span> <span class="kw">void</span> <span class="fn">Speak</span>()
{
Console.<span class="fn">WriteLine</span>(<span class="str">"야옹! 🐱"</span>);
}
}
<span class="cm">// 사용</span>
<span class="cl">Animal</span> dog = <span class="kw">new</span> <span class="cl">Dog</span>();
<span class="cl">Animal</span> cat = <span class="kw">new</span> <span class="cl">Cat</span>();
dog.<span class="fn">Speak</span>(); <span class="out">// 멍멍! 🐶</span>
cat.<span class="fn">Speak</span>(); <span class="out">// 야옹! 🐱</span></pre>
</div>
<!-- 인터랙티브 데모 -->
<div class="demo-wrap">
<div class="demo-top">
<span>동물을 선택해보세요</span>
<div class="animal-btns">
<button class="animal-btn" onclick="callSpeak('Animal')">Animal (부모)</button>
<button class="animal-btn" onclick="callSpeak('Dog')">Dog 🐶</button>
<button class="animal-btn" onclick="callSpeak('Cat')">Cat 🐱</button>
<button class="animal-btn" onclick="callSpeak('Bird')">Bird 🐦</button>
</div>
</div>
<div class="demo-body">
<div class="demo-console">
<div class="dc-bar">
<div class="dc-dot dd-r"></div>
<div class="dc-dot dd-y"></div>
<div class="dc-dot dd-g"></div>
</div>
<div class="dc-body">
<span id="demo-output" style="color:#6c7086;">← 동물을 선택하면 Speak()가 실행돼요!</span>
</div>
</div>
<div class="demo-code-box" id="demo-code">
// 버튼을 눌러보세요!
</div>
</div>
</div>
</div>
<!-- ── 03 virtual / override ── -->
<div class="section" id="sec-vt">
<div class="sec-header">
<div class="badge badge-vt">03 · virtual & override</div>
<div class="sh-title">
<h2>두 키워드의 차이</h2>
<p>부모가 열어주고 → 자식이 들어가서 바꾼다!</p>
</div>
</div>
<div class="vo-grid">
<div class="vo-card vt">
<div class="vo-head">
<div class="vo-kw">virtual</div>
<div class="vo-head-text">
<div class="vht-title">부모가 씁니다</div>
<div class="vht-sub">"자식아, 바꿔도 돼!"</div>
</div>
</div>
<div class="vo-body">
<ul>
<li>부모 클래스의 함수에 붙여요</li>
<li>"이 함수는 자식이 바꿀 수 있어" 라는 신호</li>
<li>virtual 없으면 override 불가!</li>
<li>🚪 열려있는 문 — 들어올 수 있어!</li>
</ul>
</div>
</div>
<div class="vo-card ov">
<div class="vo-head">
<div class="vo-kw">override</div>
<div class="vo-head-text">
<div class="vht-title">자식이 씁니다</div>
<div class="vht-sub">"내가 새로 만들게!"</div>
</div>
</div>
<div class="vo-body">
<ul>
<li>자식 클래스의 함수에 붙여요</li>
<li>부모의 virtual 함수를 새로 정의</li>
<li>반드시 부모에 virtual이 있어야!</li>
<li>🎨 문 열고 들어가서 새로 꾸미기!</li>
</ul>
</div>
</div>
</div>
<div class="code-block">
<div class="cb-bar">
<div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div>
<div class="cb-lang">C# — virtual & override 흐름</div>
</div>
<pre><span class="kw">class</span> <span class="cl">Parent</span>
{
<span class="cm">// virtual → "자식이 바꿔도 OK!"</span>
<span class="kw">public</span> <span class="virt">virtual</span> <span class="kw">void</span> <span class="fn">Hello</span>()
{
Console.<span class="fn">WriteLine</span>(<span class="str">"부모의 Hello"</span>);
}
}
<span class="kw">class</span> <span class="cl">Child</span> : <span class="cl">Parent</span>
{
<span class="cm">// override → "내가 새로 정의할게!"</span>
<span class="kw">public</span> <span class="over">override</span> <span class="kw">void</span> <span class="fn">Hello</span>()
{
Console.<span class="fn">WriteLine</span>(<span class="str">"자식의 Hello"</span>);
}
}
<span class="cl">Parent</span> p = <span class="kw">new</span> <span class="cl">Parent</span>();
<span class="cl">Parent</span> c = <span class="kw">new</span> <span class="cl">Child</span>();
p.<span class="fn">Hello</span>(); <span class="out">// 부모의 Hello</span>
c.<span class="fn">Hello</span>(); <span class="out">// 자식의 Hello ← override 적용!</span></pre>
</div>
<!-- 비교표 -->
<div style="overflow-x:auto;">
<table class="cmp-table">
<thead>
<tr>
<th>키워드</th>
<th>누가 쓰나</th>
<th>의미</th>
<th>없으면?</th>
</tr>
</thead>
<tbody>
<tr class="row-vt">
<td>virtual</td>
<td>부모 클래스</td>
<td>"자식아, 이 함수 바꿔도 돼!"</td>
<td>자식이 override 불가</td>
</tr>
<tr class="row-ov">
<td>override</td>
<td>자식 클래스</td>
<td>"부모 함수를 내가 새로 만들게!"</td>
<td>부모 함수 그대로 사용</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ── 핵심 정리 ── -->
<div class="summary-grid">
<div class="sum-card">
<div class="sum-dot" style="background:var(--fn-c);"></div>
<div class="sum-name" style="color:var(--fn-c);">함수 (Method)</div>
<div class="sum-desc">코드를 묶어 이름 붙이고 재사용. 반환타입 + 이름 + 매개변수 + 본문.</div>
<div class="sum-when">✦ 반복되는 코드는 함수로!</div>
</div>
<div class="sum-card">
<div class="sum-dot" style="background:var(--vt-c);"></div>
<div class="sum-name" style="color:var(--vt-c);">virtual</div>
<div class="sum-desc">부모가 사용. "자식이 이 함수를 바꿔도 돼!"라는 신호.</div>
<div class="sum-when">✦ 🚪 열려있는 문</div>
</div>
<div class="sum-card">
<div class="sum-dot" style="background:var(--ov-c);"></div>
<div class="sum-name" style="color:var(--ov-c);">override</div>
<div class="sum-desc">자식이 사용. 부모의 virtual 함수를 자식 방식으로 새로 정의.</div>
<div class="sum-when">✦ 🎨 문 열고 새로 꾸미기</div>
</div>
<div class="sum-card">
<div class="sum-dot" style="background:var(--gr-c);"></div>
<div class="sum-name" style="color:var(--gr-c);">핵심 순서</div>
<div class="sum-desc">① 부모에 virtual 추가 → ② 자식에 override 추가 → ③ 자식만의 동작!</div>
<div class="sum-when">✦ virtual 없으면 override 불가!</div>
</div>
</div>
</div>
<script>
const animals = {
Animal: {
color: '#89b4fa',
text: '... (부모 기본 동작)',
code: `Animal a = new Animal();\na.Speak();\n// → "..." (virtual 기본값)`
},
Dog: {
color: '#fab387',
text: '멍멍! 🐶 (Dog override)',
code: `Animal dog = new Dog();\ndog.Speak();\n// → "멍멍! 🐶" (override 적용!)`
},
Cat: {
color: '#f9a8d4',
text: '야옹! 🐱 (Cat override)',
code: `Animal cat = new Cat();\ncat.Speak();\n// → "야옹! 🐱" (override 적용!)`
},
Bird: {
color: '#86efac',
text: '짹짹! 🐦 (Bird override)',
code: `Animal bird = new Bird();\nbird.Speak();\n// → "짹짹! 🐦" (override 적용!)`
}
};
function callSpeak(name) {
document.querySelectorAll('.animal-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
const a = animals[name];
const out = document.getElementById('demo-output');
out.style.color = a.color;
out.textContent = a.text;
document.getElementById('demo-code').innerHTML =
`<span style="color:#44475a;font-style:italic;">// ${name}.Speak() 호출 결과</span>\n` + a.code;
}
</script>
</body>
</html>
결과와 배운 점
같은 채팅에 계속 요청을 동일하게 했더니 정리를 더 잘 해주는 것 같아요.
패턴도 이제 다 파악해서 알아서 만들어 줄까? 라고 물어보니 편하기도 합니다.
열심히 활용해 볼게요.