<!DOCTYPE html>
<html lang="KO">
<head>
<meta charset="UTF-8">
<title>computed</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
<div id="main">
<p>원본 문장 : "{{ sOriginalMessage }}"</p>
<p>대분자로 변환된 문장 : "{{ fnUpperCaseMsg }}"</p>
</div>
<script>
new Vue({
el:'#main',
// data는 머스태시 안에 넣을 값이 간달할 때 사용
data:{
sOriginalMessage: 'How are you?'
},
computed:{
// sOriginalMessage의 데이터를 모두 대문자로 변환
// 이때 데이터에 접근하기 위해 this 사용
fnUpperCaseMsg: function(){
return this.sOriginalMessage.toUpperCase();
}
}
})
</script>
</body>
</html>
* 머스태시 사용 : 문자열을 {{}} 로 감싸고 엘리먼트(P) 값 표시 - {{ sOriginalMessage }} : 문자열 변수 대체
- icons : 스플래시스크린에 표시할 아이콘 이미지 - 128dpi에 가까운 이미지를 화면에 표시 > src : 이미지 경로(절대/상대)
> sizes : 이미지 픽셀 크기
> type : 이미지 파일 유형
03-3 메인 화면 작성하기
* 010303 [DoIt!실습]index.html 파일 작성하기
- 0103/ex03_practice >index.html 파일 작성하기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<!-- PWA 매니페스트 파일 연결, 상태 표시줄 색상을 흰색으로 변경 -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#ffffff">
<!-- 모바일 기기 뷰포트, 브라우저 주소 표시줄의 파비콘 설정 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <meta name="viewport" content="width=device-width, user-scalabe=no"> -->
<link rel="shortcut icon" href="images/icons/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="images/icons/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/favicon-32x32.png">
<title>안녕하세요! PWA by JS</title>
<style>
html, body{
/* html, body 모두 높이를 100%로 고정시켜야 플렉스 박스 동작 */
heigth: 100%;
background-color: #F3A530;
color: #ffffff;
}
/* 화면에 출력될 div엘리먼트를 화면 가운데 배치하도록 설정 */
.container{
height: 100%; /* 높이를 100%로 고정 ㅇ*/
display: flex; /* 플렉스 박스로 배치 */
align-items: center; /* 상하 가운데 정렬 */
justify-content: center; /* 좌우 가운데 정렬 */
}
</style>
</head>
<body>
<div class="container">
<h1>안녕하세요!</h1>
<img src="/images/hello-pwa.png" alt=""/>
<p>by JK</p>
</div>
<!-- 서비스워커 등록 -->
<script>
if('serviceWorker' in navigator){
navigator.serviceWorker
.register('/service_worker.js')
.then(function () {
console.log("서비스워크가 등록됨!");
})
}
</script>
</body>
</html>
* 언어설정 : 설정하지 않으면 실행할 때마다 ‘디른 언어로 번역하시 겠습니 까?’라고 질문
- <html lang="ko">
* 뷰포트설정 - 모바일 브라우저로 PC 사이트 접속시 작게 보이는 현상 방지
- content의 어트리뷰트 값 설정
> width-=device-width : 몹바일 기기의 해상도로 너비값 자동 설정
> initial-scale=1 : 모바일 기기에서 확대하거나 축소 기능 허용
> user-scalable=no : 모바일 기기에서 확대하거나 축소 기능 제한
* 플렉스 박스 설정 : 화면에 표시할 글자와 이미지를 배치할 레이아웃 - 플렉스 박스(flex box) - 모바일 기기의 크기를 자동으로 고려한 최적의 레이아웃 표현 > html, body 높이를 100%로 고정 필요
* 화면 요소 배치
- 화면에 실제 출력되는 내용 : container 클래스를 선택자로 지정한 div 엘리먼
> div 엘리먼트를 화면 가운데 배치하도록 지정
* 서비스워커 등록 : if('serviceWorker' in navigator)...
- navigator.serviceworker에는 ServiceWorkerContainer라는 읽기 전용의 객체가 반환 > 이 객체 안에 있는 register() 메서드를 이용해
모바일 브라우저가 서비스 워커를 지원하는지 확인한 후
> 서비스 워커를 등록
- then() 메서드는 register() 메서드의 실행이 성공하면 실행
> register() 메서드가 실행되면 콘솔에 성공 메시지를 줄력
03-4 서비스 워커 만들고 실행하기
* 010304 [DoIt!실습]캐시를 관리하는 서비스 워커 만들기
- 0103/ex03_practice >캐시를 관리하는 서비스 워커 만들기
* 서비스워커
- 브라우저와분리되어 독립해서 실행 가능
- 캐시, 푸시 알림 , 웹 API와 연동 등 다양한 기능을 별도로 수행
> 실습대상 : 필요한 파일을 캐시하여 메모리에 저장
* service_worker.Js 파일 생성
// 캐시 제목과 캐시할 파일 선언
const sCacheName = 'hello-pwa'; // 캐시 제목 선언
const aFilesToCache = [ // 캐시할 파일 선언
'./', './index.html', './manifest.json', './images/hello-pwa.png'
];
// 서비스 워커 설치하고 캐시파일 저장
self.addEventListener('install', pEvent => {
console.log("서비스 워커 설치함!");
pEvent.waitUntil(
caches.open(sCacheName)
.then(pCache => {
console.log("파일을 캐시에 저장함!");
return pCache.addAll(aFilesToCache);
})
);
});
//고유 번호를 할당받은 서비스 워커 작동
self.addEventListener('activate', pEvent => {
console.log("서비스워커 동작 시작됨!");
});
// 데이터 요청을 받으면 네트워크 또는 캐시에서 찾아 반환
self.addEventListener('fetch', pEvent => {
pEvent.respondWith(
caches.match(pEvent.request)
.then(response => {
if(!response){
console.log("네트워크에서 데이터 요청!", pEvent.request);
return fetch(pEvent.request);
}
console.log("캐시에서 데이터 요청!", pEvent.request);
return response;
}).catch(err => console.log(err))
);
});
- 오프라인 동작을 위해 서비스워커가 캐시에 저장하
> 캐시 제목과 캐시할 파일 선언 : 캐시 제목은 서비스워커와 구분되도록 고유이름 사용
* 서비스 워커 설치 및 캐시 파일 저장 - install 이벤트
- 서비스 워커의 생애 주기
> [install -activate -fetch] 순서로 이벤트 발생
* install : PWA를 설치 - 서비스워커의 첫번째 생애주기
> 설치전 : installing
> 설치 후 대기 :installed
* 서비스 워커가 제대로 설치되면 pEvent에 포함된 waitUntil() 함수를이용해 비로소 캐시에 필요한 파일 저장 * 서비스 워커가준비될 때 캐시를 저장하는 것 : ‘프리 캐시(pre-cache)’ - waitUntil() 함수는 installing 상태에서 프리 캐시가 완료될 때까지 대기
- activate : 설치후 상태
* activate 이벤트 : 서비스 워커의 두 번째 생애 주기
- 서비스 워커 업데이트 : 서비스 워커가 고유한ID를 발급받아 브라우저에 성공적으로 등록되면 동작 > 웹 브라우저 의 개발자 도구에서 확인
* 서비스 워커 설치 후 - 업데이트 등의 이유로 캐시 제목 변경 : install 이벤트 처음부터 다시 발생
- 테스트할 때는 'Update on reload’ 체크 박스를 선텍 : 매번 캐시제목 변경 서비스워커등록 생략
> 새로 고침 : 기존 서비스 워커 ID 제거 -> 새로운 ID를 부여 : install 이벤트부터 새로 시작
* 앱 업데이트 시 서비스 워커 새로운 내용으로 교체 : activate 이벤트
- 활성중 : activating
- 활성후 : activated
* 서비스 워커의 내용 업데이트 : - 먼저 캐시 제목과 프리 캐시 파일 변경 : 새로운 서비스 워커ID로 새로운 캐시 내용이 설치 목적
- activating 상태에서 waitUntil() 함수를 사용해서 기존 캐시 제거 : 코드작성
* 오프라인 전환할 때 동작 - fetch 이벤트
- 서비스 워커의 마지막 생애 주기는 fetch 이벤트로부터 시작 > 이벤트가 발생 사례 : 브라우지 〈새로 고침〉 * 동작
- 온라인 상태 : 서버에서 조회
- 오프라인 상태 : 캐시에서 조
* 서비스 워커의 유무에 따른 fetch 이벤트 처리
- 1 : 온라인 - 서비스워커 없음
- 2 : 오프라인 - 서비스워커 있
* PWA 오프라인 동작 구조 : fetch 이 벤트 발생 후 (pEvent에 있는)respondWidth() 함수 활용
- respondWidth() 함수 : 결과값 준비 까지 네트워크 요청 일시 정지
> 캐시, 모바일 기기에 임시로 저장한 데이터로 처리
* service_worker.js
- 오프라인 처리 : PWA가 실행되면 캐시에서 가져올 데이터를 caches.match() 함수로 캐시 저장소에서 검색
> 발견된 캐시(response)를 반환하도록 코드를 작성
- 캐시를 발견하지 못하면 fetch(pEvent.request)를 통해 네트워크에 요청
* 실행 실습 후 서비스워커 삭제 : 다음실습 준
* 서비스 워커의 주요 이벤트 복습하기
- install : 서비스워커가 설치될 때 실행(앱설치) - 캐시파일 저장
- activate : 서비스워커 설치 후, 업데이트 역할 - 기존캐시 제거
- fetch : 서비스워커 설치 후 다음 실행 때 실제 작업할 내용 작성 - 브라우저 서버요청시 오프라인 상태면 캐시파일 조회
- document.querySelector('#clickme').innerHTML = '안녕하세요'; > id값이 #clickme 엘리먼트의 내용 설정
- 덧샘결과 반환 함수의 선언식 표현 : fnSayHello
- 뺄셈결과 반환 함수의 함수 표현식 : fnMinusNumbers
* 실행
* [눌러주세요!] 버튼클릭 - 안녕하세요 표시문구 변경
* 3+5 버튼 클릭
* 3-5 버튼 클릭
02-2 변수 선언
var : 함수 스코프(function scope)만 지원, 블록 스코프(block scope) 지원 안함.
- 스코프 : 유효범위
* 함수 스코프 예시
- 함수에서 선언한 변수를 함수 밖(전역)에서 사용하면 오류발생
<script>
fnMyFunction();
function fnMyFunction(){
var apple = 5;
console.log(apple); // 5 출력
}
console.log(apple); //오류! Uncaught ReferenceError: apple is not defined
</script>
* 블록 스코프 예시
- var 키워드는 블록스코프 지원하지 않음 : 블록내부 var 변수는 외부사용 가능
<script>
var apple = 3;
{
var apple = 5; // var 선언 변수는 블록 스코프가 아니어서 재선언 가능
console.log(apple); // 5 출력
}
console.log(apple); // 3 대신 5 출력
</script>
- var 변수 : 전역변수
- 블록내 var 변수 : 동일한 이름으로 다른값 할당 (재할당)
- 블록내에서 재할당한 변수는 블록에만 유효한 것이 아닌 전역변수 값을 변경함 > var는 함수스코브에서만 유효성이 제한됨
* var 특징
- 블록스코프 미지원 : 함수외부 선언 변수는 전역변수 역할
- 같은 이름으로 중복선언 가능 : 기존에 선언한 변수를 덮어씀.
> 블록으로 명시적인 스코프 적용을 의도하는 실수 등 코드 가독성을 저해하고, 디버그에 어려움 발생.
: var 대안으로 let, const 사용
* let, const 키워드 : 블록 스코프 지원
- 블록외부에서 사용 불가
- 중복선언 제한
> let : 스코프 적용한 변수선언 - 가변(mutable)
> const : 스코프 적용한 상수선언 - 불변(immutable)
<script>
{
let apple = 3;
console.log(apple); // 3 출력
apple = 5; // let 변수 재할당 가능
console.log(apple); // 5 출력
let apple = 7; // let 변수 재선언 불가 - Identifier 'apple' has already been declared
const car; // const 선언, 할당 동시 설정 필요
const carname = "하이브리드카";
console.log(carname); // '하이브리드카' 출력
carname = "디젤카"; // const 재할당 불가 - 불변성
}
console.log(apple); // 블록내 let 변수 참조 불가
console.log(carname); // 블록내 const 변수 참조 불가
</script>
* 모던 자바스크립트에서 변수 사용 3원칙
- const 우선 사용 : 변경대상이 아닌 변수
- let 차선 사용 : 변경사용 변수
- var 불가피한 경우 사용 : 전역변수 필요시 최상위 수준에서 사용
* 하나만 더 - console.log() 함수와 템플릿 리터럴로 디버깅
- 디버깅시 alert() 함수사용으로 브라우저에서 팝업창을 이용하여 결과 값 확인
- 템플릿 리터럴 : 역따옴표 [`] - 문자열 안에 표현식 적용 가능 > 달러($), 중괄호({}) 를 이용하여 여러줄 사용
- 디버그 시 브라우저 개발자도구의 콘솔 창 활용
<script>
let apple = 3;
const carname = "하이브리드카";
console.log('사과: '+apple+', 자동차: '+carname); // 이전 스타일
console.log(`사과: ${apple}, 자동차: ${carname}`); // ES6 스타일(템플릿 리터럴)
</script>
02-3 화살표 함수
* ES6 문법에서 추가된 새로운 함수표현 기존 : funtion(){ return ... } 개선 : function, return 키워드 생략, => (fat arrow) 기호로 직관적인 표현
* 화살표 함수 예
// 전통 함수 표현식
const fnPlusNumbers = function(pNum1, pNum2){
return pNum1 + pNum2;
}
// 화살표함수 표현식
const fnPlusNumbers2 = (pNum1, pNum2) => {
return pNum1 + pNum2;
}
// 화살표함수 표현식 - return 생략 (실행문 하나인 경우 불필요햔 중괄호도 함께 생략)
const fnPlusNumbers3 = (pNum1, pNum2) => pNum1+pNum2;
// 매개변수 1개인 경우 소괄호 생략
const fnPlusNumbers4 = pNum => pNum+1;
// 매개변수 0개인 경우 () 만 표시
const fnPlusNumbers5 = () => '안녕하세요.';
- 버튼을 만들고 onclick 이벤트 핸들러에 각 함수 호츨문 등록. > onclick=" ~ " - 매개변수를 두 개 입력받아 덧셈 후 반환하는 함수 선언. > fnPlusNumbers1; - function 키워드를 생략한 화살표 함수 선언. > fnPlusNumbers2;
- 화살표 함수에서 return 키워드 생략. (자동으로 결과값 반환) 한줄이므로 중괄호 {}도 생략. > fnPlusNumbers3; -화살표 함수에서 매개변수가 하나일 때는 소괄호 ()도 생략. > fnPlusNumbers4; -화살표 함수에서 매개변수가 없을 때는 반드시 소괄호 ()를 사용. > fnSayHello();
* 실행결과
02-4 모듈 내보내기와 가져오기
모듈(module) :코드를 관리하는 가장 작은 단위 - 코드 관리와 재활용에 용이 : 소스 복잡 > 변수, 함수 충돌 증가 - ES6부터는 import, export 문 추가 : 모듈을 이용한 협업 개선 - 모듈 내보내는 방식 > 여러 값을 공유하는 ‘이름으로(named) 내보내기’
> 기본값 하나만 공유하는 ‘기본으로(default) 내보내기’ 방식
* 이름으로 내보내고 가져오기 - 소스 파일에 선언된 식별자를 복수로 선택 공유 > export 키워드 작성 > 공유하고 싶은 식별자를 쉼표로 나열 : 중괄호 {}로 감싸기 (식별자 : 변수, 상수, 함수, 클래스 등을 선언한 이름)
//외부와 공유할 conHello 상수 와 fnPlusNumbers 함수 선언
const conHello = '안녕하세요';
const fnPlusNubers = (pNum1, pNum2) => pNum1 + pNum2;
//외부 사용을 위해 내보내기
export {conHello, fnPlusNubers};
- export 키워드 다음에 작성한 식별자 이름 : 모듈을 사용할 파일에서 import 키워드로 가져올 식 별자 이름과 일 > {conHello, fnPlusNubers}
- 이름을 다르게 해서 내보내기 : 내보낼 식별자 이름 뒤에 as 키워드를 이용하 새 이름 지정
> 외부 파일에서 새 이름으로 참조 가능
// js 파일에서 내보낸 식별자를 가져와 conHello, fnPlusNumbers 객체에 저장
import {conHello, fnPlusNubers} from './04-1_ibrary_named.js';
console.log("conHello : ", conHello);
console.log("fnPlusNubers : ", fnPlusNubers(1,2));
// js 파일에서 내보닌 모든 식별자를 가져와 myLibrary 객체에 저장
import * as myLibrary from './04-1_ibrary_named.js';
console.log("myLibrary.conHello : ", myLibrary.conHello);
console.log("myLibrary.fnPlusNubers : ", myLibrary.fnPlusNubers(1,2));
- 내보낸 모듈을 다른 파일에서 가져와 쓰려면 import 키워드를 사용
> from 키워드 다음에 해당식별자 가 선언된 모듈. 즉 소스 파일의 경로 선언 : './04-1_ibrary_named.js' - 모든 식별자 호출 : 별표(*) 선언
> as 키워드와 함께 객체명을 지정 : as myLibrary
> 객체에 점(.)을사용해서 식별자를사용 : myLibrary.conHello
* 기본으로 내보내고 가져오기
- 소스 파일에 있는 식별자 중 하나를 대표로 지정 > export 키워드 다음에 default 키워드를 추가
- 모듈 하나 당 하나의 함수나 클래스만 공유 > var, let, const는 사용 불가
/* HOST : 04-4_default.js*/
// 외부와 공유할 함수 : 기본(default) 내보내기는 모듈 하나에 하나의 함수나 클래스 만 가능 (var, let, const 불가)
const fnPlusNubers = (pNum1, pNum2) => pNum1 + pNum2;
// 기본 모듈 내보내기
export default fnPlusNubers;
/* GUEST */
//외부에서 기본 모듈로 내보내기 후 이름을 변경하여 사용
import fnMyFunction from './04-4_default.js';
console.log("fnMyFunction : ", fnMyFunction(1,2));
- export default로 내보낸 식별자를 가져올 때 이름 변경가능 > library_default.js 소스에서 내보낸 함수 : fnPlusNumbers() > 가져올 때 : fnMyFunction() 이름변경
[DoIt!실습] 모듈 export, import 실습하기
- 0203. : Doit 실습 - 화살표 함수 실습하기
* 코드
- \0201\ex02-03\library_named.js
//외부와 공유할 conHello 상수 와 fnPlusNumbers 함수 선언
const conHello = '안녕하세요';
const fnPlusNubers = (pNum1, pNum2) => pNum1 + pNum2;
//외부 사용을 위해 내보내기
export {conHello, fnPlusNubers as fnPlusNumbers};
// 간소화 방법
// export const conHello = '안녕하세요!';
// export const fnPlusNumbers = (pNuml, pNum2) => pNuml + pNum2;
- \0201\ex02-03\library_default.js
// 외부와 공유할 함수 : 기본(default) 내보내기는 모듈 하나에 하나의 함수나 클래스 만 가능 (var, let, const 불가)
const fnPlusNubers = (pNum1, pNum2) => pNum1 + pNum2;
// 기본 모듈 내보내기
export default fnPlusNubers;
- \0201\ex02-03\main.js
// js 파일에서 내보낸 식별자를 가져와 conHello, fnPlusNumbers 객체에 저장
import {conHello, fnPlusNumbers} from './library_named.js';
console.log(conHello, "이름으로 내보내기입니다.");
console.log("[fnPlusNubers] 1 + 2 = : ", fnPlusNumbers(1,2));
// js 파일에서 내보닌 모든 식별자를 가져와 myLibrary 객체에 저장 : *
import * as myLibrary from './library_named.js';
console.log(myLibrary.conHello, "*을 사용한 이름으로 내보내기 입니다.");
console.log("[myLibrary.fnPlusNubers] 3 + 3 = : ", myLibrary.fnPlusNumbers(3,4));
// default export를 이용한 기본 내보내기
import fnMyfunction from './library_default.js';
console.log("안녕하세요! 기본으로 내보내기입니다.");
console.log("[fnMyFunction] 5 + 6 = ", fnMyfunction(5,6));
- \0201\ex02-03\main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>module-import2</title>
</head>
<body>
<p>콘솔 창을 열어서 결과를 확인하세요!</p>
<!-- 모듈 사용 시 type 어트리뷰트값으로 "module"을 지정합니다. -->
<script type="module" src="main.js"></script>
</body>
</html>
- HTML 문서에서 import, export 문을 사용한 모듈 파일을 가져올 때 : 가져온 파일을 모듈로 인식 > <script> 엘리먼트의 type 어트리뷰트값을 반드시 "module"로 지정
> 모듈이 되면 import, export 구문을 사용, 상수 선언은 모듈 스코프로 관리
> 모듈 파일에 선언된 내용은 외부에서 사용할 수 없고 반드시 그 안에서 만 동작
02-5 콜백 함수와 비동기 처리 방식
Promise(ES6에 새롭게 추가)이해를 위한 중간 개념정리
- 동기(synchronous) 처리 방식 : 순서대로 실행 - 일렬
- 비동기(asynchronous) 처리 방식 : 선행작업까지 기다리지 않고 실행 - 병렬
* 동기와 비동기 처리 개념 이해하기
구분
동기 처리 방식
비동기 처리 방식
실행 순서
• 모든 코드가 위에서 아래 순서로 실행
• 코드들이독립적으로실행
실행 대기
• 명령 요청 후 결과 응답시 까지 대기 - 반환 결과를 계속 기다리므로 시간 지연이 큼
• 명령을 요청 후 결과와 상관없이 다음작업 실행 - 요청결과 응답시 별도 응답처리
* 콜백 함수로 비동기 처리 구현하기
- 보통 함수는값을매개변수로 전달받는데 > 자바스크립트에서 함수는 객체로 취급 - 함수의 매개 변수로 함수 전달 가능
// 콜백함수로 사용되는 함수
function fnFunctionA(pNum){
return pNum;
}
// 콜백함수를 호출할 함수
function fnFunctionB(pFunc){
console.log("fnFunctionB param fnFunctionA : "+pFunc(20));
}
fnFunctionB(fnFunctionA); // fnFunctionB 함수에 매개변수로 fnFunctionA 함수 전달
- 매개변수로 전달된 함수 : 콜백함수 - fnFunctionA
> 콜백함수는 다른 함수에 독립적으로 실행됨 : 비동기처리 구현시 사용
- 두번째로 전달받은 fnFunctionA() 함수가 fnFunction() 함수에서 fnCallBack 매개변수에 담겨 실행딜 때 비동기로 실됨.
* 단순하게 표현한 콜백함수 구조 : 콜백함수 선언과 정의 함께 작성 > ES6 부터 Promise 방식으로 개
// 매개변수 전달 시 함수 선언과 정의 함께 작성 : 비동기처리의 완료 로직 적용
fnFunctionB("콜박함수실행!", function fnFunctionA(pNumA){
console.log("pNumA : " + pNumA);
});
// 콜백함수를 호출할 함수
function fnFunctionB(pNumB, fnCallBack){
fnCallBack(pNumB);
}
* 콜백으로 호출한 함수가 실행될때 비동기로 처리되는것이라고?
- 함수 호출구조로 비동기처리가 된다?
* 콜백 비동기 호출 예 - 비동기 처리 함수 : 독립적으로 실행되고 결과도 별도로 처리하는 함수
//콜백함수 실행
fnMsg(fnHello);
// 콜백함수 : 3 실행 후 실행
function fnMsg(fnCallback){
console.log("1"); // first
fnCallback(); // 매개변수로 전달받은 함수실행 : 비동기
console.log("3"); // second
}
function fnHello(){
setTimeout(() => console.log("2"), 1000); // third - 2초 후 실행에 따라 지연출력
}
[DoIt!실습]콜백 함수의 비동기 처리 방식으로 상품 배송 서비스 구현하기
- 0204. : Doit 실습 - 비동기 처리 방식 비스 구현하기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>callback</title>
</head>
<body>
<button onclick="
/*콜백 함수를 순서대로 실행하면 콜백 지옥의 문제점 발생!*/
fnProductReady(1, 2000, function(pRet){
console.log(pRet);
fnProductReady(2, 1000, function(pRet){
console.log(pRet);
fnProductReady(3, 500, function(pRet){
console.log(pRet);
console.log('가독성이 낮고 유지,보수하기 어려운 콜백 지옥 발생!');
})
})
})
">콜백 함수 실행!</button>
<script>
//콜백 함수 생성
function fnProductReady(pNum, pTime, fnCallback){
setTimeout(() => {
console.log(pNum);
fnCallback("상품이 성공적으로 배송되었습니다");
}, pTime); // 타이머로 가상의 네트워크 지연 상황 연출
}
</script>
</body>
</html>
- 버튼클릭 : 콜백함수를 매개변수로 갖는 함수를 호출하며 자신 함수를 콜백함수로 전달 (중첩선언) > 콜백함수 2초, 1초, 0.5초 소요메서드 호출
> 지연시간 이후 다음 콜백함수 호출
- 콜백함수 : fnProductReady()
>매개변수 3개 : 실행번소, 타이머지연시간, 콜백함수
> 실행번호, 메시지 표시
- 중첩 선언으로 콜백지옥 문제점 확인
- 타이머를 이용한 시간지연 효과 적용 : setTimeout(function, milliseconds); > function : 타이머 종료 후 실행될 함수
> milliseconds : 타이머 동작시간 (ms - 1초 = 1000ms)
* 콜백 함수의 한계와 Promise
- 콜백함수 : 유지보수, 가독성 저하
> 콜백지옥 구성 : 중첩 코드 구성에서 발생
> 반환값 처리 문제 : 콜백함수 종료 후 반환값 처리시점이 분리됨 - 부적응
- 대안 : Promise
02-6 JSON과 Fetch API
* Fetch API를 이용해 서버에서 JSON 데이 터를 가져올 때 Promise를 사용하면 얼마나 편리한지 확인
* JSON 이해하기 - JSON(JavaScript object notation)
- 자바스크립트에서 데이터를 저장하고 교환할 때 사용하는 정형화된 텍스트 형식 > 대부분의 프로그래밍 언어에서 서버와 브라우저 간에 데이 터를 주고받는 형식으로 많이 사용
- 시작과끝을중괄호({))로 지정하고, 각항목은 ‘키:값’ 형식으로 묶어서 표현, 각 항목은 콤마(,)로 구분 > JSON의 원본값은 텍스트 - 객체로 바꾸는 작업 : JSON.parse() > JSON 데이터가 담긴 자바스크립트 객체 - 텍스트로 변환 : JSON.stringify()
특정 환경에서만 발생하는 크롬 브라우저 확장프로그램 오류인것 같은데, 해결이 안된다. - 오류이후 처리는 정상 진행되니... 일단패스. Uncaught TypeError: Cannot read properties of undefined (reading 'browserObject') at webcontent.js:1:266 at webcontent.js:2:463 at webcontent.js:2:467 webcontent.js:1
실행결과
- 텍스트 형태 출력 : {"name":"홍길동","age":35,"email":"test1@naver.com"}
- 객체형태 출력 : Object - 확장하면 상세정보 확인 가능
* Fetch API 이해하기
- 웹 API에서 제공 : 원격 서버에서 제공하는 데이터의 CRUD(create, read, update, delete) 작업 지원 > HTTP Request, Response의 Post, Get, Put, Delete 메서드로관리 (셋째마당) - 테스트 원격 웹 서비스 : JSONPlaceholder 웹사이트 이용 - https://jsonplaceholder.typicode.com/
(JSON 형태의 더미 데이터 제공 : 포스트, 댓글, 사진 앨범, To-Do 리스트, 사용자 등)
- fetch() 함수 : 매개변수 path에 주소 전달 - 서버 응답 정보를 Promise 객체로 반환
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>promise</title>
</head>
<body>
<button onclick="
// JSON 로컬 텍스트 표시
fetch('./data.json')
.then(pResponse => pResponse.text())
.then(pJSON => console.log(pJSON))
;
">JSON 로컬 읽기</button>
<button onclick="
// JSON 원격 텍스트 표시
fetch('https://cors-anywhere.herokuapp.com/'
+ 'https://jsonplaceholder.typicode.com/posts/1')
.then(pResponse => pResponse.text())
.then(pJSON => console.log(pJSON));
">JSON 원격 읽기</button>
</body>
</html>
- fetch() 함수로 로컬에 있는 data.json 파일을 읽어서 Promise 객체로 pResponse 매개변수에 저장 > JSON값을 텍스트로 변경해 Promise로 반환하는 text() 함수 이용 - <JSON 원격 읽기> 버튼를 클릭하면 원격(JSONPlaceholder 사이트)에서 제공하는 포스트 목록에서 첫 번째 항목 조회 > fetch() 함수의 매개변수에 'https://jsonplaceholder.typicode.com/posts/1' 경로를 전달
*JSONPlaceholder를 fetch()함수로 바로 접근하면 CORS 오류가 발생 - CORS(cross origin resource sharing) : ‘교차 줄처 리소스 공유’ - 보안을 위해 같은 도메인의 자료만 접근하도록 허용하는 정책 > 테스트 목적 대응조치 : CORS 헤더 추가 : 접속할 주소 앞에 ‘https://cors-anywhere.herokuapp.com/’ 포함
GET https://cors-anywhere.herokuapp.com/https://jsonplaceholder.typicode.com/posts/1 403 (Forbidden) - promise_getjson.html:24 onclick @ promise_getjson.html:24
See /corsdemo for more info - promise_getjson.html:27
- 최초 접속시 접속승인 버튼 클릭 > 콜백 함수를 사용하지 않아도 Promise로 원격 데이터를 읽고 표시
02-7 Promise와 비동기 처리 방식
* Promise란?
- 비동기 처리 방식으로 실행된 결과의 성공과실패를관리하는 객체
물건 거래를 예로 한 경우의 수 • 물건을 주는경우 - 약속이행(성공,resolved) • 물건을 주지 못하는 경우 — 약속 불이행(실패,rejected) • 물건을주지 못하고 지연된 경우- 약속 이행 지연(대기,pending)
- Promise : 약속을 생성하는 과정과 최종 결과로 나누어 구성
• 실행 중 결과 기다림 (pending):약속은 아직 이행되거나 거절되지 않았고 지연 상태 - 요청한 실행의 반환을 계속 대기 : 작성할 코드 없음 • 요청한 실행이 성공함 (resolved):약속을 지키기는 데 문제가 없는 경우 - 요청 실행 성공한 경우 : 필요한 코드 작성 • 요청한 실행이 실패함 (rejected):약속을 지키는 데 문제가 생겨 거절하는 경우 - 요청했던 실행이 실패한 경우 : 필요한 코드 작성
- 약속의 실행 최종 결과 구분
• 약속 지킴 최종 성공(fulfilled):약속이 지켜져서 물건을 성공적으로 받은 경우 - 코드상에서는 성공적으로 원하는 결과를 반환받은 경우 • 약속 못 지킴 최종 실패(unfulfilled):약속이 거절되어서 물건을 못 받게 되어 그다음 대책 실행 - 코드상에서는 요청이 실패해서 오류 처리를 해야 하는 경우
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>promise</title>
</head>
<body>
<script>
// 가상의 서버 데이터 요청 및 그 결과 랜덤값 반환
function RequestData(){
return Math.random() > 0.5;
}
// 약속을 생성하는 부분
const oProductReady = new Promise(function (fnResolve, fnReject){
//실행 중 결과 기다림(pending)
let bStatus = RequestData();
if(bStatus){
// 요청한 실행이 성공함(resolved)
fnResolve('상품이 성공적으로 배송되었습니다.');
}else{
// 실행 중 결과 기다림(pending)
fnReject('죄송합니다. 상품이 아직 준비되지 못했습니다.');
}
});
// 약속의 실행 최종 결과
oProductReady.then(function (pResult){
// 약속 지킴 최종 성공(fulfilled)
console.log(pResult);
}, function (pErrMsg){
// 약속 못 지킴 최종 실패(unfulfilled)
console.log(pErrMsg);
});
</script>
</body>
</html>
- 서버요청결과를 고려하여 랜덤함수의 결과로 응답결과를 분리하여 결과 확인 > Math.random() > 0.5
- 비동기 처리를 위한 Promise 생성부분 : const oProductReady
> 상품 준비 요청 결과(RequestData())에 따라 fnResolve()와 fnReject() 함수 호출
- Promise 실행결과에 따른 응답결과 처리
> then()메서드에서 콜백 함수와 비슷한기능 처리 정상처리 : 콘솔결과 성공표시 - 첫번째 파라미터로 전달된 함수 실행 실패처리 : 콘솔결과 실패표시 - 두번째 파라미터로 전달된 함수 실행
[DoIt!실습]하나의 Promise 실습하기
- 0206. : Doit 실습 -하나의 Promise 실습하기
# 0201/ex02-06/promise_resolve_reject.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Promise</title>
</head>
<body>
<button onclick="
// Promise 실행
console.log('Promise 시작!');
let bStatus = Math.random() > 0.5;
fnCreatePromise(bStatus)
// 비동기 실행으로 결과를 알려 줌
.then(pResult => console.log(pResult))
.catch(pErrMsg => console.error(pErrMsg));
console.log('Promise 종료!');
">상품 배송 시작!</button>
<script>
// Promise 생성
function fnCreatePromise(pStatus) {
return new Promise((fnResolve, fnReject) => {
// 상품 준비를 확인하는 비동기 함수를 실행했다고 가정함
setTimeout(() => {
// 상품 준비 결과는 랜덤정보 true, false로 가정함
if(pStatus) fnResolve('상품이 성공적으로 배송되었습니다.');
else fnReject('죄송합니다. 상품이 아직 준비되지 못했습니다.');
}, 3000);
});
}
</script>
</body>
</html>
- 외부 네트워크에 접속하진 않지만 비슷한상황을 연출하기 위해 타이머로 시간 지연 > setTimeout(() => ...)
- 결과의 성공 여부는 랜덤 함수로 예측할 수 없게 설정 > let bStatus = Math.random() > 0.5; > Promise를 생성하는 fnCreatePromiseQ 함수 호출 때 전달
- 시나리오 별 예상 응답 - 콘솔 메시지 순서
> 동기실행 시 예상 시나리오 : Promise 시작! - [결과] -Promise 종료!
> 비동기실행 시 예상 시나리오 :Promise시작! -Promise종료!- (3초 후)[결과]
- Promise를 실행한 후
> 성공하면 then() 메서드를 호출
> 실패하면 catch() 메서드를 호출
- fnCreatePromise() 함수에서 Promise를 생성하고 결과를 응답
> pStatus 매개변수에 성공 여부를 판단 : (앞으로 일어날) 성공, 실패 별 처리함수 fnResolve()와 fnReject() 각각 작성
* Promise 여러 개 사용하기
- Promise 연결 사용 장점
• 콜백 지옥 문제점 해결: 비동기 처리 순서대로 처리 시, (콜백 함수처럼) 중첩 불필요 - 소스를 간결하고 직관적 으로 작성 •비동기 처리 완료 후 반환값 관리 쉬움 : 성공과 실패 모두 then()과 catch()에 전달된 함수를 통해 관리
* 예제 코드 실행결과 오류발생 - async await 사용 방법에 이유가 있을듯... Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules (at 08-1_await.html:20:18) - 08-1_await.html:20
1. 서비스워커 - 웹브라우저 내부에서 웹페이지와 독립적으로 백그라운드 실행 - 브라우저와 서버 사이에서 상태 모니터링, 푸시알림 지원
- 오프라인 동작 (인터넷 연결상태와 독립된 서비스 제공)
2. 웹앱 매니페스트 - 앱 소개정보(메타데이터) 제공 (JSON) : manifest.json - 매니페스트 : 앱에서 사용하던 기능 - 브라우저에 정보제공
3. HTTPS (hypertext fransfer protocol over secure soket layer) - 보안을 강화한 웹 통신 규약 : 네트워크 영역에서 통신정보가 노출되어도 보안유지 (전자상거래 등 보안이 중요한 서비스에 적합) > 서비스워커를 이용한 PWA 배포에 필수 > PWA 성능평가 프로그램(라이트하우스 - lighthouse)에 인증받기위한 의무요소 > 홈화면 추가 기능은 HTTPS에서만 지원
4. 푸시알림 - 사용자에 알림정보 제공 > PWA 푸시알림 동의 시 : 사이트 이탈 사용자, PWA가 종료된 백그라운드 상태에도 알림 가능
5. 홈화면 추가(add to home screen - 모바일) - 옴니박스(omnibox - 데스크톱) : 설치 (즐겨찾기, 바로가기 아님)
- 웹 브라우저 사용 시 PWA 설치 안내 > 운영체제에서 앱으로 인식 - 홈화면 추가 제안 조건 : > HTTPS 접속 : PWA 호스팅 의무요소
> 매니페스트 등록 : short_name, name, icons(192px X 512px), start_url, display( : fullscreen || standalone || minimal-ui) >서비스 워커 설치 : 브라우저에 서비스워커 설치 의무
> PWA 설치여부 : 해당 PWA 서비스가 이전에 설치되지 않은 상태 - 설치된 아이콘은 네이티브 앱과 동일함
6. 웹 API
- javascript api 사용 : 웹사이트, 웹앱, PWA 사용 (네이티브 기능을 지원하기 시작 : 위치정보, 카메라 등)
* 스타벅스의 PWA
- 인터넷 접속 제한환경 극복 : 오프라인 지원
- 멀티플렛폼 범용 POS(point of sale) 시스템 구축
- 가볍고 빠른 반응속도
* PWA 사용자경험
- 온라인, 오프라인을 아우르는 신뢰성
- 네이티브 앱에 비해 쉬운 설치 지원 - HTTPS를 의무 적용한 보안성 - 알림을 통한 구독자 관리 - 멀티 플랫폼 지원
- 검색노출에 따른 확장성
- 최신 서비스 제공이 가능 신속성
- 네이티브 앱과 같이 HW 기능도 사용 가능
- 네이티브 앱 대비 빠른 배포, 실행, 반응속도
01-3 비주얼 스튜디오 코드 설치하기
* 회사 보안 이슈로 VsCode 사용 불가
- ecilpse로 대체해 보자. > 라이브서버 기능은 어떻게 대체하지...
회사에서도 함께 진행했으면 하여 eclipse에서 live server를 실행해볼까 싶어 찾아봤지만, 관련 기능을 제공하는 플러그인이 없는 듯 하다. eclipse 버전차이로 webclipse는 지원하지 않는 듯 하고, Tern Eclipse 로 대용하던게 있던것 같은데, javascirpt 관련 기능을 목적으로 쓰인 것이었던지... codemix는 또 뭐냐... 어지럽다. 일단 그냥 집에서 vsCode로 해보자. 보안팀을 졸라볼까 ㅋ