
지난 글에서는 HTML과 CSS로 자기소개 페이지의 뼈대와 모양을 만들었습니다. 여기까지 오면 화면은 그럴듯하게 보이지만, 아직은 사용자가 뭘 하더라도 가만히 있는 페이지에 가깝습니다. 이번에는 여기에 JavaScript를 아주 조금만 붙여서, 입력하고 클릭했을 때 화면이 반응하는 흐름을 직접 만들어보겠습니다.
이 지점에서 많은 입문자가 “이제부터 갑자기 어려워지는 것 아닌가” 하고 긴장하곤 합니다. 그런데 처음 JavaScript는 그렇게 접근하지 않아도 괜찮습니다. 지금 필요한 건 복잡한 문법을 많이 아는 게 아니라, 입력값을 읽고 화면의 문장을 바꾸는 흐름 하나를 눈으로 확인하는 것입니다. 이 감각이 먼저 잡혀야 이후에 AI가 만들어준 코드도 덜 낯설게 읽힙니다.
이번 글에서는 지난 글에서 만든 자기소개 페이지를 조금 확장해서, 이름을 입력하면 입력 중인 값이 바로 보이고, 버튼을 누르면 미리보기 카드에 소개 문장이 반영되는 작은 실습을 해보겠습니다. 화면 변화가 바로 보여서 초보자 실습용으로 꽤 잘 맞는 방식입니다.
HTML/CSS 다음에 왜 JavaScript를 붙이는가
웹페이지를 아주 단순하게 나누면, HTML은 내용의 구조를 만들고 CSS는 그 내용을 보기 좋게 다듬습니다. 그런데 사용자가 뭔가를 입력했을 때 문장이 바뀌거나, 버튼을 눌렀을 때 다른 결과가 나타나는 식의 반응은 보통 JavaScript가 맡습니다.
그래서 HTML/CSS 다음에 JavaScript를 붙여보는 흐름이 자연스럽습니다. 구조와 모양을 먼저 분리해서 본 다음, 그 위에 동작을 하나씩 얹는 식입니다. 이렇게 가야 초보자 입장에서도 “지금 내가 건드리는 게 구조인지, 스타일인지, 동작인지”를 조금씩 구분할 수 있습니다.
| 구분 | 무슨 역할을 하나 | 이번 글에서 하는 일 |
|---|---|---|
| HTML | 입력창, 버튼, 미리보기 카드 같은 화면 구조를 만든다 | 입력 영역과 결과 영역을 배치한다 |
| CSS | 여백, 크기, 카드 모양, 정렬을 다듬는다 | 입력창과 카드가 읽기 좋게 보이도록 정리한다 |
| JavaScript | 입력과 클릭에 반응해서 화면 내용을 바꾼다 | 입력값을 읽고 미리보기에 반영한다 |
처음 JavaScript에서는 “새 기능을 많이 안다”보다 지금 이 코드가 어떤 요소를 찾고, 어떤 값을 읽고, 어디 글자를 바꾸는지 차분하게 따라가는 것이 훨씬 중요합니다.
이번 실습에서 만들 반응과 파일 구성
이번 실습은 복잡하지 않습니다. 이름 입력칸, 한 줄 소개 입력칸, 버튼 하나, 그리고 오른쪽에 보이는 미리보기 카드 정도면 충분합니다.
- 이름 입력칸에 글자를 치면 “지금 입력 중인 이름” 문구가 바로 바뀝니다.
- 버튼을 누르면 입력한 이름과 소개가 미리보기 카드에 반영됩니다.
- 입력값이 비어 있으면 기본 문구가 대신 보이도록 처리합니다.
파일도 세 개면 끝납니다. 지난 글의 실습 폴더를 복사해서 새 폴더로 만들어도 좋고, 새 폴더를 따로 만들어도 괜찮습니다.
03-profile-interaction/
index.html
style.css
script.js
여기서 복사 명령을 굳이 터미널로 할 필요는 없습니다. 운영체제마다 폴더 복사 방식이 조금씩 다를 수 있으니, 처음에는 VS Code Explorer나 파일 탐색기에서 폴더를 통째로 복제해도 충분합니다. 중요한 건 “지난 실습을 덮어쓰지 않고 새 단계로 이어간다”는 감각입니다.
index.html에 입력창과 버튼 넣기
먼저 HTML부터 손보겠습니다. 이번에는 입력창과 버튼이 새로 들어가고, 결과를 보여줄 미리보기 카드도 조금 정리해주면 됩니다. 그리고 script.js 파일을 연결하는 줄도 같이 추가해야 합니다.
index.html은 아래처럼 구성하면 됩니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>프로필 카드 상호작용 실습</title>
<link rel="stylesheet" href="style.css">
<script defer src="script.js"></script>
</head>
<body>
<main class="layout">
<section class="panel editor-panel">
<p class="eyebrow">JavaScript Practice</p>
<h1>프로필 카드에 반응 붙이기</h1>
<p class="desc">
이름과 한 줄 소개를 입력한 뒤 버튼을 누르면 오른쪽 미리보기에 반영됩니다.
이름 입력칸은 타이핑하는 동안 아래 안내 문구도 바로 바뀌게 만들어보겠습니다.
</p>
<div class="field">
<label for="nameInput">이름</label>
<input id="nameInput" type="text" placeholder="이름을 입력해보세요">
</div>
<p class="live-text">지금 입력 중인 이름: <strong id="typingName">아직 입력 없음</strong></p>
<div class="field">
<label for="introInput">한 줄 소개</label>
<textarea id="introInput" rows="5" placeholder="예: AI와 함께 웹을 배우는 중입니다."></textarea>
</div>
<button id="applyButton" type="button">미리보기 반영하기</button>
<p id="statusText" class="status">아직 미리보기에 반영하지 않았습니다.</p>
</section>
<section class="panel preview-panel">
<p class="eyebrow">Live Preview</p>
<div class="profile-card">
<h2 id="previewName">홍길동</h2>
<p id="previewIntro">
AI와 함께 웹을 배우는 중인 초보자입니다.
버튼을 눌러 내 소개 문장을 이 카드에 반영해보세요.
</p>
</div>
</section>
</main>
</body>
</html>
여기서 눈여겨볼 부분은 두 가지입니다.
- id가 붙은 요소들입니다. 나중에 JavaScript가 이 요소들을 찾아서 값을 읽거나 글자를 바꾸게 됩니다.
<script defer src="script.js"></script>줄입니다. 이 줄이 있어야 HTML과 JavaScript 파일이 연결됩니다.
defer를 붙인 이유도 같이 이해해두면 좋습니다. 처음 실습에서는 JavaScript가 HTML 요소를 찾아서 써야 하는데, HTML이 아직 다 읽히기 전에 스크립트가 먼저 실행되면 요소를 못 찾는 일이 생길 수 있습니다. 그래서 이 단계에서는 이런 연결 방식을 익혀두는 편이 안정적입니다.
JavaScript가 안 되는 것처럼 보일 때, 의외로 가장 먼저 확인해야 할 것은 문법보다 HTML에 script.js를 제대로 연결했는지인 경우가 많습니다.
style.css로 입력 영역까지 정리하기
이제 화면을 조금 더 읽기 좋게 정리해보겠습니다. JavaScript 실습이라고 해서 CSS를 완전히 빼버리면, 입력 영역과 결과 영역이 뒤섞여 보여서 오히려 흐름을 파악하기 어려워질 수 있습니다. 그래서 이번에도 CSS는 최소한으로만 정리하되, 어느 부분이 입력 영역이고 어느 부분이 미리보기인지 구분되도록 잡겠습니다.
style.css는 아래처럼 작성하면 무난합니다.
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
background: #f3f4f6;
color: #111827;
}
.layout {
width: min(1100px, calc(100% - 32px));
margin: 48px auto;
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 24px;
}
.panel {
background: #ffffff;
border-radius: 20px;
padding: 32px;
box-shadow: 0 14px 32px rgba(15, 23, 42, 0.08);
}
.eyebrow {
margin: 0 0 12px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: #6b7280;
}
h1 {
margin: 0 0 12px;
font-size: 32px;
line-height: 1.25;
}
.desc {
margin: 0 0 24px;
line-height: 1.7;
}
.field + .field {
margin-top: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 700;
}
input,
textarea {
width: 100%;
padding: 14px 16px;
border: 1px solid #d1d5db;
border-radius: 14px;
font: inherit;
background: #ffffff;
}
textarea {
resize: vertical;
min-height: 120px;
}
.live-text {
margin: 14px 0 0;
line-height: 1.6;
color: #374151;
}
button {
margin-top: 24px;
padding: 14px 18px;
border: none;
border-radius: 14px;
font: inherit;
font-weight: 700;
cursor: pointer;
background: #111827;
color: #ffffff;
}
.status {
margin: 12px 0 0;
color: #6b7280;
line-height: 1.6;
}
.profile-card {
padding: 28px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 18px;
min-height: 260px;
}
.profile-card h2 {
margin: 0 0 12px;
font-size: 28px;
line-height: 1.2;
}
.profile-card p {
margin: 0;
line-height: 1.8;
}
@media (max-width: 820px) {
.layout {
grid-template-columns: 1fr;
margin: 32px auto;
}
.panel {
padding: 24px;
}
h1 {
font-size: 28px;
}
}
이번 CSS는 화려하게 꾸미려는 목적보다, 입력하는 곳과 결과를 보는 곳을 분리해서 읽기 쉽게 만드는 목적이 더 큽니다. 나중에 Codex에게 스타일 수정을 부탁할 때도 이런 구조가 잡혀 있으면 “버튼만 조금 크게”, “카드 간격만 더 넓게” 같은 식으로 요청하기가 쉬워집니다.
script.js로 입력과 버튼 클릭 다루기
이제 진짜 JavaScript를 붙여보겠습니다. 이번 실습에서 기억해두면 좋은 흐름은 세 단계입니다.
- 먼저 HTML 요소를 찾습니다.
- 사용자가 입력하거나 클릭할 때를 기다립니다.
- 그때 화면의 글자를 바꿉니다.
script.js에는 아래 코드를 넣어보면 됩니다.
const nameInput = document.querySelector("#nameInput");
const introInput = document.querySelector("#introInput");
const typingName = document.querySelector("#typingName");
const previewName = document.querySelector("#previewName");
const previewIntro = document.querySelector("#previewIntro");
const applyButton = document.querySelector("#applyButton");
const statusText = document.querySelector("#statusText");
nameInput.addEventListener("input", () => {
const currentName = nameInput.value.trim();
typingName.textContent = currentName || "아직 입력 없음";
});
applyButton.addEventListener("click", () => {
const nextName = nameInput.value.trim() || "이름을 입력해보세요";
const nextIntro =
introInput.value.trim() ||
"한 줄 소개를 입력하면 이 자리에 문장이 반영됩니다.";
previewName.textContent = nextName;
previewIntro.textContent = nextIntro;
statusText.textContent = "미리보기에 반영했습니다. 이제 값을 다시 바꿔보면서 흐름을 확인해보세요.";
});
처음 보면 길어 보이지만, 실제로는 그렇게 복잡하지 않습니다.
| 코드 조각 | 무슨 역할을 하나 |
|---|---|
document.querySelector(...) |
HTML에서 내가 쓸 요소를 찾아옵니다. |
nameInput.value |
입력창에 지금 들어 있는 글자를 읽습니다. |
addEventListener("input", ...) |
사용자가 입력할 때마다 실행됩니다. |
addEventListener("click", ...) |
버튼을 눌렀을 때 실행됩니다. |
textContent |
화면에 보이는 텍스트를 바꿉니다. |

조금 더 풀어보면, 첫 번째 이벤트는 이름 입력칸을 위한 것입니다. 사용자가 글자를 입력할 때마다 현재 값을 읽어서 “지금 입력 중인 이름” 자리에 바로 보여줍니다. 그래서 타이핑하는 순간 화면 반응이 생깁니다.
두 번째 이벤트는 버튼을 눌렀을 때 동작합니다. 여기서는 이름과 소개 입력값을 한 번에 읽어서, 미리보기 카드 안의 제목과 문장에 넣습니다. 만약 아무것도 입력하지 않았다면 빈칸 대신 기본 문구를 보여주도록 했습니다. 이런 식으로 기본값을 넣어두면 처음 실습에서 결과가 완전히 비어 보여 당황하는 일을 줄일 수 있습니다.
그리고 이번 실습에서는 textContent를 썼습니다. 지금은 사용자가 입력한 문장을 그대로 화면의 글자로 보여주기만 하면 되기 때문입니다. 초반에는 이처럼 “텍스트를 바꾼다”는 의도를 분명하게 가져가는 쪽이 이해하기도 쉽고, 실수도 적습니다.
처음 JavaScript에서 가장 많이 막히는 이유는 문법보다 id 이름이 HTML과 JS에서 다르거나, 찾으려는 요소 이름을 잘못 쓴 경우가 훨씬 많습니다.
브라우저에서 확인하며 자주 막히는 부분 점검하기
이제 세 파일을 모두 저장한 뒤 index.html을 브라우저에서 열어보면 됩니다. 확인 포인트는 어렵지 않습니다.
- 이름 입력칸에 글자를 입력하면 “지금 입력 중인 이름” 문구가 바로 바뀌는지
- 한 줄 소개를 작성한 뒤 버튼을 누르면 오른쪽 카드 내용이 바뀌는지
- 입력을 비워둔 상태에서도 기본 문구가 잘 보이는지
만약 화면이 아무 반응도 하지 않는다면, 아래 순서로 확인해보면 됩니다.
- script.js가 실제로 저장되었는지 확인합니다.
- index.html에
<script defer src="script.js"></script>가 들어 있는지 확인합니다. nameInput,introInput,applyButton같은 id 이름이 HTML과 JS에서 정확히 같은지 봅니다.- 다른 폴더의 index.html을 열어놓은 건 아닌지 확인합니다.
- 수정한 뒤 저장을 빼먹지는 않았는지 다시 봅니다.
초보자는 여기서 “JavaScript가 너무 어렵다”로 결론을 내리기 쉬운데, 실제로는 파일 연결이나 id 오타처럼 훨씬 단순한 문제인 경우가 많습니다. 그래서 처음에는 오류를 크게 해석하기보다, 파일 연결 → id 이름 → 저장 상태 순으로 차분히 좁혀가는 습관이 좋습니다.
Codex로 작은 수정 요청 해보기
이제 손으로 한 번 직접 만들어봤으니, 그다음부터는 Codex에게 작은 수정 요청을 해보면 좋습니다. 여기서 중요한 건 여전히 같습니다. 처음부터 “전부 더 멋지게 바꿔줘”라고 하기보다, 작업 범위를 잘게 나눠서 맡기는 편이 훨씬 낫습니다.
예를 들어 이런 식의 요청은 초보자가 따라가기에 비교적 편합니다.

@script.js
이 구조는 유지하고,
이름 입력칸이 비어 있으면 상태 문구에
"이름을 먼저 입력해보세요."가 보이도록만 수정해줘.
수정한 이유도 같이 설명해줘.

@index.html @style.css
모바일에서 입력 영역과 미리보기 카드가
조금 더 자연스럽게 세로로 보이도록 정리해줘.
전체 구조는 크게 바꾸지 말아줘.


@script.js
버튼을 눌렀을 때만 상태 문구가 바뀌는 대신,
2초 뒤에는 다시 원래 문구로 돌아오게 만들어줘.
먼저 어떤 방식으로 바꿀지 짧게 설명해줘.
수정이 끝난 뒤에는 바로 다음 단계로 넘어가기보다, “어디를 왜 바꿨는지 다시 설명해줘”라고 한 번 더 묻는 편이 좋습니다. 가능하면 /review 같은 흐름으로 바뀐 점을 다시 확인하는 습관도 같이 들이면 더 좋습니다.
AI에게 맡길 때는 “잘 만들기”보다 “작게 바꾸기”가 먼저입니다. 초반에는 그 차이가 생각보다 큽니다.
마무리
이번 글에서 한 일은 거창하지 않습니다. 입력창 두 개, 버튼 하나, 카드 하나에 JavaScript를 조금 붙였을 뿐입니다. 그런데 바로 이 정도 크기의 실습이 초보자에게는 꽤 중요합니다. 이제부터는 페이지가 단순히 보이는 데서 끝나는 게 아니라, 사용자 행동에 반응하기 시작했기 때문입니다.
여기까지 해보면 HTML은 구조, CSS는 모양, JavaScript는 반응이라는 구분이 조금씩 손에 들어오기 시작합니다. 그리고 Codex도 “전부 대신 만드는 도구”가 아니라, 작은 수정과 설명을 맡기면 훨씬 잘 쓰이는 도구라는 감각이 같이 생깁니다.
여기까지 오면 이제 정말 “따라만 하는 단계”에서 조금씩 벗어나기 시작했다고 봐도 됩니다.
'바이브코딩 > 실습' 카테고리의 다른 글
| 바이브코딩 6편: 오류가 났을 때 어디부터 봐야 할까 (0) | 2026.03.15 |
|---|---|
| 바이브코딩 5편: AI가 고친 코드를 어디부터 읽어야 할까 (0) | 2026.03.14 |
| 바이브코딩 3편: VS Code와 Codex로 자기소개 페이지 만들기 (1) | 2026.03.12 |
| 바이브코딩 2편: VS Code 터미널과 Codex 확장, 여기서부터 실습이 시작된다 (0) | 2026.03.11 |
| 실습용 바이브코딩 환경 만들기: VS Code 설치와 첫 세팅 가이드 (0) | 2026.03.10 |
