📚 필수 과제
1. 점심 메뉴 등록 화면 만들기
1️⃣ 목적
- 사용자 입력을 기반으로 데이터를 구성하는 구조를 익힌다.
- HTML<form>, <input> 등의 활용법을 익힌다.
2️⃣ 구현 내용
- 메뉴 등록용 HTML 페이지(register.html)를 만들고, 입력 필드를 구성해보세요.
- 이름, 맵기, 가격, 종류, 기분을 입력받는 form을 작성해보세요.
<button id="button-submit" type="submit">등록하기</button>
<button id="button-list" type="button" onclick="location.href='list.html'">목록보기</button>
<button id="button-home" type="button" onclick="location.href='index.html'">처음으로</button>
과제 4번을 진행하며 수행했던 등록화면을 그대로 인용하였고 일부 수정하여 기능을 추가함
기존에는 음식 이름에 대한 입력기능이 없었던 점을 고려, input type = "text" 형태의 추가 데이터 형성
필수기능 4번에서 요구하는 문제이나 화면 구성을 위해 등록하기 버튼 이외에 저장된 목록을 확인할 수 있는 방향, 그리고 초기 메인화면의 연결이 진행될 수 있도록 각 html 파일들을 클릭 시 작동할 수 있도록 연결하였음
입력받은 form 을 가상의 MockAPI 로 POST 하는 과정이 필요하므로 아래와 같이 값을 저장하고 입력되게끔 진행 하였음
<script>
/* 서버 베이스를 MockAPI URL을 사용하고자 함
Javascript 또한 이전 사용했던 구문에서 일부 수정만 진행*/
const API_BASE = 'https://68b7baee73b3ec66cec55e50.mockapi.io/lunch';
const budget = document.getElementById('budget');
const budgetOut = document.getElementById('budget-out');
const toKRW = v => Number(v).toLocaleString('ko-KR') + ' 원';
budget.addEventListener('input', () => budgetOut.textContent = toKRW(budget.value));
const form = document.getElementById('lunch-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const name = formData.get('name'||'').trim(); // 이름 입력
const spicy = formData.get('spicy'); // 문자열 (ex: "기본 매운 맛")
const budgetNum = Number(formData.get('budget')||0); // 숫자화
const category = formData.get('food-category'); // 문자열
const mood = (formData.get('mood')||'').trim();
if (!category) return alert('음식 종류를 선택하세요.');
if (!mood) return alert('오늘의 기분을 입력하세요.');
const autoName = `${category} - ${spicy || '맵기 미정'}`;
const payload = {
name,
spicy, // 원문 그대로 저장
budget: budgetNum,
category,
mood,
};
try {
await fetch(API_BASE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
alert('등록 완료!');
form.reset();
} catch (err) {
console.error(err);
alert('API 호출 실패');
}
});
</script>
2. 점심 메뉴 목록을 테이블로 보여주기
1️⃣ 목적
- API로부터 받은 리스트 데이터를 반복 렌더링하는 방법을 익힌다.
- <table> 태그를 활용하여 리스트를 예쁘게 출력해보는 경험을 한다.
2️⃣ 구현 내용
- fetch()를 이용해 메뉴 목록 API를 호출하세요.
- 목록에는 간단한 내용만 노출시킵니다.
- 각 메뉴를 테이블에 <tr>로 표현하고, 이름에 링크를 걸어 점심 메뉴 상세 내용 페이지로 연결해보세요.
<table> 태그를 활용하여 진행하는 것이 우선적으로 중요하였고
<table id="id"> 선언 , <thead> 테이블 주제가 입력되게 진행하였고, 내용입력을 통해 테이블 형태의 구조를 호출할 수 있도록 하였음
또한 이름 셀을 클릭하게 될 경우 상세 내용 페이지로 연결되도록 하기 위해 /lunch?id=<recommend>로 이동될 수 있도록 매칭 시켰음
- recommend를 사용한 이유는 API 값에서 입력된 순서 (1,2,3,4,5 ... ) 를 가볍게 나타내고 있어 해당 값을 통해 주소값을 조회할 수 있도록 기능을 연결하였음
<script>
const API_BASE = 'https://68b7baee73b3ec66cec55e50.mockapi.io/lunch';
(async () => {
try {
const res = await fetch(API_BASE, { headers: { Accept: 'application/json' } });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const rows = await res.json();
const tbody = document.querySelector('#table tbody'); // ✅ ID 선택자 추가
if (!Array.isArray(rows) || rows.length === 0) {
tbody.innerHTML = '<tr><td colspan="5">등록된 항목이 없습니다</td></tr>';
return;
}
tbody.innerHTML = rows.map(m => {
const budget = m.budget ? Number(m.budget).toLocaleString('ko-KR') + '원' : '-';
return `
<tr>
<td><a href="detail.html?id=${m.recommend}">${m.name ?? '(이름없음)'}</a></td>
<td>${budget}</td>
<td>${m.spicy ?? '-'}</td>
<td>${m.category ?? '-'}</td>
<td>${m.mood ?? '-'}</td>
</tr>
`;
}).join('');
} catch (err) {
console.error(err);
alert('목록 불러오기 실패: ' + (err.message || 'unknown'));
}
})();
</script>
페이지 로드 -> 즉시 실행 async 함수 시작 fectch(API_BASE) ->rows(JSON 배열)
입력값 0인지에 대한 검증 있다면 map으로 tr 생성을 진행
이름 칸에는 recommend 상세 이동하도록 <a href> 태그 사용
3. 점심 메뉴 상세 내용 조회 페이지 만들기
1️⃣ 목적
- URL 파라미터를 통해 특정 데이터를 불러오는 방법을 익힌다.
- 상세 데이터를 시각적으로 구성하는 UI 레이아웃 설계 능력을 기른다.
2️⃣ 구현 내용
- detail.html에서 URL에 담긴 id 값을 가져오세요.
- 해당 id에 맞는 점심 메뉴 데이터를 Mock API에서 불러와 보여주세요.
URL 파라미터의 id값으로 특정 항목을 GET 하였음
GET 한 내용들을 렌더링하도록 진행하였음
API 값을 불러오는 과정과 상태에 대해서는 status 를 통해 확인할 수 있고
불러오는 값들은 테이블 내 body 값들로 받아들이도록 설정하여 설정해놓은 항목, 내용 외의 입력이 되었던 이름부터 기분까지의 값들을 끌고 노출시킬 수 있도록 하였음
입력된 순서를 기반으로 recommend의 값이 증가하는 개념으로 첫번째로 입력된 음식의 정보가 1, 그 이후 입력값들은 1씩 증가하는 개념의 정보임을 알 수 있었음
(async () => {
$status.textContent = '불러오는 중…';
// list.html에서 detail.html?id=<recommend>로 넘어온다고 가정
const recommend = new URLSearchParams(location.search).get('id');
// recommend로 조회 (검증/에러처리 없이 최소 흐름)
const res = await fetch(`${API_BASE}?recommend=${encodeURIComponent(recommend)}`);
const arr = await res.json();
const m = arr[0] || {};
// 배경색/이모지 적용
document.body.style.background = spicyBg(m.spicy);
const emoji = moodEmoji(m.mood);
const budget = m.budget != null ? Number(m.budget).toLocaleString('ko-KR') + '원' : '-';
// 표 렌더, 감정에 따라 ${emoji}로 추가 출력
$tbody.innerHTML = `
<tr><td>메뉴 이름</td><td>${m.name ?? '(이름없음)'} </td></tr>
<tr><td>예산</td><td>${budget}</td></tr>
<tr><td>맵기</td><td>${m.spicy ?? '-'}</td></tr>
<tr><td>종류</td><td>${m.category ?? '-'}</td></tr>
<tr><td>기분</td><td>${m.mood ?? '-'} ${emoji}</td></tr>
`;
$status.textContent = '';
})();
4. 공통 UI와 흐름 연결
1️⃣ 목적
- 여러 HTML 파일 간 흐름을 자연스럽게 연결하는 방식을 익힌다.
- 사용자 인터랙션을 통해 페이지 이동 및 데이터 전달을 이해한다.
2️⃣ 구현 내용
- 메인(index.html)에 버튼을 2개 만들어 각각 등록/조회 화면으로 이동하도록 하세요.
- 페이지 간 이동 시 자연스럽게 연결되도록 location.href를 활용하세요.
상기 흐름 연결 필요성으로 인하여 점메추 등록화면 외에 목록 확인, 상세 정보 확인 페이지를 연결함으로서 보다 부드러운 화면 전환과 구성을 할 수 있도록 하였음
화면 전환 간에도 API POST와 GET을 반복해야 하는 점임을 감안하여 Javascript를 구성하고자 노력했음
❤️🔥 도전 과제
1. 감정에 따라 이모지 보여주기
1️⃣ 목적
- 문자열 데이터를 이모지로 표현하며 조건문과 객체 맵핑의 개념을 익힐 수 있어요.
2️⃣ 구현 내용
- 기분 텍스트에 따라 해당 감정을 표현하는 이모지를 함께 출력해보세요.
- *이모지 참고 사이트 : https://snskeyboard.com/emoji/*
// 기분에 따른 이모지
function moodEmoji(mood) {
const txt = (mood || '').trim();
if (/행복|맛|좋|행복해|신남|설렘|최고|기쁨/.test(txt)) return '😋';
if (/든든|배부|편안|여유|무난|보통|쏘쏘/.test(txt)) return '😌';
if (/피곤|우울|스트레스|짜증|답답/.test(txt)) return '😮💨';
return '🙂';
}
3개의 레벨로 이모지의 변화를 주고, 조건을 만족하지 않는 일반적인 경우는 마지막 return 값을 통해 표현될 수 있도록 이모지 조건문을 설정하였음
입력될 수 있는 내용 자체가 문장 형태인 관계로 .test() 메서드를 사용하고 반환 값은 true 또는 false 이므로 조건에 따라 이모지 값을 받거나 받지 않는 결과값을 뽑을 수 있음.
2. 맵기 정도에 따라 배경색 바꾸기
1️⃣ 목적
- 동적 스타일 변경을 통해 CSS와 JS의 연동을 학습할 수 있어요.
2️⃣ 구현 내용
- 메뉴 상세 보기에서 맵기 수준에 따라 배경색이 변경되도록 구현하세요.
// 맵기에 따른 배경색 변화
function spicyBg(spicy) {
const s = spicy || '';
if (/싫어|살짝/.test(s)) return 'lightblue';
if (/기본|조금/.test(s)) return 'lightyellow';
if (/화끈한|아주/.test(s)) return 'red';
return 'white';
}
기존에 구성했던 맵기 단계는 총 6가지이나 3단계의 구성으로 위의 이모지 추출 형태와 유사하게 조건을 형성하였음
맵기 정도에 따라 빨간색 배경색부터 연한 파란색까지의 참 거짓 조건 값을 받을 수 있게 하였음
3. 생성일 기준 최신순 정렬하기 (진행중)
1️⃣ 목적
- 데이터를 시간 기준으로 정렬하는 방법을 학습하고, Date 객체와 정렬 함수 사용에 익숙해질 수 있어요.
2️⃣ 구현 내용
- createdAt 값을 new Date()로 파싱하여 가장 최신 항목이 위로 오도록 정렬하세요.
☠️ 트러블 슈팅
- 발생한 Issue ①
- 문제의 요구사항이었던 음식 이름 기입을 할 수 있는 기능을 구현하지 못해 전반적인 구조를 다시 건드려야 했고 그 과정에서 상세 내용 페이지 값을 받아오지 못하는 상황이 발생
- ID값을 임의로 설정하지 않아서 전반적인 내용들을 끌고오는데 어려움이 있었음
- 문제의 요구사항이었던 음식 이름 기입을 할 수 있는 기능을 구현하지 못해 전반적인 구조를 다시 건드려야 했고 그 과정에서 상세 내용 페이지 값을 받아오지 못하는 상황이 발생
- 해결방법 / 찾아본 내용
- MockAPI 에 받아들이고 있는 값 중 recommend 내용을 활용하여 id값을 매칭시켰고 상세 페이지 값을 받아올 수 있도록 하였음
- 발생한 Issue ②
- 구현하지 못한 기능에 대해 임의로 진행 값을 인용하거나 GPT 가 안내하는 진행 방향으로 코딩을 하다보니 복잡하고 일관성 없는 코드 구성이 나타났음
- 해결방법 / 찾아본 내용
- 필요한 기능들을 이해하고 선언한 변수명 활용한 메서드의 작동원리들을 보다 근본적으로 이해하고자 하였음
- 발생한 Issue ③
- CreatedAt 개념에 대해서는 인지하였으나, 값을 받아올 때 String 형태 또는 숫자로만 값을 불러와 정렬하는 방법이나 표현 방법 수정을 어떻게 해야할지 잘 이해하지 못함
- 해결방법 / 찾아본 내용
- 기능 확인 및 추가 학습 예정
'HTML&CSS > HTML&CSS 실습' 카테고리의 다른 글
| 점심메뉴 추천기 API (0) | 2025.09.03 |
|---|---|
| 점심메뉴 추천기 (0) | 2025.09.03 |
| 할 일 목록 만들기 (0) | 2025.09.02 |
| 프로필 페이지 제작 (0) | 2025.09.02 |