23.7.25 - 23.7.26 TIL

김진주·2023년 7월 26일
0

TJL(Today Jinju Learned)

목록 보기
27/35

23.7.25

ajax

모든 웹은 다 동기적으로 작동한다.

sync (동기)

'직렬적'으로 작동하는 방식을 말한다.

ex)
우리가 무수히 쌓인 메일함에서 2페이지에 있는 메일들을 보고싶은 상황이다.
header - 사용자 정보 - 메일목록 - footer로 구성되어 있는 화면에서 다음 페이지로 넘어갈 때 전체 화면이 깜빡 거리는 것을 보게 된다면 전체 페이지를 reload 하는 동기 통신이다.
동기 통신은 메일함의 메일 목록만 바뀌길 원해도 모든 페이지다 reload 된다.

async (비동기)

비동기는 '병렬적'으로 작동하는 방식이다.

사용자가 서버에 요청을 하면 브라우저에 내장된 Ajax Engine에 요청을 보낸다.
Ajax가 필요한 데이터만 JSON DATA로 서버에 요청을 보낸다.
서버는 원하는 데이터를 찾아서 Ajax Engine에게 보내고 화면에 랜더링 해준다.
전체가 로딩되지 않고 원하는 부분만 바꿔차가 할 수 잇다.
사용자가 받은 자료들만 뿌려준다.
<이듬 범쌤 자료>

REST API (REpressentataional State Transfer)

<이듬 범쌤 자료>

CRUD

CRUD는 데이터베이스와 관련된 기본적인 작업을 의미하는 약어로서,
Create(생성), Read(읽기), Update(수정), Delete(삭제)의 네 가지 작업을 포함하고,
데이터를 관리하는데 필요한 가장 기본적인 작업들을 나타낸다.
HTTP 메소드 - GET / POST / UPDATE / DELETE
웹에서 데이터를 전송하기 위해서 프로토콜 내에서 주어진 약속이다

  • Create 작업은 새로운 데이터를 생성하므로 HTTP POST 메서드와 연관
  • Read 작업은 데이터를 조회하므로 HTTP GET 메서드와 연관.
  • Update 작업은 데이터를 수정하므로 HTTP PUT(리소스 전체 수정) 또는 PATCH(리소스 일부 수정)메서드와 연관
  • Delete 작업은 데이터를 삭제하므로 HTTP DELETE 메서드와 연관

API 요청

서버와 정보를 주고 받을 때 양식없이 막무가내로 데이터를 전송하면 안 된다.
VS Code 확장 프로그램인 Thunder Client를 사용하여 API 요청 결과를 확인할 수 있다.

아직 우린 서버가 없기 때문에 https://jsonplaceholder.typicode.com/users 이 데이터를 사용했다.

여기서 Headers는 통신할 때 사용하는 설명서 같은 것이다.


xhr

코드 패턴

open -> event -> send


[readystate]

0: uninitialized // open 위에 출력했을 때
1: loading
2: loaded      // 2, 3, 4는 
3: interactive // 그냥 console.log(xhr.readyState)로는 확인 불가능
4: complete    // onreadystatechange 이벤트 안에서 확인 가능
const xhr = new XMLHttpRequest();

➡️ xhr을 출력하면 위와 같은 객체가 나온다.

xhr.addEventListener('readystatechange', () => {
// 정보를 잘 가져오든 못 가져오든 상태가 변경되는 시점을 'readystatechange'가 체크 해준다.
    }

크롬에서의 api 요청 확인 방법

F12 -> 네트워크 -> fetch/XHR -> 헤더
성공여부를 확인할 수 있다.

값을 넣고 잘 전달됐는지 확인해 보았다.

function xhr(method, url, onSuccess, onFail, body , headers) {
  const xhr = new XMLHttpRequest(); 
  xhr.open(method, url); // GET 통신 , 통신 할 URL

  Object.entries(headers).forEach(([key, value]) => {
    xhr.setRequestHeader(key,value)
  })

  xhr.addEventListener('readystatechange', () => { 
    const { status, readyState, response } = xhr;
    if (readyState === 4) {
      
      if (status >= 200 && status < 400) {
        // 서버에서 받아온 코드를 받아오려면 콜백함수를 사용해줘야 한다.
        onSuccess(JSON.parse(response)); // 변환해서 사용해 줘야 함
      } else {
        onFail('실패');
      }
    }
  });

  xhr.send(JSON.stringify(body));
}


xhr( // post, put에서는 body 전달
  'POST',
  'https://jsonplaceholder.typicode.com/users',
  (result) => {
    console.log(result);
  },
  (err) => {
    console.log(err);
  },
  {
    name: 'tiger',
    age: 12
  },
  {
    'Content-TYpe': 'application/json',
    'Access-Contols-Allow-Origin': '*' // 이 부분이 아래 결과
  }
);

값이 잘 들어간 걸 확인 할 수 있다.


but❗❗❗
이렇게 만들어서 사용할 수도 있지만
항상 순서를 맞춰 사용하기엔 불편할 것이기 때문에 인수를 객체의 형태로 보내
순서에 상관없이 편하게 사용하도록 변경해 주었다.

function xhr({
  // 파라미터를 바로 객체구조분해 할당으로 넣기
  method = 'GET',
  url = '',
  onSuccess = null,
  onFail = null,
  body = null,
  headers = {
    'Content-Type': 'application/json',
    'Access-Contols-Allow-Origin': '*',
  },
} = {}) {

  const xhr = new XMLHttpRequest(); 
  xhr.open(method, url); // GET 통신 , 통신 할 URL
// headers를 객체로 받았기 때문에 Object.entries로 배열을 만들고 키, 값을 배열의 구조분해할당을 통해 set~에 넣어줌
  Object.entries(headers).forEach(([key, value]) => {
  /*
  setRequestHeader('Content-Type', 'application/json')

  이 메서드는 서버에 요청을 보낼 때, 요청의 헤더를 원하는 양식에 맞게 설정하는 데 도움준다.
  XMLHttpRequest 객체에 HTTP 요청의 헤더를 설정하는 데 사용되는 메서드 
  HTTP 요청을 보낼 때, 헤더는 요청에 대한 추가 정보를 담고 있다. 
  이 메서드를 사용하여 원하는 헤더를 설정할 수 있다.
  */
    xhr.setRequestHeader(key, value);
  });

  xhr.addEventListener('readystatechange', () => {
    const { status, readyState, response } = xhr;
    if (readyState === 4) {
      if (status >= 200 && status < 400) {
        onSuccess(JSON.parse(response)); 
      } else {
        onFail('실패');
      }
    }
  });
  xhr.send(JSON.stringify(body));
}

xhr({
  // post, put에서는 body 전달
  method: 'GET', // 기본값으로 설정해줬기 때문에 생략 가능
  url: 'https://jsonplaceholder.typicode.com/users',
  onSuccess: (result) => {
    console.log(result);
  },
  onFail(err) { // 일반 함수 축약형
    console.log(err);
  },
   headers: {  // 기본값으로 설정해줬기 때문에 생략 가능
     'Content-Type': 'application/json',
     'Access-Contols-Allow-Origin': '*', // 이거 이후에 CORS에러가 발생한다면 서버에서 해결해 줘야 함
   },
});

Access-Contols-Allow-Origin

내가 하나의 도메인 값을 사용했으면 보안 상의 이슈로, 또 다른 도메인의 값을 사용하지 못하게 에러가 표시되는
동일 출처 정책(SOP) 는 "Same-Origin Policy"의 약자로, 웹 브라우저에서 보안을 강화하기 위해 적용되는 정책이다.
이 정책은 웹 페이지의 자바스크립트나 XMLHTTPRequest와 같은 리소스들이 동일 출처(origin)에서만 요청하도록 제한하는 보안 메커니즘이다.
출처(origin) - 프로토콜, 호스트, 포트 번호로 구성된 URL

프론트엔드가 사용할 수 있는 해결 방법

'Access-Contols-Allow-Origin': '*'

Access-Contols-Allow-Origin를 사용하여 해결할 수 있다.
이걸로도 해결되지 않는다면 그 쪽 서버에서 막은 것이기 때문에 사용할 수 없다.
물론 우회하는 방법을 사용할 수 있겠지만, 보장되지 않은 방법이기 때문에 사용할 때 고려를 해야 한다.

⭐ 읽어보기! mdn 교차 출처 리소스 공유 (CORS)

메서드로 만들기

매번 객체로 작성하는 것도 힘들어서 xhr의 메서드로 만들었다. XMLHttpRequest 객체를 이용하여 HTTP GET, POST, PUT, DELETE 요청을 보내기 위한 헬퍼 함수들을 정의
// 1. 자바스크립트의 함수는 객체다
// 2. 사용자(협업개발자) 입장: 쉽게 쓰자
// 3. 설계자 -> 함수 안에 메서드(객체)를 넣어버리자!!

// shorthand property
xhr.get = (url, onSuccess, onFail) => {
  xhr({
    url,
    onSuccess,
    onFail,
  });
};

xhr.post = (url,body, onSuccess, onFail) => {
  xhr({
    method:"POST",
    url,
    body,
    onSuccess,
    onFail,
  });
};

xhr.put = (url,body, onSuccess, onFail) => {
  xhr({
    method:"PUT",
    url,
    body,
    onSuccess,
    onFail,
  });
};

xhr.delete = (url, onSuccess, onFail) => {
  xhr({
    method:"DELETE",
    url,
    onSuccess,
    onFail,
  });
};

이렇게 사용하면 데이터 타입이 정의되어 있지 않기 때문에 어떤 값을 넣어햐 하는지 다른 사람은 알 수 없다.
그래서 사용할 수 있는 방법이 JSDoc이다. 양식을 맞춰서 들어갈 데이터타입, 인수명과 설명을 적어주면 다음과 같이 바뀐다.
하지만 JSDoc을 사용하는 것도 강제하기보단 설명서같은 역할이다.


23.7.26

promise

Promise는 비동기 작업의 결과를 다루기 위해 사용되는 객체로, 비동기 작업이 완료되면 결과를 처리하거나 에러를 처리하는데 사용된다.


promise의 resolve의 반환값은
then의 result의 parameter로 자동전달된다

Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "성공!"

delayP( ) === 프라미스 객체
프라미스를 리턴하게 되면 프라미스 객체가 리턴됨

then / catch

  • promise.then()은 자바스크립트의 Promise 객체를 사용할 때 주로 쓰이는 메서드 중 하나이다.
    • then() 메서드는 Promise가 성공적으로 해결되었을 때 또는 실패했을 때 각각에 대해 처리를 정의한다.
      then() 메서드는 두 개의 콜백 함수를 인자로 받습니다.
    • 첫 번째 인자는 Promise가 성공적으로 해결되었을 때 실행되는 함수이고,
    • 두 번째 인자는 Promise가 실패했을 때 실행되는 함수이다. 두 번째 인자는 생략이 가능하고 catch에서 처리해 줄 수 있다.
promise.then(
	function(result) { /* 결과(result)를 다룹니다 */ },
	function(error) { /* 에러(error)를 다룹니다 */ }
);

⭐ then을 사용할 수 있는 이유 => 프라미스를 리턴하면 객체를 리턴해주기 때문에

  • catch() try/catch의 그것과 유사하고 에러를 전담마크한다.
promise.then().catch	
let promise = new Promise((resolve, reject) => {
	setTimeout(() => reject(new Error("에러 발생!")), 1000);
});		
// .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력

promise의 reject의 반환값도
then의 err의 parameter로 자동전달된다.

Object mixin

🏷️mixin을 사용하는 이유
우리가 객체를 전달해줘서 어떤 값을 처리해야 할 때 객체에게 모든 걸 전달해주기 힘든 경우가 생기게 된다.
지금은 5개 밖에 안 될지도 모르지만 나중에 15개정도 된다고 가정했을 때 내가 필요한 것만 한 두개만 골라 쓰고 싶은 경우가 생길 수도 있다.
그래서 많이 사용되는 값들은 미리 정해놓고 사용하게 할 수 있다.
구조분해할당과 차이점
믹스인은 재사용이 가능하다.

const defaultOptions = { // 기본 옵션값
  shouldReject: false,
  timeout: 1000,
  data: '성공!',
  errorMessage: '알 수 없는 오류가 발생했습니다.',
};

function delayP(options) { // 인자로 받은 새로운 옵션 객체
  let config = { ...defaultOptions }; // 객체는 할당만 하면 참조에 의한 복사만 일어나기 때문에 얕은 복사 필요

  if (typeof options === 'number') {
    config.timeout = options;
  }

  if (typeof options === 'object') {
    config = { ...defaultOptions, ...options }; // 객체 믹스인
  }

  const { shouldReject, data, errorMessage, timeout } = config;

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!shouldReject) {
        resolve(data);
      } else {
        reject({ message: errorMessage });
      }
    }, timeout);
  });
}

xhrPromise

위에서 콜백함수로 만든 비동기통신 함수를 promise를 사용하여 리팩토링

function xhrPromise(options) {

  // {} 필수! 안넣으면 디폴트옵션에 덮여씌여짐
  /* 
   Object.assign() 메서드는 여러 개의 객체를 합치는데 사용되며,
   첫 번째 인자에 빈 객체 {}를 전달하면 새로운 객체를 생성하면서 
   다른 객체들의 속성들을 그대로 복사할 수 있습니다.
  */
  const { method, url, body, errorMessage, headers } = Object.assign( {}, defaultOptions, options ); 
  const xhr = new XMLHttpRequest();

  if(!url) refError('서버와 통신할 url은 필수값 입니다');

  xhr.open(method, url);

  Object.entries(headers).forEach(([key, value]) => {
    xhr.setRequestHeader(key, value);
  });

  xhr.send(JSON.stringify(body));

  return new Promise((resolve, reject) => {
    xhr.addEventListener('readystatechange', () => {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 400) {
          resolve(JSON.parse(xhr.response));
        } else {
          reject({ message: '서버와 통신이 원활하지 않습니다' }); //? 객체로 사용하는 이유
        }
      }
    });
  });
}

// xhrPromise({
//   url: 'https://jsonplaceholder.typicode.com/users'
// })
// .then((res) => {
//   console.log(res);
// });


xhrPromise.get = (url) => {
  return xhrPromise({
    url,
  });
};

xhrPromise.post = (url,body) => {
  return xhrPromise({
    method: 'POST',
    url,
    body
  });
};

xhrPromise.put = (url,body) => {
  return xhrPromise({
    method: 'PUT',
    url,
    body
  });
};

xhrPromise.delete = (url) => {
  return xhrPromise({
    method: 'DELETE',
    url,
  });
};

포켓몬 api를 사용하여 이미지 로드해보기

async function getData(){
  const data = xhrPromise.get('https://pokeapi.co/api/v2/pokemon/25') // 프라미스 객체
  console.log(data);

  // then 방식
  data.then((res) => {
    // console.log(res);
  })
  // await 방식
  let pokemon =  await data
  console.log(pokemon.sprites['front_default']); // pokemon.sprites['front_default'] 이미지 주소

  insertLast(document.body, `<img src="${pokemon.sprites['front_default']}" alt="" />`)
  // console.log(data);
}

getData()


async await

async
function 앞에 async를 붙이면 해당 함수는 항상 프라미스를 반환한다.

async function delayA() {
  return '성공';
}
const data =  delayA();
console.log(data);

await
⭐ await promise 객체 앞에서만 사용할 수 있다
그래야 코드의 실행 흐름이 멈추고 result 값을 할당해서 사용할 수 있다.

async function delayA() {
  return '성공'
}

console.log( await delayA());

프라미스 객체에 await 를 사용하면 promiseResult 값이 나온다

fetch

xhr 보다 훨어어얼씬 쉽다
fetch()를 호출하면 브라우저는 네트워크 요청을 보내고 프라미스가 반환된다.

fetch() 기본 문법

let promise = fetch(url, [options])
  • url – 접근하고자 하는 URL
  • options – 선택 매개변수, method나 header 등을 지정할 수 있음
    options에 아무것도 넘기지 않으면 GET 요청이 일어난다.

fetchawait 두 번 호출해야 한다..
1. 통신 정보
2. 정보 값

  • fetch만 사용했을 때 - fetch는 프로미스 객체를 반환한다.
  • fetch & await 사용했을 때 - 통신 정보에 대한 값 반환 (result는 fetch의 ok/status등을 담고있는 객체다)
    • 이때 값 역시 객체이고 okstatus를 이용하여 HTTP 상태를 알 수 있다.
  • fetch & await & await 사용했을 때 - 우리가 원하는 정보를 얻을 수 있다.
    • const responseData = await response.json(); 에서 .json()은 응답을 JSON 형태로 파싱해준다.
const URL = 'https://jsonplaceholder.typicode.com/users';

const defaultOptions = {
  method: 'GET',
  body: null,
  headers: {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
  },
};

const defaultOptions = {
  method: 'GET',
  body: null,
  headers: {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
  },
};

export const pearl = async (options) => {

  const {url, ...restOptions} = {
    ...defaultOptions,
    ...options,
    headers: { // 깊은 복사가 필요
      ...defaultOptions.headers,
      ...options.headers
    }
  }
  const response = await fetch(url, restOptions); // 상태정보를 담고 있음
  //& console.log(response);
  if (response.ok) {
    //% response.json() => 프라미스 객체로 반환된다. 
    response.data = await response.json(); // 객체 안에 데이터 키를 만들어서 정보를 저장
    //&  console.log(response.data);
  }

  return response;
};

//# 메서드로 만들어서 사용  
pearl.get = (url, options) => {
  return pearl({
    url,
    ...options
  })
}

pearl.post = (url, body ,options) => {
  return pearl({
    method: 'POST',
    url,
    body: JSON.stringify(body),
    ...options,
  })
}

pearl.put = (url, body, options) => {
  return pearl({
    method: 'PUT',
    url,
    body: JSON.stringify(body),
    ...options,
  })
}

pearl.delete = (url, options) => {
  return pearl({
    method: 'DELETE',
    url,
    ...options,
  })
}
profile
진주링딩동🎵

1개의 댓글

comment-user-thumbnail
2023년 7월 26일

좋은 글 감사합니다.

답글 달기