# 은행 앱 만들기 3부: 데이터 가져오기와 사용 방법 스타트렉의 엔터프라이즈 컴퓨터를 떠올려보세요 - 피카드 함장이 함선 상태를 물어보면, 인터페이스가 멈췄다가 다시 만들어지지 않고 즉시 정보가 나타납니다. 그런 원활한 정보 흐름이 바로 우리가 동적 데이터 가져오기를 통해 구축하려는 것입니다. 지금 당신의 은행 앱은 인쇄된 신문과 같습니다 - 정보는 제공하지만 정적입니다. 우리는 그것을 NASA의 미션 컨트롤처럼, 데이터가 계속해서 실시간으로 흐르며 사용자의 작업 흐름을 방해하지 않고 업데이트되는 형태로 변환할 것입니다. 서버와 비동기적으로 통신하는 방법, 서로 다른 시점에 도착하는 데이터를 처리하는 법, 원시 정보를 사용자에게 의미 있는 형태로 변환하는 법을 배우게 될 것입니다. 이 차이가 데모와 프로덕션 준비 소프트웨어를 구분합니다. ## ⚡ 다음 5분 동안 할 수 있는 것 **바쁜 개발자를 위한 빠른 시작 경로** ```mermaid flowchart LR A[⚡ 5분] --> B[API 서버 설정] B --> C[curl로 fetch 테스트] C --> D[로그인 함수 만들기] D --> E[작동 중인 데이터 확인] ``` - **1-2분**: API 서버 시작하기 (`cd api && npm start`) 및 연결 테스트 - **3분**: fetch를 사용한 기본 `getAccount()` 함수 만들기 - **4분**: `action="javascript:login()"` 으로 로그인 폼 연결하기 - **5분**: 로그인 테스트 후 콘솔에서 계좌 데이터 확인하기 **빠른 테스트 명령어**: ```bash # API가 실행 중인지 확인 curl http://localhost:5000/api # 테스트 계정 데이터 가져오기 curl http://localhost:5000/api/accounts/test ``` **중요한 이유**: 5분 만에 현대 웹 애플리케이션이 사용하는 비동기 데이터 가져오기 방식을 체험할 수 있습니다. 이는 앱이 반응적이고 생명력 있게 느껴지게 하는 기반입니다. ## 🗺️ 데이터 기반 웹 애플리케이션 학습 여정 ```mermaid journey title 정적 페이지에서 동적 애플리케이션으로 section 진화 이해하기 전통적인 페이지 리로드: 3: You AJAX/SPA 이점 발견: 5: You Fetch API 패턴 마스터하기: 7: You section 인증 구축 로그인 기능 만들기: 4: You 비동기 작업 처리하기: 6: You 사용자 세션 관리하기: 8: You section 동적 UI 업데이트 DOM 조작 배우기: 5: You 거래 표시 만들기: 7: You 반응형 대시보드 만들기: 9: You section 전문 패턴 템플릿 기반 렌더링: 6: You 오류 처리 전략: 7: You 성능 최적화: 8: You ``` **학습 목표**: 이 수업을 마치면, 현대 웹 애플리케이션이 데이터를 동적으로 가져오고 처리하며 표시하는 방식을 이해하게 되어, 전문적인 애플리케이션에서 기대하는 원활한 사용자 경험을 구현할 수 있습니다. ## 강의 전 퀴즈 [강의 전 퀴즈](https://ff-quizzes.netlify.app/web/quiz/45) ### 사전 준비 사항 데이터 가져오기 전에 다음 항목을 준비하세요: - **이전 강의**: [로그인 및 등록 폼](../2-forms/README.md) 완료 - 이 기반 위에서 작업합니다 - **로컬 서버**: [Node.js](https://nodejs.org) 설치 및 [서버 API 실행](../api/README.md)으로 계좌 데이터 제공 - **API 연결**: 다음 명령어로 서버 연결 테스트: ```bash curl http://localhost:5000/api # 예상 응답: "Bank API v1.0.0" ``` 이 빠른 테스트는 모든 구성 요소가 올바르게 통신하는지 확인합니다: - 시스템에서 Node.js가 정상 실행 중인지 확인 - API 서버가 활성 상태이며 응답하는지 점검 - 앱이 서버에 접근 가능한지 검증 (작전 전 무전 연락 체크와 유사) ## 🧠 데이터 관리 생태계 개요 ```mermaid mindmap root((데이터 관리)) Authentication Flow Login Process Form Validation Credential Verification Session Management User State Global Account Object Navigation Guards Error Handling API Communication Fetch Patterns GET 요청 POST 요청 오류 응답 Data Formats JSON 처리 URL 인코딩 응답 파싱 Dynamic UI Updates DOM Manipulation 안전한 텍스트 업데이트 요소 생성 템플릿 복제 User Experience 실시간 업데이트 오류 메시지 로딩 상태 Security Considerations XSS Prevention textContent 사용 입력 값 정화 안전한 HTML 생성 CORS Handling 교차 출처 요청 헤더 구성 개발 설정 ``` **핵심 원리**: 현대 웹 애플리케이션은 데이터 조율 시스템입니다 - 사용자 인터페이스, 서버 API, 브라우저 보안 모델 간의 조화를 통해 원활하고 반응적인 경험을 만듭니다. --- ## 현대 웹 앱의 데이터 가져오기 이해 지난 20년 동안 웹 애플리케이션이 데이터를 처리하는 방식은 크게 변화했습니다. 이 변화를 이해하면 AJAX나 Fetch API 같은 현대 기술이 얼마나 강력하며, 왜 웹 개발자에게 필수적인 도구가 되었는지 알게 됩니다. 전통적인 웹사이트와 우리가 지금 구축하는 동적이고 반응적인 애플리케이션이 어떻게 다른지 살펴봅시다. ### 전통적 다중 페이지 애플리케이션(MPA) 초창기 웹에서는 클릭할 때마다 마치 옛날 TV 채널을 바꾸는 것처럼 화면이 깜빡이고 다시 새로운 내용에 맞춰서 켜졌습니다. 초기 웹 애플리케이션은 모든 상호작용이 전체 페이지를 완전히 다시 불러오는 것이었죠. ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: 링크 클릭 또는 양식 제출 Browser->>Server: 새 HTML 페이지 요청 Note over Browser: 페이지가 빈 화면이 됨 Server->>Browser: 완전한 HTML 페이지 반환 Browser->>User: 새 페이지 표시 (깜박임/새로고침) ```  **이 방식이 답답하게 느껴진 이유:** - 클릭할 때마다 페이지 전체를 새로 만들어야 했음 - 사용자 생각이 끊기는 짜증나는 화면 깜빡임 경험 - 같은 헤더와 푸터를 반복 다운로드해 네트워크 부하 증가 - 앱 사용 느낌이 소프트웨어라기보다 파일 서랍을 넘기는 듯함 ### 현대 단일 페이지 애플리케이션(SPA) AJAX(Asynchronous JavaScript and XML)는 이 패러다임을 완전히 바꿨습니다. 국제 우주 정거장의 모듈 설계처럼, 우주비행사가 개별 부품을 교체하는 것처럼 AJAX는 페이지 전체를 다시 로드하지 않고 특정 부분만 업데이트할 수 있게 해줍니다. 이름에 XML이 있지만, 현재는 주로 JSON을 사용하며 핵심 원리는 "변경된 부분만 업데이트" 하는 것입니다. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: 페이지와 상호작용 Browser->>JavaScript: 이벤트 핸들러 실행 JavaScript->>Server: 필요한 데이터만 가져옴 Server->>JavaScript: JSON 데이터 반환 JavaScript->>Browser: 특정 페이지 요소 업데이트 Browser->>User: 업데이트된 내용 표시 (새로고침 없음) ```  **SPA가 훨씬 좋은 이유:** - 실제로 변경된 부분만 업데이트 (똑똑하죠?) - 갑작스런 중단 없음 - 사용자는 흐름을 유지 - 줄어든 데이터 전송으로 더 빠른 로딩 시간 - 모든 것이 스마트폰 앱처럼 빠르고 반응적 느낌 ### Fetch API로의 진화 현대 브라우저는 기존의 [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)를 대체하는 [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API)를 제공합니다. 전신인 전신기와 이메일의 차이처럼, Fetch API는 프로미스를 사용해 비동기 코드를 깨끗하게 만들고 JSON도 자연스럽게 처리합니다. | 특징 | XMLHttpRequest | Fetch API | |---------|----------------|----------| | **문법** | 복잡한 콜백 기반 | 깔끔한 프로미스 기반 | | **JSON 처리** | 수동 파싱 필요 | 내장된 `.json()` 메서드 | | **오류 처리** | 제한적인 오류 정보 | 상세한 오류 정보 제공 | | **현대 지원** | 레거시 호환성 | ES6+ 프로미스 및 async/await 지원 | > 💡 **브라우저 호환성**: 좋은 소식 - Fetch API는 모든 최신 브라우저에서 작동합니다! 구체적 버전이 궁금하면 [caniuse.com](https://caniuse.com/fetch)을 참고하세요. > **결론:** - 크롬, 파이어폭스, 사파리, 엣지 등 주요 브라우저 완벽 지원 (즉, 사용자 대부분이 사용하는 곳) - 인터넷 익스플로러는 별도 지원 필요 (솔직히 IE는 이제 보내줄 때) - 이후 사용할 우아한 async/await 패턴에 딱 맞춤 ### 사용자 로그인 및 데이터 조회 구현 이제 은행 앱을 정적인 표시에서 기능성 애플리케이션으로 바꾸는 로그인 시스템을 구현해 봅시다. 군사 시설의 인증 프로토콜처럼, 사용자 자격 증명을 확인하고 특정 데이터에 접근 권한을 부여합니다. 기본 인증부터 시작해 점차 데이터 가져오기 기능을 추가하는 식으로 구축해 보겠습니다. #### 1단계: 로그인 함수 기초 만들기 `app.js` 파일을 열고 새로운 `login` 함수를 추가하세요. 이 함수는 사용자 인증을 처리합니다: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **설명:** - `async` 키워드는 자바스크립트에 "이 함수 내에서 기다릴 필요가 있을 수 있다"고 알려줍니다 - 페이지에서 폼을 가져옵니다 (복잡하지 않고 ID로 단순 선택) - 사용자가 입력한 사용자 이름 값을 뽑아냅니다 - 팁: 폼 입력값은 `name` 속성으로 접근 가능해, 별도 getElementById가 필요 없습니다! > 💡 **폼 접근 패턴**: 모든 폼 컨트롤은 `name` 속성으로 지정하며, 폼 요소의 프로퍼티로 접근할 수 있습니다. 이 방식은 데이터를 깔끔하고 읽기 쉽게 가져올 수 있습니다. #### 2단계: 계좌 데이터 가져오기 함수 만들기 다음으로, 서버에서 계좌 데이터를 불러오는 함수도 만듭니다. 등록 함수와 유사한 방식이며 데이터 조회에 집중합니다: ```javascript async function getAccount(user) { try { const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` **이 코드가 하는 일:** - 최신 `fetch` API로 비동기 데이터 요청 - 사용자 이름 파라미터를 포함한 GET 요청 URL 구성 - `encodeURIComponent()`로 URL 내 특수문자 안전 처리 - 응답을 JSON으로 변환해 데이터 조작 손쉽게 - 오류 발생 시 앱 충돌 없이 에러 객체 반환으로 우아하게 처리 > ⚠️ **보안 주의**: `encodeURIComponent()`는 해군 통신 부호화처럼 URL 내 특수문자가 잘못 해석되지 않도록 합니다. "#" 나 "&" 같은 문자가 엉뚱한 의미로 변하는 것을 방지합니다. > **중요성:** - URL 깨짐 방지 - URL 변조 공격 차단 - 서버가 의도한 데이터를 안전히 수신 - 보안 코딩 모범 사례 준수 #### HTTP GET 요청 이해하기 놀라실 수도 있는데, `fetch`에 옵션을 안 주면 기본적으로 [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) 요청을 만듭니다. 이 방식은 서버에 "이 사용자의 계좌 데이터를 보여주세요"라고 정중히 요청하는 것입니다. GET 요청은 도서관에 책을 빌리러 가는 것과 같습니다. 기존에 있는 걸 보는 요청이고, POST 요청(등록 시 사용)은 책을 새로 제출하는 것과 유사합니다. | GET 요청 | POST 요청 | |-------------|-------------| | **목적** | 기존 데이터 가져오기 | 새로운 데이터 서버에 보내기 | | **파라미터** | URL 경로나 쿼리 스트링 | 요청 본문 | | **캐싱** | 브라우저가 캐시 가능 | 보통 캐시 안 함 | | **보안** | URL/로그에 노출 | 요청 본문에 숨김 | ```mermaid sequenceDiagram participant B as 브라우저 participant S as 서버 Note over B,S: GET 요청 (데이터 조회) B->>S: GET /api/accounts/test S-->>B: 200 OK + 계정 데이터 Note over B,S: POST 요청 (데이터 제출) B->>S: POST /api/accounts + 새 계정 데이터 S-->>B: 201 생성됨 + 확인 Note over B,S: 오류 처리 B->>S: GET /api/accounts/nonexistent S-->>B: 404 찾을 수 없음 + 오류 메시지 ``` #### 3단계: 함수 연결하기 이제 계좌 가져오기 함수를 로그인 과정에 연결해서 완성합시다: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; const data = await getAccount(user); if (data.error) { return console.log('loginError', data.error); } account = data; navigate('/dashboard'); } ``` 함수의 흐름: - 폼에서 사용자 이름 추출 - 서버에 사용자의 계좌 데이터 요청 - 오류가 발생하면 처리 - 성공 시 계좌 데이터 저장 후 대시보드로 이동 > 🎯 **Async/Await 패턴**: `getAccount`가 비동기 함수이므로, `await`로 서버 응답까지 실행을 멈춰서 데이터가 undefined 상태로 진행하지 않게 합니다. #### 4단계: 데이터 저장 공간 만들기 앱에 한번 로드된 계좌 정보를 기억할 장소가 필요합니다. 앱의 단기 기억 같은 역할입니다. `app.js` 맨 위에 다음 라인을 추가하세요: ```javascript // 이것은 현재 사용자의 계정 데이터를 보관합니다 let account = null; ``` **이런 이유로 필요:** - 앱 어디서든 계좌 데이터에 접근 가능 - 초기값 `null`은 "아직 로그인 안 됨" 의미 - 로그인 또는 등록 성공 시 업데이트 - 단일 진실 출처(SoT, Single source of truth) 역할 - 로그인 사용자 혼동 방지 #### 5단계: 폼과 로그인 함수 연결하기 이제 로그인 함수를 HTML 폼과 연결합시다. 폼 태그를 다음과 같이 수정하세요: ```html
``` **이 변경점이 하는 일:** - 폼이 기본 동작(페이지 전체 새로고침)을 막음 - 대신 커스텀 자바스크립트 함수 실행 - 자연스러운 단일 페이지 앱 경험 유지 - 로그인 클릭 시 어떤 일이 일어날지 완전 제어 가능 #### 6단계: 등록 함수 개선하기 일관성을 위해 `register` 함수도 계좌 데이터를 저장하고 대시보드로 이동하도록 수정하세요: ```javascript // 이 줄들을 등록 함수의 마지막에 추가하세요 account = result; navigate('/dashboard'); ``` **이 개선점으로:** - 등록 후 대시보드로 원활한 전환 - 로그인과 등록 흐름 간 일관된 사용자 경험 제공 - 등록 성공 직후 즉시 계좌 데이터 접근 가능 #### 구현 테스트하기 ```mermaid flowchart TD A[사용자가 자격 증명 입력] --> B[로그인 함수 호출] B --> C[서버에서 계정 데이터 가져오기] C --> D{데이터를 성공적으로 받았나요?} D -->|예| E[계정 데이터를 전역에 저장] D -->|아니오| F[오류 메시지 표시] E --> G[대시보드로 이동] F --> H[사용자가 로그인 페이지에 머무름] ``` **테스트 방법:** 1. 새 계정을 만들어 문제 없는지 확인 2. 같은 계정으로 로그인 시도 3. 문제가 있으면 브라우저 콘솔(F12) 확인 4. 로그인 성공 시 대시보드 진입 여부 확인 문제가 있다면 당황하지 마세요! 대부분 오타나 API 서버 미기동 같은 사소한 문제입니다. #### 크로스 오리진 이슈 간단 설명 "포트가 다른데 웹 앱이 API 서버와 어떻게 통신하나요?" 궁금하실 겁니다. 모든 웹 개발자가 언젠가 마주치는 문제입니다. > 🔒 **교차 출처 보안**: 브라우저는 "동일 출처 정책"을 통해 허가받지 않은 도메인 간 통신을 차단합니다. 펜타곤 검문소처럼, 데이터 전송 전에 통신 권한을 검증합니다. > **우리 환경에서는:** - 웹 앱은 `localhost:3000` (개발 서버) - API 서버는 `localhost:5000` (백엔드 서버) - API 서버가 [CORS 헤더](https://developer.mozilla.org/docs/Web/HTTP/CORS)로 웹 앱의 통신을 명시적으로 허용함 이 설정은 프론트엔드와 백엔드가 보통 별도 서버로 운영되는 실제 개발 환경과 유사합니다. > 📚 **더 알아보기**: 종합적으로 API와 데이터 가져오기를 배우고 싶다면 이 [Microsoft Learn API 모듈](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon)을 참고하세요. ## HTML에서 데이터 시각화하기 이제 가져온 데이터를 DOM 조작을 통해 사용자에게 보여줍시다. 사진 암실에서 필름을 현상하듯, 눈에 보이지 않는 데이터를 눈에 띄고 상호작용할 수 있는 형태로 바꾸는 과정입니다. DOM 조작은 정적인 웹 페이지를 사용자 상호작용 및 서버 응답에 따라 콘텐츠를 업데이트하는 동적인 애플리케이션으로 변환하는 기술입니다. ### 작업에 맞는 도구 선택하기 JavaScript로 HTML을 업데이트할 때 사용할 수 있는 여러 가지 옵션이 있습니다. 이것들은 공구 상자의 다양한 도구처럼 특정 작업에 완벽하게 맞춰져 있습니다: | 메서드 | 적합한 용도 | 사용 시기 | 안전성 수준 | |--------|-------------|------------|--------------| | `textContent` | 사용자 데이터를 안전하게 표시 | 텍스트를 보여줄 때 언제든지 | ✅ 매우 안전 | | `createElement()` + `append()` | 복잡한 레이아웃 구성 | 새 섹션/목록 생성 시 | ✅ 확실히 안전 | | `innerHTML` | HTML 콘텐츠 설정 | ⚠️ 가능하면 피하기 | ❌ 위험함 | #### 텍스트를 안전하게 표시하는 방법: textContent [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) 속성은 사용자 데이터를 표시할 때 가장 좋은 친구입니다. 웹 페이지의 경비원과 같아서 위험한 내용이 통과하지 못하게 막아줍니다: ```javascript // 텍스트를 업데이트하는 안전하고 신뢰할 수 있는 방법 const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **textContent의 장점:** - 모든 내용을 일반 텍스트로 처리(스크립트 실행 방지) - 기존 콘텐츠 자동 삭제 - 단순 텍스트 업데이트에 효율적 - 악성 콘텐츠에 대한 내장 보안 제공 #### 동적 HTML 요소 생성하기 더 복잡한 콘텐츠의 경우 [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement)와 [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append) 메서드를 조합하세요: ```javascript // 새로운 요소를 안전하게 생성하는 방법 const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **이 방법 이해하기:** - 새로운 DOM 요소를 프로그래밍 방식으로 **생성** - 요소 속성과 콘텐츠를 완벽히 **통제** - 복잡하고 중첩된 요소 구조도 **허용** - 구조와 콘텐츠를 분리해 **보안 유지** > ⚠️ **보안 주의사항**: 많은 튜토리얼에서 [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML)를 등장시키지만, 이 방법은 내장된 스크립트를 실행할 수 있습니다. CERN의 보안 프로토콜이 무단 코드 실행을 막듯, `textContent`와 `createElement`를 사용하는 것이 더 안전한 대안입니다. > **innerHTML 위험성:** - 사용자 데이터 내 `