반응형

배경

GCP의 airflow에서 특정 DAG의 마지막 batch 시간 start_time을 '현재 시간'과 비교해서,

30분이상 차이가 날 경우, slack으로 알람을 보내줄 것이다. (30분이상 차이가 난다면 배치가 제대로 돌고 있지 않다는 것이므로)

 

 

구현

1. 일단 airflow에서 값을 가져오기 위한 RestAPI를 찾아보자. (버전은 1.10.3이다)

https://airflow.apache.org/docs/apache-airflow/1.10.3/api.html

원하는 API가 보인다. 각 DAG들의 latest DagRun을 리턴해주는 API. 이걸 사용해볼 예정이다.

 

 

2. API로 데이터 가져오기

일단 axios.get으로 API(http://airflow.~.com/api/experimental/latest_runs)를 쏘고, 응답을 확인해본다.

헤더에는 accountAuth 값을 포함해야 하는데, 이는 id:pw를 base64로 인코딩한 값이다.

코드

async function getDagInfoList(siteUri){
const res = await axios.get(siteUri, {
headers: {
Authorization: `Basic ${accountAuth}`
}
})
const dagInfoList = res;
console.log(dagInfoList);
return dagInfoList;
}

 

응답

저기 data:items 부분을 가져오면 될듯하다. data dagInfoList를 res.data로 수정하여 다시 가져와보자.

jane@HMH73K0H9Y jane-repo % node airflow.js
실행
{
  status: 200,
  statusText: 'OK',
  headers: {
    server: 'gunicorn/19.10.0',
    date: 'Sun, 19 Jun 2022 09:21:00 GMT',
    'content-type': 'application/json',
    'content-length': '6248',
    via: '1.1 google',
    connection: 'close'
  },
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
중략
      'user-agent': [Array],
      host: [Array]
    }
  },
  data: {
    items: [
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object]
    ]
  }
}

 

변경 코드 부분

const dagInfoList = res.data;

 

응답

해당 site에 등록된 모든 DAG의 latestRun값의 items 값이 출력된다.

내가 사용할 값은 dag_id=b의 start_date이기 때문에 res를 더 깊은 res.data.items로 수정한다.


jane@HMH73K0H9Y jane-repo % node airflow.js
실행
{
  items: [
    {
      dag_id: 'a',
      dag_run_url: '/admin/airflow/graph?dag_id=a&execution_date=2022-01-03+00%3A00%3A00%2B00%3A00',
      execution_date: '2022-01-03T00:00:00+00:00',
      start_date: '2022-01-04T03:44:50.593295+00:00'
    },
    {
      dag_id: 'b',
      dag_run_url: '/admin/airflow/graph?dag_id=b&execution_date=2022-05-05+18%3A30%3A00%2B00%3A00',
      execution_date: '2022-05-05T18:30:00+00:00',
      start_date: '2022-06-05T18:30:02.388330+00:00'
    },
    {
      dag_id: 'c',
      dag_run_url: '/admin/airflow/graph?dag_id=c&execution_date=2022-06-19+09%3A10%3A00%2B00%3A00',
      execution_date: '2022-06-19T09:10:00+00:00',
      start_date: '2022-06-19T09:15:01.873317+00:00'
중략

 

변경 코드

const dagInfoList = res.data.items;

 

응답

jane@HMH73K0H9Y jane-repo % node airflow.js 
실행
[
  {
    dag_id: 'a',
    dag_run_url: '/admin/airflow/graph?dag_id=a&execution_date=2022-01-03+00%3A00%3A00%2B00%3A00',
    execution_date: '2022-01-03T00:00:00+00:00',
    start_date: '2022-01-04T03:44:50.593295+00:00'
  },
  {
    dag_id: 'b',
    dag_run_url: '/admin/airflow/graph?dag_id=b&execution_date=2022-05-05+18%3A30%3A00%2B00%3A00',
중략

 

3. 내가 원하는 부분의 start_data만 추출하기

위에서 저장한 dagInfoList에서 targetDagId를 찾는 함수다. targetDagId는 전역변수로 미리 설정되어 있으며,

나는 a라는 DagId의 start_date만 필요하기 때문에 이를 return 값으로 가져온다.

const targetDagId = 'a';
function findTargetDagStartTime(dagInfoList, targetDagId){
const TargetDagInfo = dagInfoList.find((daginfo) => daginfo.dag_id === targetDagId);
const startTime = TargetDagInfo.start_date;
return startTime;
}

 

4. timegap 계산 (분 단위 계산)

현재 시간과 target시간을 각각 생성하여 현재 시간에서 target시간을 빼준다.

그리고 이는 정수값이기 때문에 /1000/60을 해서 분 단위의 gap을 찾는다. 여기서 초 단위를 하려면 /60대신 /3600을 하면 된다.

function calculateTimeGap(targetDagStartTime){
const now = new Date();
const TargetTime = new Date(targetDagStartTime);
console.log(`CurrentTime(KST): ${now}`);
console.log(`Target_Time(KST): ${TargetTime}`);

const timeGap = now.getTime() - TargetTime.getTime();
const minTimeGap = timeGap / 1000 / 60;
console.log(`* minTimeGap: ${minTimeGap}\n`);
return minTimeGap;
}

 

5.  timegap이 30분보다 크면 slack으로 메세지 전송

function messageToSlack(uri, minTimeGap){
webhook.send({
text: `${uri}]\n* minTimeGap: ${minTimeGap}\n`
})
}


// maincode
async function start(uri){
const dagInfoList = await getDagInfoList(uri);
console.log('Site: ' + uri);

const targetDagStartTime = findTargetDagStartTime(dagInfoList, targetDagId);
const minTimeGap = calculateTimeGap(targetDagStartTime);

if (minTimeGap > 30) messageToSlack(uri, minTimeGap);
}

 

6.  cron으로 매 초마다 timegap 체크

/*
* test
*/

cron.schedule('* * * * *', () => {
console.log('실행');
start(airflowabcUri); // 나는 두 airflow 사이트 값이 필요해서 두 개의 uri를 전역변수로 만들어줬다.
start(airflowdefUri);
});

 

7. 전체 코드 

const cron = require('node-cron');
const axios = require('axios');
const accountAuth = 'abcdefg';   // base64로 id:pw값을 인코딩해서 넣어줘야 한다.
const targetDagId = 'a';

const { IncomingWebhook } = require('@slack/webhook');
const slackUri = 'https://hooks.slack.com/services/abcdefg';  // webhook 생성 후 나오는 uri
const webhook = new IncomingWebhook(slackUri);

/************************************
* function
*************************************/

async function getDagInfoList(siteUri){
const res = await axios.get(siteUri, {
headers: {
Authorization: `Basic ${accountAuth}`
}
})
const dagInfoList = res.data.items;
console.log(dagInfoList);
return dagInfoList;
}

function findTargetDagStartTime(dagInfoList, targetDagId){
const TargetDagInfo = dagInfoList.find((daginfo) => daginfo.dag_id === targetDagId);
const startTime = TargetDagInfo.start_date;
return startTime;
}

function calculateTimeGap(targetDagStartTime){
const now = new Date();
const TargetTime = new Date(targetDagStartTime);
console.log(`CurrentTime(KST): ${now}`);
console.log(`Target_Time(KST): ${TargetTime}`);

const timeGap = now.getTime() - TargetTime.getTime();
const minTimeGap = timeGap / 1000 / 60;
console.log(`* minTimeGap: ${minTimeGap}\n`);
return minTimeGap;
}

function messageToSlack(uri, minTimeGap){
webhook.send({
text: `${uri}]\n* minTimeGap: ${minTimeGap}\n`
})
}

async function start(uri){
const dagInfoList = await getDagInfoList(uri);
console.log('Site: ' + uri);

const targetDagStartTime = findTargetDagStartTime(dagInfoList, targetDagId);
const minTimeGap = calculateTimeGap(targetDagStartTime);

if (minTimeGap > 30) messageToSlack(uri, minTimeGap);
}

/************************************
* test
*************************************/

cron.schedule('* * * * *', () => {
console.log('실행');
start(airflowAUri);
start(airflowBUri);
});

 

그럼 실행결과는 이렇게 된다.

jane@HMH73K0H9Y jane-repo % node airflow.js
실행
Site: http://airflow.abc.com/api/experimental/latest_runs
CurrentTime(KST): Sun Jun 19 2022 18:52:01 GMT+0900 (대한민국 표준시)
Target_Time(KST): Sun Jun 19 2022 18:47:03 GMT+0900 (대한민국 표준시)
* minTimeGap: 4.967416666666667


Site: http://airflow.def.com/api/experimental/latest_runs
CurrentTime(KST): Sun Jun 19 2022 18:52:01 GMT+0900 (대한민국 표준시)
Target_Time(KST): Sun Jun 19 2022 18:47:01 GMT+0900 (대한민국 표준시)
* minTimeGap: 4.991966666666666

^C

 

slack으로 webhook은 이렇게 들어온다. (gap이 30이 넘을 시에만 메세지가 오게 만들었으나, test를 위해 찍어보았다)

 

반응형
반응형

Axios란?

Axios는 브라우저, Node.js를 위해서 만들어진 Promise API를 활용하는 HTTP 비동기 통신 라이브러리 

 

Axios의 특징

  • Axios는 운영환경에 따라서 브라우저간 XMLHttpRequest 객체 또는 Node.js의 HTTP API를 사용한다.
  • Promise(ES6) API를 사용
  • 요청(Request) 응답 (reply)을 JSON 형태로 자동 변경

 

HTTP 메서드 별칭

편의를 위해 지원되는 모든 요청 메소드에 별칭이 제공됩니다.

axios.get(url[, config])            // GET
axios.post(url[, data[, config]])   // POST
axios.put(url[, data[, config]])    // PUT
axios.patch(url[, data[, config]])  // PATCH
axios.delete(url[, config])         // DELETE

axios.request(config)
axios.head(url[, config])
axios.options(url[, config])

 

Axios 사용 예시

1) GET

axios.get(url,[,config])
// 예로 만든 axios 구조
import axios from "axios"

axios.get('https://localhost:3000/login/user')
  .then((Response)=>{console.log(Response.data)})
  .catch((Error)=>{console.log(Error)})
[
  { id: 1, pw: '12345', name: 'jungho' },
  { id: 2, pw: '12345', name: 'sungJun' },
  { id: 3, pw: '12345', name: 'hyunJi' },
]

응답은 json 형태로 넘어온다.

 

 

 

구성 옵션

사용 가능한 구성(conifg) 설정 옵션입니다.

NOTE

url 속성만 필수이고, 나머지 속성은 옵션입니다. method가 지정되지 않으면 요청은 GET으로 기본 설정 됩니다.

baseURL은 서버의 기본 URL을 의미하며, headers에는 자신이 매번 전달해야하는 객체 (예: 사용자 토큰)를 넣어주시면 자동으로 삽입이 됩니다. URL 경로에는 baseURL의 뒷부분만 삽입을 해도 됨.

{
  // `url`은 요청에 사용될 서버 URL입니다.
  url: '/user',

  // `method`는 요청을 할 때 사용될 메소드 이름입니다.
  method: 'get', // 기본

  // `url` 속성 값이 절대 URL이 아니라면, `url` 앞에 `baseURL`이 붙습니다.
  // axios 인스턴스가 상대 URL을 해당 인스턴스의 메소드에 전달하도록
  // `baseURL`을 설정하는 것이 편리 할 수 ​​있습니다.
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest`는 서버에 보내기 전에 요청 데이터를 변경할 수 있습니다.
  // 요청 메소드 'PUT', 'POST' 및 'PATCH' 에만 적용 가능합니다.
  // 배열의 마지막 함수는 버퍼(buffer)의 문자열이나 인스턴스를 반환해야 합니다.
  // ArrayBuffer, FormData 또는 Stream 헤더 객체를 수정할 수 있습니다.
  transformRequest: [function (data, headers) {
    // 데이터 변환 수행 후, 반환
    // ...
    return data;
  }],

  // `transformResponse`는 응답할 데이터에 대한 변경을 전달해
  // then/catch에 전달하도록 허용합니다.
  transformResponse: [function (data) {
    // 데이터 변환 수행 후, 반환
    // ...
    return data;
  }],

  // `headers`는 서버에 전송 될 사용자 정의 헤더 입니다.
  headers: { 'X-Requested-With': 'XMLHttpRequest' },

  // `params`는 요청과 함께 전송 될 URL 매개 변수입니다.
  // 일반 객체 이거나 URLSearchParams 객체여야 합니다.
  params: {
    ID: 12345
  },

  // `paramsSerializer`는`params`를 직렬화(serializing) 하는 옵션 함수입니다.
  // (예: https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data`는 요청 본문(request body)으로 전송할 데이터입니다.
  // 'PUT', 'POST' 및 'PATCH' 요청 메소드에만 적용 가능합니다.
  // 'transformRequest`가 설정되지 않은 경우 다음 유형 중 하나여야 합니다.
  // - [ string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams ]
  // - 브라우저 전용: FormData, File, Blob
  // - Node.js 전용: Stream, Buffer
  data: {
    firstName: 'Fred'
  },

  // `timeout`은 요청이 타임 아웃되는 밀리 초(ms)를 설정합니다.
  // 요청이`timeout` 설정 시간보다 지연될 경우, 요청은 중단됩니다.
  timeout: 1000, // 기본 값: `0` (타임아웃 없음)

  // `withCredentials`는 자격 증명(credentials)을 사용하여
  // 크로스 사이트 접근 제어(cross-site Access-Control) 요청이 필요한 경우 설정합니다.
  withCredentials: false, // 기본 값

  // `adapter`는 테스트를 보다 쉽게 해주는 커스텀 핸들링 요청을 허용합니다.
  // 유효한 응답(Promise)을 반환해야 합니다. (lib/adapters/README.md 참고).
  adapter: function (config) {
    // ...
  },

  // `auth`는 HTTP 기본 인증(auth)이 사용되며, 자격 증명(credentials)을 제공함을 나타냅니다.
  // 기존의 `Authorization` 커스텀 헤더를 덮어쓰는 `Authorization` 헤더(header)를 설정합니다.
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType`은 서버에서 응답할 데이터 타입을 설정합니다.
  // [ 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' ]
  responseType: 'json', // 기본 값

  // `responseEncoding`은 응답 디코딩에 사용할 인코딩을 나타냅니다.
  // [주의!] 클라이언트 사이드 요청 또는 `responseType`이 'stream'인 경우는 무시합니다.
  responseEncoding: 'utf8', // 기본 값

  // `xsrfCookieName`은 xsrf 토큰(token)에 대한 값으로 사용할 쿠키 이름입니다.
  xsrfCookieName: 'XSRF-TOKEN', // 기본 값

  // `xsrfHeaderName`은 xsrf 토큰 값을 운반하는 HTTP 헤더 이름입니다.
  xsrfHeaderName: 'X-XSRF-TOKEN', // 기본 값

  // `onUploadProgress`는 업로드 프로그래스 이벤트를 처리합니다.
  onUploadProgress: function (progressEvent) {
    // 네이티브 프로그래스 이벤트(Native Progress Event) 처리 코드
    // ...
  },

  // `onDownloadProgress`는 다운로드 프로그래스 이벤트를 처리합니다.
  onDownloadProgress: function (progressEvent) {
    // 네이티브 프로그래스 이벤트(Native Progress Event) 처리 코드
    // ...
  },

  // `maxContentLength`는 HTTP 응답 콘텐츠의 최대 크기를 바이트(Bytes) 단위로 설정합니다.
  maxContentLength: 2000,

 // `validateStatus`는 주어진 HTTP 응답 상태 코드에 대한 약속을 해결할지 거절 할지를 정의합니다.
 // `validateStatus`가`true`를 반환하면 (또는`null`,`undefined`) promise를 resolve 합니다.
 // 그렇지 않으면 promise가 reject 됩니다.
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 기본 값
  },

  // `maxRedirects`는 Node.js에서 리디렉션 가능한 최대 갯수를 정의합니다.
  // 0으로 설정하면 리디렉션이 수행되지 않습니다.
  maxRedirects: 5, // 기본 값

  // `socketPath`는 Node.js에서 사용될 UNIX 소켓을 정의합니다.
  // 예: '/var/run/docker.sock'을 사용하여 docker 데몬에 요청을 보냅니다.
  // `socketPath` 또는`proxy`만이 지정 될 수 있습니다.
  // 둘 다 지정되면`socketPath`가 사용됩니다.
  socketPath: null, // 기본 값

  // `httpAgent`와`httpsAgent`는 각각 Node.js에서 http와 https 요청을 수행 할 때
  // 사용할 커스텀 에이전트를 정의합니다. 이것은 기본적으로 활성화되지 않은 `keepAlive`와 같은
  // 옵션을 추가 할 수 있게 합니다.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // 'proxy'는 프록시 서버의 호스트 이름과 포트를 정의합니다.
  // 기존의 `http_proxy` 및 `https_proxy` 환경 변수를 사용하여 프록시를 정의 할 수도 있습니다.
  // 프록시 설정에 환경 변수를 사용하고 있다면 `no_proxy` 환경 변수를 쉼표로 구분 된 도메인 목록으로
  // 정의하여 프록시 할 필요가 없습니다.
  // 환경 변수를 무시하고 프록시를 사용하지 않으려면 `false`를 설정합니다.
  // `auth`는 HTTP 기본 인증(Basic Auth)를 사용하여 프록시에 연결하고 자격 증명을 제공해야 함을 나타냅니다.
  // 기존의 `Proxy-Authorization` 커스텀 헤더를 덮어쓰는 `Proxy-Authorization` 헤더(header)를 설정합니다.
  proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken`은 요청을 취소하는 데 사용할 수 있는 취소 토큰을 지정합니다.
  // (자세한 내용은 해제(Cancellation) 섹션 참조).
  cancelToken: new CancelToken(function (cancel) {
    // ...
  })

}

 

 

 

 

 

출처: https://koras02.tistory.com/48 [Koras02코딩웹:티스토리]

https://yamoo9.github.io/axios/guide/api.html

 

API | Axios 러닝 가이드

API 구성(configuration) 설정을axios()에 전달하여 요청할 수 있습니다. axios(config) axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }); 1 2 3 4 5 6 7 8 9 axios({ method:'get', url:'http://bit

yamoo9.github.io

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType

 

XMLHttpRequest.responseType - Web APIs | MDN

The XMLHttpRequest property responseType is an enumerated string value specifying the type of data contained in the response.

developer.mozilla.org

"ms-stream" Non-Standard

The response is part of a streaming download; this response type is only allowed for download requests, and is only supported by Internet Explorer.

 

https://velog.io/@yiyb0603/React%EC%97%90%EC%84%9C-axios-%EC%BB%A4%EC%8A%A4%ED%85%80%ED%95%98%EA%B8%B0

반응형
반응형

nvm(node version manager): 여러 버전의 node.js 설치 전환 가능.

. LTS(Long Term Supported) 장기적 안정된 버전. 유지보수 목적의 사용자에게 추천. 짝수 버전 (ex. 8.x.x.)

. Current(현재 버전): 최신 기능 제공, 업데이트 잦고 기능 자주 변경. 개발/테스트에 적당. 홀수 버전 (ex. 9.x.x)

 

npm(node package manager): nodejs에 사용되는 각종 코드 패키지 == 앱스토어st

 

. 설치여부, 버전 조회

$ node -v

  v8.9.4

$ npm -v

  5.6.0

 

. 패키지 매니저로 설치

. macOS - Homebrew (Homebrew: macOS용 패키지 관리자)

$ brew install node@8. // Major number만 입력

$ brew install nvm // nvm 설치

 

. NVM: node.js는 버전이 자주 바뀌어서 버전 관리 매니저 설치 필요.

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash // NVM 설치

    . 설치 완료 후 ~/. bash_profile, ~/.zshrc, ~/.profile 에 nvm.sh이 실행되도록 아래 스크립트가 자동 추가됨.

       export NVM_DIR="$HOME/.nvm"

$ . ~/.nvm/nvm.sh // nvm 활성화

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

 

$ nvm install 16.14.0 // 16.14.0 노드 설치 

$ node -v // 노드 버전 조회

 

$ npm init -y // default 값으로 설정된 package.json을 만듦 

(index.js 만들면 js script 들어오고. main으로 이걸 실행시키겠다. 라고 pakcage.json 들어옴. )

 

 

. 원하는 nvm version 설치

$ nvm install <version> // nvm install 8.9.4

 

. 설치된 Node 버전 목록 확인

$ nvm ls

 

. 사용할 Node 설정

$ nvm use <version> // nvm use 8.9.4

$ nvm use <alias> // nvm use default

 

 

. package.json (프로젝트 정보와 의존성을 관리하는 문서)

어느 곳에서도 동일한 개발 환경을 구축할 수 있게 해줌. json format.

 

{

  "name": "project-name",

  "version": "1.0.0",

  "keywords": [], // npm search 사용 시 도움됨

  "description": "",   // 프로젝트 (패키지) 설명. npm search 시 도움됨

  "main": "index.js",  // 프로그램의 기본 진입 점(entry point)

  "scripts": {. // pkg 라아프 사이클에서 여러 번 실행되는 스크립트 명령. 

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "author": "HEROPY",

  "license": "MIT",

  "dependencies": {},       // 패키지 배포 시 포함될 의존성 모듈 지정

  "devDependencies": {}  // 개발 시 사용될 의존성 모듈 (배포 시는 포함되지 않음) 

  “files” // 패키지가 의존성으로 설치될 때 같이 포함된 파일들의 배열. 생략하면 모든 파일이 포함됨.

  “bugs”: { } // 패키지에 문제가 있을 때 보고될 이슈 트래커 및 이메일 주소 등에 대한 url

  “repository”: {} // 코드가 존재하는 장소. github 주소 지정 

} 

 

// package-lock.json: 개발 환경에서 내부 모듈 변경(ex. 버전)이 있을 경우, 

이를 방지하기 위해 package.json을 수정하는 모든 작업에 대해 package-lock.json이 자동으로 생성됨. 

 

. 의존성 모듈 설치

$ npm install <package>@<version>

 

. 전역으로 모듈 설치

$ npm install -g webpack  // -g 플래그

 

. 오래된 패키지 확인

$ npm outdated     // 설치된 패키지가 구형인지 확인하기 위해 레지스트리 검사

 

. 패키지를 최신 버전으로 업데이트

$ npm update <package>

$ npm upgrade   // pkg 입력하지 않으면 지정된 위치의 모든 패키지 업데이트

 

. 패키지 제거

 $ npm uninstall <pkg>

 $ npm remove

 $ npm unlink

 

. dockerfile

FROM node:12-alpine

WORKDIR /usr/src/app

MAINTAINER Jane Baek

RUN mkdir -p /usr/src/app

ADD . /usr/src/app

RUN npm install --silent

 

반응형
반응형

1. JSON.parse와 JSON.stringify 차이

 - JSON.parse: JSON 텍스트 문자열을 JavaScript 객체로 변환

 - JSON.stringify: js스크립트 객체를 JSON 텍스트로 변환하고 해당 JSON 텍스트를 문자열에 저장
 - toString(): 문자열로 변환

 

 - JSON.parse: JSON 형태의 객체로 변환

var my_object = { key_1: "some text", key_2: true, key_3: 5 };
var object_as_string_as_object = JSON.parse(object_as_string);  // {key_1: "some text", key_2: true, key_3: 5} 
typeof(object_as_string_as_object);          // "object" 

 

- JSON.stringify: JSON 형태의 문자열(string)로 변환

구문: JSON.stringify(value[, replacer[, space]])

 . value: JSON 문자열로 변환할 값.

 . replacer: json 문자열에 포함되어야 하는 객체의 속성. null or 제공되지 않으면, 객체의 모든 속성들이 포함됨.

                   (array 방식이면 array만 포함됨)

 . space: 가독성을 목적으로 문자열 출력에 공백을 삽입하는 객체 (=공백으로 사용되는 스페이스 수. \t도 사용됨.)

var my_object = { key_1: "some text", key_2: true, key_3: 5 };
var object_as_string = JSON.stringify(my_object);   // "{"key_1":"some text","key_2":true,"key_3":5} 
typeof(object_as_string);              // "string"

 

- 실습: s3에 저장된 json 파일을 하나 불러와서 JSON.parse, JSON.stringfy, string 형태로 각각 변환 후 출력.

 

- JSON_parse: 그 자체로 객체로 출력된다.

- JSON_stringify: JSON 형태의 문자열로 출력한다

- tostring: 전체를 문자열로 출력한다.

 

반응형
반응형

 

* 결론: '=='는 '값'만 비교하는 반면, '==='는 '값' 및 '자료형'을 동시에 비교하는 더 strict한 연산자이다.

구글링 중에 설명이 정말 잘 되어 있는 블로그가 있어서 내용을 그대로 가져왔다. (fileoscoder님 감사합니다!)

출처: https://velog.io/@filoscoder/-%EC%99%80-%EC%9D%98-%EC%B0%A8%EC%9D%B4-oak1091tes

  • ☝ '==' 연산자를 이용하여 서로 다른 유형의 두 변수의 [값] 비교
  • ✌ '==='는 엄격한 비교를 하는 것으로 알려져 있다 ([값 & 자료형] -> true).

 

 

🔍 간단 비교

✔ 0값은 false와 동일하므로 -> true 출력

0 == false // true 

✔ 두 피연산자의 유형이 다르기 때문에 ->false

0 === false // expected output: false 
console.log(typeof 0); 	// expected output: "number"
console.log(typeof false); // expected output: "boolean"

 

🔍 숫자와 문자열 비교

✔ 자동 유형변화 비교

2 == "2" // expected output: true

✔ 두 피연산자의 유형이 다르기 때문에 ->false

2 === "2" // expected output: false 
console.log(typeof 2);	// expected output: "number"
console.log(typeof "2");  // expected output: "string"

 

🤔 #궁굼한 케이스들

🔍 1) null 와 undefined 비교했을 때 어떤 결과가 나올 것인가?

✔ 자동 유형변화 비교

null == undefined // expected output: true

✔ 두 피연산자의 유형이 다르기 때문에 ->false

null == undefined // expected output: false 
console.log(typeof null);	// expected output: "object"
console.log(typeof undefined);  // expected output: "undefined"

 

 

🕺 변수를 비교하거나 어떤 비교를 위해 항상 '===' 연산자를 사용 할 것을 권장한다.

💃 가능한 '==' 연산자를 사용하지 않도록 하고, 대신 직접 자료형을 변환하여(casting) 보다 코드 가독성을 높이도록 한다.

 

 

 

자바스크립트: '==' 와 '===' 는 다르다!

무엇이 다른가? 저는 자바 언어를 먼저 배운 입장에서 자바스크립트에 깊게 노출되지 않았기 때문에 충격을 받았다. 비록 자바스크립트의 어떤 기능, 사건 처리, 그리고 어떤 jQuery 속임수에 상

velog.io

반응형
반응형

배경 설명

lambda에 S3를 연동시켜 두고, 해당 S3에 이벤트가 있을 때마다 람다를 실행시키는 코드를 해석해본다.

 

코드

const aws = require('aws-sdk');
const fs = require('fs');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });


exports.handler = async(event, context,  callback) => {
    const bucket = event.Records[0].s3.bucket.name;

    console.log(event);   // 실행결과 테스트 1번
    console.log(event.Records);  // 실행결과 테스트 2번
    console.log(event.Records[0]);  // 실행결과 테스트 3번

    console.log(bucket); // 그럼 이건 .s3.bucket.name인 jane-bkt만 출력됨.
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); // 여기부턴 실행결과 2로 이동
    const params = {
        Bucket: bucket,
        Key: key,
    };
    var theObject = await s3.getObject(params).promise();
    const data = JSON.parse(theObject.Body);
    console.log(data);
    // console.log(theObject.Body.toString('utf8'));  // 위와 결과값이 같지만, 보기 좋은 형태로
        try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

 

 

실행 결과 1

1) console.log(event)

Function Logs
START RequestId: a4ff7ef5-b647-40b7-9e6d-8eb2fb95360f Version: $LATEST
2022-05-22T02:23:56.937Z a4ff7ef5-b647-40b7-9e6d-8eb2fb95360f INFO {
  Records: [
    {
      eventVersion: '2.0',
      eventSource: 'aws:s3',
      awsRegion: 'us-east-1',
      eventTime: '1970-01-01T00:00:00.000Z',
      eventName: 'ObjectCreated:Put',
      userIdentity: [Object],
      requestParameters: [Object],
      responseElements: [Object],
      s3: [Object]
    }
  ]
}

 2) console.log(event.Records);  // 실행결과 테스트 2번. 좀 더 자세히 나온다.

Function Logs
START RequestId: 35e8439b-4ac0-44fa-a006-85a3fdd6d4aa Version: $LATEST
2022-05-22T02:27:20.107Z 35e8439b-4ac0-44fa-a006-85a3fdd6d4aa INFO [
  {
    eventVersion: '2.0',
    eventSource: 'aws:s3',
    awsRegion: 'us-east-1',
    eventTime: '1970-01-01T00:00:00.000Z',
    eventName: 'ObjectCreated:Put',
    userIdentity: { principalId: 'EXAMPLE' },
    requestParameters: { sourceIPAddress: '127.0.0.1' },
    responseElements: {
      'x-amz-request-id': 'EXAMPLE123456789',
      'x-amz-id-2': 'EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH'
    },
    s3: {
      s3SchemaVersion: '1.0',
      configurationId: 'testConfigRule',
      bucket: [Object],
      object: [Object]
    }
  }
]


 3) console.log(event.Records[0]);  // 테스트 3번. 테스팅 json에서 언급한 jane-bkt, s3test.json을 포함한 결과값이 나온다.

Function Logs
START RequestId: cda13909-6eab-410a-9582-dc00d25732f2 Version: $LATEST
2022-05-22T02:28:47.364Z cda13909-6eab-410a-9582-dc00d25732f2 INFO {
  eventVersion: '2.0',
  eventSource: 'aws:s3',
  awsRegion: 'us-east-1',
  eventTime: '1970-01-01T00:00:00.000Z',
  eventName: 'ObjectCreated:Put',
  userIdentity: { principalId: 'EXAMPLE' },
  requestParameters: { sourceIPAddress: '127.0.0.1' },
  responseElements: {
    'x-amz-request-id': 'EXAMPLE123456789',
    'x-amz-id-2': 'EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH'
  },
  s3: {
    s3SchemaVersion: '1.0',
    configurationId: 'testConfigRule',
    bucket: {
      name: 'jane-bkt',
      ownerIdentity: [Object],
      arn: 'arn:aws:s3:::jane-bkt'
    },
    object: {
      key: 's3test.json',
      size: 1024,
      eTag: '0123456789abcdef0123456789abcdef',
      sequencer: '0A1B2C3D4E5F678901'
    }
  }
}

 

 

 

다시 코드

   const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); // 여기부턴 실행결과 2로 이동
   console.log(event.Records[0].s3.object.key.replace(/\+/g.''));  //  object.key에 +제거(/+) 및 전역으로 확장(/g) 

    const params = {
        Bucket: bucket,
        Key: key,
    };
    var theObject = await s3.getObject(params).promise();
    console.log(theObject);  // 결과 1)
    const data = JSON.parse(theObject.Body);
    console.log(data);
    // console.log(theObject.Body.toString('utf8'));  // 위와 결과값이 같지만 보기 좋은 형태로
        try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

 

실행결과 2

1)  console.log(theObject);  // 결과 1)

Function Logs
START RequestId: ed9a9525-9994-48da-8834-16ec0ab5c2d6 Version: $LATEST
2022-05-22T02:41:07.808Z ed9a9525-9994-48da-8834-16ec0ab5c2d6 INFO {
  AcceptRanges: 'bytes',
  LastModified: 2022-05-20T02:49:10.000Z,
  ContentLength: 80,
  ETag: '"5704c18c8279716da4b82c028dddb5d4"',
  ContentType: 'application/json',
  Metadata: {},
  Body: <Buffer 7b 0a 20 20 22 6e 61 6d 65 22 3a 20 22 4a 61 6e 65 22 2c 0a 20 20 22 61 67 65 22 3a 20 22 32 38 22 2c 0a 20 20 22 63 6f 6d 70 61 6e 79 22 3a 20 22 53 ... 30 more bytes> 
// 이 body를 아래 코드에서 JSON으로 파싱 후 출력함.
 const data = JSON.parse(theObject.Body);

}

 

Note

1) try - catch: 에러 핸들링 코드 (출처:https://ko.javascript.info/try-catch)

  1. try { } 안의 코드가 먼저 실행된다.
  2. 에러가 없다면, try 안의 마지막 줄까지 실행, catch 블록은 건너뜀
  3. 에러가 있다면, try 안 코드의 실행이 중단, catch(err) 블록으로 제어 흐름이 넘어감. 변수 err(아무 이름이나 사용 가능)는 무슨 일이 일어났는지에 대한 설명이 담긴 에러 객체를 포함. 
  try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        console.log('********** Event generated by S3 ************* ');
        return ContentType;
        
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);  // 에러 던지기.
    }
};

 

- event: 호출상황에 대한 정보.

- context: 실행되는 함수 환경에 대한 정보 

- callback: 응답을 전송하기 위한 함수. (Error 및 응답을 사용)

      -> 이벤트 루프가 비워질 때까지 기다린 다음, 응답이나 오류를 호출자에게 반환. 

- async: 비동기 핸들러를 sync 맞춰주기 위함. 

반응형

+ Recent posts