서브웨이 영양 계산기를 만들었습니다
Source: Dev.to
위에 제공된 내용만으로는 번역할 텍스트가 없습니다. 번역을 원하는 본문을 알려주시면 한국어로 번역해 드리겠습니다.
Introduction
단일 HTML 파일에 방대한 JavaScript와 거대한 JSON‑유사 데이터 구조가 들어있으며, 나쁜 UI가 내 점심을 망치지 못하도록 고집스럽게 거부하고 있습니다.
제가 어떻게 만들었는지, 진행 과정에서 어떤 문제가 발생했는지, 그리고 다음에 다르게 할 점을 차근차근 설명드리겠습니다.
문제
수동으로 추가해야 합니다:
- 빵 칼로리
- 고기 칼로리
- 채소 칼로리 (대부분은 0이지만 올리브와 아보카도는 제외)
- 소스 칼로리
그런 다음 풋‑롱용으로 두 배로 늘리고, 치즈가 지방과 나트륨을 추가한다는 점을 기억하며, 소금과 후추를 잊지 마세요.
이 과정은 번거롭고 오류가 발생하기 쉽습니다 – 바로 간단한 웹 도구가 해결할 수 있는 문제입니다.
데이터 수집
I needed a complete, consistent dataset covering every possible ingredient:
| 카테고리 | 항목 |
|---|---|
| 빵 | 12 |
| 미리 만든 샌드위치 | 30+ |
| 단품 단백질 | 20+ |
| 치즈 | 5 |
| 채소 | 15 |
| 양념류 | 25+ (Normal & Light) |
| 조미료 | 3 |
| 사이드 | 2 |
| 샐러드 | 25+ |
| 랩 | 25+ |
| 빵 없는 볼 | 25+ |
| 단백질 포켓 | 4 |
| 수프 | 3 |
| 디저트 | 7 |
| 사이드킥 | 8 |
| 총계 | ≈ 300+ |
출처
- 서브웨이 공식 미국 영양 PDF (2026년 버전)
- 온라인 메뉴
- 제3자 집계 사이트
- 매장별 재료 시트
교훈: 단일 출처만을 신뢰하지 마세요. 모든 정보를 교차 검증하고 데이터가 어디서 왔는지, 어떤 부분이 추정치인지 투명하게 밝히세요.
데이터 구조
subwayMenu라는 거대한 JavaScript 객체를 만들었습니다. 이 객체는 각 카테고리별(breads, proteins, cheeses, vegetables, condiments 등) 배열을 포함하고 있습니다.
모든 항목은 동일한 스키마를 따릅니다:
{
id: 'artisan-italian-bread-6inch',
name: '6" Artisan Italian Bread',
servingSize_g: 71,
calories: 210,
totalFat: 2,
saturatedFat: 1,
transFat: 0,
cholesterol: 0,
sodium: 380,
totalCarbs: 39,
dietaryFiber: 1,
sugars: 3,
addedSugars: 2,
protein: 8,
vitaminA_mcg: 0,
vitaminC_mg: 0,
calcium_mg: 1040,
iron_mg: 16.2,
category: 'bread'
}
모든 영양 정보 필드가 각 항목에 존재하므로 반복, 계산 및 필터링이 직관적입니다.
핵심 로직
크기 배수
// quantity system (simplified)
const sizeMultiplier = isFootlong ? 2 : 1;
calculateTotalNutrition
- 모든 영양 항목에 대해 0으로 초기화된 totals 객체를 생성합니다.
- 샌드위치 크기에 따라 비례하는 카테고리에 크기 배수(풋롱은 2)를 적용합니다.
- 선택된 모든 카테고리(빵, 단백질, 치즈, 채소, 양념 등)를 순회하면서 수량 × 배수로 곱한 아이템의 영양 정보를 더합니다.
- 최종 totals와 재료 목록을 반환합니다(“재료” 패널에 사용).
반올림은 마지막 단계까지 미루며, 표시를 깔끔하게 하기 위해 값들을 소수점 한 자리까지 반올림합니다.
Source: …
UI 디자인
외부 라이브러리 없이 순수 HTML, CSS, 그리고 바닐라 JavaScript만 사용.
| 영역 | 설명 |
|---|---|
| 왼쪽 | 카테고리를 접을 수 있는 빌더(빵, 단백질, 치즈, …)와 메뉴 종류(샌드위치, 샐러드, 랩, …)를 전환하는 탭 스위처. |
| 오른쪽 | 영양 라벨, 칼로리 진행 바, 현재 선택 목록, 그리고 액션 버튼이 포함된 결과 섹션. |
탭 시스템 및 접을 수 있는 카테고리
<!-- simplified markup -->
<div class="category-header" onclick="subwayToggleDropdown(this)">
<span>Breads</span>
<svg><!-- arrow icon --></svg>
</div>
<div class="category-items">
<!-- list of breads -->
</div>
subwayToggleDropdown은 expanded 클래스를 추가/제거하고 SVG 화살표를 회전시킵니다.
카테고리 내부 검색
간단한 입력 필드가 현재 열려 있는 카테고리의 보이는 항목들을 필터링합니다.
“현재 선택” 패널
-
선택한 각 항목을 수량과 칼로리와 함께 나열합니다.
-
각 항목에는 X 버튼이 있어
subwayRemoveItemFromSelection을 호출합니다. 이 함수는:currentSelection객체를 업데이트합니다.- 해당 카테고리를 다시 렌더링하여 항목 선택을 해제합니다.
- 선택 패널 자체를 다시 렌더링합니다.
이 작은 품질‑향상 기능 덕분에 사용자는 추가한 항목을 확인하기 위해 위로 스크롤할 필요가 없습니다.
데이터 내보내기
function subwaySaveNutritionInfo() {
const { totals, ingredients } = calculateTotalNutrition();
const lines = [
`Meal: ${selectedMenuType}`,
`Size: ${isFootlong ? 'Foot‑long' : '6‑inch'}`,
'',
'Ingredients:',
...ingredients.map(i => `- ${i.name} ×${i.qty}`),
'',
'Nutrition Facts:',
`Calories: ${totals.calories} kcal`,
// …other fields…
];
const blob = new Blob([lines.join('\n')], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'subway-nutrition.txt';
a.click();
URL.revokeObjectURL(url);
// temporary “Saved!” feedback
showSaveFeedback();
}
플레인‑텍스트 내보내기는 모든 환경에서 작동하며 사용자가 데이터를 메모나 스프레드시트에 붙여넣을 수 있게 해줍니다.
알려진 문제 및 주의사항
- Bread radio group – 한 번에 하나의 빵만 선택할 수 있습니다.
- Foot‑long multiplier – 모든 크기 의존 재료에 적용되어야 합니다.
- Search resetting – 항목을 변경하면 검색 상자가 초기화됩니다; 쿼리를 유지해야 합니다.
- Save button – 일부 구형 브라우저에서는 작동하지 않습니다 (Blob 지원 필요).
요점
- Data first: 깨끗하고 일관된 데이터가 작업의 약 80 %를 차지합니다.
- Logic before UI: 인터페이스를 만들기 전에 콘솔에서 계산 엔진을 먼저 작동시킵니다.
- User‑centred design: “Current Selection” 패널과 손쉬운 내보내기 기능이 도구를 실제로 유용하게 만듭니다.
Final Thoughts
코드가 조금 지저분하고 데이터가 더 완전했으면 좋겠지만, 계산기는 작동합니다. 맞춤형 서브웨이 식사를 만들고, 정확히 무엇을 먹는지 확인하고, 결과를 나중에 저장할 수 있습니다.
다른 레스토랑 체인을 위해 비슷한 도구를 만들려는 개발자라면, 제 조언은:
- 데이터부터 시작하세요.
- 먼저 계산 엔진을 구축하세요.
- 실제 문제점을 해결하는 UI 기능을 추가하세요 (검색, 선택 목록, 내보내기).
행복한 코딩! 🚀
실제 사용자와 테스트
몇몇 친구들에게 이것을 건네고 그들이 어디서 혼란스러워하는지 지켜보았습니다. 그때 foot‑long multiplier bug에 대해 알게 되었습니다.
간단하게 유지하기
백엔드, 데이터베이스, 혹은 빌드 시스템이 필요하지 않습니다. 인라인 CSS와 JS가 포함된 단일 HTML 파일이면 이런 도구에 충분합니다.
실용적인 사용
이제 서브웨이에 들어갈 때마다 나는 정확히 무엇을 주문할지 알고 있다. 확신이 서지 않을 경우, 휴대폰에서 이 계산기를 열어 카운터에 도착하기 전에 주문을 구성한다.
목표
그게 바로 핵심입니다—도구는 여러분의 삶을 더 쉽게 만들어야지, 더 어렵게 만들면 안 됩니다.
마무리 생각
이 walkthrough가 다른 사람이 유용한 무언가를 만드는 데 도움이 되길 바랍니다. 그리고 단순히 계산기만 사용하고 싶었다면, 이제 그 내부가 어떻게 동작하는지 알게 된 것이죠.