02-1 재활용할 수 있는 블록 함수
- ES6부터 스코프를 고려한 변수 관리기능 강화 및 변수에 함수를 저장하는 함수표현식 추가.
선언식(declaration) : 기존 선언방식
- function 뒤에 함수 이름을 적고 소괄호()안에 필요한 매개변수를 쉼표(,)로 구분해서 작성, 함수가 수행할 명 령을 중괄호 {}로 감싸서 구현
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>
/*
함수명(매개변수, ...){
// 명령
}
- 호출부와 함수의 연결(인터페이스) : 매개변수
*/
function fnSayHello(){
alert("기존 함수사용방식");
return;
}
</script>
<body>
<button onclick="fnSayHello()"> 눌러주세요 </button>
</body>
</html>
- 함수이름 선언 : fnSayHello
- alert : 자바스크립트 내장함수 : 문자열을 메시지 창에 표시
- return : 처리결과 응답 (생략가능 : undefined 반환)
- 매개변수 : 없을 시 () 로 생략 가능
- 버튼 클릭시 fnSayHello() 함수 호출 : onclick
- 버튼제목 : <button> 테그사이의 문자열이 버튼에 표시됨
> 이벤트 핸들러 : 키보드나 마우스 버튼을 눌러서 이벤트가 발생하면 자바스크립트 코드와 연결시키는 역할
표현식(expression) : ES6+ 함수 표현식
- 함수명 없이 선언한 후에 객체 변수에 저장 : 함수를 호출할 때는 변수명을 이용 (모던 자바스크립트)
/*
표현식 함수형 변수 선언
*/
const fnMinusNumbers = function(pNum1, pNum2){
return pNum1 - pNum2;
}
- fnMinusNumbers() 함수의 함수 표현식 선언 : 변수명 = 함수명
- 매개변수 2개를 입력받고 처리결과 반환
/*
[표현식]
변수 = function(매개변수, ...){
// 명령
}
*/
const fnMinusNumbers = function(pNum1, pNum2){
return pNum1 - pNum2;
}
<button onclick="console.log(fnMinusNumbers(3, 5))">3 - 5 = ?</button>
자바스크립트의 호이스팅
- 자바스크립트에서 함수 선언식은 호이스팅 (hoisting)이 적용됨
- 호이스팅 : 코드를 실행하기 전에 내부에서 변수와 함수의 위치를 맨 위로 옮겨 선언
> 장점 : 변수나 함수를 좀 더 유연하게 사용
> 단점 : 오류가 생기면 찾기 어렵다 - 선언식을 사용한 fnSayHello() 함수 / 표현식을 사용한 fnSayHello2() 함수
- 선언문 보다 호출문을 먼저 작성했을 때 호이스팅 영향
/*
[호이스팅]
*/
fnSayHello1();
function fnSayHello1(){
console.log("선언식 함수 호출");
}
fnSayHello2();
const fnSayHello2 = function(){
console.log("표현식 함수선언 변수 호출");
}
- 선언식 함수를 정의하면 호이스팅으로 선언하기 전 코드에서 함수를 사용할 수 있음
- 표현식 함수선언 변수는 호이스팅 대상이 아니기 때문에 선언하기 전 코드에서 변수(함수)를 사용할 경우 오류발생
[DoIt!실습] 함수 선언식과 표현식 실습하기
- 0201. : Doit 실습 - 함수 선언식과 표현식 실습하기
- \0201\ex02-01\function.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Page Title</title>
</head>
<body>
<button onclick="console.log(fnSayHello())" id="clickme">눌러주세요!</button>
<br/>
<button onclick="alert(fnPlusNumbers(3,5))">3+5 = ?</button>
<button onclick="console.log(fnMinusNumbers(3,5))">3 - 5 = ?</button>
<script>
function fnSayHello(){
document.querySelector('#clickme').innerHTML = '안녕하세요';
}
function fnPlusNumbers(pNum1, pNum2){
return pNum1 + pNum2;
}
const fnMinusNumbers = function (pNum1, pNum2){
return pNum1 - pNum2;
}
</script>
</body>
</html>
- 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 = () => '안녕하세요.';
[DoIt!실습] 화살표 함수 실습하기
- 0202. : Doit 실습 - 화살표 함수 실습하기
- \0201\ex02-02\arrow_function.html
* 코드
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>arrow_function</title>
</head>
<body>
<button onclick="
console.log(fnSayHello());
console.log(`1+2 = `, fnPlusNubers1(1,2));
console.log(`2+3 = `, fnPlusNubers2(2,3));
console.log(`3+4 = `, fnPlusNubers3(3,4));
console.log(`4+1 = `, fnPlusNubers4(4));
" id="clickme">화살표 함수 실행!</button>
<script>
const fnPlusNubers1 = function(pNum1, pNum2){
return pNum1 + pNum2;
}
const fnPlusNubers2 = (pNum1, pNum2) => {
return pNum1 + pNum2;
}
const fnPlusNubers3 = (pNum1, pNum2) => pNum1 + pNum2;
const fnPlusNubers4 = (pNum1) => pNum1 + 1;
const fnSayHello = () => '안녕하세요!';
</script>
</body>
</html>
- 버튼을 만들고 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()
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>json</title>
</head>
<body>
<script>
// JSON.stringify()와 JSON.parse() 함수 사용예시
let myObj = {"name":"홍길동", "age":35, "email":"test1@naver.com"};
strJSON = JSON.stringify(myObj);
console.log(strJSON);
newObj = JSON.parse(strJSON);
console.log(newObj);
</script>
</body>
</html>
특정 환경에서만 발생하는 크롬 브라우저 확장프로그램 오류인것 같은데, 해결이 안된다.
- 오류이후 처리는 정상 진행되니... 일단패스.
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>json</title>
</head>
<body>
<script>
// fetch 사용예시
fetch('https://jsonplaceholder.typicode.com/users')
.then(pResponse => pResponse.json())
.then(pJSON => console.log(pJSON));
</script>
</body>
</html>
- jasonplaceholder 웹 사이트에서 테스트로 제공하는 사용자(users) 정보의 첫 번째값 조회
- 서버에서 결과를 전송하면 Promise 객체에 서버가 응답한 값을 매개변수로 전달받아서 JSON 데이터로 변경한 후 출력
[DoIt!실습] 원격에서 정보를 Promise로 받기
- 0205. : Doit 실습 - 원격에서 정보를 Promise로 받기
> 콜 백 함수보다 Promise를 사용하면 훨씬 직관적이고 간결하다는 것 확인
# 0201/ex02-05/data.json
{
"items":[
{"name":"홍길동1", "age":35, "email":"test1@naver.com"},
{"name":"홍길동2", "age":30, "email":"test2@naver.com"},
{"name":"홍길동3", "age":45, "email":"test3@naver.com"}
]
}
# 0201/ex02-05/promise_getjson.html
<!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()에 전달된 함수를 통해 관리
[DoIt!실습] 여러 개의 Promise 실습하기
- 0207. : Doit 실습 - 여러 개의 Promise 실습하기
# 0201/ex02-07/promise_multi.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>promise_multi</title>
</head>
<body>
<button onclick="
// Promise로 비동기 함수를 순서대로 실행
fnProductReady(1, 2000)
.then((pResult) => {
console.log(pResult);
return fnProductReady(2, 1000);
})
.then((pResult2) => {
console.log(pResult2);
return fnProductReady(3,500);
})
.then((pResult3) => console.log(pResult3));
">Promies : 상품배송시작!</button>
<script>
function fnProductReady(pNum, pTime){
return new Promise((fnResolve) => {
setTimeout(() => {
console.log(pNum);
fnResolve('상품이 성공적으로 배송되었습니다.');
}, pTime);
});
}
</script>
</body>
</html>
- fnProductReady()는 가상으로 상품을 준비하는 비동기 처리 함수
> 처리결과를 동기화 하여 동일한 함수를 2번 더 호출하며 3번의 비동기 호출을 진행 - 정상처리 시 then 처리
- 버튼 클릭 시, 비동기 처리가 각각 2초, 1초, 0.5초로 지연되지만 1 > 2 > 3의 실행 순서 보장
* 비동기 실행의 Promise 결과가 (시간지연과 함께) 1 > 2 > 3 순서대로 콘솔 창에 표시
- 코드보고 설명이 가능할 만큼 이해해보자. (78p)
02-8 await 연산자와 async 비동기 함수
* Promise 한계 : 비동기 처 리를 위해 then() 메서드 중첩 사용
- ES8부터는await 연산자와async 함수 사용
* await 연산자 : 결과가 나올때 까지 기다리다.
- await 연산자의 오른쪽 항을 동기로 처리
> Promise를 반환하는 함수를 대상으로 await 적용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>await</title>
</head>
<body>
<script>
// Promise 비동기 기능 동기처리
function fnProductReady(pNum, pTime){
return new Promise((resolve) => {
setTimeout(() => {
console.log(pNum);
resolve("상품이 성공적으로 배송되었습니다.");
}, pTime);
});
}
// strRet = await fnProductReady(1, 2000);
strRet = fnProductReady(1, 2000);
console.log(strRet);
</script>
</body>
</html>
* 예제 코드 실행결과 오류발생 - 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
* await 연산자 제거시 정상동작
- 다음 예제의 async 구조가 되어야 정상 동작
* await만 사용하면 결과가 나오지 않을 때 무한루프
- 비동기 방식으로 처리하는 밍령이 있는함수는 반드시 async를 실행
* async 함수
- 일반 함수를 선언할 때 앞에 async를 붙여서 비동기 처리 방식으로 선언
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>async</title>
</head>
<body>
<script>
// Promise 비동기 기능 동기처리
function fnProductReady(pNum, pTime){
return new Promise((resolve) => {
setTimeout(() => {
console.log(pNum);
resolve("상품이 성공적으로 배송되었습니다.");
}, pTime);
});
}
async function fnDoAsyncFunc(){
console.log(await fnProductReady(1, 2000)); // 2초 지연
console.log(await fnProductReady(2, 1000)); // 1초 지연
console.log(await fnProductReady(3, 500)); // 0.5초 지연
}
fnDoAsyncFunc();
</script>
</body>
</html>
- async 함수 사용방법 : 일반함수 사용과 동일
> fnDoAsyncFunc();
[DoIt!실습] await 연산자와 async 비동기 함수 실습하기
- 0208. : Doit 실습 - await 연산자와 async 비동기 함수 실습하기
# 0201/ex02-06/promise_resolve_reject.html
* await 연산자와 async 비동기 함수를 사용하면 Promise를를 좀 더 유연하게 활용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>async_await</title>
</head>
<body>
<!-- async 함수 실행 -->
<button onclick="fnDoAsyncFunc()">async, await : 상품 배송 시작!</button>
<script>
// await로 비동기 함수를 순서대로 실행
// Promise 보다 소스가 간결하고 가동성 향상
async function fnDoAsyncFunc(){
console.log(await fnProductReady(1, 2000)); // 2초 지연
console.log(await fnProductReady(2, 1000)); // 1초 지연
console.log(await fnProductReady(3, 500)); // 0.5초 지연
}
//Promise로 비동기 함수 생성
function fnProductReady(pNum, pTime){
return new Promise((resolve) => {
setTimeout(() => {
console.log(pNum);
resolve('상품이 성공적으로 배송되었습니다.');
}, pTime);
});
}
</script>
</body>
</html>
- fnProductReady() 함수 앞에 await 연산자 추가
> async 함수 안에서 await 인산자가 사용된 함수 호출문이 하나씩 실행
> 완료될 때까지 다음 함수 호출하지 않고 대기
> 처리순서 보장 : 1 > 2 >3
- 무한 루프에 빠지지 않도록
> async로 함수 전체를 감싸서 비동기로 처리
- fnProductReady() 함수 처리 시, 상품을 준비하는 동안 시간이 지 연되는 연출을 위해
> 매개변수로 번호, 지연시간 정보를 받아서 Promise를 생성하여 반환
미션 코딩! 입력된 숫자의 범주 판별하기
사용자가 숫자를 입력했을 때 메시지를 표시하는 프로그램
- 1~100 범위에 속하면 "성공적으로 입력하셨습니다!”라는 메시지 표시
- 그렇지 않으면 "입력 범위가 맞지 않습니다!”라는 메시지 표시
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ex02-mission</title>
</head>
<body>
<h1>1부터 100 범위 안의 숫자를 입력하세요.</h1>
<input type="text" id="comIn" value="" />
<button onclick="fnMeasure()">확인</button>
<p>입력 결과 : </p>
<textarea id="comOutPut" style="width:200px"></textarea>
<script>
function fnMeasure(){
let iNumber, strResult;
//입력한 숫자를 가져옴
iNumber = document.querySelector("#comIn").value;
//1부터 100까지 범위인지 확인
if(iNumber < 1 || iNumber > 100) strResult = "입력 범위가 맞지 앖습니다.";
else strResult = "성공적으로 입력하였습니다.";
document.querySelector("#comOutPut").innerHTML = strResult;
}
</script>
</body>
</html>
* github에 작성한 실습파일 패키지경로 수정
- 0104 > 0101/ex01-04
- 0201 > 0102
'book > PWA만들기' 카테고리의 다른 글
[2nd].05 뷰 고급 기능 익히기 (작성중) (1) | 2023.06.07 |
---|---|
[2nd].04 뷰 기초 쌓기 (0) | 2023.05.09 |
[1st].03 순수 자바스크립트로 PWA 만들기 (2) | 2023.05.09 |
[1st].01장 헬로! 프로그레시브 웹앱 (4) | 2023.01.25 |
프로그레시브웹앱만들기 - intro (0) | 2023.01.25 |